Merge "IMS: Avoid to set CLIR after click cancel Fixes: 131474029 Change-Id: I55aa18685746145fc478a5b03d8b887cfbc1281d"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..76e910f
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,82 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// 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.
+
+// Build the Phone app which includes the emergency dialer. See Contacts
+// for the 'other' dialer.
+
+android_app {
+    name: "TeleService",
+
+    libs: [
+        "telephony-common",
+        "voip-common",
+        "ims-common",
+        "org.apache.http.legacy",
+        "libprotobuf-java-lite",
+        "unsupportedappusage",
+    ],
+
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.preference_preference",
+        "androidx.recyclerview_recyclerview",
+        "androidx.legacy_legacy-preference-v14",
+        "guava",
+        "volley",
+        "android-support-annotations",
+        "com.android.phone.common-lib",
+    ],
+
+    srcs: [
+        ":framework-telephony-stack-shared-srcs",
+        "src/**/*.java",
+        "sip/src/**/*.java",
+        "ecc/proto/**/*.proto",
+        "src/com/android/phone/EventLogTags.logtags",
+    ],
+
+    jarjar_rules: ":jarjar-rules-shared",
+
+    resource_dirs: [
+        "res",
+        "sip/res",
+    ],
+
+    asset_dirs: [
+        "assets",
+        "ecc/output",
+    ],
+
+    aaptflags: [
+        "--extra-packages com.android.services.telephony.sip",
+    ],
+
+    platform_apis: true,
+
+    certificate: "platform",
+    privileged: true,
+
+    optimize: {
+        proguard_flags_files: [
+            "proguard.flags",
+            "sip/proguard.flags",
+        ],
+    },
+
+    defaults: ["SettingsLibDefaults"],
+
+    proto: {
+        type: "lite",
+    },
+}
\ No newline at end of file
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 04c37e6..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,57 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Phone app which includes the emergency dialer. See Contacts
-# for the 'other' dialer.
-include $(CLEAR_VARS)
-
-phone_common_dir := ../../apps/PhoneCommon
-
-src_dirs := src $(phone_common_dir)/src sip/src
-res_dirs := res $(phone_common_dir)/res sip/res
-asset_dirs := assets ecc/output
-
-LOCAL_JAVA_LIBRARIES := \
-        telephony-common \
-        voip-common \
-        ims-common \
-        org.apache.http.legacy \
-        libprotobuf-java-lite
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        androidx.appcompat_appcompat \
-        androidx.preference_preference \
-        androidx.recyclerview_recyclerview \
-        androidx.legacy_legacy-preference-v14
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-        guava \
-        volley \
-        android-support-annotations
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_SRC_FILES += $(call all-proto-files-under, ecc/proto)
-LOCAL_SRC_FILES += \
-        src/com/android/phone/EventLogTags.logtags
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-LOCAL_ASSET_DIR := $(addprefix $(LOCAL_PATH)/, $(asset_dirs))
-LOCAL_USE_AAPT2 := true
-
-LOCAL_AAPT_FLAGS := \
-    --extra-packages com.android.phone.common \
-    --extra-packages com.android.services.telephony.sip
-
-LOCAL_PACKAGE_NAME := TeleService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags sip/proguard.flags
-
-include frameworks/base/packages/SettingsLib/common.mk
-
-include $(BUILD_PACKAGE)
-
-# Build the test package
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c4c33ca..1d32288 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -36,7 +36,6 @@
     <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.intent.action.NETWORK_SET_TIME" />
-    <protected-broadcast android:name="android.intent.action.NETWORK_SET_TIMEZONE" />
     <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" />
@@ -50,7 +49,7 @@
     <protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
     <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.Telephony.SMS_EMERGENCY_CB_RECEIVED" />
+    <protected-broadcast android:name="android.provider.action.SMS_EMERGENCY_CB_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" />
@@ -62,6 +61,7 @@
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
     <protected-broadcast android:name= "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED" />
+    <protected-broadcast android:name= "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG" />
     <protected-broadcast android:name= "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
     <protected-broadcast android:name= "com.android.intent.isim_refresh" />
     <protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
@@ -95,6 +95,9 @@
     <!-- For Vendor Debugging in Telephony -->
     <protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
 
+    <!-- Allows granting runtime permissions to telephony related components. -->
+    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
+
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
@@ -138,6 +141,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
     <uses-permission android:name="android.permission.BROADCAST_SMS"/>
     <uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"/>
@@ -157,7 +161,9 @@
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
     <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
+    <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
     <uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
     <uses-permission android:name="android.permission.DUMP" />
@@ -168,7 +174,8 @@
     <!-- BIND_CARRIER_MESSAGING_SERVICE has been deprecated in favor of BIND_CARRIER_SERVICES. -->
     <uses-permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE" />
     <uses-permission android:name="android.permission.BIND_EUICC_SERVICE" />
-    <uses-permission android:name="com.android.permission.BIND_TELEPHONY_NETWORK_SERVICE" />
+    <uses-permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" />
+    <uses-permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE" />
     <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
@@ -454,6 +461,21 @@
             </intent-filter>
         </activity>
 
+        <!--
+            Handler for EuiccManager's public action intents. These are public and do not require
+            any special permissions to start, although the calling package name should be
+            whitelisted by the underlying eUICC service implementation (i.e. the LPA).
+        -->
+        <activity android:name=".euicc.EuiccPublicActionUiDispatcherActivity"
+            android:theme="@android:style/Theme.NoDisplay">
+            <!-- Max out priority to ensure nobody else will handle these intents. -->
+            <intent-filter android:priority="1000">
+                <action android:name=
+                    "android.telephony.euicc.action.START_EUICC_ACTIVATION" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="EmergencyCallbackModeExitDialog"
             android:excludeFromRecents="true"
             android:label="@string/ecm_exit_dialog"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1321ace..22845e0 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -48,3 +48,5 @@
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.services.telephony.common_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/telephony-common.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/ims-common.jar)
diff --git a/OWNERS b/OWNERS
index 1ef8344..5be6fe5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,3 +11,4 @@
 shuoq@google.com
 paulye@google.com
 nazaninb@google.com
+sarahchin@google.com
\ No newline at end of file
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..e75dcb0
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "TeleServiceTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..86ebe3a
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,28 @@
+apex {
+    name: "com.android.telephony",
+
+    manifest: "apex_manifest.json",
+
+    // optional. if unspecified, a default one is auto-generated
+    androidManifest: "AndroidManifest.xml",
+
+    java_libs: ["telephony-common", "ims-common"],
+    //apps: ["TeleService", "StkLib", "ONSLib"],
+    apps: ["StkLib"],
+
+    key: "com.android.telephony.key",
+    certificate: ":com.android.telephony.certificate",
+}
+
+apex_key {
+    name: "com.android.telephony.key",
+    public_key: "com.android.telephony.avbpubkey",
+    private_key: "com.android.telephony.pem",
+}
+
+android_app_certificate {
+    name: "com.android.telephony.certificate",
+    // This will use com.android.telephony.x509.pem (the cert) and
+    // com.android.telephony.pk8 (the private key)
+    certificate: "com.android.telephony",
+}
diff --git a/apex/AndroidManifest.xml b/apex/AndroidManifest.xml
new file mode 100644
index 0000000..a32ab33
--- /dev/null
+++ b/apex/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.telephony">
+    <!-- APEX does not have classes.dex -->
+    <application android:hasCode="false" />
+    <!-- Setting maxSdk to lock the module to R. minSdk is auto-set by build system -->
+    <uses-sdk
+        android:maxSdkVersion="30"
+        android:targetSdkVersion="30"
+    />
+</manifest>
\ No newline at end of file
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
new file mode 100644
index 0000000..493d6fd
--- /dev/null
+++ b/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.telephony",
+  "version": 1
+}
\ No newline at end of file
diff --git a/apex/com.android.telephony.avbpubkey b/apex/com.android.telephony.avbpubkey
new file mode 100644
index 0000000..cba14427
--- /dev/null
+++ b/apex/com.android.telephony.avbpubkey
Binary files differ
diff --git a/apex/com.android.telephony.pem b/apex/com.android.telephony.pem
new file mode 100644
index 0000000..19d8c05
--- /dev/null
+++ b/apex/com.android.telephony.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAvEOU0eLO65uykCUYoLBXxjsIB7pCIXahtyIMYiGuUl6tnYb2
+gjPxsD9fNrqgdT+WgzHlfMPr4GqkT7prSI4/y9NglnFk2nAKZLQwiwtLOmm54Cnp
+OqqQZ9+g4MvA2rsrmstJ/Pdu4t+9Bv9bZHYLEPCW4LV1v5S2jY7ie/C+SbZm8yFz
+X9RgTX7loH7oSNDqvVMRUeyrlH3gRHxPMQxIMfLMro1NCfkA4q8QnwPpM2+YEBf0
+kktXkvbfJUB0Ywo6BCVAoqxkghNQQTLBuBDFnho13gQSMOLu8C62pzdAyPm/n5Yv
+aqEdmmGkBsy2qiHTiRuiWWl9aGDhd0U6e+hYK4OM3ISDQiTr5OuraIKt8EH7jo8H
+Zb/WqJFygOq5RqXmAvbtq+eBAgLlUNAl5F9RwLU6zPTREsSGG/g1fqUSD4ZS2qHt
+d9aBdSepvL4xDCg9BDTFMt4Xjc83hk+v7nIIPB4cayA9s2kgjXwjZhiMOJuoNMmr
+Wu2GP9zw37WmUF5jXKgICVtoq//qofA3KA+YZ8NcmPRmeBzCEks5fCAZN64AIh5l
+vi8pPv+s8lbhGui/dkgJAGoMWKwukMZZDxTFfgbPU74yIWUpoiXGEv+0WPsOyxKe
+HEC0uXL6h6HtTaxTTyk9OJp1Z12ih2CdHmoK8aHeL1ND+YXxko4rcVyJGvcCAwEA
+AQKCAgAve1gAa5TGhZlOk5yXgoJQw7itwkNFj1cQCi1XXqlJqwlcrppjklhFnGYW
+NpGboLMEWcRIaF2IVz0jwp+mSAI9FQ3KQ8Us9o8YgP2xQ+LwU5QGvmtNWZFcMWGw
+8JKuWACSZO0/OOSlocpaBnN1bfQyjItxFzMgY5B7OWtT2Q8VJkTm0yv3Img7g2LE
+zN0m6FIb9+RpZiiMJgc3HqcWdsOKnXMUHx0sA7o+cereQC1DxMR0YqZTc18d+Nek
+3JG+HOku/HpFzPb4e4iFORj0pH5C9tdZLGuuGr4qo417tQrI5Xx38nf5C7OcqT2m
+XWFAnZFthiO3K0zu02hqHXK3p4z81aerzOAFHQP9Dvw0LrEevhMS7POwnoBMWIYT
+j2oJAKf84AIt+URNxRMcOH4Vs8133ZJxesotYzEdZBN4KBukCC+MzLtkJ3C3vLSu
+PPlwYfS0T/wMLBlQRfGM2pZbuldc4Th+VuE8lvDIvmZKxUXdAh/OkTJRt95SSpFQ
+PZy9NauagIaDyE08z3vgiYUH/498piibXiv4ox9puyfDI30W7VlZA9NBV7UBhkcV
+vxmop3U4KJdI0dcwB/8oQao/eM8Bk8TOI58Ux/gM+7tLM+pzeIPlKsZ3GWjZYyZi
+Fds7JdprR2AljUoKXAGA3G2IejhiB6VSivR8IRLwdWZSAaM9iQKCAQEA512QfnYR
+MDjYzUBYySHANVFi09q3taoYJ1Ovq4w3YnC4Yy6OM1rI1g55mhzGGnrQhC8//Lwy
+v9fxhxEQkx8rY40KJRizfc28l3gIFzF+/mP5DqUQiVZQBiAm0m0y3LA1xTCRxyc/
+yLWCodFOojcg8H3vxDUiL+80wLRX/zXmUHjhGmg+sZVtuWTVpgdFEGR1guVAoXPM
+t6p9+Zpr+yhURRuce7IfjVxIXtjzaZ6fglCYSnakhxvBMWvvhhD2Sx60bl0sBpsT
+oj4VZmd7i01bde1H2BlJ9ca23uGPylvmC+kVFVoqnMv7dZuhHgQY06zg+zts8Grm
+mVcTa5xKtEr2FQKCAQEA0E8t9/XV6tEH5BoiZ6SLIYeh+yk/h8hQKGn+lnzcywon
+L/EEtvqzoeIkbLATOk4ffbiYtI86GxldPUp293ups9rJCJdb7fIahJJd4Pl9d9QF
+dEWkuMC9btyBq0WjvJLIVxKLcBv9NAMnfLYIWk4OfAXQ2Kgv4OrYmxqtLdnzqIT0
+4jgQcHkaDbPIBkOAfAcyy6b6TNsdhiR/ojZ/3s7e0YpUhUkvGqHPeGkg827fznui
+mOSvuIC1eg92Kpsv/NzZkSkhh+Y4yT3mXTPkdRrIoRiwfUsBx9c3CUB1bhMlqYdP
+p9IwfcDYPmPIWqD/5EIm5CIFTh/qFuJuB/RP1xr72wKCAQEAm6BZGGdsqmYilvWI
+g0o7kuG6u0xstDrE4nctPBQmEQZHURe4uRyA6VN1Qk/GhXtFFRT/Uo55CIhFw5eb
+8YpO0jjFsSxLV/zytWQc9RTrGky+87XeSLMRQv2Pf43KX537nTdNZ4ukIOrPAu/a
+l8zDUGE1zWH8LAgYEON+KYHk+Q5GRFAIk8UyuDj2aSaMszkPvktczP0qoASDlkjk
+B14aSR4IhT/Mb3a6i2odWVdEbhCuT7A8fVETA55Z0HFKaj8uGOp9ObhIXaKrHGXR
+OWOAePNxlw9zq2nOCl/pK7HjLPHMp78t2Khw7awQlTMmTAQGlZdkcNdHY6+ptYG8
+0kd1/QKCAQAaosVB+5FK4UedByp3DjcsJmez8eZJundlc4g2PekNnVK9cpgjvZLS
+lFQMAcezoOGt9biw/c90P54IhEkKBCoC8WrbSl9a4aY6ZWzTo5NDizzXTUBuz1Zp
+opCCEfmJ10r+t0RVJX8O84hNDgmrYme/vA6kZX0ZyW3BB3w1bLUXS0j01C5l81Y9
+gkuFYYRAV7tLByLiSPJRgMFBQK7BErqzGHkOQflf8mjRAMBR4E+F25FC85r0gKCz
+5t45PHa6YpL/WL8M3YdNVDjsmgIQZ0hYMQwAOWMU5pQG/tMFLDWYo9CvqNTCf7Im
+6CNqU0PlMABBE4iSIMqR+ooPQlIjMzQZAoIBAQC9JulZdQJzjZSI5tBj+zPScJJQ
+s/+sRfQBy7SalPtc+oLYBP2Js9c1JsR4Dk2vC3V5z7BaL4Lg4YcFVRcz9B4yUVRm
+BL/E34XGLfn5KBI8NeqIvHiivvlAPgoHwHAVsqtE35E2sYjOYbnn1FEaNm6EKiL3
+Dv1auVdLlP3jALJIEwHPzc2ZOc/Sh/LolfajR427j5YlgbMAk5DdXuxz7t9qEcyk
+Nc8HFaoXiBP1jK+L5C4mVvGeI9xghNoJXhNywEmMa2Shab0pmtOMQWuvNMQa3gRC
+OKbaM3A6zjvAl3rjFRBrGORqbu1E/InLOSXWuB8knYVf8LkWEDRjOHqMO3pJ
+-----END RSA PRIVATE KEY-----
diff --git a/apex/com.android.telephony.pk8 b/apex/com.android.telephony.pk8
new file mode 100644
index 0000000..b15a884
--- /dev/null
+++ b/apex/com.android.telephony.pk8
Binary files differ
diff --git a/apex/com.android.telephony.x509.pem b/apex/com.android.telephony.x509.pem
new file mode 100644
index 0000000..8b45823
--- /dev/null
+++ b/apex/com.android.telephony.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGKTCCBBGgAwIBAgIUCmopA1YmjspwjjHHjmtsDPqxBxgwDQYJKoZIhvcNAQEL
+BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC50ZWxlcGhvbnkxIjAgBgkqhkiG9w0B
+CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjA0MDAzNjAxWhgPNDc1NzEw
+MzAwMDM2MDFaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
+MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE
+CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQudGVsZXBob255MSIwIAYJ
+KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA4qQYe8S6yte4MUDF2/JBUulJLF15CdnALkDJuGYLRom2
+DxRpq2/IqX8gpwVfZCfVjtthKGMhsdLW9gI+oKck0eVB7Q2cU/WO+1Cr9cuKuT6B
+cfJUck125gRGmowWZomDXv1to6ZrGgqBlVGA546w3CNGXW/PoKr8im+xLEgD0TPA
+3jJCbxt2v2IDjMwPChHOF57y9wRkXN5aJHgwIvayReVy3Cko/rJOuPnNo2kLKI5u
+qfM4NovYNAzKbHHPCNzqhySoh2H0iTmHLby5bvXYh32ooGPmxrP6q8PubeNb+Xfc
+yY33tE74ityiqu/2FDuK2iZyL8EZkEWHbX4ddt/JREda1Mcnt95TUudqA28xHgC4
+wRYXLUADvKDVWOYu/ydWBes1iu7ZsEhy8oFdECsZGRQm2BkdmWAsMPUXRWeiaCQ4
+GFfpB38UPczY5GTgYwKAK3mmY5EeBKRu/hFMCfYGJvio2rUujRzIGOd6ovk+G/74
+Eqdbsphrkr6+UOCUrNOYUD0j7kDHVVDiWMoeKL6Qb+dFcewdDO1rRbZv8ZeExf6l
+vacfKKcEE+wls8jqof/y7cu+McGEn4R8+KKiRDcDQqLvZN4Q+CCtNRLiXA2KdrkB
+hKfhnTBBYfDNZA1WOhGrKM+Yb97ObgAgf2HuhZ2F40F3dRZ6w8yaURXAjkdCqycC
+AwEAAaNTMFEwHQYDVR0OBBYEFDCf58Ogbx07WK+hWEylNMxKYrj6MB8GA1UdIwQY
+MBaAFDCf58Ogbx07WK+hWEylNMxKYrj6MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBABoGWqoOfPBsXFYzP7aNhywhuCr6ddjAi3ss9vwod6JuVuVg
+Inuw1i/qPb/bO1JFEwYVH3w0JASMwzIl9h5sQv++z0P+cWsLW02AXyszb0jGD3CI
+ni/irO5PO0DdaTSHQWc8DEv0293EcqpKWmQDQ2XWH19rJ5f+Gx1046sFlUi633D2
+sFO5dG94pCtT/TcqwwkeEwA6cV2Re8F1AW8elal3sINBU5MI/OJ3GQDe+pqp9VTg
+xIEQfZ9oP6Ewl6Ect+M2hVG5X4mCk9HQL7aSuPKo2Ej6l3Xq4aQYlpO4wOVZgCJC
+chMbdAn2pjuk8+mDSR39hdIusOgr7zLZscmaGKyoxVz9KVNwd/mmu+9/mftGfYae
+RomF15hVnkhZAw21fSnhKry62+DuKt2WIyu5LqvTvRxFs0Hvoy3wrQY/oruHlgk1
+J1v1rXm3ZTrh2qv1XG8mOdTegurEvjkYQm0E38jdlkhByIHmhDdBARX2Aeb7Lrd8
+9k/7tr9+58+45punZBBMX6iW1xfwaCDF8xfZrrC96azXP6Kv/JZTxbfNm/dqHyKK
+zM2WhELaoJg7uFO9TC5eMsnBQzDoYXsr3Mu7nC7V1Bs/D7jYmFG0dG0LQJwUUGr1
+WqJT4LB0BMnzNrMH1hTQTBVYDlfn7c6fnihNBOWQt72x2GGFQsGAMUWb7pxC
+-----END CERTIFICATE-----
diff --git a/assets/eri_311480_BA01450000000000.xml b/assets/eri_311480_BA01450000000000.xml
new file mode 100644
index 0000000..ea18c73
--- /dev/null
+++ b/assets/eri_311480_BA01450000000000.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 that IconMode can be only 0, ON or 1, FLASHING
+     The icon is turned OFF if then IconIndex = 1 -->
+
+<EriFile VersionNumber="7"
+    NumberOfEriEntries="7"
+    EriFileType="2">
+
+  <CallPromptId Id="0"
+      CallPromptText="CallPromptId0"/>
+
+  <CallPromptId Id="1"
+      CallPromptText="CallPromptId1"/>
+
+  <CallPromptId Id="2"
+      CallPromptText="CallPromptId2"/>
+
+  <EriInfo RoamingIndicator="64"
+      IconIndex="1"
+      IconMode="0"
+      EriText="Xfinity Mobile"
+      CallPromptId="0"
+      AlertId="0"
+      DataServiceSupport="1"/>
+
+  <EriInfo RoamingIndicator="65"
+      IconIndex="1"
+      IconMode="0"
+      EriText="Xfinity Mobile"
+      CallPromptId="0"
+      AlertId="0"
+      DataServiceSupport="1"/>
+
+  <EriInfo RoamingIndicator="66"
+      IconIndex="1"
+      IconMode="0"
+      EriText="Extended Network"
+      CallPromptId="0"
+      AlertId="4"
+      DataServiceSupport="0"/>
+
+  <EriInfo RoamingIndicator="67"
+      IconIndex="0"
+      IconMode="0"
+      EriText="Extended Network"
+      CallPromptId="0"
+      AlertId="4"
+      DataServiceSupport="0"/>
+
+  <EriInfo RoamingIndicator="68"
+      IconIndex="0"
+      IconMode="0"
+      EriText="Roaming"
+      CallPromptId="0"
+      AlertId="5"
+      DataServiceSupport="0"/>
+
+  <EriInfo RoamingIndicator="69"
+      IconIndex="2"
+      IconMode="0"
+      EriText="Extended Network"
+      CallPromptId="0"
+      AlertId="4"
+      DataServiceSupport="0"/>
+
+  <EriInfo RoamingIndicator="70"
+      IconIndex="2"
+      IconMode="0"
+      EriText="Roaming"
+      CallPromptId="0"
+      AlertId="5"
+      DataServiceSupport="0"/>
+
+</EriFile>
diff --git a/ecc/conversion_toolset_v1/gen_eccdata.sh b/ecc/conversion_toolset_v1/gen_eccdata.sh
index 803f39b..9fc0efc 100644
--- a/ecc/conversion_toolset_v1/gen_eccdata.sh
+++ b/ecc/conversion_toolset_v1/gen_eccdata.sh
@@ -19,8 +19,7 @@
 source "${LOCAL_TOOLSET_DIR}/env.sh"
 
 echo "Converting eccdata..."
-${ANDROID_BUILD_TOP}/prebuilts/misc/${KERNEL}-x86/protobuf/aprotoc \
-  --encode=ecc.AllInfo proto/protobuf_ecc_data.proto \
+aprotoc --encode=ecc.AllInfo proto/protobuf_ecc_data.proto \
   < "${INPUT_DATA}" > "${RAW_DATA}"
 
 echo "Compressing eccdata..."
diff --git a/ecc/conversion_toolset_v1/proto/Android.bp b/ecc/conversion_toolset_v1/proto/Android.bp
index f633f90..e1e0643 100644
--- a/ecc/conversion_toolset_v1/proto/Android.bp
+++ b/ecc/conversion_toolset_v1/proto/Android.bp
@@ -22,7 +22,7 @@
         ],
     },
     srcs: ["protobuf_ecc_data.proto"],
-    sdk_version: "core_platform",
+    sdk_version: "system_current",
     jarjar_rules: "jarjar-rules.txt",
     java_version: "1.8",
-}
\ No newline at end of file
+}
diff --git a/ecc/gen_eccdata.sh b/ecc/gen_eccdata.sh
index 4c1f616..2888585 100755
--- a/ecc/gen_eccdata.sh
+++ b/ecc/gen_eccdata.sh
@@ -20,24 +20,8 @@
   exit 1
 fi
 
-case $(uname -s) in
-  Darwin)
-    KERNEL=darwin
-    ;;
-  Linux)
-    KERNEL=linux
-    ;;
-  *)
-    echo "Unknown kernel \"`uname -s`\"" 1>&2
-    exit 1
-    ;;
-esac
-
-read -d "" PROTOC_COMMAND << END || :
-${ANDROID_BUILD_TOP}/prebuilts/misc/${KERNEL}-x86/protobuf/aprotoc
-END
-if ! [ -x "${PROTOC_COMMAND}" ] ; then
-  echo "Missing ${PROTOC_COMMAND}." 1>&2
+if ! [ -x "$(which aprotoc)" ] ; then
+  echo "Missing aprotoc, build it with 'm aprotoc'." 1>&2
   exit 1
 fi
 
diff --git a/res/drawable/btn_emergency_information.xml b/res/drawable/btn_emergency_information.xml
index 29b4a7a..cd57bd3 100644
--- a/res/drawable/btn_emergency_information.xml
+++ b/res/drawable/btn_emergency_information.xml
@@ -16,7 +16,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <corners android:radius="8dp"/>
-    <!-- White, Opacity 16% -->
+    <solid android:color="@color/emergency_info_btn_background_color"/>
     <stroke android:width="1dp"
-            android:color="#40FFFFFF"/>
-</shape>
\ No newline at end of file
+            android:color="@color/emergency_info_btn_bolder"/>
+</shape>
diff --git a/res/drawable/btn_emergency_shortcuts.xml b/res/drawable/btn_emergency_shortcuts.xml
index 449e4e0..9717e81 100644
--- a/res/drawable/btn_emergency_shortcuts.xml
+++ b/res/drawable/btn_emergency_shortcuts.xml
@@ -17,4 +17,4 @@
        android:shape="rectangle">
     <corners android:radius="8dp"/>
     <solid android:color="@color/emergency_shortcut_button_background_color"/>
-</shape>
\ No newline at end of file
+</shape>
diff --git a/res/drawable/ic_sim_card.xml b/res/drawable/ic_sim_card.xml
new file mode 100644
index 0000000..f563d23
--- /dev/null
+++ b/res/drawable/ic_sim_card.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+<path
+    android:fillColor="#757575"
+    android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM13,17h-2v-2h2v2zM13,13h-2L11,8h2v5z"/>
+</vector>
diff --git a/res/drawable/phone_type_icon_background.xml b/res/drawable/phone_type_icon_background.xml
index b51c3b2..352db2e 100644
--- a/res/drawable/phone_type_icon_background.xml
+++ b/res/drawable/phone_type_icon_background.xml
@@ -15,5 +15,5 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="oval">
-    <solid android:color="@color/emergency_shortcut_confirm_button_background_color"/>
-</shape>
\ No newline at end of file
+    <solid android:color="@color/emergency_shortcut_phone_type_icon_color"/>
+</shape>
diff --git a/res/layout/band_mode.xml b/res/layout/band_mode.xml
new file mode 100644
index 0000000..b43dd1d
--- /dev/null
+++ b/res/layout/band_mode.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:padding="4dip"
+              android:gravity="center_horizontal"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+    <ListView android:id="@+id/band"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:textSize="7sp">
+    </ListView>
+
+</LinearLayout>
diff --git a/res/layout/emergency_dialer.xml b/res/layout/emergency_dialer.xml
index 892b8f4..d14a679 100644
--- a/res/layout/emergency_dialer.xml
+++ b/res/layout/emergency_dialer.xml
@@ -46,7 +46,9 @@
                 android:layout_height="@dimen/dialpad_button_height"
                 android:background="@drawable/floating_action_button_red"
                 android:contentDescription="@string/description_dialpad_button"
-                android:src="@drawable/ic_dialpad_white_24"/>
+                android:src="@drawable/ic_dialpad_white_24"
+                android:scaleType="centerInside"
+                android:backgroundTint="@color/emergency_dialpad_fab_tint_color"/>
         </FrameLayout>
     </FrameLayout>
 
diff --git a/res/layout/emergency_information.xml b/res/layout/emergency_information.xml
index 524387f..3ed1748 100644
--- a/res/layout/emergency_information.xml
+++ b/res/layout/emergency_information.xml
@@ -19,7 +19,8 @@
     android:layout_height="@dimen/emergency_info_button_height"
     android:layout_width="match_parent"
     android:layout_marginHorizontal="@dimen/emergency_shortcut_buttons_margin_horizontal"
-    android:layout_marginVertical="@dimen/emergency_info_button_margin_vertical">
+    android:layout_marginTop="@dimen/emergency_info_button_margin_top"
+    android:layout_marginBottom="@dimen/emergency_info_button_margin_bottom">
     <FrameLayout
         android:id="@+id/emergency_info_view"
         android:layout_height="match_parent"
@@ -38,7 +39,7 @@
                 android:layout_marginEnd="@dimen/emergency_dialer_image_margin_end"
                 android:layout_height="@dimen/emergency_info_image_height"
                 android:layout_width="@dimen/emergency_info_image_width"
-                android:scaleType="centerCrop"/>
+                android:scaleType="centerInside"/>
             <LinearLayout
                 android:layout_height="wrap_content"
                 android:layout_width="match_parent"
@@ -53,8 +54,7 @@
                     android:maxLines="1"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/emergency_info_name_line_height"
-                    android:fontFamily="@*android:string/config_headlineFontFamily"
-                    android:textAppearance="@style/HeadlineTextAppearance"/>
+                    android:textAppearance="@style/EmergencyInfoNameTextAppearance"/>
                 <TextView
                     android:id="@+id/emergency_info_hint"
                     android:layout_height="wrap_content"
@@ -62,8 +62,7 @@
                     android:maxLines="2"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/emergency_info_hint_line_height"
-                    android:alpha="0.7"
-                    android:textAppearance="@style/SubtitleTextAppearance"
+                    android:textAppearance="@style/EmergencyInfoHintTextAppearance"
                     android:text="@string/emergency_information_hint"/>
             </LinearLayout>
         </LinearLayout>
@@ -88,7 +87,7 @@
                 android:layout_width="@dimen/emergency_info_image_width"
                 android:layout_marginStart="@dimen/emergency_dialer_image_margin_start"
                 android:layout_marginEnd="@dimen/emergency_dialer_image_margin_end"
-                android:scaleType="centerCrop"/>
+                android:scaleType="centerInside"/>
             <TextView
                 android:id="@+id/confirmed_emergency_info"
                 android:layout_height="wrap_content"
@@ -99,8 +98,7 @@
                 android:maxLines="2"
                 android:ellipsize="end"
                 android:lineHeight="@dimen/confirmed_emergency_info_line_height"
-                android:fontFamily="@*android:string/config_headlineFontFamily"
-                android:textAppearance="@style/PhoneCallHintTextAppearance"
+                android:textAppearance="@style/EmergencyInfoTapHintTextAppearance"
                 android:text="@string/emergency_information_confirm_hint"/>
         </LinearLayout>
     </FrameLayout>
diff --git a/res/layout/emergency_shortcut_button.xml b/res/layout/emergency_shortcut_button.xml
index 3c5eaa2..ce4d60e 100644
--- a/res/layout/emergency_shortcut_button.xml
+++ b/res/layout/emergency_shortcut_button.xml
@@ -16,7 +16,8 @@
 <com.android.phone.EmergencyShortcutButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="@dimen/emergency_shortcut_button_height"
-    android:layout_width="match_parent">
+    android:layout_width="match_parent"
+    android:layout_marginBottom="@dimen/emergency_shortcut_button_margin_bottom">
     <!-- Normal emergency call button view -->
     <FrameLayout
         android:id="@+id/emergency_call_number_info_view"
@@ -42,7 +43,8 @@
                     android:id="@+id/phone_type_icon"
                     android:layout_width="@dimen/phone_number_type_image_height"
                     android:layout_height="@dimen/phone_number_type_image_width"
-                    android:layout_gravity="center"/>
+                    android:layout_gravity="center"
+                    android:scaleType="centerInside"/>
             </FrameLayout>
             <LinearLayout
                 android:layout_height="wrap_content"
@@ -57,7 +59,6 @@
                     android:maxLines="1"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/phone_number_line_height"
-                    android:fontFamily="@*android:string/config_headlineFontFamily"
                     android:textAppearance="@style/PhoneNumberTextAppearance"/>
                 <TextView
                     android:id="@+id/phone_number_description"
@@ -66,8 +67,7 @@
                     android:alpha="0.7"
                     android:maxLines="1"
                     android:ellipsize="end"
-                    android:fontFamily="sans-serif-medium"
-                    android:textAppearance="@style/SubtitleTextAppearance"/>
+                    android:textAppearance="@style/PhoneNumberTypeAppearance"/>
             </LinearLayout>
         </LinearLayout>
         <FrameLayout
@@ -79,7 +79,9 @@
                 android:id="@+id/microphone_icon"
                 android:layout_height="@dimen/phone_icon_height"
                 android:layout_width="@dimen/phone_icon_width"
-                android:src="@drawable/ic_emergency_callback_mode"/>
+                android:src="@drawable/ic_emergency_callback_mode"
+                android:tint="@color/emergency_call_icon_color"
+                android:scaleType="centerInside"/>
         </FrameLayout>
     </FrameLayout>
 
@@ -111,6 +113,7 @@
                     android:layout_width="@dimen/phone_number_type_image_height"
                     android:layout_height="@dimen/phone_number_type_image_width"
                     android:layout_gravity="center"
+                    android:scaleType="centerInside"
                     android:tint="@color/emergency_shortcut_confirm_button_background_color"/>
             </FrameLayout>
             <FrameLayout
@@ -124,8 +127,7 @@
                     android:maxLines="2"
                     android:ellipsize="end"
                     android:lineHeight="@dimen/phone_call_hint_line_height"
-                    android:fontFamily="@*android:string/config_headlineFontFamily"
-                    android:textAppearance="@style/PhoneCallHintTextAppearance"/>
+                    android:textAppearance="@style/PhoneNumberTapHintAppearance"/>
             </FrameLayout>
         </LinearLayout>
         <FrameLayout
@@ -137,7 +139,9 @@
             <ImageView
                 android:layout_height="@dimen/phone_icon_height"
                 android:layout_width="@dimen/phone_icon_width"
-                android:src="@drawable/ic_emergency_callback_mode"/>
+                android:src="@drawable/ic_emergency_callback_mode"
+                android:tint="@color/emergency_call_icon_color"
+                android:scaleType="centerInside"/>
         </FrameLayout>
     </FrameLayout>
 </com.android.phone.EmergencyShortcutButton>
diff --git a/res/layout/emergency_shortcut_buttons_group.xml b/res/layout/emergency_shortcut_buttons_group.xml
index 54563c9..8e2c256 100644
--- a/res/layout/emergency_shortcut_buttons_group.xml
+++ b/res/layout/emergency_shortcut_buttons_group.xml
@@ -37,8 +37,7 @@
                 android:maxLines="2"
                 android:ellipsize="end"
                 android:lineHeight="@dimen/emergency_number_title_line_height"
-                android:fontFamily="sans-serif-medium"
-                android:textColor="@android:color/white"
+                android:textAppearance="@style/ShortcutViewHintTextAppearance"
                 android:textSize="@dimen/emergency_number_title_text_size"
                 android:text="@string/single_emergency_number_title"/>
         </FrameLayout>
@@ -49,13 +48,13 @@
             android:layout_marginStart="@dimen/emergency_number_title_container_width"
             android:layout_gravity="center_vertical|end"
             android:gravity="center_vertical"
-            android:orientation="horizontal"
-            android:alpha="0.7">
+            android:orientation="horizontal">
             <ImageView
                 android:id="@+id/location_icon"
                 android:layout_width="@dimen/location_image_width"
                 android:layout_height="@dimen/location_image_height"
-                android:src="@drawable/place_gm2_24px"/>
+                android:src="@drawable/place_gm2_24px"
+                android:scaleType="centerInside"/>
             <TextView
                 android:id="@+id/location_text"
                 android:layout_height="wrap_content"
@@ -64,7 +63,7 @@
                 android:maxLines="2"
                 android:ellipsize="end"
                 android:lineHeight="@dimen/location_text_line_height"
-                android:textColor="@android:color/white"
+                android:textAppearance="@style/ShortcutViewHintTextAppearance"
                 android:textSize="@dimen/emergency_location_text_size"/>
         </LinearLayout>
     </FrameLayout>
@@ -73,8 +72,6 @@
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
         android:layout_marginHorizontal="@dimen/emergency_shortcut_buttons_margin_horizontal"
-        android:orientation="vertical"
-        android:divider="@drawable/emergency_shortcuts_divider"
-        android:showDividers="middle">
+        android:orientation="vertical">
     </LinearLayout>
 </LinearLayout>
diff --git a/res/layout/radio_info.xml b/res/layout/radio_info.xml
new file mode 100644
index 0000000..40c2e53
--- /dev/null
+++ b/res/layout/radio_info.xml
@@ -0,0 +1,414 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/Settings/assets/res/any/layout/radio_info.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout style="@style/info_layout"
+        android:descendantFocusability="beforeDescendants"
+        android:focusableInTouchMode="true">
+
+        <!-- Phone index -->
+        <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/phone_index_label"
+                style="@style/info_label"
+                />
+
+        <Spinner android:id="@+id/phoneIndex"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                />
+
+        <!-- IMEI -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_imei_label" style="@style/info_label" />
+            <TextView android:id="@+id/imei" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Phone Number -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_phone_number_label" style="@style/info_label" />
+            <TextView android:id="@+id/number" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Subscription ID -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_subid" style="@style/info_label" />
+            <TextView android:id="@+id/subid" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Default data subscription -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_dds" style="@style/info_label" />
+            <TextView android:id="@+id/dds" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- IMSI -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_imsi_label" style="@style/info_label" />
+            <TextView android:id="@+id/imsi" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Network Identifier -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_current_network_label" style="@style/info_label" />
+            <TextView android:id="@+id/operator" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Roaming -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_roaming_label" style="@style/info_label" />
+            <TextView android:id="@+id/roaming" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Data Service Status -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_gprs_service_label" style="@style/info_label" />
+            <TextView android:id="@+id/gprs" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Data Network Type -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_data_network_type_label" style="@style/info_label" />
+            <TextView android:id="@+id/data_network" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Voice Service Status -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_gsm_service_label" style="@style/info_label" />
+            <TextView android:id="@+id/gsm" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Voice Network Type -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_voice_network_type_label" style="@style/info_label" />
+            <TextView android:id="@+id/voice_network" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Signal Strength -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_signal_strength_label" style="@style/info_label" />
+            <TextView android:id="@+id/dbm" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Link Bandwidth -->
+        <LinearLayout style="@style/RadioInfo_entry_layout" android:orientation="horizontal">
+            <TextView android:text="@string/radio_info_dl_kbps" style="@style/info_label" />
+            <TextView android:id="@+id/dl_kbps" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Link Bandwidth -->
+        <LinearLayout style="@style/RadioInfo_entry_layout" android:orientation="horizontal">
+            <TextView android:text="@string/radio_info_ul_kbps" style="@style/info_label" />
+            <TextView android:id="@+id/ul_kbps" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Physical Channel Config -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_phy_chan_config" style="@style/info_label" />
+            <TextView android:id="@+id/phy_chan_config" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- Preferred Network Type -->
+        <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/radio_info_set_perferred_label"
+                style="@style/info_label"
+                />
+
+        <Spinner android:id="@+id/preferredNetworkType"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                />
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- Radio Power -->
+        <Switch android:id="@+id/radio_power"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/radio_info_radio_power"/>
+
+        <!-- VoLTE provisioned -->
+        <Switch android:id="@+id/volte_provisioned_switch"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/volte_provisioned_switch_string"/>
+
+        <!-- VT provisioned -->
+        <Switch android:id="@+id/vt_provisioned_switch"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/vt_provisioned_switch_string"/>
+
+        <!-- Wifi Calling provisioned -->
+        <Switch android:id="@+id/wfc_provisioned_switch"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/wfc_provisioned_switch_string"/>
+
+        <!-- EAB/Presence provisioned -->
+        <Switch android:id="@+id/eab_provisioned_switch"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/eab_provisioned_switch_string"/>
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- Enable/Disable CBRS data -->
+        <Switch android:id="@+id/cbrs_data_switch"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/cbrs_data_switch_string" />
+
+        <!-- Switch between SSSS(single sim single standby) and DSDS(dual sim dual standby). -->
+        <Switch android:id="@+id/dsds_switch"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/dsds_switch_string" />
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- Ping stats -->
+        <Button android:id="@+id/ping_test"
+                android:textSize="14sp"
+                android:layout_marginTop="8dip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ping_test_label"
+                />
+ 
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_ping_hostname_v4" style="@style/info_label" />
+            <TextView android:id="@+id/pingHostnameV4" style="@style/info_value" />
+        </LinearLayout>
+
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_ping_hostname_v6" style="@style/info_label" />
+            <TextView android:id="@+id/pingHostnameV6" style="@style/info_value" />
+        </LinearLayout>
+
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_http_client_test" style="@style/info_label" />
+            <TextView android:id="@+id/httpClientTest" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- PPP Sent -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_ppp_sent_label"
+                style="@style/info_label" />
+            <TextView android:id="@+id/sent" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- PPP Received -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_ppp_received_label"
+                style="@style/info_label" />
+            <TextView android:id="@+id/received" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- PPP Sent since last received -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_ppp_resets_label"
+                style="@style/info_label" />
+            <TextView android:id="@+id/sentSinceReceived" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- Call Status -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_call_status_label" style="@style/info_label" />
+            <TextView android:id="@+id/call" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Message Waiting Indicator -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_message_waiting_label" style="@style/info_label" />
+            <TextView android:id="@+id/mwi" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Call Forwarding Indicator -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_call_redirect_label" style="@style/info_label" />
+            <TextView android:id="@+id/cfi" style="@style/info_value" />
+        </LinearLayout>
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- CellInfoListRate Selection -->
+        <!-- Location -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_signal_location_label" style="@style/info_label" />
+            <TextView android:id="@+id/location" style="@style/info_value" />
+        </LinearLayout>
+
+        <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/radio_info_cell_info_refresh_rate"
+                style="@style/info_label"
+                />
+
+        <Spinner android:id="@+id/cell_info_rate_select"
+                 android:layout_width="match_parent"
+                 android:layout_height="wrap_content"
+                />
+
+        <!-- CellInfo -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/radio_info_cellinfo_label"
+                      style="@style/info_label" />
+        </LinearLayout>
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:id="@+id/cellinfo"
+                      style="@style/info_value"
+                      android:minHeight="300dip"
+                      android:textSize="12sp" />
+        </LinearLayout>
+
+        <!-- Horizontal Rule -->
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- Launch OEM-specific Info/Settings Activity (if any) -->
+        <!-- Carrier Provisioning -->
+        <LinearLayout style="@style/RadioInfo_entry_layout"
+                      android:orientation="horizontal" >
+            <Button android:id="@+id/carrier_provisioning"
+                    android:layout_marginTop="8dip"
+                    android:layout_weight="1"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:text="@string/carrier_provisioning"
+                    android:textSize="14sp"/>
+            <Button android:id="@+id/trigger_carrier_provisioning"
+                    android:layout_marginTop="8dip"
+                    android:layout_weight="1"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:text="@string/trigger_carrier_provisioning"
+                    android:textSize="14sp"/>
+            <Button android:id="@+id/oem_info"
+                    android:layout_marginTop="8dip"
+                    android:layout_weight="1"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:text="@string/oem_radio_info_label"
+                    android:textSize="14sp"/>
+        </LinearLayout>
+
+        <!-- SMSC -->
+        <RelativeLayout android:layout_width="match_parent"
+                        android:layout_height="wrap_content">
+            <TextView android:id="@+id/smsc_label"
+                      android:text="@string/radio_info_smsc_label"
+                      android:layout_alignBaseline="@+id/update_smsc"
+                      style="@style/info_label" />
+            <Button android:id="@+id/refresh_smsc"
+                    android:textSize="14sp"
+                    android:layout_marginTop="8dip"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/radio_info_smsc_refresh_label"
+                    android:layout_alignParentEnd="true"
+                    />
+            <Button android:id="@+id/update_smsc"
+                    android:textSize="14sp"
+                    android:layout_marginTop="8dip"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/radio_info_smsc_update_label"
+                    android:layout_toStartOf="@+id/refresh_smsc"
+                    android:layout_alignBaseline="@+id/refresh_smsc"
+                    />
+            <EditText android:id="@+id/smsc"
+                      style="@style/form_value"
+                      android:layout_alignBaseline="@+id/refresh_smsc"
+                      android:layout_toStartOf="@id/update_smsc"
+                      android:layout_toEndOf="@id/smsc_label" />
+        </RelativeLayout>
+
+        <!-- Test setting to ignore bad DNS, useful in lab environments -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <Button android:id="@+id/dns_check_toggle"
+                    android:textSize="14sp"
+                    android:layout_marginTop="8dip"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/radio_info_toggle_dns_check_label"
+                    />
+            <TextView android:id="@+id/dnsCheckState" style="@style/info_value" />
+        </LinearLayout>
+
+
+    </LinearLayout>
+</ScrollView>
diff --git a/res/values-h535dp/dimens.xml b/res/values-h535dp/dimens.xml
new file mode 100644
index 0000000..c3b2341
--- /dev/null
+++ b/res/values-h535dp/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <!-- Height and vertical margin for the emergency information button. -->
+    <dimen name="emergency_info_button_height">80dp</dimen>
+    <dimen name="emergency_info_button_margin_top">35dp</dimen>
+    <dimen name="emergency_info_button_margin_bottom">40dp</dimen>
+
+    <!-- The text size of emergency info name and hint -->
+    <dimen name="emergency_info_name_text_size">18.3sp</dimen>
+    <dimen name="emergency_info_hint_text_size">13.3sp</dimen>
+    <dimen name="emergency_info_tap_hint_text_size">15sp</dimen>
+
+    <!-- The height for title of emergency number and location info. -->
+    <dimen name="emergency_number_title_height">40dp</dimen>
+    <!-- The text size for emergency number title.-->
+    <dimen name="emergency_number_title_text_size">11.6sp</dimen>
+
+    <!-- The height and width for the image of location info.-->
+    <dimen name="location_image_height">13.3dp</dimen>
+    <dimen name="location_image_width">13.3dp</dimen>
+
+    <!-- The text size for emergency location.-->
+    <dimen name="emergency_location_text_size">11.6sp</dimen>
+
+    <!-- The height and margin for button of emergency shortcut. -->
+    <dimen name="emergency_shortcut_button_height">80dp</dimen>
+    <dimen name="emergency_shortcut_button_margin_bottom">6.7dp</dimen>
+
+    <!-- The text size of emergency number, type and hint -->
+    <dimen name="emergency_shortcut_number_text_size">27sp</dimen>
+    <dimen name="emergency_shortcut_type_text_size">11.6sp</dimen>
+    <dimen name="emergency_shortcut_tap_hint_text_size">15sp</dimen>
+
+    <!-- The height and width for the image of phone number type.-->
+    <dimen name="phone_number_type_image_height">33dp</dimen>
+    <dimen name="phone_number_type_image_width">33dp</dimen>
+
+    <!-- The height and width of phone icon.-->
+    <dimen name="phone_icon_height">20dp</dimen>
+    <dimen name="phone_icon_width">20dp</dimen>
+
+    <!-- Margin of dialpad button -->
+    <dimen name="emergency_dialer_dialpad_button_margin">27dp</dimen>
+
+    <!-- The height and width for the dialpad button -->
+    <dimen name="dialpad_button_height">47dp</dimen>
+    <dimen name="dialpad_button_width">47dp</dimen>
+</resources>
diff --git a/res/values-h590dp/dimens.xml b/res/values-h590dp/dimens.xml
new file mode 100644
index 0000000..3684f4c
--- /dev/null
+++ b/res/values-h590dp/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <!-- Height and vertical margin for the emergency information button. -->
+    <dimen name="emergency_info_button_height">87dp</dimen>
+    <dimen name="emergency_info_button_margin_top">44dp</dimen>
+    <dimen name="emergency_info_button_margin_bottom">44dp</dimen>
+
+    <!-- The text size of emergency info name and hint -->
+    <dimen name="emergency_info_name_text_size">20sp</dimen>
+    <dimen name="emergency_info_hint_text_size">14.5sp</dimen>
+    <dimen name="emergency_info_tap_hint_text_size">16.4sp</dimen>
+
+    <!-- The height for title of emergency number and location info. -->
+    <dimen name="emergency_number_title_height">44dp</dimen>
+    <!-- The text size for emergency number title.-->
+    <dimen name="emergency_number_title_text_size">12.7sp</dimen>
+
+    <!-- The height and width for the image of location info.-->
+    <dimen name="location_image_height">14.5dp</dimen>
+    <dimen name="location_image_width">14.5dp</dimen>
+
+    <!-- The text size for emergency location.-->
+    <dimen name="emergency_location_text_size">12.7sp</dimen>
+
+    <!-- The height and margin for button of emergency shortcut. -->
+    <dimen name="emergency_shortcut_button_height">87dp</dimen>
+    <dimen name="emergency_shortcut_button_margin_bottom">7.3dp</dimen>
+
+    <!-- The text size of emergency number, type and hint -->
+    <dimen name="emergency_shortcut_number_text_size">29sp</dimen>
+    <dimen name="emergency_shortcut_type_text_size">12.7sp</dimen>
+    <dimen name="emergency_shortcut_tap_hint_text_size">16.4sp</dimen>
+
+    <!-- The height and width for the image of phone number type.-->
+    <dimen name="phone_number_type_image_height">36dp</dimen>
+    <dimen name="phone_number_type_image_width">36dp</dimen>
+
+    <!-- The height and width of phone icon.-->
+    <dimen name="phone_icon_height">22dp</dimen>
+    <dimen name="phone_icon_width">22dp</dimen>
+
+    <!-- Margin of dialpad button -->
+    <dimen name="emergency_dialer_dialpad_button_margin">29dp</dimen>
+
+    <!-- The height and width for the dialpad button -->
+    <dimen name="dialpad_button_height">51dp</dimen>
+    <dimen name="dialpad_button_width">51dp</dimen>
+</resources>
diff --git a/res/values-h720dp/dimens.xml b/res/values-h720dp/dimens.xml
new file mode 100644
index 0000000..7bb1f27
--- /dev/null
+++ b/res/values-h720dp/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <!-- Height and vertical margin for the emergency information button. -->
+    <dimen name="emergency_info_button_height">96dp</dimen>
+    <dimen name="emergency_info_button_margin_top">72dp</dimen>
+    <dimen name="emergency_info_button_margin_bottom">60dp</dimen>
+
+    <!-- The text size of emergency info name and hint -->
+    <dimen name="emergency_info_name_text_size">22sp</dimen>
+    <dimen name="emergency_info_hint_text_size">16sp</dimen>
+    <dimen name="emergency_info_tap_hint_text_size">18sp</dimen>
+
+    <!-- The height for title of emergency number and location info. -->
+    <dimen name="emergency_number_title_height">48dp</dimen>
+    <!-- The text size for emergency number title.-->
+    <dimen name="emergency_number_title_text_size">14sp</dimen>
+
+    <!-- The height and width for the image of location info.-->
+    <dimen name="location_image_height">16dp</dimen>
+    <dimen name="location_image_width">16dp</dimen>
+
+    <!-- The text size for emergency location.-->
+    <dimen name="emergency_location_text_size">14sp</dimen>
+
+    <!-- The height and margin for button of emergency shortcut. -->
+    <dimen name="emergency_shortcut_button_height">96dp</dimen>
+    <dimen name="emergency_shortcut_button_margin_bottom">8dp</dimen>
+
+    <!-- The text size of emergency number, type and hint -->
+    <dimen name="emergency_shortcut_number_text_size">32sp</dimen>
+    <dimen name="emergency_shortcut_type_text_size">14sp</dimen>
+    <dimen name="emergency_shortcut_tap_hint_text_size">18sp</dimen>
+
+    <!-- The height and width for the image of phone number type.-->
+    <dimen name="phone_number_type_image_height">40dp</dimen>
+    <dimen name="phone_number_type_image_width">40dp</dimen>
+
+    <!-- The height and width of phone icon.-->
+    <dimen name="phone_icon_height">24dp</dimen>
+    <dimen name="phone_icon_width">24dp</dimen>
+
+    <!-- Margin of dialpad button -->
+    <dimen name="emergency_dialer_dialpad_button_margin">64dp</dimen>
+
+    <!-- The height and width for the dialpad button -->
+    <dimen name="dialpad_button_height">56dp</dimen>
+    <dimen name="dialpad_button_width">56dp</dimen>
+</resources>
diff --git a/res/values-mcc208-mnc10-fr/strings.xml b/res/values-mcc208-mnc10-fr/strings.xml
index ac5ef59..eae41e9 100644
--- a/res/values-mcc208-mnc10-fr/strings.xml
+++ b/res/values-mcc208-mnc10-fr/strings.xml
@@ -3,4 +3,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="enhanced_4g_lte_mode_summary">Utiliser la fonction Voix 4G (HD) lorsqu\'elle est disponible</string>
     <string name="enhanced_4g_lte_mode_title">Activer Voix 4G (HD)</string>
+    <string name="wifi_calling">Activer Appels Wi-Fi</string>
 </resources>
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
new file mode 100644
index 0000000..1f336b9
--- /dev/null
+++ b/res/values-night/colors.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+
+    <!-- Colors for shortcut view -->
+    <color name="emergency_info_btn_bolder">#85ffffff</color>
+    <color name="emergency_info_btn_background_color">#202124</color>
+    <color name="emergency_shortcut_button_background_color">#202124</color>
+    <color name="emergency_shortcut_phone_type_icon_color">#f28b82</color>
+    <color name="emergency_shortcut_confirm_button_background_color">#3c4043</color>
+    <color name="emergency_shortcut_confirm_phone_type_icon_color">#f28b82</color>
+    <color name="emergency_call_icon_color">#e8eaed</color>
+    <color name="emergency_dialpad_fab_tint_color">#f28b82</color>
+</resources>
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index 648ceeb..fa5c8a1 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -27,4 +27,44 @@
         <item name="android:dialogTheme">@style/DialerAlertDialogTheme</item>
     </style>
 
+    <style name="EmergencyInfoNameTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textSize">@dimen/emergency_info_name_text_size</item>
+    </style>
+
+    <style name="EmergencyInfoHintTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@color/white_70_percent</item>
+        <item name="android:textSize">@dimen/emergency_info_hint_text_size</item>
+    </style>
+
+    <style name="EmergencyInfoTapHintTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textSize">@dimen/emergency_info_tap_hint_text_size</item>
+    </style>
+
+    <style name="ShortcutViewHintTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/white</item>
+    </style>
+
+    <style name="PhoneNumberTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textSize">@dimen/emergency_shortcut_number_text_size</item>
+    </style>
+
+    <style name="PhoneNumberTypeAppearance">
+        <item name="android:fontFamily">roboto</item>
+        <item name="android:textColor">@color/white_70_percent</item>
+        <item name="android:textSize">@dimen/emergency_shortcut_type_text_size</item>
+    </style>
+
+    <style name="PhoneNumberTapHintAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textSize">@dimen/emergency_shortcut_tap_hint_text_size</item>
+    </style>
 </resources>
diff --git a/res/values-sw345dp/dimens.xml b/res/values-sw345dp/dimens.xml
deleted file mode 100644
index 84db80b..0000000
--- a/res/values-sw345dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<resources>
-    <dimen name="emergency_info_button_height">88dp</dimen>
-    <dimen name="emergency_info_button_margin_vertical">48dp</dimen>
-    <dimen name="emergency_shortcut_button_height">88dp</dimen>
-    <dimen name="emergency_number_title_text_size">12sp</dimen>
-    <dimen name="emergency_location_text_size">12sp</dimen>
-</resources>
\ No newline at end of file
diff --git a/res/values-sw360dp/dimens.xml b/res/values-sw360dp/dimens.xml
deleted file mode 100644
index c578065..0000000
--- a/res/values-sw360dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<resources>
-    <dimen name="emergency_info_button_height">96dp</dimen>
-    <dimen name="emergency_info_button_margin_vertical">56dp</dimen>
-    <dimen name="emergency_shortcut_button_height">96dp</dimen>
-    <dimen name="emergency_number_title_text_size">14sp</dimen>
-    <dimen name="emergency_location_text_size">14sp</dimen>
-</resources>
\ No newline at end of file
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 4854db7..1309f77 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -437,7 +437,7 @@
     <string name="deleting_fdn_contact" msgid="5669163206349319969">"正在删除固定拨号联系人..."</string>
     <string name="fdn_contact_deleted" msgid="7154162327112259569">"已删除固定拨号联系人。"</string>
     <string name="pin2_invalid" msgid="5470854099230755944">"固定拨号未更新,因为输入的 PIN 码有误。"</string>
-    <string name="fdn_invalid_number" msgid="2062898833049589309">"未能更新 FDN,因为号码超过 20 位数。"</string>
+    <string name="fdn_invalid_number" msgid="2062898833049589309">"未能更新 FDN,因为号码超过 <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> 位数。"</string>
     <string name="pin2_or_fdn_invalid" msgid="6025144083384701197">"固定拨号未更新。PIN2 码有误,或电话号码遭拒。"</string>
     <string name="fdn_failed" msgid="540018079008319747">"固定拨号操作失败。"</string>
     <string name="simContacts_emptyLoading" msgid="2203331234764498011">"正在从SIM卡读取..."</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3ccaa6c..02fa380 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -439,7 +439,7 @@
     <string name="deleting_fdn_contact" msgid="5669163206349319969">"正在刪除固定撥號…"</string>
     <string name="fdn_contact_deleted" msgid="7154162327112259569">"已刪除固定撥號。"</string>
     <string name="pin2_invalid" msgid="5470854099230755944">"您所輸入的 PIN 不正確,因此未更新 FDN。"</string>
-    <string name="fdn_invalid_number" msgid="2062898833049589309">"固定撥號的號碼超過 20 位數,因此無法更新。"</string>
+    <string name="fdn_invalid_number" msgid="2062898833049589309">"固定撥號的號碼超過 <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> 位數,因此無法更新。"</string>
     <string name="pin2_or_fdn_invalid" msgid="6025144083384701197">"未更新 FDN。可能是因為 PIN2 碼不正確或電話號碼遭拒。"</string>
     <string name="fdn_failed" msgid="540018079008319747">"FDN 操作失敗。"</string>
     <string name="simContacts_emptyLoading" msgid="2203331234764498011">"正在從 SIM 卡讀取…"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index fca8acf..8d84baf 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -15,6 +15,21 @@
 -->
 
 <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/colors.xml b/res/values/colors.xml
index 679eba6..2db3aa0 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -54,8 +54,6 @@
 
     <color name="dialer_dialpad_touch_tint">#330288d1</color>
     <color name="floating_action_button_touch_tint">#80ffffff</color>
-    <color name="emergency_shortcut_button_background_color">#40FFFFFF</color>
-    <color name="emergency_shortcut_confirm_button_background_color">#E25142</color>
 
     <!-- Color matches dialer settings light M2 theme.-->
     <color name="dialer_background_color">#ffffff</color>
@@ -63,4 +61,14 @@
     <color name="dialer_primary_text_color">#202124</color>
     <color name="dialer_secondary_text_color">#5f6368</color>
 
+    <!-- Colors for shortcut view -->
+    <color name="emergency_info_btn_bolder">#40ffffff</color>
+    <color name="emergency_info_btn_background_color">@android:color/transparent</color>
+    <color name="emergency_shortcut_button_background_color">#eae8e9</color>
+    <color name="emergency_shortcut_phone_type_icon_color">#e25142</color>
+    <color name="emergency_shortcut_confirm_button_background_color">#e25142</color>
+    <color name="emergency_shortcut_confirm_phone_type_icon_color">#e25142</color>
+    <color name="emergency_call_icon_color">#3c4043</color>
+    <color name="emergency_dialpad_fab_tint_color">@null</color>
+    <color name="white_70_percent">#b3ffffff</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 23d20fc..e1742d8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -118,20 +118,12 @@
     <dimen name="emergency_dialer_image_margin_start">20dp</dimen>
     <dimen name="emergency_dialer_image_margin_end">16dp</dimen>
 
-    <!-- Margin of dialpad button -->
-    <dimen name="emergency_dialer_dialpad_button_margin">40dp</dimen>
-
     <!-- Horizontal margin for button of emergency shortcut. -->
     <dimen name="emergency_shortcut_buttons_margin_horizontal">16dp</dimen>
 
     <!-- Horizontal padding for group of emergency number title-->
     <dimen name="emergency_number_title_group_padding_horizontal">16dp</dimen>
 
-    <!-- Height and vertical margin for the emergency information button. -->
-    <dimen name="emergency_info_button_height">80dp</dimen>
-    <dimen name="emergency_info_button_margin_vertical">40dp</dimen>
-    <dimen name="emergency_info_button_fix_margin_vertical">40dp</dimen>
-
     <!-- Margin for the emergency information button text. -->
     <dimen name="emergency_info_text_margin_end">20dp</dimen>
 
@@ -143,32 +135,10 @@
     <dimen name="emergency_shortcuts_function_icon_height">24dp</dimen>
     <dimen name="emergency_shortcuts_function_icon_width">24dp</dimen>
 
-    <!-- The height and width for the dialpad button -->
-    <dimen name="dialpad_button_height">56dp</dimen>
-    <dimen name="dialpad_button_width">56dp</dimen>
-
-    <!-- The height for title of emergency number and location info. -->
-    <dimen name="emergency_number_title_height">48dp</dimen>
-
-    <!-- The height and width for the image of location info.-->
-    <dimen name="location_image_height">16dp</dimen>
-    <dimen name="location_image_width">16dp</dimen>
-
-    <!-- The height for button of emergency shortcut. -->
-    <dimen name="emergency_shortcut_button_height">80dp</dimen>
-
     <!-- The height and width for the circle image of phone number type.-->
     <dimen name="phone_number_type_circle_image_height">40dp</dimen>
     <dimen name="phone_number_type_circle_image_width">40dp</dimen>
 
-    <!-- The height and width for the image of phone number type.-->
-    <dimen name="phone_number_type_image_height">24dp</dimen>
-    <dimen name="phone_number_type_image_width">24dp</dimen>
-
-    <!-- The height and width of phone icon.-->
-    <dimen name="phone_icon_height">24dp</dimen>
-    <dimen name="phone_icon_width">24dp</dimen>
-
     <!-- Margin for the emergency shortcut button.-->
     <dimen name="emergency_shortcuts_margin_end">60dp</dimen>
 
@@ -197,12 +167,6 @@
     <!-- The width for emergency number title container.-->
     <dimen name="emergency_number_title_container_width">210dp</dimen>
 
-    <!-- The text size for emergency number title.-->
-    <dimen name="emergency_number_title_text_size">13sp</dimen>
-
-    <!-- The text size for emergency location.-->
-    <dimen name="emergency_location_text_size">13sp</dimen>
-
     <!-- The text size for titles in settings page.-->
     <dimen name="dialer_head1_font_size">18dp</dimen>
 
@@ -211,4 +175,54 @@
 
     <!-- The text size for description in settings page.-->
     <dimen name="dialer_secondary_font_size">14dp</dimen>
+
+
+    <!-- Shortcut view vertical dimens. These values are tuned according to display
+         height in values-h<height>dp/dimens.xml -->
+    <!-- Height and vertical margin for the emergency information button. -->
+    <dimen name="emergency_info_button_height">74dp</dimen>
+    <dimen name="emergency_info_button_margin_top">32dp</dimen>
+    <dimen name="emergency_info_button_margin_bottom">37dp</dimen>
+
+    <!-- The text size of emergency info name and hint -->
+    <dimen name="emergency_info_name_text_size">16.9sp</dimen>
+    <dimen name="emergency_info_hint_text_size">12.3sp</dimen>
+    <dimen name="emergency_info_tap_hint_text_size">13.8sp</dimen>
+
+    <!-- The height for title of emergency number and location info. -->
+    <dimen name="emergency_number_title_height">37dp</dimen>
+    <!-- The text size for emergency number title.-->
+    <dimen name="emergency_number_title_text_size">10.7sp</dimen>
+
+    <!-- The height and width for the image of location info.-->
+    <dimen name="location_image_height">12.3dp</dimen>
+    <dimen name="location_image_width">12.3dp</dimen>
+
+    <!-- The text size for emergency location.-->
+    <dimen name="emergency_location_text_size">10.7sp</dimen>
+
+    <!-- The height and margin for button of emergency shortcut. -->
+    <dimen name="emergency_shortcut_button_height">74dp</dimen>
+    <dimen name="emergency_shortcut_button_margin_bottom">6.2dp</dimen>
+
+    <!-- The text size of emergency number, type and hint -->
+    <dimen name="emergency_shortcut_number_text_size">25sp</dimen>
+    <dimen name="emergency_shortcut_type_text_size">10.7sp</dimen>
+    <dimen name="emergency_shortcut_tap_hint_text_size">13.8sp</dimen>
+
+    <!-- The height and width for the image of phone number type.-->
+    <dimen name="phone_number_type_image_height">31dp</dimen>
+    <dimen name="phone_number_type_image_width">31dp</dimen>
+
+    <!-- The height and width of phone icon.-->
+    <dimen name="phone_icon_height">18.4dp</dimen>
+    <dimen name="phone_icon_width">18.4dp</dimen>
+
+    <!-- Margin of dialpad button -->
+    <dimen name="emergency_dialer_dialpad_button_margin">25dp</dimen>
+
+    <!-- The height and width for the dialpad button -->
+    <dimen name="dialpad_button_height">43dp</dimen>
+    <dimen name="dialpad_button_width">43dp</dimen>
+    <!-- End of Shortcut view vertical dimens. -->
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c68deb2..b97ef10 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -581,6 +581,12 @@
     <string name="roaming_check_price_warning">Check with your network provider for pricing.</string>
     <!-- Mobile network settings screen, dialog message title when user selects the Data roaming check box -->
     <string name="roaming_alert_title">Allow data roaming?</string>
+    <!-- Notification title for limited sim function during dual sim -->
+    <string name="limited_sim_function_notification_title">Limited SIM functionality</string>
+    <!-- Notification message for limited sim function during dual sim [CHAR LIMIT=80]-->
+    <string name="limited_sim_function_with_phone_num_notification_message"><xliff:g id="carrier_name">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="phone_number">%2$s</xliff:g>.</string>
+    <!-- Notification message for limited sim function during dual sim [CHAR LIMIT=80]-->
+    <string name="limited_sim_function_notification_message"><xliff:g id="carrier_name">%1$s</xliff:g> calls and data services may be blocked while using another SIM.</string>
     <!-- Mobile network settings screen, data usage setting check box name -->
     <string name="data_usage_title">App data usage</string>
     <!-- Summary about how much data has been used in a date range [CHAR LIMIT=100] -->
@@ -971,7 +977,7 @@
     <!-- FDN settings: error message displayed in a popup (toast) -->
     <string name="pin2_invalid">FDN wasn\'t updated because you typed an incorrect PIN.</string>
     <!-- FDN settings: error message displayed in a popup (toast) -->
-    <string name="fdn_invalid_number">FDN wasn\'t updated because the number exceeds 20 digits.</string>
+    <string name="fdn_invalid_number">FDN wasn\'t updated because the number exceeds <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> digits.</string>
     <!-- FDN settings: error message displayed in a popup (toast), when the entered
          FDN number was inappropriate, OR, PIN2 the user entered was incorrect.
          Because of API restriction, there's no way to determine which is the exact
@@ -1190,6 +1196,9 @@
          emergency calling is not currently available. -->
     <string name="dial_emergency_calling_not_available">Emergency calling not available</string>
 
+    <!-- Only handle pin entry when phone user is system-->
+    <string name="pin_puk_system_user_only">Only the owner of the device can enter PIN/PUK codes.</string>
+
     <!-- Text for description of police calling type -->
     <string name="police_type_description">Police</string>
     <!-- Text for description of ambulance calling type -->
@@ -1547,6 +1556,15 @@
          message is shown when an in-progress call is ended due to the battery being low. -->
     <string name="callFailed_low_battery">Video call ended due to low battery.</string>
 
+    <!-- In-call screen: error message shown when the user attempts to place an emergency call
+         over wifi calling (WFC), but emergency services are not available in the current
+         location. -->
+    <string name="callFailed_emergency_call_over_wfc_not_available">Emergency calls over Wi-Fi calling not available in this location.</string>
+
+    <!-- In-call screen: error message shown when the user attempts to place a call over wifi
+         calling (WFC), but wifi calling is not available in the current location. -->
+    <string name="callFailed_wfc_service_not_available_in_this_location">Wi-Fi calling is not available in this location.</string>
+
     <!-- The title for the change voicemail PIN activity -->
     <string name="change_pin_title">Change Voicemail PIN</string>
     <!-- The label for the continue button in change voicemail PIN activity -->
@@ -1758,7 +1776,7 @@
     <!-- Call barring settings screen, change password -->
     <string name="call_barring_change_pwd_description">Change call barring password</string>
     <!-- Call barring settings screen, not possible to change call barring password -->
-    <string name="call_barring_change_pwd_description_disabled">Cannot change call barring password.</string>
+    <string name="call_barring_change_pwd_description_disabled">Cannot change call barring password</string>
     <!-- Call barring settings screen, change password -->
     <string name="call_barring_pwd_not_match">Passwords do not match</string>
     <!-- Call barring settings screen, change password -->
@@ -1872,4 +1890,209 @@
     <string name="supp_service_over_ut_precautions_roaming_dual_sim">To use <xliff:g id="supp service">%1$s</xliff:g>, make sure mobile data and data roaming are turned on for SIM <xliff:g id="sim number">%2$d</xliff:g>. You can change these in mobile network settings.</string>
     <!-- supplementary services over ut precaution exit dialog choice -->
     <string name="supp_service_over_ut_precautions_dialog_dismiss">Dismiss</string>
+
+    <!-- Data Connection Enable. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="radio_info_data_connection_enable">Enable Data Connection</string>
+    <!-- Data Connection Disable. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="radio_info_data_connection_disable">Disable Data Connection</string>
+
+    <!-- VoLTE provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="volte_provisioned_switch_string">VoLTE Provisioned</string>
+
+    <!-- Video calling provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="vt_provisioned_switch_string">Video Calling Provisioned</string>
+
+    <!-- Wifi Calling provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="wfc_provisioned_switch_string">Wifi Calling Provisioned</string>
+
+    <!-- EAB provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="eab_provisioned_switch_string">EAB/Presence Provisioned</string>
+
+    <!-- Cbrs enable disable flag. Only shown in diagnostic screen, so precise translation is not needed -->
+    <string name="cbrs_data_switch_string">Cbrs Data</string>
+
+    <!-- Dsds enable/disable flag. Only shown in diagnostic screen, so precise translation is not needed, [CHAR LIMIT=none] -->
+    <string name="dsds_switch_string">Enable DSDS</string>
+
+    <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+    <string name="dsds_dialog_title">Restart Device?</string>
+
+    <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+    <string name="dsds_dialog_message">You need to restart your device to change this setting.</string>
+
+    <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+    <string name="dsds_dialog_confirm">Restart</string>
+
+    <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+    <string name="dsds_dialog_cancel">Cancel</string>
+
+    <!-- Title for controlling on/off for Mobile phone's radio power. Only shown in diagnostic screen, so precise translation is not needed. -->
+    <string name="radio_info_radio_power">Mobile Radio Power</string>
+
+    <!-- Phone Info screen. Menu item label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_menu_viewADN">View SIM Address Book</string>
+    <!-- Phone Info screen. Menu item label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_menu_viewFDN">View Fixed Dialing Numbers</string>
+    <!-- Phone Info screen. Menu item label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_menu_viewSDN">View Service Dialing Numbers</string>
+    <!-- Phone Info screen. Menu item label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_menu_getIMS">IMS Service Status</string>
+
+    <!-- Phone Info screen. IMS Registration Title.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status_title">IMS Status</string>
+
+    <!-- Phone Info screen. IMS Status - Registered.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status_registered">Registered</string>
+    <!-- Phone Info screen. Ims Status - Unregistered.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status_not_registered">Not Registered</string>
+
+    <!-- Phone Info screen. Ims Feature Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_feature_status_available">Available</string>
+    <!-- Phone Info screen. Ims Feature status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_feature_status_unavailable">Unavailable</string>
+
+    <!-- Phone Info screen. IMS Registration.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ims_reg_status">IMS Registration: <xliff:g id="status" example="registered">%1$s</xliff:g>\u000AVoice over LTE: <xliff:g id="availability" example="available">%2$s</xliff:g>\u000AVoice over WiFi: <xliff:g id="availability" example="available">%3$s</xliff:g>\u000AVideo Calling: <xliff:g id="availability" example="available">%4$s</xliff:g>\u000AUT Interface: <xliff:g id="availability" example="available">%5$s</xliff:g></string>
+
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_service_in">In Service</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_service_out">Out of Service</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_service_emergency">Emergency Calls Only</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_service_off">Radio Off</string>
+
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_roaming_in">Roaming</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_roaming_not">Not Roaming</string>
+
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_phone_idle">Idle</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_phone_ringing">Ringing</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_phone_offhook">Call in Progress</string>
+
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_data_disconnected">Disconnected</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_data_connecting">Connecting</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_data_connected">Connected</string>
+    <!-- Phone Info screen. Status label.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_data_suspended">Suspended</string>
+
+    <!-- Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_unknown">Unknown</string>
+    <!-- Phone Info screen. Units shown after a value.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_display_packets">pkts</string>
+    <!-- Phone Info screen. Units shown after a value.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_display_bytes">bytes</string>
+    <!-- Phone Info screen. Units shown after a value.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_display_dbm">dBm</string>
+    <!-- Phone Info screen. Units shown after a value.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_display_asu">asu</string>
+    <!-- Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_lac">LAC</string>
+    <!-- Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radioInfo_cid">CID</string>
+
+
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_subid">Current subId:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_dds">SubId of default data SIM:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_dl_kbps">DL Bandwidth (kbps):</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ul_kbps">UL Bandwidth (kbps):</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_signal_location_label">Cell Location Info (deprecated):</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_phy_chan_config">LTE Physical Channel Configuration:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_cell_info_refresh_rate">Cell Info Refresh Rate:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_cellinfo_label">All Cell Measurement Info:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_gprs_service_label">Data Service:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_roaming_label">Roaming:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_imei_label">IMEI:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, DO NOT TRANSLATE. -->
+    <string name="radio_info_imsi_label">IMSI:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_call_redirect_label">Call Redirect:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ppp_resets_label">Number of PPP Reset Since Boot:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_current_network_label">Current Network:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ppp_received_label">Data Received:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_gsm_service_label">Voice Service:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_signal_strength_label">Signal Strength:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_call_status_label">Voice Call Status:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ppp_sent_label">Data Sent:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_message_waiting_label">Message Waiting:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_phone_number_label">Phone Number:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_band_mode_label">Select Radio Band</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_voice_network_type_label">Voice Network Type:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_data_network_type_label">Data Network Type:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="phone_index_label">Select phone index</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_set_perferred_label">Set Preferred Network Type:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ping_hostname_v4">Ping Hostname(www.google.com) IPv4:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_ping_hostname_v6">Ping Hostname(www.google.com) IPv6:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_http_client_test">HTTP Client Test:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="ping_test_label">Run Ping Test</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_smsc_label">SMSC:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_smsc_update_label">Update</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_smsc_refresh_label">Refresh</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_toggle_dns_check_label">Toggle DNS Check</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="oem_radio_info_label">OEM-specific Info/Settings</string>
+
+    <!-- Band Mode Selection -->
+    <!-- Band mode screen.  Title of activity. -->
+    <string name="band_mode_title">Set Radio Band Mode</string>
+    <!-- Band mode screen.  Loading message. -->
+    <string name="band_mode_loading">Loading Band List\u2026</string>
+    <!-- Band mode screen. Button caption to set the bandmode. -->
+    <string name="band_mode_set">Set</string>
+    <!-- Band mode screen. Status message when unsuccessful. -->
+    <string name="band_mode_failed">Unsuccessful</string>
+    <!-- Band mode screen. Statusm essage when successful. -->
+    <string name="band_mode_succeeded">Successful</string>
+
+    <!-- The title of the activity to see phone info -->
+    <string name="phone_info_label" product="tablet">Tablet info</string>
+    <!-- The title of the activity to see phone info -->
+    <string name="phone_info_label" product="default">Phone info</string>
+
+    <!-- Carrier Provisioning Info [CHAR LIMIT=NONE] -->
+    <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 5f9a453..e95142b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -45,6 +45,23 @@
         <item name="android:layout_height">wrap_content</item>
     </style>
 
+    <style name="RadioInfo_entry_layout">
+        <item name="android:orientation">horizontal</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="info_value">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.info_value</item>
+    </style>
+
+    <style name="form_value">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">match_parent</item>
+    </style>
+
     <style name="TextAppearance" parent="android:TextAppearance">
     </style>
 
@@ -53,6 +70,11 @@
         <item name="android:textStyle">bold</item>
     </style>
 
+    <style name="TextAppearance.info_value">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
     <!-- Preference Style for the phone number preferences -->
     <style name="EditPhoneNumberPreference">
         <item name="enableButtonText">@string/enable</item>
@@ -189,15 +211,6 @@
         <item name="android:textColor">?android:attr/textColorPrimaryInverseDisableOnly</item>
     </style>
 
-    <style name="NetworkOperatorsSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="android:forceDarkAllowed">true</item>
-        <item name="android:switchPreferenceStyle">@style/SettingsSwitchPreference</item>
-        <item name="android:preferenceCategoryStyle">@style/SettingsPreferenceCategory</item>
-        <item name="android:preferenceStyle">@style/SettingsPreference</item>
-        <item name="android:dialogPreferenceStyle">@style/SettingsDialogPreference</item>
-        <item name="android:preferenceScreenStyle">@style/SettingsPreferenceScreen</item>
-    </style>
-
     <style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
         <item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
         <item name="android:minHeight">3dip</item>
@@ -335,25 +348,44 @@
         <item name="android:listDivider">@null</item>
     </style>
 
-    <style name="HeadlineTextAppearance">
-        <item name="android:textColor">@android:color/white</item>
-        <item name="android:textSize">22sp</item>
+    <style name="EmergencyInfoNameTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@*android:color/primary_text_default_material_dark</item>
+        <item name="android:textSize">@dimen/emergency_info_name_text_size</item>
     </style>
 
-    <style name="SubtitleTextAppearance" parent="@style/HeadlineTextAppearance">
-        <item name="android:textSize">14sp</item>
+    <style name="EmergencyInfoHintTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@*android:color/secondary_text_default_material_dark</item>
+        <item name="android:textSize">@dimen/emergency_info_hint_text_size</item>
+    </style>
+
+    <style name="EmergencyInfoTapHintTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/white</item>
+        <item name="android:textSize">@dimen/emergency_info_tap_hint_text_size</item>
+    </style>
+
+    <style name="ShortcutViewHintTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@*android:color/secondary_text_default_material_dark</item>
     </style>
 
     <style name="PhoneNumberTextAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textColor">@*android:color/primary_text_default_material_light</item>
+        <item name="android:textSize">@dimen/emergency_shortcut_number_text_size</item>
+    </style>
+
+    <style name="PhoneNumberTypeAppearance">
+        <item name="android:fontFamily">roboto</item>
+        <item name="android:textColor">@*android:color/secondary_text_default_material_light</item>
+        <item name="android:textSize">@dimen/emergency_shortcut_type_text_size</item>
+    </style>
+
+    <style name="PhoneNumberTapHintAppearance">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
         <item name="android:textColor">@android:color/white</item>
-        <item name="android:textSize">32sp</item>
-    </style>
-
-    <style name="PhoneCallHintTextAppearance" parent="@style/PhoneNumberTextAppearance">
-        <item name="android:textSize">18sp</item>
-    </style>
-
-    <style name="ShortcutsHintTextAppearance" parent="@style/HeadlineTextAppearance">
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">@dimen/emergency_shortcut_tap_hint_text_size</item>
     </style>
 </resources>
diff --git a/res/values/styles_preference.xml b/res/values/styles_preference.xml
index ceea4a6..fef4f72 100644
--- a/res/values/styles_preference.xml
+++ b/res/values/styles_preference.xml
@@ -29,10 +29,6 @@
         <item name="android:singleLineTitle">false</item>
     </style>
 
-    <style name="SettingsPreferenceCategory" parent="@*android:style/Preference.DeviceDefault.Category">
-        <item name="android:layout">@layout/preference_category_material_settings_with_divider</item>
-    </style>
-
     <style name="SettingsDialogPreference" parent="@*android:style/Preference.DeviceDefault.DialogPreference">
         <item name="android:singleLineTitle">false</item>
         <item name="android:iconSpaceReserved">true</item>
diff --git a/res/xml/callbarring_options.xml b/res/xml/callbarring_options.xml
index 6f2c48a..6506031 100644
--- a/res/xml/callbarring_options.xml
+++ b/res/xml/callbarring_options.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-     xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+     xmlns:phone="http://schemas.android.com/apk/res-auto"
      android:title="@string/call_barring_settings">
 
     <!-- Note for all com.android.phone.EditPinPreference objects
diff --git a/res/xml/callforward_options.xml b/res/xml/callforward_options.xml
index 0ff1e90..775151e 100644
--- a/res/xml/callforward_options.xml
+++ b/res/xml/callforward_options.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-     xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+     xmlns:phone="http://schemas.android.com/apk/res-auto"
      android:title="@string/call_forwarding_settings">
 
 
diff --git a/res/xml/voicemail_settings.xml b/res/xml/voicemail_settings.xml
index 021a764..cbe1cf4 100644
--- a/res/xml/voicemail_settings.xml
+++ b/res/xml/voicemail_settings.xml
@@ -15,7 +15,7 @@
 -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+    xmlns:phone="http://schemas.android.com/apk/res-auto"
     android:title="@string/voicemail">
 
     <com.android.phone.settings.VoicemailProviderListPreference
diff --git a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
index ec77ff1..1cf7f4b 100644
--- a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
+++ b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
@@ -126,7 +126,7 @@
      * @param context The context.
      */
     void verifyAndPurgeInvalidPhoneAccounts(Context context) {
-        TelecomManager telecomManager = TelecomManager.from(context);
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
         SipProfileDb profileDb = new SipProfileDb(context);
         List<PhoneAccountHandle> accountHandles = telecomManager.getPhoneAccountsSupportingScheme(
                 PhoneAccount.SCHEME_SIP);
@@ -188,7 +188,8 @@
 
         // Un-register its PhoneAccount.
         PhoneAccountHandle handle = SipUtil.createAccountHandle(context, sipProfileName);
-        TelecomManager.from(context).unregisterPhoneAccount(handle);
+        TelecomManager tm = context.getSystemService(TelecomManager.class);
+        tm.unregisterPhoneAccount(handle);
     }
 
     /**
@@ -235,7 +236,7 @@
     private void startSipProfiles(Context context, String sipProfileName, boolean enableProfile) {
         final SipPreferences sipPreferences = new SipPreferences(context);
         boolean isReceivingCalls = sipPreferences.isReceivingCallsEnabled();
-        TelecomManager telecomManager = TelecomManager.from(context);
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
         SipManager sipManager = SipManager.newInstance(context);
         SipProfileDb profileDb = new SipProfileDb(context);
         List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
diff --git a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
index 19f5882..3212c00 100644
--- a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
+++ b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
@@ -73,7 +73,7 @@
         if (accountHandle != null) {
             Bundle extras = new Bundle();
             extras.putParcelable(SipUtil.EXTRA_INCOMING_CALL_INTENT, intent);
-            TelecomManager tm = TelecomManager.from(context);
+            TelecomManager tm = context.getSystemService(TelecomManager.class);
             PhoneAccount phoneAccount = tm.getPhoneAccount(accountHandle);
             if (phoneAccount != null && phoneAccount.isEnabled()) {
                 tm.addNewIncomingCall(accountHandle, extras);
diff --git a/sip/src/com/android/services/telephony/sip/SipProfileDb.java b/sip/src/com/android/services/telephony/sip/SipProfileDb.java
index bb1c7ec..812ebb4 100644
--- a/sip/src/com/android/services/telephony/sip/SipProfileDb.java
+++ b/sip/src/com/android/services/telephony/sip/SipProfileDb.java
@@ -16,11 +16,10 @@
 
 package com.android.services.telephony.sip;
 
-import com.android.internal.os.AtomicFile;
-
 import android.content.Context;
 import android.net.sip.SipProfile;
 import android.text.TextUtils;
+import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
 
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 320fc24..3f57cae 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -19,13 +19,16 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+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.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.os.UserManager;
@@ -109,6 +112,50 @@
      * Click Listeners, handle click based on objects attached to UI.
      */
 
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            log("onReceive: " + intent.getAction());
+
+            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
+                log("ACTION_AIRPLANE_MODE_CHANGED");
+
+                boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
+                handleAirplaneModeChange(isAirplaneModeOn);
+            }
+        }
+    };
+
+    private void handleAirplaneModeChange(boolean isAirplaneModeOn) {
+        PersistableBundle b = null;
+        if (mSubscriptionInfoHelper.hasSubId()) {
+            b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+                    mSubscriptionInfoHelper.getSubId());
+        } else {
+            b = PhoneGlobals.getInstance().getCarrierConfig();
+        }
+
+        if (b != null && b.getBoolean(
+                CarrierConfigManager.KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL)) {
+            PreferenceScreen preferenceScreen = getPreferenceScreen();
+            Preference callForwarding = preferenceScreen.findPreference(
+                    GsmUmtsCallOptions.CALL_FORWARDING_KEY);
+            Preference callBarring = preferenceScreen.findPreference(
+                    GsmUmtsCallOptions.CALL_BARRING_KEY);
+            Preference additional = preferenceScreen.findPreference(
+                    GsmUmtsCallOptions.ADDITIONAL_GSM_SETTINGS_KEY);
+            if (callForwarding != null) {
+                callForwarding.setEnabled(!isAirplaneModeOn);
+            }
+            if (callBarring != null) {
+                callBarring.setEnabled(!isAirplaneModeOn);
+            }
+            if (additional != null) {
+                additional.setEnabled(!isAirplaneModeOn);
+            }
+        }
+    }
+
     // Click listener for all toggle events
     @Override
     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
@@ -181,9 +228,9 @@
                                 startActivity(intent);
                             }
                         };
-                builder.setMessage(getResources().getString(
+                builder.setMessage(getResourcesForSubId().getString(
                                 R.string.enable_video_calling_dialog_msg))
-                        .setNeutralButton(getResources().getString(
+                        .setNeutralButton(getResourcesForSubId().getString(
                                 R.string.enable_video_calling_dialog_settings),
                                 networkSettingsClickListener)
                         .setPositiveButton(android.R.string.ok, null)
@@ -202,7 +249,8 @@
         if (DBG) log("onCreate: Intent is " + getIntent());
 
         // Make sure we are running as an admin user.
-        if (!UserManager.get(this).isAdminUser()) {
+        UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+        if (!userManager.isAdminUser()) {
             Toast.makeText(this, R.string.call_settings_admin_user_only,
                     Toast.LENGTH_SHORT).show();
             finish();
@@ -210,10 +258,10 @@
         }
 
         mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
-        mSubscriptionInfoHelper.setActionBarTitle(
-                getActionBar(), getResources(), R.string.call_settings_with_label);
         mPhone = mSubscriptionInfoHelper.getPhone();
-        mTelecomManager = TelecomManager.from(this);
+        mSubscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
+        mTelecomManager = getSystemService(TelecomManager.class);
     }
 
     private void updateImsManager(Phone phone) {
@@ -228,8 +276,8 @@
     }
 
     private void listenPhoneState(boolean listen) {
-        TelephonyManager telephonyManager =
-                (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+        TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(mPhone.getSubId());
         telephonyManager.listen(mPhoneStateListener, listen
                 ? PhoneStateListener.LISTEN_CALL_STATE : PhoneStateListener.LISTEN_NONE);
     }
@@ -238,10 +286,7 @@
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
-            // Use TelecomManager#getCallStete instead of 'state' parameter because it needs
-            // to check the current state of all phone calls.
-            boolean isCallStateIdle =
-                    mTelecomManager.getCallState() == TelephonyManager.CALL_STATE_IDLE;
+            boolean isCallStateIdle = state == TelephonyManager.CALL_STATE_IDLE;
             if (mEnableVideoCalling != null) {
                 mEnableVideoCalling.setEnabled(isCallStateIdle);
             }
@@ -267,6 +312,7 @@
     protected void onPause() {
         super.onPause();
         listenPhoneState(false);
+        unregisterReceiver(mReceiver);
 
         // Remove callback for provisioning changes.
         try {
@@ -317,7 +363,7 @@
         mButtonAutoRetry = (SwitchPreference) findPreference(BUTTON_RETRY_KEY);
 
         mEnableVideoCalling = (SwitchPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
-        mButtonWifiCalling = findPreference(getResources().getString(
+        mButtonWifiCalling = findPreference(getResourcesForSubId().getString(
                 R.string.wifi_calling_settings_key));
 
         PersistableBundle carrierConfig =
@@ -379,6 +425,10 @@
         } catch (ImsException e) {
             Log.w(LOG_TAG, "onResume: Unable to register callback for provisioning changes.");
         }
+
+        IntentFilter intentFilter =
+                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        registerReceiver(mReceiver, intentFilter);
     }
 
     private void updateVtWfc() {
@@ -426,8 +476,7 @@
         } else if (!mImsMgr.isWfcEnabledByPlatform() || !mImsMgr.isWfcProvisionedOnDevice()) {
             prefSet.removePreference(mButtonWifiCalling);
         } else {
-            String title = SubscriptionManager.getResourcesForSubId(mPhone.getContext(),
-                    mPhone.getSubId()).getString(R.string.wifi_calling);
+            String title = getResourcesForSubId().getString(R.string.wifi_calling);
             mButtonWifiCalling.setTitle(title);
 
             int resId = com.android.internal.R.string.wifi_calling_off_summary;
@@ -449,7 +498,7 @@
                         if (DBG) log("Unexpected WFC mode value: " + wfcMode);
                 }
             }
-            mButtonWifiCalling.setSummary(resId);
+            mButtonWifiCalling.setSummary(getResourcesForSubId().getString(resId));
             Intent intent = mButtonWifiCalling.getIntent();
             if (intent != null) {
                 intent.putExtra(Settings.EXTRA_SUB_ID, mPhone.getSubId());
@@ -511,9 +560,9 @@
         setIntent(newIntent);
 
         mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
-        mSubscriptionInfoHelper.setActionBarTitle(
-                getActionBar(), getResources(), R.string.call_settings_with_label);
         mPhone = mSubscriptionInfoHelper.getPhone();
+        mSubscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
     }
 
     private static void log(String msg) {
@@ -542,4 +591,12 @@
         activity.startActivity(intent);
         activity.finish();
     }
+
+    private Resources getResourcesForSubId() {
+        if (mPhone != null) {
+            return SubscriptionManager.getResourcesForSubId(mPhone.getContext(), mPhone.getSubId());
+        } else {
+            return getResources();
+        }
+    }
 }
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index d1eb3dc..d451ccf 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -20,6 +20,7 @@
 import static android.service.carrier.CarrierService.ICarrierServiceWrapper.RESULT_ERROR;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -55,7 +56,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SubscriptionInfoUpdater;
 import com.android.internal.telephony.TelephonyPermissions;
-import com.android.internal.util.ArrayUtils;
+import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 
@@ -94,6 +95,8 @@
     private PersistableBundle[] mConfigFromDefaultApp;
     // Carrier configs from privileged carrier config app, indexed by phoneID.
     private PersistableBundle[] mConfigFromCarrierApp;
+    // Persistent Carrier configs that are provided via the override test API, indexed by phone ID.
+    private PersistableBundle[] mPersistentOverrideConfigs;
     // Carrier configs that are provided via the override test API, indexed by phone ID.
     private PersistableBundle[] mOverrideConfigs;
     // Service connection for binding to config app.
@@ -107,7 +110,7 @@
     private final BroadcastReceiver mBootReceiver = new ConfigLoaderBroadcastReceiver();
     // Broadcast receiver for SIM and pkg intents, register intent filter in constructor.
     private final BroadcastReceiver mPackageReceiver = new ConfigLoaderBroadcastReceiver();
-    private final LocalLog mCarrierConfigLoadingLog = new LocalLog(50);
+    private final LocalLog mCarrierConfigLoadingLog = new LocalLog(100);
 
 
     // Message codes; see mHandler below.
@@ -141,6 +144,8 @@
     private static final int EVENT_FETCH_CARRIER_TIMEOUT = 15;
     // SubscriptionInfoUpdater has finished updating the sub for the carrier config.
     private static final int EVENT_SUBSCRIPTION_INFO_UPDATED = 16;
+    // Multi-SIM config changed.
+    private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 17;
 
     private static final int BIND_TIMEOUT_MILLIS = 30000;
 
@@ -149,6 +154,8 @@
     private static final String TAG_VERSION = "package_version";
     private static final String TAG_BUNDLE = "bundle_data";
 
+    private static final String OVERRIDE_PACKAGE_ADDITION = "-override";
+
     // SharedPreferences key for last known build fingerprint.
     private static final String KEY_FINGERPRINT = "build_fingerprint";
 
@@ -174,31 +181,26 @@
         public void handleMessage(Message msg) {
             final int phoneId = msg.arg1;
             logWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+            if (!SubscriptionManager.isValidPhoneId(phoneId)
+                    && msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
+                return;
+            }
             switch (msg.what) {
                 case EVENT_CLEAR_CONFIG:
                 {
-                    /* Ignore clear configuration request if device is being shutdown. */
-                    Phone phone = PhoneFactory.getPhone(phoneId);
-                    if (phone != null) {
-                        if (phone.isShuttingDown()) {
-                            break;
-                        }
-                    }
-
-                    mConfigFromDefaultApp[phoneId] = null;
-                    mConfigFromCarrierApp[phoneId] = null;
-                    mServiceConnection[phoneId] = null;
-                    broadcastConfigChangedIntent(phoneId, false);
+                    clearConfigForPhone(phoneId, true);
                     break;
                 }
 
                 case EVENT_SYSTEM_UNLOCKED:
                 {
-                    for (int i = 0; i < TelephonyManager.from(mContext).getMaxPhoneCount(); ++i) {
+                    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.
                         if (mHasSentConfigChange[i]) {
+                            logWithLocalLog("System unlocked");
                             updateConfigForPhoneId(i);
                         }
                     }
@@ -211,8 +213,10 @@
                     // Only update if there are cached config removed to avoid updating config for
                     // unrelated packages.
                     if (clearCachedConfigForPackage(carrierPackageName)) {
-                        int numPhones = TelephonyManager.from(mContext).getMaxPhoneCount();
+                        int numPhones = TelephonyManager.from(mContext).getActiveModemCount();
                         for (int i = 0; i < numPhones; ++i) {
+                            logWithLocalLog("Package changed: " + carrierPackageName
+                                    + ", phone=" + i);
                             updateConfigForPhoneId(i);
                         }
                     }
@@ -221,8 +225,17 @@
 
                 case EVENT_DO_FETCH_DEFAULT:
                 {
-                    final PersistableBundle config =
-                            restoreConfigFromXml(mPlatformCarrierConfigPackage, phoneId);
+                    // Restore persistent override values.
+                    PersistableBundle config = restoreConfigFromXml(
+                            mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
+                    if (config != null) {
+                        log("Loaded persistent override config from XML. package="
+                                + mPlatformCarrierConfigPackage
+                                + " phoneId=" + phoneId);
+                        mPersistentOverrideConfigs[phoneId] = config;
+                    }
+
+                    config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
                     if (config != null) {
                         log(
                                 "Loaded config from XML. package="
@@ -284,8 +297,8 @@
                                     }
                                     PersistableBundle config =
                                             resultData.getParcelable(KEY_CONFIG_BUNDLE);
-                                    saveConfigToXml(
-                                            mPlatformCarrierConfigPackage, phoneId, config);
+                                    saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
+                                        carrierId, config);
                                     mConfigFromDefaultApp[phoneId] = config;
                                     sendMessage(
                                             obtainMessage(
@@ -352,7 +365,7 @@
                 {
                     final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
                     final PersistableBundle config =
-                            restoreConfigFromXml(carrierPackageName, phoneId);
+                            restoreConfigFromXml(carrierPackageName, "", phoneId);
                     if (config != null) {
                         log(
                                 "Loaded config from XML. package="
@@ -415,8 +428,8 @@
                                     }
                                     PersistableBundle config =
                                             resultData.getParcelable(KEY_CONFIG_BUNDLE);
-                                    saveConfigToXml(
-                                            getCarrierPackageForPhoneId(phoneId), phoneId, config);
+                                    saveConfigToXml(getCarrierPackageForPhoneId(phoneId), "",
+                                            phoneId, carrierId, config);
                                     mConfigFromCarrierApp[phoneId] = config;
                                     sendMessage(
                                             obtainMessage(
@@ -494,6 +507,9 @@
                 case EVENT_SUBSCRIPTION_INFO_UPDATED:
                     broadcastConfigChangedIntent(phoneId);
                     break;
+                case EVENT_MULTI_SIM_CONFIG_CHANGED:
+                    onMultiSimConfigChanged();
+                    break;
             }
         }
     }
@@ -523,9 +539,10 @@
         pkgFilter.addDataScheme("package");
         context.registerReceiver(mPackageReceiver, pkgFilter);
 
-        int numPhones = TelephonyManager.from(context).getMaxPhoneCount();
+        int numPhones = TelephonyManager.from(context).getSupportedModemCount();
         mConfigFromDefaultApp = new PersistableBundle[numPhones];
         mConfigFromCarrierApp = new PersistableBundle[numPhones];
+        mPersistentOverrideConfigs = new PersistableBundle[numPhones];
         mOverrideConfigs = new PersistableBundle[numPhones];
         mServiceConnection = new CarrierServiceConnection[numPhones];
         mHasSentConfigChange = new boolean[numPhones];
@@ -553,6 +570,23 @@
         }
     }
 
+    private void clearConfigForPhone(int phoneId, boolean sendBroadcast) {
+        /* Ignore clear configuration request if device is being shutdown. */
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        if (phone != null) {
+            if (phone.isShuttingDown()) {
+                return;
+            }
+        }
+
+        mConfigFromDefaultApp[phoneId] = null;
+        mConfigFromCarrierApp[phoneId] = null;
+        mServiceConnection[phoneId] = null;
+        mHasSentConfigChange[phoneId] = false;
+
+        if (sendBroadcast) broadcastConfigChangedIntent(phoneId, false);
+    }
+
     private void notifySubscriptionInfoUpdater(int phoneId) {
         String configPackagename;
         PersistableBundle configToSend;
@@ -566,6 +600,14 @@
             configPackagename = mPlatformCarrierConfigPackage;
             configToSend = mConfigFromDefaultApp[phoneId];
         }
+
+        // mOverrideConfigs is for testing. And it will override current configs.
+        PersistableBundle config = mOverrideConfigs[phoneId];
+        if (config != null) {
+            configToSend = new PersistableBundle(configToSend);
+            configToSend.putAll(config);
+        }
+
         mSubscriptionInfoUpdater.updateSubscriptionByCarrierConfigAndNotifyComplete(
                 phoneId, configPackagename, configToSend,
                 mHandler.obtainMessage(EVENT_SUBSCRIPTION_INFO_UPDATED, phoneId, -1));
@@ -710,10 +752,13 @@
      * In case of errors or invalid input, no file will be written.
      *
      * @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.
      */
-    private void saveConfigToXml(String packageName, int phoneId, PersistableBundle config) {
+    private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
+            CarrierIdentifier carrierId, PersistableBundle config) {
         if (SubscriptionManager.getSimStateForSlotIndex(phoneId)
                 != TelephonyManager.SIM_STATE_LOADED) {
             loge("Skip save config because SIM records are not loaded.");
@@ -721,7 +766,7 @@
         }
 
         final String iccid = getIccIdForPhoneId(phoneId);
-        final int cid = getSpecificCarrierIdForPhoneId(phoneId);
+        final int cid = carrierId.getSpecificCarrierId();
         if (packageName == null || iccid == null) {
             loge("Cannot save config with null packageName or iccid.");
             return;
@@ -747,7 +792,7 @@
         try {
             outFile = new FileOutputStream(
                     new File(mContext.getFilesDir(),
-                            getFilenameForConfig(packageName, iccid, cid)));
+                            getFilenameForConfig(packageName, extraString, iccid, cid)));
             FastXmlSerializer out = new FastXmlSerializer();
             out.setOutput(outFile, "utf-8");
             out.startDocument("utf-8", true);
@@ -781,11 +826,13 @@
      * current version, then null will be returned.
      *
      * @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.
      * @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.
      */
-    private PersistableBundle restoreConfigFromXml(String packageName, int phoneId) {
+    private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
+            int phoneId) {
         final String version = getPackageVersion(packageName);
         if (version == null) {
             loge("Failed to get package version for: " + packageName);
@@ -805,11 +852,12 @@
         }
 
         PersistableBundle restoredBundle = null;
+        File file = null;
         FileInputStream inFile = null;
         try {
-            inFile = new FileInputStream(
-                    new File(mContext.getFilesDir(),
-                            getFilenameForConfig(packageName, iccid, cid)));
+            file = new File(mContext.getFilesDir(),
+                    getFilenameForConfig(packageName, extraString, iccid, cid));
+            inFile = new FileInputStream(file);
             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
             parser.setInput(inFile, "utf-8");
 
@@ -831,7 +879,9 @@
             inFile.close();
         }
         catch (FileNotFoundException e) {
-            loge(e.toString());
+            // 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 (XmlPullParserException e) {
             loge(e.toString());
@@ -872,13 +922,13 @@
     }
 
     /** Builds a canonical file name for a config file. */
-    private String getFilenameForConfig(@NonNull String packageName, @NonNull String iccid,
-                                        int cid) {
+    private String getFilenameForConfig(@NonNull String packageName, @NonNull String extraString,
+                                        @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
         // cases SIM OTA resolves to different carrier while iccid remains the same.
-        return "carrierconfig-" + packageName + "-" + iccid + "-" + cid + ".xml";
+        return "carrierconfig-" + packageName + extraString + "-" + iccid + "-" + cid + ".xml";
     }
 
     /** Return the current version code of a package, or null if the name is not found. */
@@ -908,6 +958,13 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
     }
 
+    private void onMultiSimConfigChanged() {
+        for (int i = TelephonyManager.from(mContext).getActiveModemCount();
+                i < mConfigFromDefaultApp.length; i++) {
+            clearConfigForPhone(i, false);
+        }
+    }
+
     @Override
     public @NonNull PersistableBundle getConfigForSubId(int subId, String callingPackage) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
@@ -921,24 +978,32 @@
             PersistableBundle config = mConfigFromDefaultApp[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+                if (getCarrierPackageForPhoneId(phoneId) == null) {
+                    retConfig.putBoolean(
+                            CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+                }
             }
             config = mConfigFromCarrierApp[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
                 retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
+            config = mPersistentOverrideConfigs[phoneId];
+            if (config != null) {
+                retConfig.putAll(config);
+                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+            }
             config = mOverrideConfigs[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
         }
         return retConfig;
     }
 
     @Override
-    public void overrideConfig(int subscriptionId, PersistableBundle overrides) {
+    public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrides,
+            boolean persistent) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MODIFY_PHONE_STATE, null);
         //TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
@@ -947,18 +1012,38 @@
             log("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
             return;
         }
+        overrideConfig(mOverrideConfigs, phoneId, overrides);
 
-        if (overrides == null) {
-            mOverrideConfigs[phoneId] = new PersistableBundle();
-        } else if (mOverrideConfigs[phoneId] == null) {
-            mOverrideConfigs[phoneId] = overrides;
-        } else {
-            mOverrideConfigs[phoneId].putAll(overrides);
+        if (persistent) {
+            overrideConfig(mPersistentOverrideConfigs, phoneId, overrides);
+
+            if (overrides != null) {
+                final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
+                saveConfigToXml(mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId,
+                        carrierId, mPersistentOverrideConfigs[phoneId]);
+            } else {
+                final String iccid = getIccIdForPhoneId(phoneId);
+                final int cid = getSpecificCarrierIdForPhoneId(phoneId);
+                String fileName = getFilenameForConfig(mPlatformCarrierConfigPackage,
+                        OVERRIDE_PACKAGE_ADDITION, iccid, cid);
+                File fileToDelete = new File(mContext.getFilesDir(), fileName);
+                fileToDelete.delete();
+            }
         }
-
         notifySubscriptionInfoUpdater(phoneId);
     }
 
+    private void overrideConfig(@NonNull PersistableBundle[] currentOverrides, int phoneId,
+            @Nullable PersistableBundle overrides) {
+        if (overrides == null) {
+            currentOverrides[phoneId] = new PersistableBundle();
+        } else if (currentOverrides[phoneId] == null) {
+            currentOverrides[phoneId] = overrides;
+        } else {
+            currentOverrides[phoneId].putAll(overrides);
+        }
+    }
+
     @Override
     public void notifyConfigChangedForSubId(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
@@ -1030,6 +1115,8 @@
             // display ConfigFromCarrierApp
             printConfig(mConfigFromCarrierApp[i], pw, "mConfigFromCarrierApp");
             pw.println("");
+            printConfig(mPersistentOverrideConfigs[i], pw, "mPersistentOverrideConfigs");
+            pw.println("");
             printConfig(mOverrideConfigs[i], pw, "mOverrideConfigs");
         }
 
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 74b8a45..35af20d 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -136,9 +136,9 @@
         a.recycle();
 
         //get the summary settings, use CheckBoxPreference as the standard.
-        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 = context.obtainStyledAttributes(attrs, R.styleable.CheckBoxPreference, 0, 0);
+        mSummaryOn = a.getString(R.styleable.CheckBoxPreference_summaryOn);
+        mSummaryOff = a.getString(R.styleable.CheckBoxPreference_summaryOff);
         a.recycle();
     }
 
diff --git a/src/com/android/phone/EmergencyActionGroup.java b/src/com/android/phone/EmergencyActionGroup.java
index b8bf7a8..53ec1eb 100644
--- a/src/com/android/phone/EmergencyActionGroup.java
+++ b/src/com/android/phone/EmergencyActionGroup.java
@@ -157,21 +157,19 @@
     public void onClick(View v) {
         Intent intent = (Intent) v.getTag(R.id.tag_intent);
 
-        switch (v.getId()) {
-            case R.id.action1:
-            case R.id.action2:
-            case R.id.action3:
-                if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
-                    getContext().startActivity(intent);
-                } else {
-                    revealTheButton(v);
-                }
-                break;
-            case R.id.selected_container:
-                if (!mHiding) {
-                    getContext().startActivity(intent);
-                }
-                break;
+        if (v.getId() == R.id.action1 || v.getId() == R.id.action2 || v.getId() == R.id.action3) {
+            AccessibilityManager accessibilityMgr =
+                    (AccessibilityManager) mContext.getSystemService(
+                            Context.ACCESSIBILITY_SERVICE);
+            if (accessibilityMgr.isTouchExplorationEnabled()) {
+                getContext().startActivity(intent);
+            } else {
+                revealTheButton(v);
+            }
+        } else if (v.getId() == R.id.selected_container) {
+            if (!mHiding) {
+                getContext().startActivity(intent);
+            }
         }
     }
 
diff --git a/src/com/android/phone/EmergencyAssistanceHelper.java b/src/com/android/phone/EmergencyAssistanceHelper.java
index 33788d3..79ee515 100644
--- a/src/com/android/phone/EmergencyAssistanceHelper.java
+++ b/src/com/android/phone/EmergencyAssistanceHelper.java
@@ -31,8 +31,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.internal.util.CollectionUtils;
-
 import java.util.List;
 
 /**
@@ -120,8 +118,9 @@
     private static String getDefaultEmergencyPackage(Context context) {
         long identity = Binder.clearCallingIdentity();
         try {
-            return CollectionUtils.firstOrNull(context.getSystemService(RoleManager.class)
-                    .getRoleHolders(RoleManager.ROLE_EMERGENCY));
+            List<String> roleHolders = context.getSystemService(RoleManager.class)
+                    .getRoleHolders(RoleManager.ROLE_EMERGENCY);
+            return roleHolders.isEmpty() ? null : roleHolders.get(0);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 3cf74c8..7531aca 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -31,6 +32,7 @@
 import android.database.DataSetObserver;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
 import android.net.Uri;
@@ -68,12 +70,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
 
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.colorextraction.drawable.ScrimDrawable;
-import com.android.phone.EmergencyDialerMetricsLogger.DialedFrom;
-import com.android.phone.EmergencyDialerMetricsLogger.PhoneNumberType;
-import com.android.phone.EmergencyDialerMetricsLogger.UiModeErrorCode;
 import com.android.phone.common.dialpad.DialpadKeyButton;
 import com.android.phone.common.util.ViewUtil;
 import com.android.phone.common.widget.ResizingTextEditText;
@@ -100,7 +96,8 @@
  */
 public class EmergencyDialer extends Activity implements View.OnClickListener,
         View.OnLongClickListener, View.OnKeyListener, TextWatcher,
-        DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener,
+        DialpadKeyButton.OnPressedListener,
+        WallpaperManager.OnColorsChangedListener,
         EmergencyShortcutButton.OnConfirmClickListener,
         EmergencyInfoGroup.OnConfirmClickListener {
 
@@ -212,8 +209,7 @@
     private String mLastNumber; // last number we tried to dial. Used to restore error dialog.
 
     // Background gradient
-    private ColorExtractor mColorExtractor;
-    private ScrimDrawable mBackgroundDrawable;
+    private ColorDrawable mBackgroundDrawable;
     private boolean mSupportsDarkText;
 
     private boolean mIsWfcEmergencyCallingWarningEnabled;
@@ -222,8 +218,6 @@
     private int mEntryType;
     private ShortcutViewUtils.Config mShortcutViewConfig;
 
-    private EmergencyDialerMetricsLogger mMetricsLogger;
-
     @Override
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
         // Do nothing
@@ -258,8 +252,6 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mMetricsLogger = new EmergencyDialerMetricsLogger(this);
-
         mEntryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
         Log.d(LOG_TAG, "Launched from " + entryTypeToString(mEntryType));
 
@@ -276,15 +268,13 @@
         Log.d(LOG_TAG, "Enable emergency dialer shortcut: "
                 + mShortcutViewConfig.isEnabled());
 
-        mColorExtractor = new ColorExtractor(this);
-
         if (mShortcutViewConfig.isEnabled()) {
             // Shortcut view doesn't support dark text theme.
             updateTheme(false);
         } else {
-            GradientColors lockScreenColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
-                    ColorExtractor.TYPE_EXTRA_DARK);
-            updateTheme(lockScreenColors.supportsDarkText());
+            WallpaperColors wallpaperColors =
+                    getWallpaperManager().getWallpaperColors(WallpaperManager.FLAG_LOCK);
+            updateTheme(supportsDarkText(wallpaperColors));
         }
 
         setContentView(R.layout.emergency_dialer);
@@ -298,7 +288,7 @@
         mDefaultDigitsTextSize = mDigits.getScaledTextSize();
         maybeAddNumberFormatting();
 
-        mBackgroundDrawable = new ScrimDrawable();
+        mBackgroundDrawable = new ColorDrawable();
         Point displaySize = new Point();
         ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay().getSize(displaySize);
@@ -471,16 +461,15 @@
 
     @Override
     public boolean onKey(View view, int keyCode, KeyEvent event) {
-        switch (view.getId()) {
-            case R.id.digits:
-                // Happen when "Done" button of the IME is pressed. This can happen when this
-                // Activity is forced into landscape mode due to a desk dock.
-                if (keyCode == KeyEvent.KEYCODE_ENTER
-                        && event.getAction() == KeyEvent.ACTION_UP) {
-                    placeCall();
-                    return true;
-                }
-                break;
+        if (view.getId()
+                == R.id.digits) { // Happen when "Done" button of the IME is pressed. This can
+            // happen when this
+            // Activity is forced into landscape mode due to a desk dock.
+            if (keyCode == KeyEvent.KEYCODE_ENTER
+                    && event.getAction() == KeyEvent.ACTION_UP) {
+                placeCall();
+                return true;
+            }
         }
         return false;
     }
@@ -501,10 +490,6 @@
         if (!TextUtils.isEmpty(phoneNumber)) {
             if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
 
-            // Write metrics when user has intention to make a call from a shortcut button.
-            mMetricsLogger.logPlaceCall(DialedFrom.SHORTCUT, PhoneNumberType.HAS_SHORTCUT,
-                    mShortcutViewConfig.getPhoneInfo());
-
             placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
                     mShortcutViewConfig.getPhoneInfo());
         } else {
@@ -524,27 +509,22 @@
 
     @Override
     public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.deleteButton: {
-                keyPressed(KeyEvent.KEYCODE_DEL);
-                return;
+        if (view.getId() == R.id.deleteButton) {
+            keyPressed(KeyEvent.KEYCODE_DEL);
+            return;
+        } else if (view.getId() == R.id.floating_action_button) {
+            view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+            placeCall();
+            return;
+        } else if (view.getId() == R.id.digits) {
+            if (mDigits.length() != 0) {
+                mDigits.setCursorVisible(true);
             }
-            case R.id.floating_action_button: {
-                view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-                placeCall();
-                return;
-            }
-            case R.id.digits: {
-                if (mDigits.length() != 0) {
-                    mDigits.setCursorVisible(true);
-                }
-                return;
-            }
-            case R.id.floating_action_button_dialpad: {
-                mDigits.getText().clear();
-                switchView(mDialpadView, mEmergencyShortcutView, true);
-                return;
-            }
+            return;
+        } else if (view.getId() == R.id.floating_action_button_dialpad) {
+            mDigits.getText().clear();
+            switchView(mDialpadView, mEmergencyShortcutView, true);
+            return;
         }
     }
 
@@ -553,67 +533,54 @@
         if (!pressed) {
             return;
         }
-        switch (view.getId()) {
-            case R.id.one: {
-                playTone(ToneGenerator.TONE_DTMF_1);
-                keyPressed(KeyEvent.KEYCODE_1);
-                return;
-            }
-            case R.id.two: {
-                playTone(ToneGenerator.TONE_DTMF_2);
-                keyPressed(KeyEvent.KEYCODE_2);
-                return;
-            }
-            case R.id.three: {
-                playTone(ToneGenerator.TONE_DTMF_3);
-                keyPressed(KeyEvent.KEYCODE_3);
-                return;
-            }
-            case R.id.four: {
-                playTone(ToneGenerator.TONE_DTMF_4);
-                keyPressed(KeyEvent.KEYCODE_4);
-                return;
-            }
-            case R.id.five: {
-                playTone(ToneGenerator.TONE_DTMF_5);
-                keyPressed(KeyEvent.KEYCODE_5);
-                return;
-            }
-            case R.id.six: {
-                playTone(ToneGenerator.TONE_DTMF_6);
-                keyPressed(KeyEvent.KEYCODE_6);
-                return;
-            }
-            case R.id.seven: {
-                playTone(ToneGenerator.TONE_DTMF_7);
-                keyPressed(KeyEvent.KEYCODE_7);
-                return;
-            }
-            case R.id.eight: {
-                playTone(ToneGenerator.TONE_DTMF_8);
-                keyPressed(KeyEvent.KEYCODE_8);
-                return;
-            }
-            case R.id.nine: {
-                playTone(ToneGenerator.TONE_DTMF_9);
-                keyPressed(KeyEvent.KEYCODE_9);
-                return;
-            }
-            case R.id.zero: {
-                playTone(ToneGenerator.TONE_DTMF_0);
-                keyPressed(KeyEvent.KEYCODE_0);
-                return;
-            }
-            case R.id.pound: {
-                playTone(ToneGenerator.TONE_DTMF_P);
-                keyPressed(KeyEvent.KEYCODE_POUND);
-                return;
-            }
-            case R.id.star: {
-                playTone(ToneGenerator.TONE_DTMF_S);
-                keyPressed(KeyEvent.KEYCODE_STAR);
-                return;
-            }
+        if (view.getId() == R.id.one) {
+            playTone(ToneGenerator.TONE_DTMF_1);
+            keyPressed(KeyEvent.KEYCODE_1);
+            return;
+        } else if (view.getId() == R.id.two) {
+            playTone(ToneGenerator.TONE_DTMF_2);
+            keyPressed(KeyEvent.KEYCODE_2);
+            return;
+        } else if (view.getId() == R.id.three) {
+            playTone(ToneGenerator.TONE_DTMF_3);
+            keyPressed(KeyEvent.KEYCODE_3);
+            return;
+        } else if (view.getId() == R.id.four) {
+            playTone(ToneGenerator.TONE_DTMF_4);
+            keyPressed(KeyEvent.KEYCODE_4);
+            return;
+        } else if (view.getId() == R.id.five) {
+            playTone(ToneGenerator.TONE_DTMF_5);
+            keyPressed(KeyEvent.KEYCODE_5);
+            return;
+        } else if (view.getId() == R.id.six) {
+            playTone(ToneGenerator.TONE_DTMF_6);
+            keyPressed(KeyEvent.KEYCODE_6);
+            return;
+        } else if (view.getId() == R.id.seven) {
+            playTone(ToneGenerator.TONE_DTMF_7);
+            keyPressed(KeyEvent.KEYCODE_7);
+            return;
+        } else if (view.getId() == R.id.eight) {
+            playTone(ToneGenerator.TONE_DTMF_8);
+            keyPressed(KeyEvent.KEYCODE_8);
+            return;
+        } else if (view.getId() == R.id.nine) {
+            playTone(ToneGenerator.TONE_DTMF_9);
+            keyPressed(KeyEvent.KEYCODE_9);
+            return;
+        } else if (view.getId() == R.id.zero) {
+            playTone(ToneGenerator.TONE_DTMF_0);
+            keyPressed(KeyEvent.KEYCODE_0);
+            return;
+        } else if (view.getId() == R.id.pound) {
+            playTone(ToneGenerator.TONE_DTMF_P);
+            keyPressed(KeyEvent.KEYCODE_POUND);
+            return;
+        } else if (view.getId() == R.id.star) {
+            playTone(ToneGenerator.TONE_DTMF_S);
+            keyPressed(KeyEvent.KEYCODE_STAR);
+            return;
         }
     }
 
@@ -623,16 +590,13 @@
     @Override
     public boolean onLongClick(View view) {
         int id = view.getId();
-        switch (id) {
-            case R.id.deleteButton: {
-                mDigits.getText().clear();
-                return true;
-            }
-            case R.id.zero: {
-                removePreviousDigitIfPossible();
-                keyPressed(KeyEvent.KEYCODE_PLUS);
-                return true;
-            }
+        if (id == R.id.deleteButton) {
+            mDigits.getText().clear();
+            return true;
+        } else if (id == R.id.zero) {
+            removePreviousDigitIfPossible();
+            keyPressed(KeyEvent.KEYCODE_PLUS);
+            return true;
         }
         return false;
     }
@@ -641,21 +605,20 @@
     protected void onStart() {
         super.onStart();
 
-        mMetricsLogger.logLaunchEmergencyDialer(mEntryType,
-                mShortcutViewConfig.isEnabled() ? UiModeErrorCode.SUCCESS
-                        : UiModeErrorCode.UNSPECIFIED_ERROR);
-
         if (mShortcutViewConfig.isEnabled()) {
             // Shortcut view doesn't support dark text theme.
-            mBackgroundDrawable.setColor(Color.BLACK, false);
+            mBackgroundDrawable.setColor(Color.BLACK);
             updateTheme(false);
         } else {
-            mColorExtractor.addOnColorsChangedListener(this);
-            GradientColors lockScreenColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
-                    ColorExtractor.TYPE_EXTRA_DARK);
-            // Do not animate when view isn't visible yet, just set an initial state.
-            mBackgroundDrawable.setColor(lockScreenColors.getMainColor(), false);
-            updateTheme(lockScreenColors.supportsDarkText());
+            WallpaperManager wallpaperManager = getWallpaperManager();
+            if (wallpaperManager.isWallpaperSupported()) {
+                wallpaperManager.addOnColorsChangedListener(this, null);
+            }
+
+            WallpaperColors wallpaperColors =
+                    wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
+            mBackgroundDrawable.setColor(getPrimaryColor(wallpaperColors));
+            updateTheme(supportsDarkText(wallpaperColors));
         }
 
         if (mShortcutViewConfig.isEnabled()) {
@@ -696,7 +659,11 @@
     @Override
     protected void onStop() {
         super.onStop();
-        mColorExtractor.removeOnColorsChangedListener(this);
+
+        WallpaperManager wallpaperManager = getWallpaperManager();
+        if (wallpaperManager.isWallpaperSupported()) {
+            wallpaperManager.removeOnColorsChangedListener(this);
+        }
     }
 
     /**
@@ -741,28 +708,20 @@
         // nothing and just returns input number.
         mLastNumber = PhoneNumberUtils.convertToEmergencyNumber(this, mLastNumber);
 
-        @DialedFrom final int dialedFrom =
-                mShortcutViewConfig.isEnabled() ? DialedFrom.FASTER_LAUNCHER_DIALPAD
-                        : DialedFrom.TRADITIONAL_DIALPAD;
-        @PhoneNumberType final int phoneNumberType;
-
         boolean isEmergencyNumber;
         ShortcutViewUtils.PhoneInfo phoneToMakeCall = null;
         if (mShortcutAdapter != null && mShortcutAdapter.hasShortcut(mLastNumber)) {
             isEmergencyNumber = true;
             phoneToMakeCall = mShortcutViewConfig.getPhoneInfo();
-            phoneNumberType = PhoneNumberType.HAS_SHORTCUT;
         } else if (mShortcutViewConfig.hasPromotedEmergencyNumber(mLastNumber)) {
             // If a number from SIM/network/... is categoried as police/ambulance/fire,
             // hasPromotedEmergencyNumber() will return true, but it maybe not promoted as a
             // shortcut button because a number provided by database has higher priority.
             isEmergencyNumber = true;
             phoneToMakeCall = mShortcutViewConfig.getPhoneInfo();
-            phoneNumberType = PhoneNumberType.NO_SHORTCUT;
         } else {
             isEmergencyNumber = getSystemService(TelephonyManager.class)
                     .isEmergencyNumber(mLastNumber);
-            phoneNumberType = PhoneNumberType.NO_SHORTCUT;
         }
 
         if (isEmergencyNumber) {
@@ -775,19 +734,11 @@
                 return;
             }
 
-            // Write metrics when user has intention to make a call from dialpad
-            mMetricsLogger.logPlaceCall(dialedFrom, phoneNumberType, phoneToMakeCall);
-
             placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
                     phoneToMakeCall);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
 
-            // Write metrics when user has intention to make a call of a non-emergency number, even
-            // this number is rejected.
-            mMetricsLogger.logPlaceCall(dialedFrom, PhoneNumberType.NOT_EMERGENCY_NUMBER,
-                    phoneToMakeCall);
-
             showDialog(BAD_EMERGENCY_NUMBER_DIALOG);
         }
         mDigits.getText().delete(0, mDigits.getText().length());
@@ -937,12 +888,10 @@
     }
 
     @Override
-    public void onColorsChanged(ColorExtractor extractor, int which) {
+    public void onColorsChanged(WallpaperColors colors, int which) {
         if ((which & WallpaperManager.FLAG_LOCK) != 0) {
-            GradientColors colors = extractor.getColors(WallpaperManager.FLAG_LOCK,
-                    ColorExtractor.TYPE_EXTRA_DARK);
-            mBackgroundDrawable.setColor(colors.getMainColor(), true /* animated */);
-            updateTheme(colors.supportsDarkText());
+            mBackgroundDrawable.setColor(getPrimaryColor(colors));
+            updateTheme(supportsDarkText(colors));
         }
     }
 
@@ -1097,11 +1046,6 @@
                 if (mEmergencyShortcutButtonList.size() > 1) {
                     emergencyNumberTitle.setText(getString(
                             R.string.numerous_emergency_numbers_title));
-                    // Update mEmergencyInfoGroup margin to avoid UI overlay when
-                    // emergency shortcut button more than 2.
-                    if (mEmergencyShortcutButtonList.size() > 2) {
-                        mEmergencyInfoGroup.updateLayoutMargin();
-                    }
                 } else {
                     emergencyNumberTitle.setText(getText(R.string.single_emergency_number_title));
                 }
@@ -1236,4 +1180,28 @@
                 return "Unknown-" + callSource;
         }
     }
+
+    private WallpaperManager getWallpaperManager() {
+        return getSystemService(WallpaperManager.class);
+    }
+
+    private static boolean supportsDarkText(WallpaperColors colors) {
+        if (colors != null) {
+            return (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+        }
+        // It's possible that wallpaper colors are null (e.g. when colors are being
+        // processed or a live wallpaper is used). In this case, fallback to same
+        // behavior as when shortcut view is enabled.
+        return false;
+    }
+
+    private static int getPrimaryColor(WallpaperColors colors) {
+        if (colors != null) {
+            return colors.getPrimaryColor().toArgb();
+        }
+        // It's possible that wallpaper colors are null (e.g. when colors are being
+        // processed or a live wallpaper is used). In this case, fallback to same
+        // behavior as when shortcut view is enabled.
+        return Color.BLACK;
+    }
 }
diff --git a/src/com/android/phone/EmergencyDialerMetricsLogger.java b/src/com/android/phone/EmergencyDialerMetricsLogger.java
deleted file mode 100644
index b9ba352..0000000
--- a/src/com/android/phone/EmergencyDialerMetricsLogger.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * 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 android.annotation.IntDef;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * EmergencyCallMetricsLogger is a utility to collect metrics of emergency calls
- */
-class EmergencyDialerMetricsLogger {
-    private static final String LOG_TAG = "EmergencyDialerLogger";
-
-    @IntDef({
-            DialedFrom.TRADITIONAL_DIALPAD,
-            DialedFrom.SHORTCUT,
-            DialedFrom.FASTER_LAUNCHER_DIALPAD,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface DialedFrom {
-        int TRADITIONAL_DIALPAD = 0;
-        int SHORTCUT = 1;
-        int FASTER_LAUNCHER_DIALPAD = 2;
-    }
-
-    @IntDef({
-            LaunchedFrom.UNDEFINED,
-            LaunchedFrom.LOCK_SCREEN,
-            LaunchedFrom.POWER_KEY_MENU,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface LaunchedFrom {
-        int UNDEFINED = 0;
-        int LOCK_SCREEN = 1;
-        int POWER_KEY_MENU = 2;
-    }
-
-    @IntDef({
-            PhoneNumberType.HAS_SHORTCUT,
-            PhoneNumberType.NO_SHORTCUT,
-            PhoneNumberType.NOT_EMERGENCY_NUMBER,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface PhoneNumberType {
-        int HAS_SHORTCUT = 0;
-        int NO_SHORTCUT = 1;
-        int NOT_EMERGENCY_NUMBER = 2;
-    }
-
-    @IntDef({
-            UiModeErrorCode.UNSPECIFIED_ERROR,
-            UiModeErrorCode.SUCCESS,
-            UiModeErrorCode.CONFIG_ENTRY_POINT,
-            UiModeErrorCode.CONFIG_SIM_OPERATOR,
-            UiModeErrorCode.UNSUPPORTED_COUNTRY,
-            UiModeErrorCode.AIRPLANE_MODE,
-            UiModeErrorCode.NO_PROMOTED_NUMBER,
-            UiModeErrorCode.NO_CAPABLE_PHONE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface UiModeErrorCode {
-        int UNSPECIFIED_ERROR = -1;
-        int SUCCESS = 0;
-        int CONFIG_ENTRY_POINT = 1;
-        int CONFIG_SIM_OPERATOR = 2;
-        int UNSUPPORTED_COUNTRY = 3;
-        int AIRPLANE_MODE = 4;
-        int NO_PROMOTED_NUMBER = 5;
-        int NO_CAPABLE_PHONE = 6;
-    }
-
-    private class TelephonyInfo {
-        private final String mNetworkCountryIso;
-        private final String mNetworkOperator;
-
-        TelephonyInfo(String networkCountryIso, String networkOperator) {
-            mNetworkCountryIso = networkCountryIso;
-            mNetworkOperator = networkOperator;
-        }
-    }
-
-    private final MetricsLogger mMetricsLogger = new MetricsLogger();
-
-    private final Context mAppContext;
-
-    @LaunchedFrom
-    private int mLaunchedFrom;
-    @UiModeErrorCode
-    private int mUiModeErrorCode;
-
-    EmergencyDialerMetricsLogger(Context context) {
-        mAppContext = context.getApplicationContext();
-    }
-
-    /**
-     * Log when Emergency Dialer is launched.
-     * - Where the user launch Emergency Dialer from.
-     * - Whether shortcut view is enabled, and the reason why it's not enabled.
-     *
-     * @param entryType
-     * @param uiModeErrorCode
-     */
-    public void logLaunchEmergencyDialer(int entryType,
-            @UiModeErrorCode int uiModeErrorCode) {
-        final @EmergencyDialerMetricsLogger.LaunchedFrom int launchedFrom;
-        if (entryType == EmergencyDialer.ENTRY_TYPE_LOCKSCREEN_BUTTON) {
-            launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.LOCK_SCREEN;
-        } else if (entryType == EmergencyDialer.ENTRY_TYPE_POWER_MENU) {
-            launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.POWER_KEY_MENU;
-        } else {
-            launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.UNDEFINED;
-        }
-
-        mLaunchedFrom = launchedFrom;
-        mUiModeErrorCode = uiModeErrorCode;
-    }
-
-    /**
-     * Log when user tring to place an emergency call.
-     * - Which UI (traditional dialpad, shortcut button, dialpad in shortcut view) the user place
-     *   the call from.
-     * - The number is promoted in shortcut view or not, or not even an emergency number?
-     * - Whether the device is locked.
-     * - Network country ISO and network operator.
-     *
-     * @param dialedFrom
-     * @param phoneNumberType
-     * @param phoneInfo
-     */
-    public void logPlaceCall(@DialedFrom int dialedFrom,
-            @PhoneNumberType int phoneNumberType,
-            @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) {
-        TelephonyInfo telephonyInfo = getTelephonyInfo(phoneNumberType, phoneInfo);
-        final KeyguardManager keyguard = mAppContext.getSystemService(KeyguardManager.class);
-
-        logBeforeMakeCall(dialedFrom, phoneNumberType, keyguard.isKeyguardLocked(),
-                telephonyInfo.mNetworkCountryIso, telephonyInfo.mNetworkOperator);
-    }
-
-    private TelephonyInfo getTelephonyInfo(@PhoneNumberType int phoneNumberType,
-            @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) {
-        final TelephonyManager telephonyManager = mAppContext.getSystemService(
-                TelephonyManager.class);
-        final TelephonyManager subTelephonyManager;
-        final String networkCountryIso;
-        final String networkOperator;
-        if (phoneNumberType == PhoneNumberType.HAS_SHORTCUT && phoneInfo != null) {
-            subTelephonyManager = telephonyManager.createForSubscriptionId(phoneInfo.getSubId());
-            networkCountryIso = phoneInfo.getCountryIso();
-        } else {
-            // No specific phone to make this call. Take information of default network.
-            subTelephonyManager = null;
-            networkCountryIso = telephonyManager.getNetworkCountryIso();
-        }
-        if (subTelephonyManager != null) {
-            networkOperator = subTelephonyManager.getNetworkOperator();
-        } else {
-            // This could be:
-            // - No specific phone to make this call.
-            // - Subscription changed! Maybe the device roamed to another network?
-            // Take information of default network.
-            networkOperator = telephonyManager.getNetworkOperator();
-        }
-
-        return new TelephonyInfo(networkCountryIso, networkOperator);
-    }
-
-    private void logBeforeMakeCall(@DialedFrom int dialedFrom,
-            @PhoneNumberType int phoneNumberType,
-            boolean isDeviceLocked,
-            String networkCountryIso,
-            String networkOperator) {
-        if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
-            Log.d(LOG_TAG, "EmergencyDialer session: dialedFrom=" + dialFromToString(dialedFrom)
-                    + ", launchedFrom=" + launchedFromToString(mLaunchedFrom)
-                    + ", uimode=" + uiModeErrorCodeToString(mUiModeErrorCode)
-                    + ", type=" + phoneNumberTypeToString(phoneNumberType)
-                    + ", locked=" + isDeviceLocked
-                    + ", country=" + networkCountryIso
-                    + ", operator=" + networkOperator);
-        }
-        mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL_V2)
-                .setType(MetricsEvent.TYPE_ACTION)
-                .setSubtype(dialedFrom)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_LAUNCH_FROM, mLaunchedFrom)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_UI_MODE_ERROR_CODE,
-                        mUiModeErrorCode)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE,
-                        phoneNumberType)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_DEVICE_LOCKED,
-                        isDeviceLocked ? 1 : 0)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_NETWORK_COUNTRY_ISO,
-                        networkCountryIso)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_NETWORK_OPERATOR,
-                        networkOperator)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_RADIO_VERSION,
-                        Build.getRadioVersion())
-        );
-    }
-
-    private String dialFromToString(@DialedFrom int dialedFrom) {
-        switch (dialedFrom) {
-            case DialedFrom.TRADITIONAL_DIALPAD:
-                return "traditional";
-            case DialedFrom.SHORTCUT:
-                return "shortcut";
-            case DialedFrom.FASTER_LAUNCHER_DIALPAD:
-                return "dialpad";
-            default:
-                return "unknown_error";
-        }
-    }
-
-    private String launchedFromToString(@LaunchedFrom int launchedFrom) {
-        switch (launchedFrom) {
-            case LaunchedFrom.UNDEFINED:
-                return "undefined";
-            case LaunchedFrom.LOCK_SCREEN:
-                return "lockscreen";
-            case LaunchedFrom.POWER_KEY_MENU:
-                return "powermenu";
-            default:
-                return "unknown_error";
-        }
-    }
-
-    private String phoneNumberTypeToString(@PhoneNumberType int phoneNumberType) {
-        switch (phoneNumberType) {
-            case PhoneNumberType.HAS_SHORTCUT:
-                return "has_shortcut";
-            case PhoneNumberType.NO_SHORTCUT:
-                return "no_shortcut";
-            case PhoneNumberType.NOT_EMERGENCY_NUMBER:
-                return "not_emergency";
-            default:
-                return "unknown_error";
-        }
-    }
-
-    private String uiModeErrorCodeToString(@UiModeErrorCode int uiModeErrorCode) {
-        switch (uiModeErrorCode) {
-            case UiModeErrorCode.UNSPECIFIED_ERROR:
-                return "unspecified_error";
-            case UiModeErrorCode.SUCCESS:
-                return "success";
-            case UiModeErrorCode.CONFIG_ENTRY_POINT:
-                return "config_entry_point";
-            case UiModeErrorCode.CONFIG_SIM_OPERATOR:
-                return "config_sim_operator";
-            case UiModeErrorCode.UNSUPPORTED_COUNTRY:
-                return "unsupported_country";
-            case UiModeErrorCode.AIRPLANE_MODE:
-                return "airplane_mode";
-            case UiModeErrorCode.NO_PROMOTED_NUMBER:
-                return "no_promoted_number";
-            case UiModeErrorCode.NO_CAPABLE_PHONE:
-                return "no_capable_phone";
-            default:
-                return "unknown_error";
-        }
-    }
-}
diff --git a/src/com/android/phone/EmergencyInfoGroup.java b/src/com/android/phone/EmergencyInfoGroup.java
index 14c24d8..f5aca7f 100644
--- a/src/com/android/phone/EmergencyInfoGroup.java
+++ b/src/com/android/phone/EmergencyInfoGroup.java
@@ -35,7 +35,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -154,7 +153,7 @@
     private Drawable getCircularUserIcon() {
         final UserManager userManager = (UserManager) getContext().getSystemService(
                 Context.USER_SERVICE);
-        Bitmap bitmapUserIcon = userManager.getUserIcon(UserHandle.getCallingUserId());
+        Bitmap bitmapUserIcon = userManager.getUserIcon();
 
         if (bitmapUserIcon == null) {
             // get default user icon.
@@ -208,23 +207,21 @@
 
     @Override
     public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.emergency_info_view:
-                if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
-                    if (mOnConfirmClickListener != null) {
-                        mOnConfirmClickListener.onConfirmClick(this);
-                    }
-                } else {
-                    revealSelectedButton();
-                }
-                break;
-            case R.id.emergency_info_confirm_view:
+        if (view.getId() == R.id.emergency_info_view) {
+            AccessibilityManager accessibilityMgr =
+                    (AccessibilityManager) mContext.getSystemService(
+                            Context.ACCESSIBILITY_SERVICE);
+            if (accessibilityMgr.isTouchExplorationEnabled()) {
                 if (mOnConfirmClickListener != null) {
                     mOnConfirmClickListener.onConfirmClick(this);
                 }
-                break;
-            default:
-                break;
+            } else {
+                revealSelectedButton();
+            }
+        } else if (view.getId() == R.id.emergency_info_confirm_view) {
+            if (mOnConfirmClickListener != null) {
+                mOnConfirmClickListener.onConfirmClick(this);
+            }
         }
     }
 
@@ -284,18 +281,4 @@
             hideSelectedButton();
         }
     };
-
-    /**
-     * Update layout margin when emergency shortcut button more than 2.
-     */
-    public void updateLayoutMargin() {
-        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
-
-        params.topMargin = getResources().getDimensionPixelSize(
-                R.dimen.emergency_info_button_fix_margin_vertical);
-        params.bottomMargin = getResources().getDimensionPixelSize(
-                R.dimen.emergency_info_button_fix_margin_vertical);
-
-        setLayoutParams(params);
-    }
 }
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index 59b3794..d147ce4 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -19,8 +19,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -32,9 +30,6 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
 /**
  * Emergency shortcut button displays a local emergency phone number information(including phone
  * number, and phone type). To decrease false clicking, it need to click twice to confirm to place
@@ -66,12 +61,6 @@
 
     private boolean mConfirmViewHiding;
 
-    /**
-     * The time, in millis, since boot when user taps on shortcut button to reveal confirm view.
-     * This is used for metrics when calculating the interval between reveal tap and confirm tap.
-     */
-    private long mTimeOfRevealTapInMillis = 0;
-
     public EmergencyShortcutButton(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -193,32 +182,23 @@
 
     @Override
     public void onClick(View view) {
-        switch (view.getId()) {
-            case R.id.emergency_call_number_info_view:
-                if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
-                    // TalkBack itself includes a prompt to confirm click action implicitly,
-                    // so we don't need an additional confirmation with second tap on button.
-                    if (mOnConfirmClickListener != null) {
-                        mOnConfirmClickListener.onConfirmClick(this);
-                    }
-                } else {
-                    revealSelectedButton();
-                }
-                break;
-            case R.id.emergency_call_confirm_view:
-                if (mTimeOfRevealTapInMillis != 0) {
-                    long timeBetweenTwoTaps =
-                            SystemClock.elapsedRealtime() - mTimeOfRevealTapInMillis;
-                    // Reset reveal time to zero for next reveal-confirm taps pair.
-                    mTimeOfRevealTapInMillis = 0;
-
-                    writeMetricsForConfirmTap(timeBetweenTwoTaps);
-                }
-
+        if (view.getId() == R.id.emergency_call_number_info_view) {
+            AccessibilityManager accessibilityMgr =
+                    (AccessibilityManager) mContext.getSystemService(
+                            Context.ACCESSIBILITY_SERVICE);
+            if (accessibilityMgr.isTouchExplorationEnabled()) {
+                // TalkBack itself includes a prompt to confirm click action implicitly,
+                // so we don't need an additional confirmation with second tap on button.
                 if (mOnConfirmClickListener != null) {
                     mOnConfirmClickListener.onConfirmClick(this);
                 }
-                break;
+            } else {
+                revealSelectedButton();
+            }
+        } else if (view.getId() == R.id.emergency_call_confirm_view) {
+            if (mOnConfirmClickListener != null) {
+                mOnConfirmClickListener.onConfirmClick(this);
+            }
         }
     }
 
@@ -226,7 +206,6 @@
         mConfirmViewHiding = false;
 
         mConfirmView.setVisibility(View.VISIBLE);
-        mTimeOfRevealTapInMillis = SystemClock.elapsedRealtime();
         int centerX = mCallNumberInfoView.getLeft() + mCallNumberInfoView.getWidth() / 2;
         int centerY = mCallNumberInfoView.getTop() + mCallNumberInfoView.getHeight() / 2;
         Animator reveal = ViewAnimationUtils.createCircularReveal(
@@ -263,8 +242,6 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mConfirmView.setVisibility(INVISIBLE);
-                // Reset reveal time to zero for next reveal-confirm taps pair.
-                mTimeOfRevealTapInMillis = 0;
             }
         });
         reveal.start();
@@ -279,12 +256,4 @@
             hideSelectedButton();
         }
     };
-
-    private void writeMetricsForConfirmTap(long timeBetweenTwoTaps) {
-        LogMaker logContent = new LogMaker(MetricsEvent.EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP)
-                .setType(MetricsEvent.TYPE_ACTION)
-                .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL,
-                        timeBetweenTwoTaps);
-        MetricsLogger.action(logContent);
-    }
 }
diff --git a/src/com/android/phone/GsmUmtsCallOptions.java b/src/com/android/phone/GsmUmtsCallOptions.java
index 88cae54..51d1b66 100644
--- a/src/com/android/phone/GsmUmtsCallOptions.java
+++ b/src/com/android/phone/GsmUmtsCallOptions.java
@@ -21,6 +21,7 @@
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.view.MenuItem;
 
@@ -32,7 +33,7 @@
 
     public static final String CALL_FORWARDING_KEY = "call_forwarding_key";
     public static final String CALL_BARRING_KEY = "call_barring_key";
-    private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
+    public static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -69,12 +70,22 @@
             b = PhoneGlobals.getInstance().getCarrierConfig();
         }
 
+        boolean isAirplaneModeOff = true;
+        if (b != null && b.getBoolean(
+                CarrierConfigManager.KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL)) {
+            int airplaneMode = Settings.Global.getInt(
+                    subInfoHelper.getPhone().getContext().getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, PhoneGlobals.AIRPLANE_OFF);
+            isAirplaneModeOff = PhoneGlobals.AIRPLANE_ON != airplaneMode;
+        }
+
         Preference callForwardingPref = prefScreen.findPreference(CALL_FORWARDING_KEY);
         if (callForwardingPref != null) {
             if (b != null && b.getBoolean(
                     CarrierConfigManager.KEY_CALL_FORWARDING_VISIBILITY_BOOL)) {
                 callForwardingPref.setIntent(
                         subInfoHelper.getIntent(GsmUmtsCallForwardOptions.class));
+                callForwardingPref.setEnabled(isAirplaneModeOff);
             } else {
                 prefScreen.removePreference(callForwardingPref);
             }
@@ -89,6 +100,7 @@
                     CarrierConfigManager.KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL))) {
                 additionalGsmSettingsPref.setIntent(
                         subInfoHelper.getIntent(GsmUmtsAdditionalCallOptions.class));
+                additionalGsmSettingsPref.setEnabled(isAirplaneModeOff);
             } else {
                 prefScreen.removePreference(additionalGsmSettingsPref);
             }
@@ -98,6 +110,7 @@
         if (callBarringPref != null) {
             if (b != null && b.getBoolean(CarrierConfigManager.KEY_CALL_BARRING_VISIBILITY_BOOL)) {
                 callBarringPref.setIntent(subInfoHelper.getIntent(GsmUmtsCallBarringOptions.class));
+                callBarringPref.setEnabled(isAirplaneModeOff);
             } else {
                 prefScreen.removePreference(callBarringPref);
             }
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
new file mode 100644
index 0000000..06d2367
--- /dev/null
+++ b/src/com/android/phone/ImsRcsController.java
@@ -0,0 +1,229 @@
+/*
+ * 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 android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+
+import java.util.List;
+
+/**
+ * Implementation of the IImsRcsController interface.
+ */
+public class ImsRcsController extends IImsRcsController.Stub {
+    private static final String TAG = "ImsRcsController";
+
+    /** The singleton instance. */
+    private static ImsRcsController sInstance;
+
+    private PhoneGlobals mApp;
+
+    /**
+     * Initialize the singleton ImsRcsController instance.
+     * This is only done once, at startup, from PhoneApp.onCreate().
+     */
+    static ImsRcsController init(PhoneGlobals app) {
+        synchronized (ImsRcsController.class) {
+            if (sInstance == null) {
+                sInstance = new ImsRcsController(app);
+            } else {
+                Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
+            }
+            return sInstance;
+        }
+    }
+
+    /** Private constructor; @see init() */
+    private ImsRcsController(PhoneGlobals app) {
+        Log.i(TAG, "ImsRcsController");
+        mApp = app;
+        ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
+    }
+
+    /**
+     * Register a capability callback which will provide RCS availability updates for the
+     * subscription specified.
+     *
+     * @param subId the subscription ID
+     * @param callback The ImsCapabilityCallback to be registered.
+     */
+    @Override
+    public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
+            throws RemoteException {
+        enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            getRcsFeatureManager(subId).registerRcsAvailabilityCallback(callback);
+        } catch (com.android.ims.ImsException e) {
+            Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
+            throw new ServiceSpecificException(e.getCode());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Remove the registered capability callback.
+     *
+     * @param subId the subscription ID
+     * @param callback The ImsCapabilityCallback to be removed.
+     */
+    @Override
+    public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
+        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());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Query for the capability of an IMS RCS service
+     *
+     * @param subId the subscription ID
+     * @param capability the RCS capability to query.
+     * @param radioTech the radio tech that this capability failed for
+     * @return true if the RCS capability is capable for this subscription, false otherwise.
+     */
+    @Override
+    public boolean isCapable(int subId,
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        enforceReadPrivilegedPermission("isCapable");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return getRcsFeatureManager(subId).isCapable(capability, radioTech);
+        } catch (com.android.ims.ImsException e) {
+            Log.e(TAG, "isCapable: sudId=" + subId
+                    + ", capability=" + capability + ", " + e.getMessage());
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Query the availability of an IMS RCS capability.
+     *
+     * @param subId the subscription ID
+     * @param capability the RCS capability to query.
+     * @return true if the RCS capability is currently available for the associated subscription,
+     * false otherwise.
+     */
+    @Override
+    public boolean isAvailable(int subId,
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        enforceReadPrivilegedPermission("isAvailable");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return getRcsFeatureManager(subId).isAvailable(capability);
+        } catch (com.android.ims.ImsException e) {
+            Log.e(TAG, "isAvailable: sudId=" + subId
+                    + ", capability=" + capability + ", " + e.getMessage());
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void requestCapabilities(int subId, List<Uri> contactNumbers,
+            IRcsUceControllerCallback c) {
+        enforceReadPrivilegedPermission("requestCapabilities");
+    }
+
+    @Override
+    public int getUcePublishState(int subId) {
+        enforceReadPrivilegedPermission("getUcePublishState");
+        return -1;
+    }
+
+    @Override
+    public boolean isUceSettingEnabled(int subId) {
+        enforceReadPrivilegedPermission("isUceSettingEnabled");
+        return false;
+    }
+
+    @Override
+    public void setUceSettingEnabled(int subId, boolean isEnabled) {
+        enforceModifyPermission();
+    }
+
+    /**
+     * Make sure either called from same process as self (phone) or IPC caller has read privilege.
+     *
+     * @throws SecurityException if the caller does not have the required permission
+     */
+    private void enforceReadPrivilegedPermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+    }
+
+    /**
+     * Make sure the caller has the MODIFY_PHONE_STATE permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission
+     */
+    private void enforceModifyPermission() {
+        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
+    }
+
+    /**
+     * Retrieve RcsFeatureManager instance.
+     *
+     * @param subId the subscription ID
+     * @return The RcsFeatureManager instance
+     * @throws SecurityException if getting Phone or RcsFeatureManager instance failed.
+     */
+    private RcsFeatureManager getRcsFeatureManager(int subId) {
+        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_UNSUPPORTED_OPERATION,
+                    "Cannot find ImsPhone instance: " + subId);
+        }
+        RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
+        if (rcsFeatureManager == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+                    "Cannot find RcsFeatureManager instance: " + subId);
+        }
+        return rcsFeatureManager;
+    }
+}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 43c5b11..fe55335 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.READ_PHONE_STATE;
 
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -41,7 +42,6 @@
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract.PhoneLookup;
 import android.provider.Settings;
-import android.telecom.DefaultDialerManager;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -63,6 +63,7 @@
 import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.phone.settings.VoicemailSettingsActivity;
 
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -93,6 +94,7 @@
     static final int CALL_FORWARD_NOTIFICATION = 4;
     static final int DATA_ROAMING_NOTIFICATION = 5;
     static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
+    static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
 
     // Event for network selection notification.
     private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
@@ -102,6 +104,8 @@
 
     private static final int STATE_UNKNOWN_SERVICE = -1;
 
+    private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
+
     /** The singleton NotificationMgr instance. */
     private static NotificationMgr sInstance;
 
@@ -118,6 +122,9 @@
     // used to track the notification of selected network unavailable, per subscription id.
     private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
 
+    // used to track the notification of limited sim function under dual sim, per subscription id.
+    private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
+
     // used to track whether the message waiting indicator is visible, per subscription id.
     private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
 
@@ -156,7 +163,7 @@
                 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
         mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
         mSubscriptionManager = SubscriptionManager.from(mContext);
-        mTelecomManager = TelecomManager.from(mContext);
+        mTelecomManager = app.getSystemService(TelecomManager.class);
         mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
     }
 
@@ -368,12 +375,11 @@
 
             final Notification notification = builder.build();
             List<UserInfo> users = mUserManager.getUsers(true);
-            for (int i = 0; i < users.size(); i++) {
-                final UserInfo user = users.get(i);
+            for (UserInfo user : users) {
                 final UserHandle userHandle = user.getUserHandle();
-                if (!mUserManager.hasUserRestriction(
+                if (!hasUserRestriction(
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
-                        && !user.isManagedProfile()) {
+                        && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
                             pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
                         notifyAsUser(
@@ -386,12 +392,11 @@
             }
         } else {
             List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
-            for (int i = 0; i < users.size(); i++) {
-                final UserInfo user = users.get(i);
+            for (UserInfo user : users) {
                 final UserHandle userHandle = user.getUserHandle();
-                if (!mUserManager.hasUserRestriction(
+                if (!hasUserRestriction(
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
-                        && !user.isManagedProfile()) {
+                        && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
                             false, userHandle, isRefresh)) {
                         cancelAsUser(
@@ -404,6 +409,12 @@
         }
     }
 
+    private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
+        final List<UserManager.EnforcingUser> sources = mUserManager
+                .getUserRestrictionSources(restrictionKey, userHandle);
+        return (sources != null && !sources.isEmpty());
+    }
+
     /**
      * Sends a broadcast with the voicemail notification information to the default dialer. This
      * method is also used to indicate to the default dialer when to clear the
@@ -458,8 +469,8 @@
     }
 
     private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
-        String dialerPackage = DefaultDialerManager
-                .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
+        String dialerPackage = mContext.getSystemService(TelecomManager.class)
+                .getDefaultDialerPackage(userHandle.getIdentifier());
         return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
                 .setPackage(dialerPackage);
     }
@@ -546,7 +557,7 @@
         } else {
             List<UserInfo> users = mUserManager.getUsers(true);
             for (UserInfo user : users) {
-                if (user.isManagedProfile()) {
+                if (mUserManager.isManagedProfile(user.getUserHandle().getIdentifier())) {
                     continue;
                 }
                 UserHandle userHandle = user.getUserHandle();
@@ -638,6 +649,95 @@
     }
 
     /**
+     * Shows the "Limited SIM functionality" warning notification, which appears when using a
+     * special carrier under dual sim. limited function applies for DSDS in general when two SIM
+     * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
+     */
+    public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
+        if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
+                + " subId: " + subId);
+        if (mLimitedSimFunctionNotify.contains(subId)) {
+            // handle the case that user swipe the notification but condition triggers
+            // frequently which cause the same notification consistently displayed.
+            if (DBG) log("showLimitedSimFunctionWarningNotification, "
+                    + "not display again if already displayed");
+            return;
+        }
+        // Navigate to "Network Selection Settings" which list all subscriptions.
+        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
+                new Intent(ACTION_MOBILE_NETWORK_LIST), 0);
+        // Display phone number from the other sub
+        String line1Num = null;
+        SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService(
+            Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
+        for (SubscriptionInfo sub : subList) {
+            if (sub.getSubscriptionId() != subId) {
+                line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId());
+            }
+        }
+        final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
+            String.format(mContext.getText(
+                R.string.limited_sim_function_notification_message).toString(), carrierName) :
+            String.format(mContext.getText(
+                R.string.limited_sim_function_with_phone_num_notification_message).toString(),
+                carrierName, line1Num);
+        final Notification.Builder builder = new Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_sim_card)
+                .setContentTitle(mContext.getText(
+                        R.string.limited_sim_function_notification_title))
+                .setContentText(contentText)
+                .setOnlyAlertOnce(true)
+                .setOngoing(true)
+                .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
+                .setContentIntent(contentIntent);
+        final Notification notification = new Notification.BigTextStyle(builder).bigText(
+                contentText).build();
+
+        notifyAsUser(Integer.toString(subId),
+                LIMITED_SIM_FUNCTION_NOTIFICATION,
+                notification, UserHandle.ALL);
+        mLimitedSimFunctionNotify.add(subId);
+    }
+
+    /**
+     * Dismiss the "Limited SIM functionality" warning notification for the given subId.
+     */
+    public void dismissLimitedSimFunctionWarningNotification(int subId) {
+        if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            // dismiss all notifications
+            for (int id : mLimitedSimFunctionNotify) {
+                cancelAsUser(Integer.toString(id),
+                        LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+            }
+            mLimitedSimFunctionNotify.clear();
+        } else if (mLimitedSimFunctionNotify.contains(subId)) {
+            cancelAsUser(Integer.toString(subId),
+                    LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+            mLimitedSimFunctionNotify.remove(subId);
+        }
+    }
+
+    /**
+     * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
+     */
+    public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
+        if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
+        // dismiss notification for inactive subscriptions.
+        // handle the corner case that SIM change by SIM refresh doesn't clear the notification
+        // from the old SIM if both old & new SIM configured to display the notification.
+        mLimitedSimFunctionNotify.removeIf(id -> {
+            if (!mSubscriptionManager.isActiveSubId(id)) {
+                cancelAsUser(Integer.toString(id),
+                        LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+                return true;
+            }
+            return false;
+        });
+    }
+
+    /**
      * Display the network selection "no service" notification
      * @param operator is the numeric operator number
      * @param subId is the subscription ID
diff --git a/src/com/android/phone/PhoneDisplayMessage.java b/src/com/android/phone/PhoneDisplayMessage.java
index 2b86a61..199fbb8 100644
--- a/src/com/android/phone/PhoneDisplayMessage.java
+++ b/src/com/android/phone/PhoneDisplayMessage.java
@@ -78,10 +78,11 @@
         sDisplayMessageDialog.getWindow().setType(
                 WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
         sDisplayMessageDialog.getWindow().addFlags(
-                WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+                WindowManager.LayoutParams.FLAG_DIM_BEHIND
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 
         sDisplayMessageDialog.show();
-        PhoneGlobals.getInstance().wakeUpScreen();
     }
 
     /**
@@ -91,6 +92,9 @@
         if (DBG) log("Dissmissing Display Info Record...");
 
         if (sDisplayMessageDialog != null) {
+            sDisplayMessageDialog.getWindow().clearFlags(
+                    WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
             sDisplayMessageDialog.dismiss();
             sDisplayMessageDialog = null;
         }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index a3a7945..5d4d9b3 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -37,16 +37,16 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
-import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UpdateLock;
 import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.sysprop.TelephonyProperties;
 import android.telecom.TelecomManager;
 import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -76,6 +76,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 
 /**
  * Global state for the telephony subsystem when running in the primary
@@ -146,6 +147,7 @@
     CallerInfoCache callerInfoCache;
     NotificationMgr notificationMgr;
     public PhoneInterfaceManager phoneMgr;
+    public ImsRcsController imsRcsController;
     CarrierConfigLoader configLoader;
 
     private Phone phoneInEcm;
@@ -185,8 +187,6 @@
     private PowerManager.WakeLock mPartialWakeLock;
     private KeyguardManager mKeyguardManager;
 
-    private UpdateLock mUpdateLock;
-
     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private final LocalLog mDataRoamingNotifLog = new LocalLog(50);
 
@@ -272,7 +272,8 @@
                     // not want this running if the device is still in the FBE encrypted state.
                     // This is the same procedure that is triggered in the SipIncomingCallReceiver
                     // upon BOOT_COMPLETED.
-                    UserManager userManager = UserManager.get(sMe);
+                    UserManager userManager =
+                            (UserManager) sMe.getSystemService(Context.USER_SERVICE);
                     if (userManager != null && userManager.isUserUnlocked()) {
                         SipUtil.startSipService();
                     }
@@ -345,12 +346,6 @@
 
             mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
 
-            // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
-            // during phone calls.
-            mUpdateLock = new UpdateLock("phone");
-
-            if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
-
             // Create the CallerInfoCache singleton, which remembers custom ring tone and
             // send-to-voicemail settings.
             //
@@ -359,6 +354,8 @@
 
             phoneMgr = PhoneInterfaceManager.init(this);
 
+            imsRcsController = ImsRcsController.init(this);
+
             configLoader = CarrierConfigLoader.init(this);
 
             // Create the CallNotifier singleton, which handles
@@ -372,6 +369,10 @@
             // register for MMI/USSD
             mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
 
+            // Initialize cell status using current airplane mode.
+            handleAirplaneModeChange(this, Settings.Global.getInt(getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, AIRPLANE_OFF));
+
             // Register for misc other intent broadcasts.
             IntentFilter intentFilter =
                     new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
@@ -503,19 +504,6 @@
         mPUKEntryProgressDialog = dialog;
     }
 
-    /**
-     * If we are not currently keeping the screen on, then poke the power
-     * manager to wake up the screen for the user activity timeout duration.
-     */
-    /* package */ void wakeUpScreen() {
-        synchronized (this) {
-            if (mWakeState == WakeState.SLEEP) {
-                if (DBG) Log.d(LOG_TAG, "pulse screen lock");
-                mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.phone:WAKE");
-            }
-        }
-    }
-
     KeyguardManager getKeyguardManager() {
         return mKeyguardManager;
     }
@@ -566,7 +554,7 @@
         Log.i(LOG_TAG, "Turning radio off - airplane");
         Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
                  PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
-        SystemProperties.set("persist.radio.airplane_mode_on", "1");
+        TelephonyProperties.airplane_mode_on(true); // true means int value 1
         Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
         PhoneUtils.setRadioPower(false);
     }
@@ -577,7 +565,7 @@
                 PhoneConstants.CELL_ON_FLAG);
         Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
                 1);
-        SystemProperties.set("persist.radio.airplane_mode_on", "0");
+        TelephonyProperties.airplane_mode_on(false); // false means int value 0
         PhoneUtils.setRadioPower(true);
     }
 
@@ -589,7 +577,9 @@
 
             if (tm != null && tm.isInEmergencyCall()) {
                 // Switch airplane mode back to off.
-                ConnectivityManager.from(this).setAirplaneMode(false);
+                ConnectivityManager cm =
+                        (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
+                cm.setAirplaneMode(false);
                 Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
                         .show();
                 Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
@@ -672,6 +662,7 @@
                 // Roaming status could be overridden by carrier config, so we need to update it.
                 if (VDBG) Log.v(LOG_TAG, "carrier config changed.");
                 updateDataRoamingStatus();
+                updateLimitedSimFunctionForDualSim();
             } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
                 // We also need to pay attention when default data subscription changes.
                 if (VDBG) Log.v(LOG_TAG, "default data sub changed.");
@@ -809,6 +800,38 @@
         return getPhone(subId).getServiceState().getDataRoaming();
     }
 
+    private void updateLimitedSimFunctionForDualSim() {
+        if (DBG) Log.d(LOG_TAG, "updateLimitedSimFunctionForDualSim");
+        // check conditions to display limited SIM function notification under dual SIM
+        SubscriptionManager subMgr = (SubscriptionManager) getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
+        if (subList != null && subList.size() > 1) {
+            CarrierConfigManager configMgr = (CarrierConfigManager)
+                    getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            for (SubscriptionInfo info : subList) {
+                PersistableBundle b = configMgr.getConfigForSubId(info.getSubscriptionId());
+                if (b != null) {
+                    if (b.getBoolean(CarrierConfigManager
+                            .KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL)) {
+                        notificationMgr.showLimitedSimFunctionWarningNotification(
+                                info.getSubscriptionId(),
+                                info.getDisplayName().toString());
+                    } else {
+                        notificationMgr.dismissLimitedSimFunctionWarningNotification(
+                                info.getSubscriptionId());
+                    }
+                }
+            }
+        } else {
+            // cancel notifications for all subs
+            notificationMgr.dismissLimitedSimFunctionWarningNotification(
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        }
+        notificationMgr.dismissLimitedSimFunctionWarningNotificationForInactiveSubs();
+
+    }
+
     public Phone getPhoneInEcm() {
         return phoneInEcm;
     }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e5e9fe7..296525d 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -45,6 +45,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -56,13 +57,16 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.preference.PreferenceManager;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.Annotation.ApnType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
+import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoWcdma;
@@ -92,11 +96,11 @@
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.gsm.GsmCellLocation;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -104,6 +108,7 @@
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -140,8 +145,6 @@
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SmsApplication;
-import com.android.internal.telephony.SmsApplication.SmsApplicationData;
 import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsPermissions;
 import com.android.internal.telephony.SubscriptionController;
@@ -174,7 +177,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -259,6 +261,10 @@
     private static final int EVENT_ENABLE_MODEM_DONE = 69;
     private static final int CMD_GET_MODEM_STATUS = 70;
     private static final int EVENT_GET_MODEM_STATUS_DONE = 71;
+    private static final int CMD_SET_FORBIDDEN_PLMNS = 72;
+    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;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -299,6 +305,12 @@
     private static final int MANUFACTURER_CODE_LENGTH = 8;
 
     /**
+     * Experiment flag to enable erase modem config on reset network, default value is false
+     */
+    public static final String RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED =
+            "reset_network_erase_modem_config_enabled";
+
+    /**
      * A request object to use for transmitting data to an ICC.
      */
     private static final class IccAPDUArgument {
@@ -1074,11 +1086,13 @@
                     try {
                         if (ar.exception != null) {
                             Log.e(LOG_TAG, "Exception retrieving CellInfo=" + ar.exception);
-                            cb.onError(TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR,
-                                    new android.os.ParcelableException(ar.exception));
+                            cb.onError(
+                                    TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR,
+                                    ar.exception.getClass().getName(),
+                                    ar.exception.toString());
                         } else if (ar.result == null) {
                             Log.w(LOG_TAG, "Timeout Waiting for CellInfo!");
-                            cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null);
+                            cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null, null);
                         } else {
                             // use the result as returned
                             cb.onCellInfo((List<CellInfo>) ar.result);
@@ -1166,6 +1180,58 @@
                     }
                     notifyRequester(request);
                     break;
+                case EVENT_SET_FORBIDDEN_PLMNS_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null && ar.result != null) {
+                        request.result = ar.result;
+                    } else {
+                        request.result = -1;
+                        loge("Failed to set Forbidden Plmns");
+                        if (ar.result == null) {
+                            loge("setForbidenPlmns: Empty response");
+                        } else if (ar.exception != null) {
+                            loge("setForbiddenPlmns: Exception: " + ar.exception);
+                            request.result = -1;
+                        } else {
+                            loge("setForbiddenPlmns: Unknown exception");
+                        }
+                    }
+                    notifyRequester(request);
+                    break;
+                case CMD_SET_FORBIDDEN_PLMNS:
+                    request = (MainThreadRequest) msg.obj;
+                    uiccCard = getUiccCardFromRequest(request);
+                    if (uiccCard == null) {
+                        loge("setForbiddenPlmns: UiccCard is null");
+                        request.result = -1;
+                        notifyRequester(request);
+                        break;
+                    }
+                    Pair<Integer, List<String>> setFplmnsArgs =
+                            (Pair<Integer, List<String>>) request.argument;
+                    appType = setFplmnsArgs.first;
+                    List<String> fplmns = setFplmnsArgs.second;
+                    uiccApp = uiccCard.getApplicationByType(appType);
+                    if (uiccApp == null) {
+                        loge("setForbiddenPlmns: no app with specified type -- " + appType);
+                        request.result = -1;
+                        loge("Failed to get UICC App");
+                        notifyRequester(request);
+                    } else {
+                        onCompleted = obtainMessage(EVENT_SET_FORBIDDEN_PLMNS_DONE, request);
+                        ((SIMRecords) uiccApp.getIccRecords())
+                                .setForbiddenPlmns(onCompleted, fplmns);
+                    }
+                    break;
+                case CMD_ERASE_MODEM_CONFIG:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_ERASE_MODEM_CONFIG_DONE, request);
+                    defaultPhone.eraseModemConfig(onCompleted);
+                    break;
+                case EVENT_ERASE_MODEM_CONFIG_DONE:
+                    handleNullReturnEvent(msg, "eraseModemConfig");
+                    break;
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -1374,6 +1440,20 @@
         return PhoneFactory.getPhone(mSubscriptionController.getPhoneId(subId));
     }
 
+    private void sendEraseModemConfig(Phone phone) {
+        if (phone != null) {
+            TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                  mApp, phone.getSubId(), "eraseModemConfig");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                Boolean success = (Boolean) sendRequest(CMD_ERASE_MODEM_CONFIG, null);
+                if (DBG) log("eraseModemConfig:" + ' ' + (success ? "ok" : "fail"));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
     public void dial(String number) {
         dialForSubscriber(getPreferredVoiceSubscription(), number);
     }
@@ -1698,6 +1778,7 @@
     }
 
     public boolean needMobileRadioShutdown() {
+        enforceReadPrivilegedPermission("needMobileRadioShutdown");
         /*
          * If any of the Radios are available, it will need to be
          * shutdown. So return true if any Radio is available.
@@ -1952,7 +2033,15 @@
     }
 
     @Override
-    public String getNetworkCountryIsoForPhone(int phoneId) {
+    public String getNetworkCountryIsoForPhone(int phoneId, String callingPackage) {
+        if (!TextUtils.isEmpty(callingPackage)) {
+            final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
+            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                    mApp, subId, callingPackage, "getNetworkCountryIsoForPhone")) {
+                return "";
+            }
+        }
+
         // 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();
@@ -2349,6 +2438,11 @@
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
     }
 
+    private void enforceActiveEmergencySessionPermission() {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
+    }
+
     /**
      * Make sure the caller has the CALL_PHONE permission.
      *
@@ -2358,9 +2452,8 @@
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
     }
 
-    private void enforceConnectivityInternalPermission() {
-        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
-                "ConnectivityService");
+    private void enforceSettingsPermission() {
+        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS, null);
     }
 
     private String createTelUrl(String number) {
@@ -2588,7 +2681,8 @@
     @Override
     public Bundle getVisualVoicemailSettings(String callingPackage, int subId) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-        String systemDialer = TelecomManager.from(mApp).getSystemDialerPackage();
+        TelecomManager tm = mApp.getSystemService(TelecomManager.class);
+        String systemDialer = tm.getSystemDialerPackage();
         if (!TextUtils.equals(callingPackage, systemDialer)) {
             throw new SecurityException("caller must be system dialer");
         }
@@ -2814,8 +2908,8 @@
     public void sendDialerSpecialCode(String callingPackage, String inputCode) {
         final Phone defaultPhone = getDefaultPhone();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-        String defaultDialer = TelecomManager.from(defaultPhone.getContext())
-                .getDefaultDialerPackage();
+        TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
+        String defaultDialer = tm.getDefaultDialerPackage();
         if (!TextUtils.equals(callingPackage, defaultDialer)) {
             TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
                     getDefaultSubscription(), "sendDialerSpecialCode");
@@ -2858,6 +2952,10 @@
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c)
             throws RemoteException {
         enforceReadPrivilegedPermission("registerImsRegistrationCallback");
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -2876,24 +2974,97 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        Binder.withCleanCallingIdentity(() -> {
-            try {
-                // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
-                ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
-                        .removeRegistrationCallbackForSubscription(c, subId);
-            } catch (ImsException e) {
-                Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId
-                        + "is inactive, ignoring unregister.");
-                // If the subscription is no longer active, just return, since the callback
-                // will already have been removed internally.
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
+            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
+                    .removeRegistrationCallbackForSubscription(c, subId);
+        } catch (ImsException e) {
+            Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId
+                    + "is inactive, ignoring unregister.");
+            // If the subscription is no longer active, just return, since the callback
+            // will already have been removed internally.
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Get the IMS service registration state for the MmTelFeature associated with this sub id.
+     */
+    @Override
+    public void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer) {
+        enforceReadPrivilegedPermission("getImsMmTelRegistrationState");
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subId);
+            if (phone == null) {
+                Log.w(LOG_TAG, "getImsMmTelRegistrationState: called with an invalid subscription '"
+                        + subId + "'");
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
             }
-        });
+            phone.getImsRegistrationState(regState -> {
+                try {
+                    consumer.accept((regState == null)
+                            ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
+                } catch (RemoteException e) {
+                    // Ignore if the remote process is no longer available to call back.
+                    Log.w(LOG_TAG, "getImsMmTelRegistrationState: callback not available.");
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Get the transport type for the IMS service registration state.
+     */
+    @Override
+    public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) {
+        enforceReadPrivilegedPermission("getImsMmTelRegistrationTransportType");
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subId);
+            if (phone == null) {
+                Log.w(LOG_TAG, "getImsMmTelRegistrationState: called with an invalid subscription '"
+                        + subId + "'");
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+            }
+            phone.getImsRegistrationTech(regTech -> {
+                // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
+                int regTechConverted = (regTech == null)
+                        ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
+                regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(
+                        regTechConverted);
+                try {
+                    consumer.accept(regTechConverted);
+                } catch (RemoteException e) {
+                    // Ignore if the remote process is no longer available to call back.
+                    Log.w(LOG_TAG, "getImsMmTelRegistrationState: callback not available.");
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
     public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c)
             throws RemoteException {
         enforceReadPrivilegedPermission("registerMmTelCapabilityCallback");
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
         // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
         final long token = Binder.clearCallingIdentity();
         try {
@@ -2909,22 +3080,23 @@
     @Override
     public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) {
         enforceReadPrivilegedPermission("unregisterMmTelCapabilityCallback");
-
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        Binder.withCleanCallingIdentity(() -> {
-            try {
-                // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
-                ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
+            ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
                         .removeCapabilitiesCallbackForSubscription(c, subId);
-            } catch (ImsException e) {
-                Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId
-                        + "is inactive, ignoring unregister.");
-                // If the subscription is no longer active, just return, since the callback
-                // will already have been removed internally.
-            }
-        });
+        } catch (ImsException e) {
+            Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId
+                     + "is inactive, ignoring unregister.");
+             // If the subscription is no longer active, just return, since the callback
+             // will already have been removed internally.
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
@@ -2959,6 +3131,44 @@
         }
     }
 
+    /**
+     * Determines if the MmTel feature capability is supported by the carrier configuration for this
+     * subscription.
+     * @param subId The subscription to use to check the configuration.
+     * @param callback The callback that will be used to send the result.
+     * @param capability The MmTelFeature capability that will be used to send the result.
+     * @param transportType The transport type of the MmTelFeature capability.
+     */
+    @Override
+    public void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability,
+            int transportType) {
+        enforceReadPrivilegedPermission("isMmTelCapabilitySupported");
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            int slotId = getSlotIndex(subId);
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                Log.w(LOG_TAG, "isMmTelCapabilitySupported: called with an inactive subscription '"
+                        + subId + "'");
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+            }
+            ImsManager.getInstance(mApp, slotId).isSupported(capability,
+                    transportType, aBoolean -> {
+                        try {
+                            callback.accept((aBoolean == null) ? 0 : (aBoolean ? 1 : 0));
+                        } catch (RemoteException e) {
+                            Log.w(LOG_TAG, "isMmTelCapabilitySupported: remote caller is not "
+                                    + "running. Ignore");
+                        }
+                    });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public boolean isAdvancedCallingSettingEnabled(int subId) {
         enforceReadPrivilegedPermission("enforceReadPrivilegedPermission");
@@ -4031,7 +4241,7 @@
             if (response instanceof String[]) {
                 return (String[]) response;
             }
-            // Response is an Exception of some kind,
+            // Response is an Exception of some kind
             // which is signalled to the user as a NULL retval
             return null;
         } finally {
@@ -4039,6 +4249,47 @@
         }
     }
 
+    /**
+     * Set the forbidden PLMN list from the given app type (ex APPTYPE_USIM) on a particular
+     * subscription.
+     *
+     * @param subId the id of the subscription.
+     * @param appType the uicc app type, must be USIM or SIM.
+     * @param fplmns the Forbiden plmns list that needed to be written to the SIM.
+     * @param callingPackage the op Package name.
+     * @return number of fplmns that is successfully written to the SIM.
+     */
+    public int setForbiddenPlmns(
+            int subId, int appType, List<String> fplmns, String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mApp, subId, callingPackage, "setForbiddenPlmns")) {
+            if (DBG) logv("no permissions for setForbiddenplmns");
+            throw new IllegalStateException("No Permissions for setForbiddenPlmns");
+        }
+        if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
+            loge("setForbiddenPlmnList(): App Type must be USIM or SIM");
+            throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM");
+        }
+        if (fplmns == null) {
+            throw new IllegalArgumentException("Fplmn List provided is null");
+        }
+        for (String fplmn : fplmns) {
+            if (!CellIdentity.isValidPlmn(fplmn)) {
+                throw new IllegalArgumentException("Invalid fplmn provided: " + fplmn);
+            }
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Object response = sendRequest(
+                    CMD_SET_FORBIDDEN_PLMNS,
+                    new Pair<Integer, List<String>>(new Integer(appType), fplmns),
+                    subId);
+            return (int) response;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public String sendEnvelopeWithStatus(int subId, String content) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
@@ -4378,12 +4629,48 @@
                 // may happen if the device does not support IMS.
                 return "";
             }
-            return resolver.getImsServiceConfiguration(slotId, isCarrierImsService);
+            // TODO: change API to query RCS separately.
+            return resolver.getImsServiceConfiguration(slotId, isCarrierImsService,
+                    ImsFeature.FEATURE_MMTEL);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
+    /**
+     * Get the MmTelFeature state associated with the requested subscription id.
+     * @param subId The subscription that the MmTelFeature is associated with.
+     * @param callback A callback with an integer containing the
+     * {@link android.telephony.ims.feature.ImsFeature.ImsState} associated with the MmTelFeature.
+     */
+    @Override
+    public void getImsMmTelFeatureState(int subId, IIntegerConsumer callback) {
+        enforceReadPrivilegedPermission("getImsMmTelFeatureState");
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            int slotId = getSlotIndex(subId);
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                Log.w(LOG_TAG, "getImsMmTelFeatureState: called with an inactive subscription '"
+                        + subId + "'");
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+            }
+            ImsManager.getInstance(mApp, slotId).getImsServiceState(anInteger -> {
+                try {
+                    callback.accept(anInteger == null ? ImsFeature.STATE_UNAVAILABLE : anInteger);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, "getImsMmTelFeatureState: remote caller is no longer running. "
+                            + "Ignore");
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     public void setImsRegistrationState(boolean registered) {
         enforceModifyPermission();
 
@@ -4659,7 +4946,7 @@
      * @hide
      */
     @Override
-    public boolean getTetherApnRequiredForSubscriber(int subId) {
+    public boolean isTetheringApnRequiredForSubscriber(int subId) {
         enforceModifyPermission();
         final long identity = Binder.clearCallingIdentity();
         final Phone phone = getPhone(subId);
@@ -4792,6 +5079,52 @@
         }
     }
 
+    private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim,
+            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);
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            SubscriptionInfo subInfo = subController.getSubscriptionInfo(phone.getSubId());
+            SubscriptionManager subManager = (SubscriptionManager)
+                    phone.getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+            for (String pkg : packages) {
+                if (subManager.canManageSubscription(subInfo, pkg)) {
+                    return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+                }
+            }
+            return privilegeFromSim;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, Phone phone,
+            String pkgName) {
+        //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;
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            SubscriptionInfo subInfo = subController.getSubscriptionInfo(phone.getSubId());
+            SubscriptionManager subManager = (SubscriptionManager)
+                    phone.getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+            return subManager.canManageSubscription(subInfo, pkgName)
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : privilegeFromSim;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public int getCarrierPrivilegeStatus(int subId) {
         final Phone phone = getPhone(subId);
@@ -4804,8 +5137,10 @@
             loge("getCarrierPrivilegeStatus: No UICC");
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
         }
-        return card.getCarrierPrivilegeStatusForCurrentTransaction(
-                phone.getContext().getPackageManager());
+
+        return getCarrierPrivilegeStatusFromCarrierConfigRules(
+            card.getCarrierPrivilegeStatusForCurrentTransaction(
+                phone.getContext().getPackageManager()), phone);
     }
 
     @Override
@@ -4821,7 +5156,9 @@
             loge("getCarrierPrivilegeStatusForUid: No UICC");
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
         }
-        return profile.getCarrierPrivilegeStatusForUid(phone.getContext().getPackageManager(), uid);
+        return getCarrierPrivilegeStatusFromCarrierConfigRules(
+            profile.getCarrierPrivilegeStatusForUid(
+                phone.getContext().getPackageManager(), uid), phone);
     }
 
     @Override
@@ -4836,8 +5173,9 @@
             loge("checkCarrierPrivilegesForPackage: No UICC on subId " + subId);
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
         }
-
-        return card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName);
+        return getCarrierPrivilegeStatusFromCarrierConfigRules(
+            card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
+            getPhone(phoneId), pkgName);
     }
 
     @Override
@@ -4852,7 +5190,9 @@
               continue;
             }
 
-            result = card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName);
+            result = getCarrierPrivilegeStatusFromCarrierConfigRules(
+                card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
+                getPhone(i), pkgName);
             if (result == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                 break;
             }
@@ -4887,9 +5227,10 @@
                 if (packages == null) {
                     // Only check packages in user 0 for now
                     packages = pm.getInstalledPackagesAsUser(
-                            PackageManager.MATCH_DISABLED_COMPONENTS
-                                    | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                                    | PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
+                        PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                            | PackageManager.GET_SIGNING_CERTIFICATES,
+                            UserHandle.USER_SYSTEM);
                 }
                 for (int p = packages.size() - 1; p >= 0; p--) {
                     PackageInfo pkgInfo = packages.get(p);
@@ -4906,9 +5247,17 @@
 
     @Override
     public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+        enforceReadPrivilegedPermission("getPackagesWithCarrierPrivilegesForAllPhones");
+
+        final long identity = Binder.clearCallingIdentity();
+
         List<String> privilegedPackages = new ArrayList<>();
-        for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
-           privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i));
+        try {
+            for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+                privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
         return privilegedPackages;
     }
@@ -5111,6 +5460,49 @@
     }
 
     @Override
+    public String[] getMergedSubscriberIdsFromGroup(int subId, String callingPackage) {
+        enforceReadPrivilegedPermission("getMergedSubscriberIdsFromGroup");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final TelephonyManager telephonyManager = mApp.getSystemService(
+                    TelephonyManager.class);
+            String subscriberId = telephonyManager.getSubscriberId(subId);
+            if (subscriberId == null) {
+                if (DBG) {
+                    log("getMergedSubscriberIdsFromGroup can't find subscriberId for subId "
+                            + subId);
+                }
+                return null;
+            }
+
+            final SubscriptionInfo info = SubscriptionController.getInstance()
+                    .getSubscriptionInfo(subId);
+            final ParcelUuid groupUuid = info.getGroupUuid();
+            // If it doesn't belong to any group, return just subscriberId of itself.
+            if (groupUuid == null) {
+                return new String[]{subscriberId};
+            }
+
+            // Get all subscriberIds from the group.
+            final List<String> mergedSubscriberIds = new ArrayList<>();
+            final List<SubscriptionInfo> groupInfos = SubscriptionController.getInstance()
+                    .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName());
+            for (SubscriptionInfo subInfo : groupInfos) {
+                subscriberId = telephonyManager.getSubscriberId(subInfo.getSubscriptionId());
+                if (subscriberId != null) {
+                    mergedSubscriberIds.add(subscriberId);
+                }
+            }
+
+            return mergedSubscriberIds.toArray(new String[mergedSubscriberIds.size()]);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+
+        }
+    }
+
+    @Override
     public boolean setOperatorBrandOverride(int subId, String brand) {
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
                 subId, "setOperatorBrandOverride");
@@ -5280,7 +5672,7 @@
 
     @Override
     public boolean isTtyModeSupported() {
-        TelecomManager telecomManager = TelecomManager.from(mApp);
+        TelecomManager telecomManager = mApp.getSystemService(TelecomManager.class);
         return telecomManager.isTtySupported();
     }
 
@@ -5327,12 +5719,7 @@
     public boolean isRttEnabled(int subscriptionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            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);
+            return isRttSupported(subscriptionId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5391,6 +5778,21 @@
     }
 
     @Override
+    public int getSubIdForPhoneAccountHandle(
+            PhoneAccountHandle phoneAccountHandle, String callingPackage) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, getDefaultSubscription(),
+                callingPackage, "getSubIdForPhoneAccountHandle")) {
+            throw new SecurityException("Requires READ_PHONE_STATE permission.");
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -5458,7 +5860,7 @@
 
     @Override
     public void factoryReset(int subId) {
-        enforceConnectivityInternalPermission();
+        enforceSettingsPermission();
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
         }
@@ -5479,6 +5881,18 @@
             // in and combined with those stale ones. In case this happens again,
             // user can reset all network settings which will clean up this table.
             cleanUpSmsRawTable(getDefaultPhone().getContext());
+            // Clean up IMS settings as well here.
+            int slotId = getSlotIndex(subId);
+            if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                ImsManager.getInstance(mApp, slotId).factoryReset();
+            }
+
+            // Erase modem config if erase modem on network setting is enabled.
+            String configValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TELEPHONY,
+                    RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED);
+            if (configValue != null && Boolean.parseBoolean(configValue)) {
+              sendEraseModemConfig(getDefaultPhone());
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5719,8 +6133,8 @@
             PhoneAccountHandle phoneAccountHandle, Uri uri) {
         final Phone defaultPhone = getDefaultPhone();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-        if (!TextUtils.equals(callingPackage,
-                TelecomManager.from(defaultPhone.getContext()).getDefaultDialerPackage())) {
+        TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
+        if (!TextUtils.equals(callingPackage, tm.getDefaultDialerPackage())) {
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle),
                     "setVoicemailRingtoneUri");
@@ -5776,8 +6190,8 @@
             PhoneAccountHandle phoneAccountHandle, boolean enabled) {
         final Phone defaultPhone = getDefaultPhone();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
-        if (!TextUtils.equals(callingPackage,
-                TelecomManager.from(defaultPhone.getContext()).getDefaultDialerPackage())) {
+        TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
+        if (!TextUtils.equals(callingPackage, tm.getDefaultDialerPackage())) {
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle),
                     "setVoicemailVibrationEnabled");
@@ -6120,7 +6534,8 @@
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
             String[] args, ShellCallback callback, ResultReceiver resultReceiver)
             throws RemoteException {
-        (new TelephonyShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);
+        (new TelephonyShellCommand(this, getDefaultPhone().getContext()))
+                .exec(this, in, out, err, args, callback, resultReceiver);
     }
 
     /**
@@ -6700,75 +7115,6 @@
         }
     }
 
-    private void ensureUserRunning(int userId) {
-        if (!mUserManager.isUserRunning(userId)) {
-            throw new IllegalStateException("User " + userId + " does not exist or not running");
-        }
-    }
-
-    /**
-     * Returns a list of SMS apps on a given user.
-     *
-     * Only the shell user (UID 2000 or 0) can call it.
-     * Target user must be running.
-     */
-    @Override
-    public String[] getSmsApps(int userId) {
-        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getSmsApps");
-        ensureUserRunning(userId);
-
-        final Collection<SmsApplicationData> apps =
-                SmsApplication.getApplicationCollectionAsUser(mApp, userId);
-
-        String[] ret = new String[apps.size()];
-        int i = 0;
-        for (SmsApplicationData app : apps) {
-            ret[i++] = app.mPackageName;
-        }
-        return ret;
-    }
-
-    /**
-     * Returns the default SMS app package name on a given user.
-     *
-     * Only the shell user (UID 2000 or 0) can call it.
-     * Target user must be running.
-     */
-    @Override
-    public String getDefaultSmsApp(int userId) {
-        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDefaultSmsApp");
-        ensureUserRunning(userId);
-
-        final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(mApp,
-                /* updateIfNeeded= */ true, userId);
-        return cn == null ? null : cn.getPackageName();
-    }
-
-    /**
-     * Set a package as the default SMS app on a given user.
-     *
-     * Only the shell user (UID 2000 or 0) can call it.
-     * Target user must be running.
-     */
-    @Override
-    public void setDefaultSmsApp(int userId, String packageName) {
-        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDefaultSmsApp");
-        ensureUserRunning(userId);
-
-        boolean found = false;
-        for (String pkg : getSmsApps(userId)) {
-            if (TextUtils.equals(packageName, pkg)) {
-                found = true;
-                break;
-            }
-        }
-        if (!found) {
-            throw new IllegalArgumentException("Package " + packageName + " is not an SMS app");
-        }
-
-        SmsApplication.setDefaultApplicationAsUser(packageName, mApp, userId);
-    }
-
     @Override
     public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
             String callingPackage) {
@@ -6865,6 +7211,57 @@
     }
 
     @Override
+    public int getEmergencyNumberDbVersion(int subId) {
+        enforceReadPrivilegedPermission("getEmergencyNumberDbVersion");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final Phone phone = getPhone(subId);
+            if (phone == null) {
+                loge("getEmergencyNumberDbVersion fails with invalid subId: " + subId);
+                return TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION;
+            }
+            return phone.getEmergencyNumberDbVersion();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void notifyOtaEmergencyNumberDbInstalled() {
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (Phone phone: PhoneFactory.getPhones()) {
+                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+                if (tracker != null) {
+                    tracker.updateOtaEmergencyNumberDatabase();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath) {
+        enforceActiveEmergencySessionPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (Phone phone: PhoneFactory.getPhones()) {
+                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+                if (tracker != null) {
+                    tracker.updateTestOtaEmergencyNumberDbFilePath(otaFilePath);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public List<String> getCertsFromCarrierPrivilegeAccessRules(int subId) {
         enforceReadPrivilegedPermission("getCertsFromCarrierPrivilegeAccessRules");
         Phone phone = getPhone(subId);
@@ -7116,10 +7513,8 @@
      */
     @Override
     public boolean isDataEnabledForApn(int apnType, int subId, String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, subId, callingPackage, "isDataEnabledForApn")) {
-            throw new SecurityException("Needs READ_PHONE_STATE for isDataEnabledForApn");
-        }
+        enforceReadPrivilegedPermission("Needs READ_PRIVILEGED_PHONE_STATE for "
+                + "isDataEnabledForApn");
 
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index e46cfc0..9835ba1 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -31,6 +31,7 @@
 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;
@@ -43,10 +44,7 @@
 import android.widget.Toast;
 
 import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.CallerInfoAsyncQuery;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.MmiCode;
@@ -54,10 +52,8 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyCapabilities;
-import com.android.phone.CallGatewayManager.RawGatewayInfo;
 import com.android.phone.settings.SuppServicesUiUtil;
 
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -131,79 +127,35 @@
     }
 
     /**
-     * @see placeCall below
-     */
-    public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
-            boolean isEmergencyCall) {
-        return placeCall(context, phone, number, contactRef, isEmergencyCall,
-                CallGatewayManager.EMPTY_INFO, null);
-    }
-
-    /**
      * Dial the number using the phone passed in.
      *
-     * If the connection is establised, this method issues a sync call
-     * that may block to query the caller info.
-     * TODO: Change the logic to use the async query.
-     *
      * @param context To perform the CallerInfo query.
      * @param phone the Phone object.
      * @param number to be dialed as requested by the user. This is
      * NOT the phone number to connect to. It is used only to build the
      * call card and to update the call log. See above for restrictions.
-     * @param contactRef that triggered the call. Typically a 'tel:'
-     * uri but can also be a 'content://contacts' one.
-     * @param isEmergencyCall indicates that whether or not this is an
-     * emergency call
-     * @param gatewayUri Is the address used to setup the connection, null
-     * if not using a gateway
-     * @param callGateway Class for setting gateway data on a successful call.
      *
      * @return either CALL_STATUS_DIALED or CALL_STATUS_FAILED
      */
-    public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
-            boolean isEmergencyCall, RawGatewayInfo gatewayInfo, CallGatewayManager callGateway) {
-        final Uri gatewayUri = gatewayInfo.gatewayUri;
+    public static int placeOtaspCall(Context context, Phone phone, String number) {
+        final Uri gatewayUri = null;
 
         if (VDBG) {
             log("placeCall()... number: '" + number + "'"
-                    + ", GW:'" + gatewayUri + "'"
-                    + ", contactRef:" + contactRef
-                    + ", isEmergencyCall: " + isEmergencyCall);
+                    + ", GW:'" + gatewayUri + "'");
         } else {
             log("placeCall()... number: " + toLogSafePhoneNumber(number)
-                    + ", GW: " + (gatewayUri != null ? "non-null" : "null")
-                    + ", emergency? " + isEmergencyCall);
+                    + ", GW: " + (gatewayUri != null ? "non-null" : "null"));
         }
         final PhoneGlobals app = PhoneGlobals.getInstance();
 
         boolean useGateway = false;
-        if (null != gatewayUri &&
-            !isEmergencyCall &&
-            PhoneUtils.isRoutableViaGateway(number)) {  // Filter out MMI, OTA and other codes.
-            useGateway = true;
-        }
+        Uri contactRef = null;
 
         int status = CALL_STATUS_DIALED;
         Connection connection;
         String numberToDial;
-        if (useGateway) {
-            // TODO: 'tel' should be a constant defined in framework base
-            // somewhere (it is in webkit.)
-            if (null == gatewayUri || !PhoneAccount.SCHEME_TEL.equals(gatewayUri.getScheme())) {
-                Log.e(LOG_TAG, "Unsupported URL:" + gatewayUri);
-                return CALL_STATUS_FAILED;
-            }
-
-            // We can use getSchemeSpecificPart because we don't allow #
-            // in the gateway numbers (treated a fragment delim.) However
-            // if we allow more complex gateway numbers sequence (with
-            // passwords or whatnot) that use #, this may break.
-            // TODO: Need to support MMI codes.
-            numberToDial = gatewayUri.getSchemeSpecificPart();
-        } else {
-            numberToDial = number;
-        }
+        numberToDial = number;
 
         try {
             connection = app.mCM.dial(phone, numberToDial, VideoProfile.STATE_AUDIO_ONLY);
@@ -228,33 +180,6 @@
             if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                 updateCdmaCallStateOnNewOutgoingCall(app, connection);
             }
-
-            if (gatewayUri == null) {
-                // phone.dial() succeeded: we're now in a normal phone call.
-                // attach the URI to the CallerInfo Object if it is there,
-                // otherwise just attach the Uri Reference.
-                // if the uri does not have a "content" scheme, then we treat
-                // it as if it does NOT have a unique reference.
-                String content = context.getContentResolver().SCHEME_CONTENT;
-                if ((contactRef != null) && (contactRef.getScheme().equals(content))) {
-                    Object userDataObject = connection.getUserData();
-                    if (userDataObject == null) {
-                        connection.setUserData(contactRef);
-                    } else {
-                        // TODO: This branch is dead code, we have
-                        // just created the connection which has
-                        // no user data (null) by default.
-                        if (userDataObject instanceof CallerInfo) {
-                        ((CallerInfo) userDataObject).contactRefUri = contactRef;
-                        } else {
-                        ((CallerInfoToken) userDataObject).currentInfo.contactRefUri =
-                            contactRef;
-                        }
-                    }
-                }
-            }
-
-            startGetCallerInfo(context, connection, null, null, gatewayInfo);
         }
 
         return status;
@@ -299,7 +224,7 @@
                                           MmiCode mmiCode,
                                           Message buttonCallbackMessage,
                                           Dialog previousAlert) {
-        log("displayMMIInitiate: " + android.telecom.Log.pii(mmiCode.toString()));
+        log("displayMMIInitiate: " + Rlog.pii(LOG_TAG, mmiCode.toString()));
         if (previousAlert != null) {
             previousAlert.dismiss();
         }
@@ -662,533 +587,12 @@
         return canceled;
     }
 
-    /**
-     * Returns the caller-id info corresponding to the specified Connection.
-     * (This is just a simple wrapper around CallerInfo.getCallerInfo(): we
-     * extract a phone number from the specified Connection, and feed that
-     * number into CallerInfo.getCallerInfo().)
-     *
-     * The returned CallerInfo may be null in certain error cases, like if the
-     * specified Connection was null, or if we weren't able to get a valid
-     * phone number from the Connection.
-     *
-     * Finally, if the getCallerInfo() call did succeed, we save the resulting
-     * CallerInfo object in the "userData" field of the Connection.
-     *
-     * NOTE: This API should be avoided, with preference given to the
-     * asynchronous startGetCallerInfo API.
-     */
-    static CallerInfo getCallerInfo(Context context, Connection c) {
-        CallerInfo info = null;
-
-        if (c != null) {
-            //See if there is a URI attached.  If there is, this means
-            //that there is no CallerInfo queried yet, so we'll need to
-            //replace the URI with a full CallerInfo object.
-            Object userDataObject = c.getUserData();
-            if (userDataObject instanceof Uri) {
-                info = CallerInfo.getCallerInfo(context, (Uri) userDataObject);
-                if (info != null) {
-                    c.setUserData(info);
-                }
-            } else {
-                if (userDataObject instanceof CallerInfoToken) {
-                    //temporary result, while query is running
-                    info = ((CallerInfoToken) userDataObject).currentInfo;
-                } else {
-                    //final query result
-                    info = (CallerInfo) userDataObject;
-                }
-                if (info == null) {
-                    // No URI, or Existing CallerInfo, so we'll have to make do with
-                    // querying a new CallerInfo using the connection's phone number.
-                    String number = c.getAddress();
-
-                    if (DBG) log("getCallerInfo: number = " + toLogSafePhoneNumber(number));
-
-                    if (!TextUtils.isEmpty(number)) {
-                        info = CallerInfo.getCallerInfo(context, number);
-                        if (info != null) {
-                            c.setUserData(info);
-                        }
-                    }
-                }
-            }
-        }
-        return info;
-    }
-
-    /**
-     * Class returned by the startGetCallerInfo call to package a temporary
-     * CallerInfo Object, to be superceded by the CallerInfo Object passed
-     * into the listener when the query with token mAsyncQueryToken is complete.
-     */
-    public static class CallerInfoToken {
-        /**indicates that there will no longer be updates to this request.*/
-        public boolean isFinal;
-
-        public CallerInfo currentInfo;
-        public CallerInfoAsyncQuery asyncQuery;
-    }
-
-    /**
-     * place a temporary callerinfo object in the hands of the caller and notify
-     * caller when the actual query is done.
-     */
-    static CallerInfoToken startGetCallerInfo(Context context, Connection c,
-            CallerInfoAsyncQuery.OnQueryCompleteListener listener, Object cookie,
-            RawGatewayInfo info) {
-        CallerInfoToken cit;
-
-        if (c == null) {
-            //TODO: perhaps throw an exception here.
-            cit = new CallerInfoToken();
-            cit.asyncQuery = null;
-            return cit;
-        }
-
-        Object userDataObject = c.getUserData();
-
-        // There are now 3 states for the Connection's userData object:
-        //
-        //   (1) Uri - query has not been executed yet
-        //
-        //   (2) CallerInfoToken - query is executing, but has not completed.
-        //
-        //   (3) CallerInfo - query has executed.
-        //
-        // In each case we have slightly different behaviour:
-        //   1. If the query has not been executed yet (Uri or null), we start
-        //      query execution asynchronously, and note it by attaching a
-        //      CallerInfoToken as the userData.
-        //   2. If the query is executing (CallerInfoToken), we've essentially
-        //      reached a state where we've received multiple requests for the
-        //      same callerInfo.  That means that once the query is complete,
-        //      we'll need to execute the additional listener requested.
-        //   3. If the query has already been executed (CallerInfo), we just
-        //      return the CallerInfo object as expected.
-        //   4. Regarding isFinal - there are cases where the CallerInfo object
-        //      will not be attached, like when the number is empty (caller id
-        //      blocking).  This flag is used to indicate that the
-        //      CallerInfoToken object is going to be permanent since no
-        //      query results will be returned.  In the case where a query
-        //      has been completed, this flag is used to indicate to the caller
-        //      that the data will not be updated since it is valid.
-        //
-        //      Note: For the case where a number is NOT retrievable, we leave
-        //      the CallerInfo as null in the CallerInfoToken.  This is
-        //      something of a departure from the original code, since the old
-        //      code manufactured a CallerInfo object regardless of the query
-        //      outcome.  From now on, we will append an empty CallerInfo
-        //      object, to mirror previous behaviour, and to avoid Null Pointer
-        //      Exceptions.
-
-        if (userDataObject instanceof Uri) {
-            // State (1): query has not been executed yet
-
-            //create a dummy callerinfo, populate with what we know from URI.
-            cit = new CallerInfoToken();
-            cit.currentInfo = new CallerInfo();
-            cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
-                    (Uri) userDataObject, sCallerInfoQueryListener, c);
-            cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
-            cit.isFinal = false;
-
-            c.setUserData(cit);
-
-            if (DBG) log("startGetCallerInfo: query based on Uri: " + userDataObject);
-
-        } else if (userDataObject == null) {
-            // No URI, or Existing CallerInfo, so we'll have to make do with
-            // querying a new CallerInfo using the connection's phone number.
-            String number = c.getAddress();
-
-            if (info != null && info != CallGatewayManager.EMPTY_INFO) {
-                // Gateway number, the connection number is actually the gateway number.
-                // need to lookup via dialed number.
-                number = info.trueNumber;
-            }
-
-            if (DBG) {
-                log("PhoneUtils.startGetCallerInfo: new query for phone number...");
-                log("- number (address): " + toLogSafePhoneNumber(number));
-                log("- c: " + c);
-                log("- phone: " + c.getCall().getPhone());
-                int phoneType = c.getCall().getPhone().getPhoneType();
-                log("- phoneType: " + phoneType);
-                switch (phoneType) {
-                    case PhoneConstants.PHONE_TYPE_NONE: log("  ==> PHONE_TYPE_NONE"); break;
-                    case PhoneConstants.PHONE_TYPE_GSM: log("  ==> PHONE_TYPE_GSM"); break;
-                    case PhoneConstants.PHONE_TYPE_IMS: log("  ==> PHONE_TYPE_IMS"); break;
-                    case PhoneConstants.PHONE_TYPE_CDMA: log("  ==> PHONE_TYPE_CDMA"); break;
-                    case PhoneConstants.PHONE_TYPE_SIP: log("  ==> PHONE_TYPE_SIP"); break;
-                    case PhoneConstants.PHONE_TYPE_THIRD_PARTY:
-                        log("  ==> PHONE_TYPE_THIRD_PARTY");
-                        break;
-                    default: log("  ==> Unknown phone type"); break;
-                }
-            }
-
-            cit = new CallerInfoToken();
-            cit.currentInfo = new CallerInfo();
-
-            // Store CNAP information retrieved from the Connection (we want to do this
-            // here regardless of whether the number is empty or not).
-            cit.currentInfo.cnapName =  c.getCnapName();
-            cit.currentInfo.name = cit.currentInfo.cnapName; // This can still get overwritten
-                                                             // by ContactInfo later
-            cit.currentInfo.numberPresentation = c.getNumberPresentation();
-            cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
-            if (VDBG) {
-                log("startGetCallerInfo: number = " + number);
-                log("startGetCallerInfo: CNAP Info from FW(1): name="
-                    + cit.currentInfo.cnapName
-                    + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
-            }
-
-            // handling case where number is null (caller id hidden) as well.
-            if (!TextUtils.isEmpty(number)) {
-                // Check for special CNAP cases and modify the CallerInfo accordingly
-                // to be sure we keep the right information to display/log later
-                number = modifyForSpecialCnapCases(context, cit.currentInfo, number,
-                        cit.currentInfo.numberPresentation);
-
-                cit.currentInfo.phoneNumber = number;
-                // For scenarios where we may receive a valid number from the network but a
-                // restricted/unavailable presentation, we do not want to perform a contact query
-                // (see note on isFinal above). So we set isFinal to true here as well.
-                if (cit.currentInfo.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
-                    cit.isFinal = true;
-                } else {
-                    if (DBG) log("==> Actually starting CallerInfoAsyncQuery.startQuery()...");
-                    cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
-                            number, sCallerInfoQueryListener, c);
-                    cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
-                    cit.isFinal = false;
-                }
-            } else {
-                // This is the case where we are querying on a number that
-                // is null or empty, like a caller whose caller id is
-                // blocked or empty (CLIR).  The previous behaviour was to
-                // throw a null CallerInfo object back to the user, but
-                // this departure is somewhat cleaner.
-                if (DBG) log("startGetCallerInfo: No query to start, send trivial reply.");
-                cit.isFinal = true; // please see note on isFinal, above.
-            }
-
-            c.setUserData(cit);
-
-            if (DBG) {
-                log("startGetCallerInfo: query based on number: " + toLogSafePhoneNumber(number));
-            }
-
-        } else if (userDataObject instanceof CallerInfoToken) {
-            // State (2): query is executing, but has not completed.
-
-            // just tack on this listener to the queue.
-            cit = (CallerInfoToken) userDataObject;
-
-            // handling case where number is null (caller id hidden) as well.
-            if (cit.asyncQuery != null) {
-                cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
-
-                if (DBG) log("startGetCallerInfo: query already running, adding listener: " +
-                        listener.getClass().toString());
-            } else {
-                // handling case where number/name gets updated later on by the network
-                String updatedNumber = c.getAddress();
-
-                if (info != null) {
-                    // Gateway number, the connection number is actually the gateway number.
-                    // need to lookup via dialed number.
-                    updatedNumber = info.trueNumber;
-                }
-
-                if (DBG) {
-                    log("startGetCallerInfo: updatedNumber initially = "
-                            + toLogSafePhoneNumber(updatedNumber));
-                }
-                if (!TextUtils.isEmpty(updatedNumber)) {
-                    // Store CNAP information retrieved from the Connection
-                    cit.currentInfo.cnapName =  c.getCnapName();
-                    // This can still get overwritten by ContactInfo
-                    cit.currentInfo.name = cit.currentInfo.cnapName;
-                    cit.currentInfo.numberPresentation = c.getNumberPresentation();
-                    cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
-                    updatedNumber = modifyForSpecialCnapCases(context, cit.currentInfo,
-                            updatedNumber, cit.currentInfo.numberPresentation);
-
-                    cit.currentInfo.phoneNumber = updatedNumber;
-                    if (DBG) {
-                        log("startGetCallerInfo: updatedNumber="
-                                + toLogSafePhoneNumber(updatedNumber));
-                    }
-                    if (VDBG) {
-                        log("startGetCallerInfo: CNAP Info from FW(2): name="
-                                + cit.currentInfo.cnapName
-                                + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
-                    } else if (DBG) {
-                        log("startGetCallerInfo: CNAP Info from FW(2)");
-                    }
-                    // For scenarios where we may receive a valid number from the network but a
-                    // restricted/unavailable presentation, we do not want to perform a contact query
-                    // (see note on isFinal above). So we set isFinal to true here as well.
-                    if (cit.currentInfo.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
-                        cit.isFinal = true;
-                    } else {
-                        cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
-                                updatedNumber, sCallerInfoQueryListener, c);
-                        cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
-                        cit.isFinal = false;
-                    }
-                } else {
-                    if (DBG) log("startGetCallerInfo: No query to attach to, send trivial reply.");
-                    if (cit.currentInfo == null) {
-                        cit.currentInfo = new CallerInfo();
-                    }
-                    // Store CNAP information retrieved from the Connection
-                    cit.currentInfo.cnapName = c.getCnapName();  // This can still get
-                                                                 // overwritten by ContactInfo
-                    cit.currentInfo.name = cit.currentInfo.cnapName;
-                    cit.currentInfo.numberPresentation = c.getNumberPresentation();
-                    cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
-                    if (VDBG) {
-                        log("startGetCallerInfo: CNAP Info from FW(3): name="
-                                + cit.currentInfo.cnapName
-                                + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
-                    } else if (DBG) {
-                        log("startGetCallerInfo: CNAP Info from FW(3)");
-                    }
-                    cit.isFinal = true; // please see note on isFinal, above.
-                }
-            }
-        } else {
-            // State (3): query is complete.
-
-            // The connection's userDataObject is a full-fledged
-            // CallerInfo instance.  Wrap it in a CallerInfoToken and
-            // return it to the user.
-
-            cit = new CallerInfoToken();
-            cit.currentInfo = (CallerInfo) userDataObject;
-            cit.asyncQuery = null;
-            cit.isFinal = true;
-            // since the query is already done, call the listener.
-            if (DBG) log("startGetCallerInfo: query already done, returning CallerInfo");
-            if (DBG) log("==> cit.currentInfo = " + cit.currentInfo);
-        }
-        return cit;
-    }
-
-    /**
-     * Static CallerInfoAsyncQuery.OnQueryCompleteListener instance that
-     * we use with all our CallerInfoAsyncQuery.startQuery() requests.
-     */
-    private static final int QUERY_TOKEN = -1;
-    static CallerInfoAsyncQuery.OnQueryCompleteListener sCallerInfoQueryListener =
-        new CallerInfoAsyncQuery.OnQueryCompleteListener () {
-            /**
-             * When the query completes, we stash the resulting CallerInfo
-             * object away in the Connection's "userData" (where it will
-             * later be retrieved by the in-call UI.)
-             */
-            public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
-                if (DBG) log("query complete, updating connection.userdata");
-                Connection conn = (Connection) cookie;
-
-                // Added a check if CallerInfo is coming from ContactInfo or from Connection.
-                // If no ContactInfo, then we want to use CNAP information coming from network
-                if (DBG) log("- onQueryComplete: CallerInfo:" + ci);
-                if (ci.contactExists || ci.isEmergencyNumber() || ci.isVoiceMailNumber()) {
-                    // If the number presentation has not been set by
-                    // the ContactInfo, use the one from the
-                    // connection.
-
-                    // TODO: Need a new util method to merge the info
-                    // from the Connection in a CallerInfo object.
-                    // Here 'ci' is a new CallerInfo instance read
-                    // from the DB. It has lost all the connection
-                    // info preset before the query (see PhoneUtils
-                    // line 1334). We should have a method to merge
-                    // back into this new instance the info from the
-                    // connection object not set by the DB. If the
-                    // Connection already has a CallerInfo instance in
-                    // userData, then we could use this instance to
-                    // fill 'ci' in. The same routine could be used in
-                    // PhoneUtils.
-                    if (0 == ci.numberPresentation) {
-                        ci.numberPresentation = conn.getNumberPresentation();
-                    }
-                } else {
-                    // No matching contact was found for this number.
-                    // Return a new CallerInfo based solely on the CNAP
-                    // information from the network.
-
-                    CallerInfo newCi = getCallerInfo(null, conn);
-
-                    // ...but copy over the (few) things we care about
-                    // from the original CallerInfo object:
-                    if (newCi != null) {
-                        newCi.phoneNumber = ci.phoneNumber; // To get formatted phone number
-                        newCi.geoDescription = ci.geoDescription; // To get geo description string
-                        ci = newCi;
-                    }
-                }
-
-                if (DBG) log("==> Stashing CallerInfo " + ci + " into the connection...");
-                conn.setUserData(ci);
-            }
-        };
-
-
-    /**
-     * Returns a single "name" for the specified given a CallerInfo object.
-     * If the name is null, return defaultString as the default value, usually
-     * context.getString(R.string.unknown).
-     */
-    static String getCompactNameFromCallerInfo(CallerInfo ci, Context context) {
-        if (DBG) log("getCompactNameFromCallerInfo: info = " + ci);
-
-        String compactName = null;
-        if (ci != null) {
-            if (TextUtils.isEmpty(ci.name)) {
-                // Perform any modifications for special CNAP cases to
-                // the phone number being displayed, if applicable.
-                compactName = modifyForSpecialCnapCases(context, ci, ci.phoneNumber,
-                                                        ci.numberPresentation);
-            } else {
-                // Don't call modifyForSpecialCnapCases on regular name. See b/2160795.
-                compactName = ci.name;
-            }
-        }
-
-        if ((compactName == null) || (TextUtils.isEmpty(compactName))) {
-            // If we're still null/empty here, then check if we have a presentation
-            // string that takes precedence that we could return, otherwise display
-            // "unknown" string.
-            if (ci != null && ci.numberPresentation == PhoneConstants.PRESENTATION_RESTRICTED) {
-                compactName = context.getString(R.string.private_num);
-            } else if (ci != null && ci.numberPresentation == PhoneConstants.PRESENTATION_PAYPHONE) {
-                compactName = context.getString(R.string.payphone);
-            } else {
-                compactName = context.getString(R.string.unknown);
-            }
-        }
-        if (VDBG) log("getCompactNameFromCallerInfo: compactName=" + compactName);
-        return compactName;
-    }
-
-    static boolean isInEmergencyCall(CallManager cm) {
-        Call fgCall = cm.getActiveFgCall();
-        // isIdle includes checks for the DISCONNECTING/DISCONNECTED state.
-        if(!fgCall.isIdle()) {
-            for (Connection cn : fgCall.getConnections()) {
-                if (PhoneNumberUtils.isLocalEmergencyNumber(PhoneGlobals.getInstance(),
-                        cn.getAddress())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
 
     //
     // Misc UI policy helper functions
     //
 
     /**
-     * Based on the input CNAP number string,
-     * @return _RESTRICTED or _UNKNOWN for all the special CNAP strings.
-     * Otherwise, return CNAP_SPECIAL_CASE_NO.
-     */
-    private static int checkCnapSpecialCases(String n) {
-        if (n.equals("PRIVATE") ||
-                n.equals("P") ||
-                n.equals("RES")) {
-            if (DBG) log("checkCnapSpecialCases, PRIVATE string: " + n);
-            return PhoneConstants.PRESENTATION_RESTRICTED;
-        } else if (n.equals("UNAVAILABLE") ||
-                n.equals("UNKNOWN") ||
-                n.equals("UNA") ||
-                n.equals("U")) {
-            if (DBG) log("checkCnapSpecialCases, UNKNOWN string: " + n);
-            return PhoneConstants.PRESENTATION_UNKNOWN;
-        } else {
-            if (DBG) log("checkCnapSpecialCases, normal str. number: " + n);
-            return CNAP_SPECIAL_CASE_NO;
-        }
-    }
-
-    /**
-     * Handles certain "corner cases" for CNAP. When we receive weird phone numbers
-     * from the network to indicate different number presentations, convert them to
-     * expected number and presentation values within the CallerInfo object.
-     * @param number number we use to verify if we are in a corner case
-     * @param presentation presentation value used to verify if we are in a corner case
-     * @return the new String that should be used for the phone number
-     */
-    /* package */ static String modifyForSpecialCnapCases(Context context, CallerInfo ci,
-            String number, int presentation) {
-        // Obviously we return number if ci == null, but still return number if
-        // number == null, because in these cases the correct string will still be
-        // displayed/logged after this function returns based on the presentation value.
-        if (ci == null || number == null) return number;
-
-        if (DBG) {
-            log("modifyForSpecialCnapCases: initially, number="
-                    + toLogSafePhoneNumber(number)
-                    + ", presentation=" + presentation + " ci " + ci);
-        }
-
-        // "ABSENT NUMBER" is a possible value we could get from the network as the
-        // phone number, so if this happens, change it to "Unknown" in the CallerInfo
-        // and fix the presentation to be the same.
-        final String[] absentNumberValues =
-                context.getResources().getStringArray(R.array.absent_num);
-        if (Arrays.asList(absentNumberValues).contains(number)
-                && presentation == PhoneConstants.PRESENTATION_ALLOWED) {
-            number = context.getString(R.string.unknown);
-            ci.numberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
-        }
-
-        // Check for other special "corner cases" for CNAP and fix them similarly. Corner
-        // cases only apply if we received an allowed presentation from the network, so check
-        // if we think we have an allowed presentation, or if the CallerInfo presentation doesn't
-        // match the presentation passed in for verification (meaning we changed it previously
-        // because it's a corner case and we're being called from a different entry point).
-        if (ci.numberPresentation == PhoneConstants.PRESENTATION_ALLOWED
-                || (ci.numberPresentation != presentation
-                        && presentation == PhoneConstants.PRESENTATION_ALLOWED)) {
-            int cnapSpecialCase = checkCnapSpecialCases(number);
-            if (cnapSpecialCase != CNAP_SPECIAL_CASE_NO) {
-                // For all special strings, change number & numberPresentation.
-                if (cnapSpecialCase == PhoneConstants.PRESENTATION_RESTRICTED) {
-                    number = context.getString(R.string.private_num);
-                } else if (cnapSpecialCase == PhoneConstants.PRESENTATION_UNKNOWN) {
-                    number = context.getString(R.string.unknown);
-                }
-                if (DBG) {
-                    log("SpecialCnap: number=" + toLogSafePhoneNumber(number)
-                            + "; presentation now=" + cnapSpecialCase);
-                }
-                ci.numberPresentation = cnapSpecialCase;
-            }
-        }
-        if (DBG) {
-            log("modifyForSpecialCnapCases: returning number string="
-                    + toLogSafePhoneNumber(number));
-        }
-        return number;
-    }
-
-    //
-    // Support for 3rd party phone service providers.
-    //
-
-    /**
      * Check if a phone number can be route through a 3rd party
      * gateway. The number must be a global phone number in numerical
      * form (1-800-666-SEXY won't work).
diff --git a/src/com/android/phone/ShortcutViewUtils.java b/src/com/android/phone/ShortcutViewUtils.java
index 8e5ab42..e3c5b64 100644
--- a/src/com/android/phone/ShortcutViewUtils.java
+++ b/src/com/android/phone/ShortcutViewUtils.java
@@ -33,6 +33,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.telephony.util.ArrayUtils;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -225,8 +227,8 @@
         PhoneAccountHandle defaultHandle = telecomManager.getDefaultOutgoingPhoneAccount(
                 PhoneAccount.SCHEME_TEL);
         if (defaultHandle != null) {
-            PhoneInfo phone = loadPhoneInfo(defaultHandle, telephonyManager, telecomManager,
-                    promotedLists);
+            PhoneInfo phone = loadPhoneInfo(context, defaultHandle, telephonyManager,
+                    telecomManager, promotedLists);
             if (phone.isSufficientForEmergencyCall(context)) {
                 return phone;
             }
@@ -240,7 +242,7 @@
         List<PhoneAccountHandle> allHandles = telecomManager.getCallCapablePhoneAccounts();
         if (allHandles != null && !allHandles.isEmpty()) {
             for (PhoneAccountHandle handle : allHandles) {
-                PhoneInfo phone = loadPhoneInfo(handle, telephonyManager, telecomManager,
+                PhoneInfo phone = loadPhoneInfo(context, handle, telephonyManager, telecomManager,
                         promotedLists);
                 if (phone.isSufficientForEmergencyCall(context)) {
                     return phone;
@@ -272,8 +274,11 @@
         return false;
     }
 
-    private static PhoneInfo loadPhoneInfo(@NonNull PhoneAccountHandle handle,
-            @NonNull TelephonyManager telephonyManager, @NonNull TelecomManager telecomManager,
+    private static PhoneInfo loadPhoneInfo(
+            @NonNull Context context,
+            @NonNull PhoneAccountHandle handle,
+            @NonNull TelephonyManager telephonyManager,
+            @NonNull TelecomManager telecomManager,
             Map<Integer, List<EmergencyNumber>> promotedLists) {
         boolean canPlaceEmergencyCall = false;
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -293,12 +298,69 @@
         }
 
         if (promotedLists != null) {
-            emergencyNumberList = promotedLists.get(subId);
+            emergencyNumberList = removeCarrierSpecificPrefixes(context, subId,
+                    promotedLists.get(subId));
         }
 
         return new PhoneInfo(handle, canPlaceEmergencyCall, subId, countryIso, emergencyNumberList);
     }
 
+    @Nullable
+    private static String[] getCarrierSpecificPrefixes(@NonNull Context context, int subId) {
+        CarrierConfigManager configMgr = context.getSystemService(CarrierConfigManager.class);
+        if (configMgr == null) {
+            return null;
+        }
+        PersistableBundle b = configMgr.getConfigForSubId(subId);
+        return b == null ? null : b.getStringArray(
+                CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
+    }
+
+    // Removes carrier specific emergency number prefixes (if there is any) from every emergency
+    // number and create a new list without duplications. Returns the original list if there is no
+    // prefixes.
+    @NonNull
+    private static List<EmergencyNumber> removeCarrierSpecificPrefixes(
+            @NonNull Context context,
+            int subId,
+            @NonNull List<EmergencyNumber> emergencyNumberList) {
+        String[] prefixes = getCarrierSpecificPrefixes(context, subId);
+        if (ArrayUtils.isEmpty(prefixes)) {
+            return emergencyNumberList;
+        }
+
+        List<EmergencyNumber> newList = new ArrayList<>(emergencyNumberList.size());
+        for (EmergencyNumber emergencyNumber : emergencyNumberList) {
+            // If no prefix was removed from emergencyNumber, add it to the newList directly.
+            EmergencyNumber newNumber = emergencyNumber;
+            String number = emergencyNumber.getNumber();
+            for (String prefix : prefixes) {
+                // If emergencyNumber starts with this prefix, remove this prefix to retrieve the
+                // actual emergency number.
+                // However, if emergencyNumber is exactly the same with this prefix, it could be
+                // either a real emergency number, or composed with another prefix. It shouldn't be
+                // processed with this prefix whatever.
+                if (!TextUtils.isEmpty(prefix) && number.startsWith(prefix)
+                        && !number.equals(prefix)) {
+                    newNumber = new EmergencyNumber(
+                            number.substring(prefix.length()),
+                            emergencyNumber.getCountryIso(),
+                            emergencyNumber.getMnc(),
+                            emergencyNumber.getEmergencyServiceCategoryBitmask(),
+                            emergencyNumber.getEmergencyUrns(),
+                            emergencyNumber.getEmergencyNumberSourceBitmask(),
+                            emergencyNumber.getEmergencyCallRouting());
+                    // There should not be more than one prefix attached to a number.
+                    break;
+                }
+            }
+            if (!newList.contains(newNumber)) {
+                newList.add(newNumber);
+            }
+        }
+        return newList;
+    }
+
     @NonNull
     private static Map<Integer, List<EmergencyNumber>> getPromotedEmergencyNumberLists(
             @NonNull TelephonyManager telephonyManager) {
diff --git a/src/com/android/phone/SpecialCharSequenceMgr.java b/src/com/android/phone/SpecialCharSequenceMgr.java
index 514b2c9..674449e 100644
--- a/src/com/android/phone/SpecialCharSequenceMgr.java
+++ b/src/com/android/phone/SpecialCharSequenceMgr.java
@@ -21,6 +21,7 @@
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionInfo;
@@ -233,32 +234,45 @@
         // if a dialstring is an MMI code.
         if ((input.startsWith("**04") || input.startsWith("**05"))
                 && input.endsWith("#")) {
-            PhoneGlobals app = PhoneGlobals.getInstance();
-            Phone phone;
-            int subId;
-            if (input.startsWith("**04")) {
-                subId = getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED, context);
-            } else {
-                subId = getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED, context);
-            }
-            if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                log("get phone with subId: " + subId);
-                phone = PhoneGlobals.getPhone(subId);
-            } else {
-                log("get default phone");
-                phone = PhoneGlobals.getPhone();
-            }
-            boolean isMMIHandled = phone.handlePinMmi(input);
+            UserManager userManager = (UserManager) pukInputActivity
+                       .getSystemService(Context.USER_SERVICE);
+            if (userManager.isSystemUser()) {
+                PhoneGlobals app = PhoneGlobals.getInstance();
+                Phone phone;
+                int subId;
+                if (input.startsWith("**04")) {
+                    subId = getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED, context);
+                } else {
+                    subId = getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED, context);
+                }
+                if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                    log("get phone with subId: " + subId);
+                    phone = PhoneGlobals.getPhone(subId);
+                } else {
+                    log("get default phone");
+                    phone = PhoneGlobals.getPhone();
+                }
+                boolean isMMIHandled = phone.handlePinMmi(input);
 
-            // if the PUK code is recognized then indicate to the
-            // phone app that an attempt to unPUK the device was
-            // made with this activity.  The PUK code may still
-            // fail though, but we won't know until the MMI code
-            // returns a result.
-            if (isMMIHandled && input.startsWith("**05")) {
-                app.setPukEntryActivity(pukInputActivity);
+                // if the PUK code is recognized then indicate to the
+                // phone app that an attempt to unPUK the device was
+                // made with this activity.  The PUK code may still
+                // fail though, but we won't know until the MMI code
+                // returns a result.
+                if (isMMIHandled && input.startsWith("**05")) {
+                    app.setPukEntryActivity(pukInputActivity);
+                }
+                return isMMIHandled;
+            } else {
+                AlertDialog dialog = new AlertDialog.Builder(context)
+                        .setMessage(R.string.pin_puk_system_user_only)
+                        .setPositiveButton(R.string.ok, null)
+                        .setCancelable(true).create();
+                dialog.show();
+                dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+                dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+                return true;
             }
-            return isMMIHandled;
         }
         return false;
     }
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index a34abc0..0806fd1 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -16,20 +16,27 @@
 
 package com.android.phone;
 
+import android.content.Context;
 import android.os.Binder;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ShellCommand;
-import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.util.Log;
 
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
 
 /**
  * Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
@@ -45,9 +52,12 @@
     private static final int DEFAULT_PHONE_ID = 0;
 
     private static final String IMS_SUBCOMMAND = "ims";
-    private static final String SMS_SUBCOMMAND = "sms";
     private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
     private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
+    private static final String CARRIER_CONFIG_SUBCOMMAND = "cc";
+    private static final String DATA_TEST_MODE = "data";
+    private static final String DATA_ENABLE = "enable";
+    private static final String DATA_DISABLE = "disable";
 
     private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
     private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
@@ -58,18 +68,68 @@
     // support CEP data.
     private static final String IMS_CEP = "conference-event-package";
 
-    private static final String SMS_GET_APPS = "get-apps";
-    private static final String SMS_GET_DEFAULT_APP = "get-default-app";
-    private static final String SMS_SET_DEFAULT_APP = "set-default-app";
-
     private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package";
     private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call";
 
+    private static final String CC_GET_VALUE = "get-value";
+    private static final String CC_SET_VALUE = "set-value";
+    private static final String CC_CLEAR_VALUES = "clear-values";
+
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
 
-    public TelephonyShellCommand(ITelephony binder) {
+    private SubscriptionManager mSubscriptionManager;
+    private CarrierConfigManager mCarrierConfigManager;
+
+    private enum CcType {
+        BOOLEAN, DOUBLE, DOUBLE_ARRAY, INT, INT_ARRAY, LONG, LONG_ARRAY, STRING,
+                STRING_ARRAY, UNKNOWN
+    }
+
+    private class CcOptionParseResult {
+        public int mSubId;
+        public boolean mPersistent;
+    }
+
+    // Maps carrier config keys to type. It is possible to infer the type for most carrier config
+    // keys by looking at the end of the string which usually tells the type.
+    // For instance: "xxxx_string", "xxxx_string_array", etc.
+    // The carrier config keys in this map does not follow this convention. It is therefore not
+    // possible to infer the type for these keys by looking at the string.
+    private static final Map<String, CcType> CC_TYPE_MAP = new HashMap<String, CcType>() {{
+            put(CarrierConfigManager.Gps.KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_GPS_LOCK_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_LPP_PROFILE_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_NFW_PROXY_APPS_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_SUPL_ES_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_SUPL_HOST_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_SUPL_MODE_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_SUPL_PORT_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_SUPL_VER_STRING, CcType.STRING);
+            put(CarrierConfigManager.Gps.KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING,
+                    CcType.STRING);
+            put(CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                    CcType.STRING_ARRAY);
+            put(CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                    CcType.STRING_ARRAY);
+            put(CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_MMS_USER_AGENT_STRING, CcType.STRING);
+            put(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES, CcType.STRING_ARRAY);
+        }
+    };
+
+    public TelephonyShellCommand(ITelephony binder, Context context) {
         mInterface = binder;
+        mCarrierConfigManager =
+                (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        mSubscriptionManager = (SubscriptionManager)
+                context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
     }
 
     @Override
@@ -82,13 +142,15 @@
             case IMS_SUBCOMMAND: {
                 return handleImsCommand();
             }
-            case SMS_SUBCOMMAND: {
-                return handleSmsCommand();
-            }
             case NUMBER_VERIFICATION_SUBCOMMAND:
                 return handleNumberVerificationCommand();
             case EMERGENCY_NUMBER_TEST_MODE:
                 return handleEmergencyNumberTestModeCommand();
+            case CARRIER_CONFIG_SUBCOMMAND: {
+                return handleCcCommand();
+            }
+            case DATA_TEST_MODE:
+                return handleDataTestModeCommand();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -103,13 +165,16 @@
         pw.println("    Print this help text.");
         pw.println("  ims");
         pw.println("    IMS Commands.");
-        pw.println("  sms");
-        pw.println("    SMS Commands.");
         pw.println("  emergency-number-test-mode");
         pw.println("    Emergency Number Test Mode Commands.");
+        pw.println("  data");
+        pw.println("    Data Test Mode Commands.");
+        pw.println("  cc");
+        pw.println("    Carrier Config Commands.");
         onHelpIms();
-        onHelpSms();
         onHelpEmergencyNumber();
+        onHelpDataTestMode();
+        onHelpCc();
     }
 
     private void onHelpIms() {
@@ -139,18 +204,6 @@
         pw.println("    enables or disables handling or network conference event package data.");
     }
 
-    private void onHelpSms() {
-        PrintWriter pw = getOutPrintWriter();
-        pw.println("SMS Commands:");
-        pw.println("  sms get-apps [--user USER_ID]");
-        pw.println("    Print all SMS apps on a user.");
-        pw.println("  sms get-default-app [--user USER_ID]");
-        pw.println("    Get the default SMS app.");
-        pw.println("  sms set-default-app [--user USER_ID] PACKAGE_NAME");
-        pw.println("    Set PACKAGE_NAME as the default SMS app.");
-    }
-
-
     private void onHelpNumberVerification() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Number verification commands");
@@ -162,6 +215,13 @@
         pw.println("    1 if the call would have been intercepted, 0 otherwise.");
     }
 
+    private void onHelpDataTestMode() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Mobile Data Test Mode Commands:");
+        pw.println("  data enable: enable mobile data connectivity");
+        pw.println("  data disable: disable mobile data connectivity");
+    }
+
     private void onHelpEmergencyNumber() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Emergency Number Test Mode Commands:");
@@ -176,6 +236,34 @@
         pw.println("      -p: get the full emergency number list in the test mode.");
     }
 
+    private void onHelpCc() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Carrier Config Commands:");
+        pw.println("  cc get-value [-s SLOT_ID] [KEY]");
+        pw.println("    Print carrier config values.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to read carrier config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("    KEY: The key to the carrier config value to print. All values are printed");
+        pw.println("         if KEY is not specified.");
+        pw.println("  cc set-value [-s SLOT_ID] [-p] KEY [NEW_VALUE]");
+        pw.println("    Set carrier config KEY to NEW_VALUE.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to set carrier config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      -p: Value will be stored persistent");
+        pw.println("    NEW_VALUE specifies the new value for carrier config KEY. Null will be");
+        pw.println("      used if NEW_VALUE is not set. Strings should be encapsulated with");
+        pw.println("      quotation marks. Spaces needs to be escaped. Example: \"Hello\\ World\"");
+        pw.println("      Separate items in arrays with space . Example: \"item1\" \"item2\"");
+        pw.println("  cc clear-values [-s SLOT_ID]");
+        pw.println("    Clear all carrier override values that has previously been set");
+        pw.println("    with set-value");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to clear carrier config values for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+    }
+
     private int handleImsCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -204,6 +292,41 @@
         return -1;
     }
 
+    private int handleDataTestModeCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        String arg = getNextArgRequired();
+        if (arg == null) {
+            onHelpDataTestMode();
+            return 0;
+        }
+        switch (arg) {
+            case DATA_ENABLE: {
+                try {
+                    mInterface.enableDataConnectivity();
+                } catch (RemoteException ex) {
+                    Log.w(LOG_TAG, "data enable, error " + ex.getMessage());
+                    errPw.println("Exception: " + ex.getMessage());
+                    return -1;
+                }
+                break;
+            }
+            case DATA_DISABLE: {
+                try {
+                    mInterface.disableDataConnectivity();
+                } catch (RemoteException ex) {
+                    Log.w(LOG_TAG, "data disable, error " + ex.getMessage());
+                    errPw.println("Exception: " + ex.getMessage());
+                    return -1;
+                }
+                break;
+            }
+            default:
+                onHelpDataTestMode();
+                break;
+        }
+        return 0;
+    }
+
     private int handleEmergencyNumberTestModeCommand() {
         PrintWriter errPw = getErrPrintWriter();
         String opt = getNextOption();
@@ -501,85 +624,65 @@
         return slotId;
     }
 
-    private int handleSmsCommand() {
-        String arg = getNextArg();
-        if (arg == null) {
-            onHelpSms();
-            return 0;
-        }
+    // Parse options related to Carrier Config Commands.
+    private CcOptionParseResult parseCcOptions(String tag, boolean allowOptionPersistent) {
+        PrintWriter errPw = getErrPrintWriter();
+        CcOptionParseResult result = new CcOptionParseResult();
+        result.mSubId = SubscriptionManager.getDefaultSubscriptionId();
+        result.mPersistent = false;
 
-        try {
-            switch (arg) {
-                case SMS_GET_APPS: {
-                    return handleSmsGetApps();
-                }
-                case SMS_GET_DEFAULT_APP: {
-                    return handleSmsGetDefaultApp();
-                }
-                case SMS_SET_DEFAULT_APP: {
-                    return handleSmsSetDefaultApp();
-                }
-                default:
-                    getErrPrintWriter().println("Unknown command " + arg);
-            }
-        } catch (RemoteException e) {
-            getErrPrintWriter().println("RemoteException: " + e.getMessage());
-        }
-
-        return -1;
-    }
-
-    private int maybeParseUserIdArg() {
-        int userId = UserHandle.USER_SYSTEM;
         String opt;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "--user": {
+                case "-s": {
                     try {
-                        userId = Integer.parseInt(getNextArgRequired());
-                    } catch (NumberFormatException e) {
-                        getErrPrintWriter().println("Invalid user ID for --user");
-                        return -1;
+                        result.mSubId = slotStringToSubId(tag, getNextArgRequired());
+                        if (!SubscriptionManager.isValidSubscriptionId(result.mSubId)) {
+                            errPw.println(tag + "No valid subscription found.");
+                            return null;
+                        }
+
+                    } catch (IllegalArgumentException e) {
+                        // Missing slot id
+                        errPw.println(tag + "SLOT_ID expected after -s.");
+                        return null;
                     }
                     break;
                 }
+                case "-p": {
+                    if (allowOptionPersistent) {
+                        result.mPersistent = true;
+                    } else {
+                        errPw.println(tag + "Unexpected option " + opt);
+                        return null;
+                    }
+                    break;
+                }
+                default: {
+                    errPw.println(tag + "Unknown option " + opt);
+                    return null;
+                }
             }
         }
-        return userId;
+        return result;
     }
 
-    private int handleSmsGetApps() throws RemoteException {
-        final int userId = maybeParseUserIdArg();
-        if (userId < 0) {
-            return -1;
+    private int slotStringToSubId(String tag, String slotString) {
+        int slotId = -1;
+        try {
+            slotId = Integer.parseInt(slotString);
+        } catch (NumberFormatException e) {
+            getErrPrintWriter().println(tag + slotString + " is not a valid SLOT_ID.");
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }
 
-        for (String packageName : mInterface.getSmsApps(userId)) {
-            getOutPrintWriter().println(packageName);
+        SubscriptionInfo subInfo =
+                mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(slotId);
+        if (subInfo == null) {
+            getErrPrintWriter().println(tag + "No subscription found in slot " + slotId + ".");
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
         }
-        return 0;
-    }
-
-    private int handleSmsGetDefaultApp() throws RemoteException {
-        final int userId = maybeParseUserIdArg();
-        if (userId < 0) {
-            return -1;
-        }
-
-        getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
-        return 0;
-    }
-
-    private int handleSmsSetDefaultApp() throws RemoteException {
-        final int userId = maybeParseUserIdArg();
-        if (userId < 0) {
-            return -1;
-        }
-
-        String packageName = getNextArgRequired();
-        mInterface.setDefaultSmsApp(userId, packageName);
-        getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
-        return 0;
+        return subInfo.getSubscriptionId();
     }
 
     private boolean checkShellUid() {
@@ -587,4 +690,432 @@
         return Binder.getCallingUid() == Process.SHELL_UID
                 || Binder.getCallingUid() == Process.ROOT_UID;
     }
+
+    private int handleCcCommand() {
+        // Verify that the user is allowed to run the command. Only allowed in rooted device in a
+        // non user build.
+        if (Binder.getCallingUid() != Process.ROOT_UID || TelephonyUtils.IS_USER) {
+            getErrPrintWriter().println("cc: Permission denied.");
+            return -1;
+        }
+
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpCc();
+            return 0;
+        }
+
+        switch (arg) {
+            case CC_GET_VALUE: {
+                return handleCcGetValue();
+            }
+            case CC_SET_VALUE: {
+                return handleCcSetValue();
+            }
+            case CC_CLEAR_VALUES: {
+                return handleCcClearValues();
+            }
+            default: {
+                getErrPrintWriter().println("cc: Unknown argument: " + arg);
+            }
+        }
+        return -1;
+    }
+
+    // cc get-value
+    private int handleCcGetValue() {
+        PrintWriter errPw = getErrPrintWriter();
+        String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_GET_VALUE + ": ";
+        String key = null;
+
+        // Parse all options
+        CcOptionParseResult options =  parseCcOptions(tag, false);
+        if (options == null) {
+            return -1;
+        }
+
+        // Get bundle containing all carrier configuration values.
+        PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+        if (bundle == null) {
+            errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+            return -1;
+        }
+
+        // Get the key.
+        key = getNextArg();
+        if (key != null) {
+            // A key was provided. Verify if it is a valid key
+            if (!bundle.containsKey(key)) {
+                errPw.println(tag + key + " is not a valid key.");
+                return -1;
+            }
+
+            // Print the carrier config value for key.
+            getOutPrintWriter().println(ccValueToString(key, getType(tag, key, bundle), bundle));
+        } else {
+            // No key provided. Show all values.
+            // Iterate over a sorted list of all carrier config keys and print them.
+            TreeSet<String> sortedSet = new TreeSet<String>(bundle.keySet());
+            for (String k : sortedSet) {
+                getOutPrintWriter().println(ccValueToString(k, getType(tag, k, bundle), bundle));
+            }
+        }
+        return 0;
+    }
+
+    // cc set-value
+    private int handleCcSetValue() {
+        PrintWriter errPw = getErrPrintWriter();
+        String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUE + ": ";
+
+        // Parse all options
+        CcOptionParseResult options =  parseCcOptions(tag, true);
+        if (options == null) {
+            return -1;
+        }
+
+        // Get bundle containing all current carrier configuration values.
+        PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+        if (originalValues == null) {
+            errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+            return -1;
+        }
+
+        // Get the key.
+        String key = getNextArg();
+        if (key == null || key.equals("")) {
+            errPw.println(tag + "KEY is missing");
+            return -1;
+        }
+
+        // Verify if the key is valid
+        if (!originalValues.containsKey(key)) {
+            errPw.println(tag + key + " is not a valid key.");
+            return -1;
+        }
+
+        // Remaining arguments is a list of new values. Add them all into an ArrayList.
+        ArrayList<String> valueList = new ArrayList<String>();
+        while (peekNextArg() != null) {
+            valueList.add(getNextArg());
+        }
+
+        // Find the type of the carrier config value
+        CcType type = getType(tag, key, originalValues);
+        if (type == CcType.UNKNOWN) {
+            errPw.println(tag + "ERROR: Not possible to override key with unknown type.");
+            return -1;
+        }
+
+        // Create an override bundle containing the key and value that should be overriden.
+        PersistableBundle overrideBundle = getOverrideBundle(tag, type, key, valueList);
+        if (overrideBundle == null) {
+            return -1;
+        }
+
+        // Override the value
+        mCarrierConfigManager.overrideConfig(options.mSubId, overrideBundle, options.mPersistent);
+
+        // Find bundle containing all new carrier configuration values after the override.
+        PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+        if (newValues == null) {
+            errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+            return -1;
+        }
+
+        // Print the original and new value.
+        String originalValueString = ccValueToString(key, type, originalValues);
+        String newValueString = ccValueToString(key, type, newValues);
+        getOutPrintWriter().println("Previous value: \n" + originalValueString);
+        getOutPrintWriter().println("New value: \n" + newValueString);
+
+        return 0;
+    }
+
+    // cc clear-values
+    private int handleCcClearValues() {
+        PrintWriter errPw = getErrPrintWriter();
+        String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_CLEAR_VALUES + ": ";
+
+        // Parse all options
+        CcOptionParseResult options =  parseCcOptions(tag, false);
+        if (options == null) {
+            return -1;
+        }
+
+        // Clear all values that has previously been set.
+        mCarrierConfigManager.overrideConfig(options.mSubId, null, true);
+        getOutPrintWriter()
+                .println("All previously set carrier config override values has been cleared");
+        return 0;
+    }
+
+    private CcType getType(String tag, String key, PersistableBundle bundle) {
+        // Find the type by checking the type of the current value stored in the bundle.
+        Object value = bundle.get(key);
+
+        if (CC_TYPE_MAP.containsKey(key)) {
+            return CC_TYPE_MAP.get(key);
+        } else if (value != null) {
+            if (value instanceof Boolean) {
+                return CcType.BOOLEAN;
+            } else if (value instanceof Double) {
+                return CcType.DOUBLE;
+            } else if (value instanceof double[]) {
+                return CcType.DOUBLE_ARRAY;
+            } else if (value instanceof Integer) {
+                return CcType.INT;
+            } else if (value instanceof int[]) {
+                return CcType.INT_ARRAY;
+            } else if (value instanceof Long) {
+                return CcType.LONG;
+            } else if (value instanceof long[]) {
+                return CcType.LONG_ARRAY;
+            } else if (value instanceof String) {
+                return CcType.STRING;
+            } else if (value instanceof String[]) {
+                return CcType.STRING_ARRAY;
+            }
+        } else {
+            // Current value was null and can therefore not be used in order to find the type.
+            // Check the name of the key to infer the type. This check is not needed for primitive
+            // data types (boolean, double, int and long), since they can not be null.
+            if (key.endsWith("double_array")) {
+                return CcType.DOUBLE_ARRAY;
+            }
+            if (key.endsWith("int_array")) {
+                return CcType.INT_ARRAY;
+            }
+            if (key.endsWith("long_array")) {
+                return CcType.LONG_ARRAY;
+            }
+            if (key.endsWith("string")) {
+                return CcType.STRING;
+            }
+            if (key.endsWith("string_array") || key.endsWith("strings")) {
+                return CcType.STRING_ARRAY;
+            }
+        }
+
+        // Not possible to infer the type by looking at the current value or the key.
+        PrintWriter errPw = getErrPrintWriter();
+        errPw.println(tag + "ERROR: " + key + " has unknown type.");
+        return CcType.UNKNOWN;
+    }
+
+    private String ccValueToString(String key, CcType type, PersistableBundle bundle) {
+        String result;
+        StringBuilder valueString = new StringBuilder();
+        String typeString = type.toString();
+        Object value = bundle.get(key);
+
+        if (value == null) {
+            valueString.append("null");
+        } else {
+            switch (type) {
+                case DOUBLE_ARRAY: {
+                    // Format the string representation of the int array as value1 value2......
+                    double[] valueArray = (double[]) value;
+                    for (int i = 0; i < valueArray.length; i++) {
+                        if (i != 0) {
+                            valueString.append(" ");
+                        }
+                        valueString.append(valueArray[i]);
+                    }
+                    break;
+                }
+                case INT_ARRAY: {
+                    // Format the string representation of the int array as value1 value2......
+                    int[] valueArray = (int[]) value;
+                    for (int i = 0; i < valueArray.length; i++) {
+                        if (i != 0) {
+                            valueString.append(" ");
+                        }
+                        valueString.append(valueArray[i]);
+                    }
+                    break;
+                }
+                case LONG_ARRAY: {
+                    // Format the string representation of the int array as value1 value2......
+                    long[] valueArray = (long[]) value;
+                    for (int i = 0; i < valueArray.length; i++) {
+                        if (i != 0) {
+                            valueString.append(" ");
+                        }
+                        valueString.append(valueArray[i]);
+                    }
+                    break;
+                }
+                case STRING: {
+                    valueString.append("\"" + value.toString() + "\"");
+                    break;
+                }
+                case STRING_ARRAY: {
+                    // Format the string representation of the string array as "value1" "value2"....
+                    String[] valueArray = (String[]) value;
+                    for (int i = 0; i < valueArray.length; i++) {
+                        if (i != 0) {
+                            valueString.append(" ");
+                        }
+                        if (valueArray[i] != null) {
+                            valueString.append("\"" + valueArray[i] + "\"");
+                        } else {
+                            valueString.append("null");
+                        }
+                    }
+                    break;
+                }
+                default: {
+                    valueString.append(value.toString());
+                }
+            }
+        }
+        return String.format("%-70s %-15s %s", key, typeString, valueString);
+    }
+
+    private PersistableBundle getOverrideBundle(String tag, CcType type, String key,
+            ArrayList<String> valueList) {
+        PrintWriter errPw = getErrPrintWriter();
+        PersistableBundle bundle = new PersistableBundle();
+
+        // First verify that a valid number of values has been provided for the type.
+        switch (type) {
+            case BOOLEAN:
+            case DOUBLE:
+            case INT:
+            case LONG: {
+                if (valueList.size() != 1) {
+                    errPw.println(tag + "Expected 1 value for type " + type
+                            + ". Found: " + valueList.size());
+                    return null;
+                }
+                break;
+            }
+            case STRING: {
+                if (valueList.size() > 1) {
+                    errPw.println(tag + "Expected 0 or 1 values for type " + type
+                            + ". Found: " + valueList.size());
+                    return null;
+                }
+                break;
+            }
+        }
+
+        // Parse the value according to type and add it to the Bundle.
+        switch (type) {
+            case BOOLEAN: {
+                if ("true".equalsIgnoreCase(valueList.get(0))) {
+                    bundle.putBoolean(key, true);
+                } else if ("false".equalsIgnoreCase(valueList.get(0))) {
+                    bundle.putBoolean(key, false);
+                } else {
+                    errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+                    return null;
+                }
+                break;
+            }
+            case DOUBLE: {
+                try {
+                    bundle.putDouble(key, Double.parseDouble(valueList.get(0)));
+                } catch (NumberFormatException nfe) {
+                    // Not a valid double
+                    errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+                    return null;
+                }
+                break;
+            }
+            case DOUBLE_ARRAY: {
+                double[] valueDoubleArray = null;
+                if (valueList.size() > 0) {
+                    valueDoubleArray = new double[valueList.size()];
+                    for (int i = 0; i < valueList.size(); i++) {
+                        try {
+                            valueDoubleArray[i] = Double.parseDouble(valueList.get(i));
+                        } catch (NumberFormatException nfe) {
+                            // Not a valid double
+                            errPw.println(
+                                    tag + "Unable to parse " + valueList.get(i) + " as a double.");
+                            return null;
+                        }
+                    }
+                }
+                bundle.putDoubleArray(key, valueDoubleArray);
+                break;
+            }
+            case INT: {
+                try {
+                    bundle.putInt(key, Integer.parseInt(valueList.get(0)));
+                } catch (NumberFormatException nfe) {
+                    // Not a valid integer
+                    errPw.println(tag + "Unable to parse " + valueList.get(0) + " as an " + type);
+                    return null;
+                }
+                break;
+            }
+            case INT_ARRAY: {
+                int[] valueIntArray = null;
+                if (valueList.size() > 0) {
+                    valueIntArray = new int[valueList.size()];
+                    for (int i = 0; i < valueList.size(); i++) {
+                        try {
+                            valueIntArray[i] = Integer.parseInt(valueList.get(i));
+                        } catch (NumberFormatException nfe) {
+                            // Not a valid integer
+                            errPw.println(tag
+                                    + "Unable to parse " + valueList.get(i) + " as an integer.");
+                            return null;
+                        }
+                    }
+                }
+                bundle.putIntArray(key, valueIntArray);
+                break;
+            }
+            case LONG: {
+                try {
+                    bundle.putLong(key, Long.parseLong(valueList.get(0)));
+                } catch (NumberFormatException nfe) {
+                    // Not a valid long
+                    errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+                    return null;
+                }
+                break;
+            }
+            case LONG_ARRAY: {
+                long[] valueLongArray = null;
+                if (valueList.size() > 0) {
+                    valueLongArray = new long[valueList.size()];
+                    for (int i = 0; i < valueList.size(); i++) {
+                        try {
+                            valueLongArray[i] = Long.parseLong(valueList.get(i));
+                        } catch (NumberFormatException nfe) {
+                            // Not a valid long
+                            errPw.println(
+                                    tag + "Unable to parse " + valueList.get(i) + " as a long");
+                            return null;
+                        }
+                    }
+                }
+                bundle.putLongArray(key, valueLongArray);
+                break;
+            }
+            case STRING: {
+                String value = null;
+                if (valueList.size() > 0) {
+                    value = valueList.get(0);
+                }
+                bundle.putString(key, value);
+                break;
+            }
+            case STRING_ARRAY: {
+                String[] valueStringArray = null;
+                if (valueList.size() > 0) {
+                    valueStringArray = new String[valueList.size()];
+                    valueList.toArray(valueStringArray);
+                }
+                bundle.putStringArray(key, valueStringArray);
+                break;
+            }
+        }
+        return bundle;
+    }
 }
diff --git a/src/com/android/phone/euicc/EuiccPublicActionUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccPublicActionUiDispatcherActivity.java
new file mode 100644
index 0000000..64a40b9
--- /dev/null
+++ b/src/com/android/phone/euicc/EuiccPublicActionUiDispatcherActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.euicc;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.service.euicc.EuiccService;
+import android.telephony.euicc.EuiccManager;
+import android.util.Log;
+
+/**
+ * Trampoline activity to forward public eUICC intents to the active UI implementation.
+ *
+ * <p>Unlike {@link EuiccUiDispatcherActivity}, this activity does not require any permissions to
+ * start.
+ */
+public class EuiccPublicActionUiDispatcherActivity extends EuiccUiDispatcherActivity {
+    private static final String TAG = "EuiccPublicActionUiDispatcherActivity";
+
+    @Override
+    @Nullable
+    protected Intent getEuiccUiIntent() {
+        String action = getIntent().getAction();
+
+        Intent intent = new Intent();
+        // Propagate the extras from the original Intent.
+        intent.putExtras(getIntent());
+        switch (action) {
+            case EuiccManager.ACTION_START_EUICC_ACTIVATION:
+                intent.setAction(EuiccService.ACTION_START_EUICC_ACTIVATION);
+                break;
+            default:
+                Log.w(TAG, "Unsupported action: " + action);
+                return null;
+        }
+
+        return intent;
+    }
+}
diff --git a/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
index 0f083c9..fd741a0 100644
--- a/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.service.euicc.EuiccService;
 import android.telephony.euicc.EuiccManager;
+import android.util.ArraySet;
 import android.util.Log;
 
 /**
@@ -33,6 +34,14 @@
 public class EuiccResolutionUiDispatcherActivity extends EuiccUiDispatcherActivity {
     private static final String TAG = "EuiccResUiDispatcher";
 
+    /** List of all valid resolution actions for validation purposes. */
+    private static final ArraySet<String> RESOLUTION_ACTIONS = new ArraySet<>();
+    static {
+        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
+        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
+        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS);
+    }
+
     @Override
     @Nullable
     protected Intent getEuiccUiIntent() {
@@ -45,7 +54,7 @@
         String euiccUiAction =
                 getIntent().getStringExtra(
                         EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION);
-        if (!EuiccService.RESOLUTION_ACTIONS.contains(euiccUiAction)) {
+        if (!RESOLUTION_ACTIONS.contains(euiccUiAction)) {
             Log.w(TAG, "Unknown resolution action: " + euiccUiAction);
             return null;
         }
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index 7c7b75d..c47e014 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -27,6 +28,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.service.euicc.EuiccService;
 import android.telephony.euicc.EuiccManager;
 import android.util.Log;
@@ -94,7 +96,7 @@
 
         grantDefaultPermissionsToActiveLuiApp(activityInfo);
 
-        euiccUiIntent.setComponent(activityInfo.getComponentName());
+        euiccUiIntent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
         return euiccUiIntent;
     }
 
@@ -138,7 +140,7 @@
     protected void grantDefaultPermissionsToActiveLuiApp(ActivityInfo activityInfo) {
         try {
             mPackageManager.grantDefaultPermissionsToActiveLuiApp(
-                    activityInfo.packageName, getUserId());
+                    activityInfo.packageName, UserHandle.myUserId());
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to grant permissions to active LUI app.", e);
         }
@@ -150,10 +152,11 @@
         try {
             Set<String> luiApps = getAllLuiAppPackageNames(intent);
             String[] luiAppsArray = luiApps.toArray(new String[luiApps.size()]);
-            mPackageManager.revokeDefaultPermissionsFromLuiApps(luiAppsArray, getUserId());
+            mPackageManager.revokeDefaultPermissionsFromLuiApps(luiAppsArray,
+                    UserHandle.myUserId());
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to revoke LUI app permissions.");
-            throw e.rethrowAsRuntimeException();
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/src/com/android/phone/otasp/OtaspActivationService.java b/src/com/android/phone/otasp/OtaspActivationService.java
index 7490880..6ed2ea8 100644
--- a/src/com/android/phone/otasp/OtaspActivationService.java
+++ b/src/com/android/phone/otasp/OtaspActivationService.java
@@ -150,11 +150,9 @@
         mPhone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_PROVISION_STATUS_UPDATE, null);
         mPhone.registerForPreciseCallStateChanged(mHandler, EVENT_CALL_STATE_CHANGED, null);
         logd("startNonInteractiveOtasp: placing call to '" + OTASP_NUMBER + "'...");
-        int callStatus = PhoneUtils.placeCall(this,
+        int callStatus = PhoneUtils.placeOtaspCall(this,
                 getPhone(),
-                OTASP_NUMBER,
-                null,   // contactRef
-                false); // isEmergencyCall
+                OTASP_NUMBER);
         if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
             if (DBG) logd("  ==> success return from placeCall(): callStatus = " + callStatus);
         } else {
diff --git a/src/com/android/phone/otasp/OtaspSimStateReceiver.java b/src/com/android/phone/otasp/OtaspSimStateReceiver.java
index 78f7baf..bb4022a 100644
--- a/src/com/android/phone/otasp/OtaspSimStateReceiver.java
+++ b/src/com/android/phone/otasp/OtaspSimStateReceiver.java
@@ -18,9 +18,11 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -32,15 +34,32 @@
     private static final boolean DBG = true;
     private Context mContext;
 
-    private PhoneStateListener mPhoneStateListener = new PhoneStateListener(){
+    private static final int EVENT_OTASP_CHANGED = 1;
+
+    private Handler mOtaspHandler = new Handler() {
         @Override
-        public void onOtaspChanged(int otaspMode) {
-            logd("onOtaspChanged: otaspMode=" + otaspMode);
-            if (otaspMode == TelephonyManager.OTASP_NEEDED) {
-                logd("otasp activation required, start otaspActivationService");
-                mContext.startService(new Intent(mContext, OtaspActivationService.class));
-            } else if (otaspMode == TelephonyManager.OTASP_NOT_NEEDED) {
-                OtaspActivationService.updateActivationState(mContext, true);
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            switch (msg.what) {
+                case EVENT_OTASP_CHANGED:
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception == null && ar.result != null) {
+                        int otaspMode = (Integer) ar.result;
+                        logd("EVENT_OTASP_CHANGED: otaspMode=" + otaspMode);
+                        if (otaspMode == TelephonyManager.OTASP_NEEDED) {
+                            logd("otasp activation required, start otaspActivationService");
+                            mContext.startService(
+                                    new Intent(mContext, OtaspActivationService.class));
+                        } else if (otaspMode == TelephonyManager.OTASP_NOT_NEEDED) {
+                            OtaspActivationService.updateActivationState(mContext, true);
+                        }
+                    } else {
+                        logd("EVENT_OTASP_CHANGED: exception=" + ar.exception);
+                    }
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
             }
         }
     };
@@ -74,13 +93,17 @@
         if(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
             if (DBG) logd("Received intent: " + intent.getAction());
             if (PhoneGlobals.getPhone().getIccRecordsLoaded() && isCarrierSupported()) {
-                final TelephonyManager telephonyManager = TelephonyManager.from(context);
-                telephonyManager.listen(mPhoneStateListener,
-                        PhoneStateListener.LISTEN_OTASP_CHANGED);
+                registerOtaspChangedHandler();
             }
         }
     }
 
+    // It's fine to call mutiple times, as the registrants are de-duped by Handler object.
+    private void registerOtaspChangedHandler() {
+        final Phone phone = PhoneGlobals.getPhone();
+        phone.registerForOtaspChange(mOtaspHandler, EVENT_OTASP_CHANGED, null);
+    }
+
     private static void logd(String s) {
         Log.d(TAG, s);
     }
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index 5a70fe2..c9aa630 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -166,17 +166,6 @@
             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;
         }
 
@@ -206,20 +195,11 @@
     }
 
     private boolean shouldShowRttSetting() {
-        CarrierConfigManager configManager =
-                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         // Go through all the subs -- if we want to display the RTT setting for any of them, do
         // display it.
         for (int subId : SubscriptionController.getInstance().getActiveSubIdList(true)) {
-            // In order to display the setting, we want:
-            // 1. The subscription supports RTT
-            // 2. The subscription isn't configured by the carrier to have the setting always-on
-            //    (see the documentation for the carrier config key)
             if (PhoneGlobals.getInstance().phoneMgr.isRttSupported(subId)) {
-                if (!configManager.getConfigForSubId(subId).getBoolean(
-                        CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false)) {
-                    return true;
-                }
+                return true;
             }
         }
         return false;
diff --git a/src/com/android/phone/settings/BandMode.java b/src/com/android/phone/settings/BandMode.java
new file mode 100644
index 0000000..853075a
--- /dev/null
+++ b/src/com/android/phone/settings/BandMode.java
@@ -0,0 +1,255 @@
+/*
+ * 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.settings;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.R;
+
+/**
+ * Radio Band Mode Selection Class
+ *
+ * It will query baseband about all available band modes and display them
+ * in screen. It will display all six band modes if the query failed.
+ *
+ * After user select one band, it will send the selection to baseband.
+ *
+ * It will alter user the result of select operation and exit, no matter success
+ * or not.
+ *
+ */
+public class BandMode extends Activity {
+    private static final String LOG_TAG = "phone";
+    private static final boolean DBG = false;
+
+    private static final int EVENT_BAND_SCAN_COMPLETED = 100;
+    private static final int EVENT_BAND_SELECTION_DONE = 200;
+
+    //Directly maps to RIL_RadioBandMode from ril.h
+    private static final String[] BAND_NAMES = new String[] {
+            "Automatic",
+            "Europe",
+            "United States",
+            "Japan",
+            "Australia",
+            "Australia 2",
+            "Cellular 800",
+            "PCS",
+            "Class 3 (JTACS)",
+            "Class 4 (Korea-PCS)",
+            "Class 5",
+            "Class 6 (IMT2000)",
+            "Class 7 (700Mhz-Upper)",
+            "Class 8 (1800Mhz-Upper)",
+            "Class 9 (900Mhz)",
+            "Class 10 (800Mhz-Secondary)",
+            "Class 11 (Europe PAMR 400Mhz)",
+            "Class 15 (US-AWS)",
+            "Class 16 (US-2500Mhz)"
+    };
+
+    private ListView mBandList;
+    private ArrayAdapter mBandListAdapter;
+    private BandListItem mTargetBand = null;
+    private DialogInterface mProgressPanel;
+
+    private Phone mPhone = null;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+        setContentView(R.layout.band_mode);
+
+        mPhone = PhoneFactory.getDefaultPhone();
+
+        mBandList = (ListView) findViewById(R.id.band);
+        mBandListAdapter = new ArrayAdapter<BandListItem>(this,
+                android.R.layout.simple_list_item_1);
+        mBandList.setAdapter(mBandListAdapter);
+        mBandList.setOnItemClickListener(mBandSelectionHandler);
+
+        loadBandList();
+    }
+
+    private AdapterView.OnItemClickListener mBandSelectionHandler =
+            new AdapterView.OnItemClickListener() {
+                public void onItemClick(AdapterView parent, View v,
+                        int position, long id) {
+
+                    getWindow().setFeatureInt(
+                            Window.FEATURE_INDETERMINATE_PROGRESS,
+                            Window.PROGRESS_VISIBILITY_ON);
+
+                    mTargetBand = (BandListItem) parent.getAdapter().getItem(position);
+
+                    if (DBG) log("Select band : " + mTargetBand.toString());
+
+                    Message msg =
+                            mHandler.obtainMessage(EVENT_BAND_SELECTION_DONE);
+                    mPhone.setBandMode(mTargetBand.getBand(), msg);
+                }
+            };
+
+    private static class BandListItem {
+        private int mBandMode = Phone.BM_UNSPECIFIED;
+
+        BandListItem(int bm) {
+            mBandMode = bm;
+        }
+
+        public int getBand() {
+            return mBandMode;
+        }
+
+        public String toString() {
+            if (mBandMode >= BAND_NAMES.length) return "Band mode " + mBandMode;
+            return BAND_NAMES[mBandMode];
+        }
+    }
+
+    private void loadBandList() {
+        String str = getString(R.string.band_mode_loading);
+
+        if (DBG) log(str);
+
+
+        //ProgressDialog.show(this, null, str, true, true, null);
+        mProgressPanel = new AlertDialog.Builder(this)
+            .setMessage(str)
+            .show();
+
+        Message msg = mHandler.obtainMessage(EVENT_BAND_SCAN_COMPLETED);
+        mPhone.queryAvailableBandMode(msg);
+
+    }
+
+    private void bandListLoaded(AsyncResult result) {
+        if (DBG) log("network list loaded");
+
+        if (mProgressPanel != null) mProgressPanel.dismiss();
+
+        clearList();
+
+        boolean addBandSuccess = false;
+        BandListItem item;
+
+        if (result.result != null) {
+            int [] bands = (int []) result.result;
+
+            if (bands.length == 0) {
+                Log.wtf(LOG_TAG, "No Supported Band Modes");
+                return;
+            }
+
+            int size = bands[0];
+
+            if (size > 0) {
+                mBandListAdapter.add(
+                        new BandListItem(Phone.BM_UNSPECIFIED)); //Always include AUTOMATIC
+                for (int i = 1; i <= size; i++) {
+                    if (bands[i] == Phone.BM_UNSPECIFIED) {
+                        continue;
+                    }
+                    item = new BandListItem(bands[i]);
+                    mBandListAdapter.add(item);
+                    if (DBG) log("Add " + item.toString());
+                }
+                addBandSuccess = true;
+            }
+        }
+
+        if (!addBandSuccess) {
+            if (DBG) log("Error in query, add default list");
+            for (int i = 0; i < Phone.BM_NUM_BAND_MODES; i++) {
+                item = new BandListItem(i);
+                mBandListAdapter.add(item);
+                if (DBG) log("Add default " + item.toString());
+            }
+        }
+        mBandList.requestFocus();
+    }
+
+    private void displayBandSelectionResult(Throwable ex) {
+        String status = getString(R.string.band_mode_set)
+                + " [" + mTargetBand.toString() + "] ";
+
+        if (ex != null) {
+            status = status + getString(R.string.band_mode_failed);
+        } else {
+            status = status + getString(R.string.band_mode_succeeded);
+        }
+
+        mProgressPanel = new AlertDialog.Builder(this)
+            .setMessage(status)
+            .setPositiveButton(android.R.string.ok, null).show();
+    }
+
+    private void clearList() {
+        while (mBandListAdapter.getCount() > 0) {
+            mBandListAdapter.remove(
+                    mBandListAdapter.getItem(0));
+        }
+    }
+
+    private void log(String msg) {
+        Log.d(LOG_TAG, "[BandsList] " + msg);
+    }
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            switch (msg.what) {
+                case EVENT_BAND_SCAN_COMPLETED:
+                    ar = (AsyncResult) msg.obj;
+
+                    bandListLoaded(ar);
+                    break;
+
+                case EVENT_BAND_SELECTION_DONE:
+                    ar = (AsyncResult) msg.obj;
+
+                    getWindow().setFeatureInt(
+                            Window.FEATURE_INDETERMINATE_PROGRESS,
+                            Window.PROGRESS_VISIBILITY_OFF);
+
+                    if (!isFinishing()) {
+                        displayBandSelectionResult(ar.exception);
+                    }
+                    break;
+            }
+        }
+    };
+
+
+}
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 0846be1..3811a77 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -8,7 +8,6 @@
 import android.graphics.drawable.Icon;
 import android.net.sip.SipManager;
 import android.os.Bundle;
-import android.os.PersistableBundle;
 import android.os.UserManager;
 import android.preference.ListPreference;
 import android.preference.Preference;
@@ -102,7 +101,7 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mTelecomManager = TelecomManager.from(getActivity());
+        mTelecomManager = getActivity().getSystemService(TelecomManager.class);
         mTelephonyManager = TelephonyManager.from(getActivity());
         mSubscriptionManager = SubscriptionManager.from(getActivity());
     }
@@ -558,12 +557,18 @@
 
         List<SubscriptionInfo> subscriptions =
                 mSubscriptionManager.getActiveSubscriptionInfoList();
-        if ((subscriptions == null) || (subscriptions.size() <= 1)) {
+        if (subscriptions == null) {
             return null;
         }
 
-        List<String> componentNames = subscriptions
-                .stream()
+        List<SubscriptionInfo> effectiveSubscriptions = subscriptions.stream()
+                .filter(subInfo -> !subInfo.isOpportunistic())
+                .collect(Collectors.toList());
+        if (effectiveSubscriptions.size() < 2) {
+            return null;
+        }
+
+        List<String> componentNames = effectiveSubscriptions.stream()
                 .map(subInfo -> configManager.getConfigForSubId(subInfo.getSubscriptionId()))
                 .filter(bundle -> (bundle != null))
                 .map(bundle -> bundle.getString(
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
new file mode 100644
index 0000000..ce5b839
--- /dev/null
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -0,0 +1,1772 @@
+/*
+ * 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.settings;
+
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.provider.Settings.Global.PREFERRED_NETWORK_MODE;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellLocation;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.PreciseCallState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog.Builder;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.R;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Radio Information Class
+ *
+ * Allows user to read and alter some of the radio related information.
+ *
+ */
+public class RadioInfo extends AppCompatActivity {
+    private static final String TAG = "RadioInfo";
+
+    private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
+
+    private static final String[] PREFERRED_NETWORK_LABELS = {
+            "WCDMA preferred",
+            "GSM only",
+            "WCDMA only",
+            "GSM auto (PRL)",
+            "CDMA auto (PRL)",
+            "CDMA only",
+            "EvDo only",
+            "Global auto (PRL)",
+            "LTE/CDMA auto (PRL)",
+            "LTE/UMTS auto (PRL)",
+            "LTE/CDMA/UMTS auto (PRL)",
+            "LTE only",
+            "LTE/WCDMA",
+            "TD-SCDMA only",
+            "TD-SCDMA/WCDMA",
+            "LTE/TD-SCDMA",
+            "TD-SCDMA/GSM",
+            "TD-SCDMA/UMTS",
+            "LTE/TD-SCDMA/WCDMA",
+            "LTE/TD-SCDMA/UMTS",
+            "TD-SCDMA/CDMA/UMTS",
+            "Global/TD-SCDMA",
+            "Unknown"
+    };
+
+    private static String[] sPhoneIndexLabels;
+
+    private static final int sCellInfoListRateDisabled = Integer.MAX_VALUE;
+    private static final int sCellInfoListRateMax = 0;
+
+    private static final String OEM_RADIO_INFO_INTENT =
+            "com.android.phone.settings.OEM_RADIO_INFO";
+
+    private static final String DSDS_MODE_PROPERTY = "ro.boot.hardware.dsds";
+
+    /**
+     * A value indicates the device is always on dsds mode.
+     * @see {@link #DSDS_MODE_PROPERTY}
+     */
+    private static final int ALWAYS_ON_DSDS_MODE = 1;
+
+    private static final int IMS_VOLTE_PROVISIONED_CONFIG_ID =
+            ImsConfig.ConfigConstants.VLT_SETTING_ENABLED;
+
+    private static final int IMS_VT_PROVISIONED_CONFIG_ID =
+            ImsConfig.ConfigConstants.LVC_SETTING_ENABLED;
+
+    private static final int IMS_WFC_PROVISIONED_CONFIG_ID =
+            ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED;
+
+    private static final int EAB_PROVISIONED_CONFIG_ID =
+            ImsConfig.ConfigConstants.EAB_SETTING_ENABLED;
+
+    //Values in must match CELL_INFO_REFRESH_RATES
+    private static final String[] CELL_INFO_REFRESH_RATE_LABELS = {
+            "Disabled",
+            "Immediate",
+            "Min 5s",
+            "Min 10s",
+            "Min 60s"
+    };
+
+    //Values in seconds, must match CELL_INFO_REFRESH_RATE_LABELS
+    private static final int [] CELL_INFO_REFRESH_RATES = {
+        sCellInfoListRateDisabled,
+        sCellInfoListRateMax,
+        5000,
+        10000,
+        60000
+    };
+
+    private static void log(String s) {
+        Log.d(TAG, s);
+    }
+
+    private static final int EVENT_CFI_CHANGED = 302;
+
+    private static final int EVENT_QUERY_PREFERRED_TYPE_DONE = 1000;
+    private static final int EVENT_SET_PREFERRED_TYPE_DONE = 1001;
+    private static final int EVENT_QUERY_SMSC_DONE = 1005;
+    private static final int EVENT_UPDATE_SMSC_DONE = 1006;
+    private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 1007;
+
+    private static final int MENU_ITEM_SELECT_BAND         = 0;
+    private static final int MENU_ITEM_VIEW_ADN            = 1;
+    private static final int MENU_ITEM_VIEW_FDN            = 2;
+    private static final int MENU_ITEM_VIEW_SDN            = 3;
+    private static final int MENU_ITEM_GET_IMS_STATUS      = 4;
+    private static final int MENU_ITEM_TOGGLE_DATA         = 5;
+
+    private TextView mDeviceId; //DeviceId is the IMEI in GSM and the MEID in CDMA
+    private TextView mLine1Number;
+    private TextView mSubscriptionId;
+    private TextView mDds;
+    private TextView mSubscriberId;
+    private TextView mCallState;
+    private TextView mOperatorName;
+    private TextView mRoamingState;
+    private TextView mGsmState;
+    private TextView mGprsState;
+    private TextView mVoiceNetwork;
+    private TextView mDataNetwork;
+    private TextView mDBm;
+    private TextView mMwi;
+    private TextView mCfi;
+    private TextView mLocation;
+    private TextView mCellInfo;
+    private TextView mSent;
+    private TextView mReceived;
+    private TextView mPingHostnameV4;
+    private TextView mPingHostnameV6;
+    private TextView mHttpClientTest;
+    private TextView mPhyChanConfig;
+    private TextView mDnsCheckState;
+    private TextView mDownlinkKbps;
+    private TextView mUplinkKbps;
+    private EditText mSmsc;
+    private Switch mRadioPowerOnSwitch;
+    private Button mCellInfoRefreshRateButton;
+    private Button mDnsCheckToggleButton;
+    private Button mPingTestButton;
+    private Button mUpdateSmscButton;
+    private Button mRefreshSmscButton;
+    private Button mOemInfoButton;
+    private Button mCarrierProvisioningButton;
+    private Button mTriggerCarrierProvisioningButton;
+    private Switch mImsVolteProvisionedSwitch;
+    private Switch mImsVtProvisionedSwitch;
+    private Switch mImsWfcProvisionedSwitch;
+    private Switch mEabProvisionedSwitch;
+    private Switch mCbrsDataSwitch;
+    private Switch mDsdsSwitch;
+    private Spinner mPreferredNetworkType;
+    private Spinner mSelectPhoneIndex;
+    private Spinner mCellInfoRefreshRateSpinner;
+
+    private static final long RUNNABLE_TIMEOUT_MS = 5 * 60 * 1000L;
+
+    private ThreadPoolExecutor mQueuedWork;
+
+    private ConnectivityManager mConnectivityManager;
+    private TelephonyManager mTelephonyManager;
+    private ImsManager mImsManager = null;
+    private Phone mPhone = null;
+
+    private String mPingHostnameResultV4;
+    private String mPingHostnameResultV6;
+    private String mHttpClientTestResult;
+    private boolean mMwiValue = false;
+    private boolean mCfiValue = false;
+
+    private List<CellInfo> mCellInfoResult = null;
+    private CellLocation mCellLocationResult = null;
+
+    private int mPreferredNetworkTypeResult;
+    private int mCellInfoRefreshRateIndex;
+    private int mSelectedPhoneIndex;
+
+    private final NetworkRequest mDefaultNetworkRequest = new NetworkRequest.Builder()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+            .build();
+
+    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+        public void onCapabilitiesChanged(Network n, NetworkCapabilities nc) {
+            int dlbw = nc.getLinkDownstreamBandwidthKbps();
+            int ulbw = nc.getLinkUpstreamBandwidthKbps();
+            updateBandwidths(dlbw, ulbw);
+        }
+    };
+
+    // not final because we need to recreate this object to register on a new subId (b/117555407)
+    private PhoneStateListener mPhoneStateListener = new RadioInfoPhoneStateListener();
+    private class RadioInfoPhoneStateListener extends PhoneStateListener {
+        @Override
+        public void onDataConnectionStateChanged(int state) {
+            updateDataState();
+            updateNetworkType();
+        }
+
+        @Override
+        public void onDataActivity(int direction) {
+            updateDataStats2();
+        }
+
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            updateNetworkType();
+            updatePhoneState(state);
+        }
+
+        @Override
+        public void onPreciseCallStateChanged(PreciseCallState preciseState) {
+            updateNetworkType();
+        }
+
+        @Override
+        public void onCellLocationChanged(CellLocation location) {
+            updateLocation(location);
+        }
+
+        @Override
+        public void onMessageWaitingIndicatorChanged(boolean mwi) {
+            mMwiValue = mwi;
+            updateMessageWaiting();
+        }
+
+        @Override
+        public void onCallForwardingIndicatorChanged(boolean cfi) {
+            mCfiValue = cfi;
+            updateCallRedirect();
+        }
+
+        @Override
+        public void onCellInfoChanged(List<CellInfo> arrayCi) {
+            log("onCellInfoChanged: arrayCi=" + arrayCi);
+            mCellInfoResult = arrayCi;
+            updateCellInfo(mCellInfoResult);
+        }
+
+        @Override
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            log("onSignalStrengthChanged: SignalStrength=" + signalStrength);
+            updateSignalStrength(signalStrength);
+        }
+
+        @Override
+        public void onServiceStateChanged(ServiceState serviceState) {
+            log("onServiceStateChanged: ServiceState=" + serviceState);
+            updateServiceState(serviceState);
+            updateRadioPowerState();
+            updateNetworkType();
+            updateImsProvisionedState();
+        }
+
+    }
+
+    private void updatePhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
+        StringBuilder sb = new StringBuilder();
+        String div = "";
+        sb.append("{");
+        if (configs != null) {
+            for (PhysicalChannelConfig c : configs) {
+                sb.append(div).append(c);
+                div = ",";
+            }
+        }
+        sb.append("}");
+        mPhyChanConfig.setText(sb.toString());
+    }
+
+    private void updatePreferredNetworkType(int type) {
+        if (type >= PREFERRED_NETWORK_LABELS.length || type < 0) {
+            log("EVENT_QUERY_PREFERRED_TYPE_DONE: unknown "
+                    + "type=" + type);
+            type = PREFERRED_NETWORK_LABELS.length - 1; //set to Unknown
+        }
+        mPreferredNetworkTypeResult = type;
+
+        mPreferredNetworkType.setSelection(mPreferredNetworkTypeResult, true);
+    }
+
+    private void updatePhoneIndex(int phoneIndex, int subId) {
+        // unregister listeners on the old subId
+        unregisterPhoneStateListener();
+        mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
+
+        // update the subId
+        mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
+
+        // update the phoneId
+        mImsManager = ImsManager.getInstance(getApplicationContext(), phoneIndex);
+        mPhone = PhoneFactory.getPhone(phoneIndex);
+
+        updateAllFields();
+    }
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            switch (msg.what) {
+                case EVENT_QUERY_PREFERRED_TYPE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception == null && ar.result != null) {
+                        updatePreferredNetworkType(((int []) ar.result)[0]);
+                    } else {
+                        //In case of an exception, we will set this to unknown
+                        updatePreferredNetworkType(PREFERRED_NETWORK_LABELS.length - 1);
+                    }
+                    break;
+                case EVENT_SET_PREFERRED_TYPE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        log("Set preferred network type failed.");
+                    }
+                    break;
+                case EVENT_QUERY_SMSC_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        mSmsc.setText("refresh error");
+                    } else {
+                        mSmsc.setText((String) ar.result);
+                    }
+                    break;
+                case EVENT_UPDATE_SMSC_DONE:
+                    mUpdateSmscButton.setEnabled(true);
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        mSmsc.setText("update error");
+                    }
+                    break;
+                case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        mPhyChanConfig.setText(("update error"));
+                    }
+                    updatePhysicalChannelConfiguration((List<PhysicalChannelConfig>) ar.result);
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
+
+            }
+        }
+    };
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        if (!android.os.Process.myUserHandle().isSystem()) {
+            Log.e(TAG, "Not run from system user, don't do anything.");
+            finish();
+            return;
+        }
+
+        setContentView(R.layout.radio_info);
+
+        log("Started onCreate");
+
+        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();
+
+        mImsManager = ImsManager.getInstance(getApplicationContext(),
+                SubscriptionManager.getDefaultVoicePhoneId());
+
+        sPhoneIndexLabels = getPhoneIndexLabels(mTelephonyManager);
+
+        mDeviceId = (TextView) findViewById(R.id.imei);
+        mLine1Number = (TextView) findViewById(R.id.number);
+        mSubscriptionId = (TextView) findViewById(R.id.subid);
+        mDds = (TextView) findViewById(R.id.dds);
+        mSubscriberId = (TextView) findViewById(R.id.imsi);
+        mCallState = (TextView) findViewById(R.id.call);
+        mOperatorName = (TextView) findViewById(R.id.operator);
+        mRoamingState = (TextView) findViewById(R.id.roaming);
+        mGsmState = (TextView) findViewById(R.id.gsm);
+        mGprsState = (TextView) findViewById(R.id.gprs);
+        mVoiceNetwork = (TextView) findViewById(R.id.voice_network);
+        mDataNetwork = (TextView) findViewById(R.id.data_network);
+        mDBm = (TextView) findViewById(R.id.dbm);
+        mMwi = (TextView) findViewById(R.id.mwi);
+        mCfi = (TextView) findViewById(R.id.cfi);
+        mLocation = (TextView) findViewById(R.id.location);
+        mCellInfo = (TextView) findViewById(R.id.cellinfo);
+        mCellInfo.setTypeface(Typeface.MONOSPACE);
+
+        mSent = (TextView) findViewById(R.id.sent);
+        mReceived = (TextView) findViewById(R.id.received);
+        mSmsc = (EditText) findViewById(R.id.smsc);
+        mDnsCheckState = (TextView) findViewById(R.id.dnsCheckState);
+        mPingHostnameV4 = (TextView) findViewById(R.id.pingHostnameV4);
+        mPingHostnameV6 = (TextView) findViewById(R.id.pingHostnameV6);
+        mHttpClientTest = (TextView) findViewById(R.id.httpClientTest);
+
+        mPhyChanConfig = (TextView) findViewById(R.id.phy_chan_config);
+
+        mPreferredNetworkType = (Spinner) findViewById(R.id.preferredNetworkType);
+        ArrayAdapter<String> mPreferredNetworkTypeAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_spinner_item, PREFERRED_NETWORK_LABELS);
+        mPreferredNetworkTypeAdapter
+                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mPreferredNetworkType.setAdapter(mPreferredNetworkTypeAdapter);
+
+        mSelectPhoneIndex = (Spinner) findViewById(R.id.phoneIndex);
+        ArrayAdapter<String> phoneIndexAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_spinner_item, sPhoneIndexLabels);
+        phoneIndexAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mSelectPhoneIndex.setAdapter(phoneIndexAdapter);
+
+        mCellInfoRefreshRateSpinner = (Spinner) findViewById(R.id.cell_info_rate_select);
+        ArrayAdapter<String> cellInfoAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_spinner_item, CELL_INFO_REFRESH_RATE_LABELS);
+        cellInfoAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mCellInfoRefreshRateSpinner.setAdapter(cellInfoAdapter);
+
+        mImsVolteProvisionedSwitch = (Switch) findViewById(R.id.volte_provisioned_switch);
+        mImsVtProvisionedSwitch = (Switch) findViewById(R.id.vt_provisioned_switch);
+        mImsWfcProvisionedSwitch = (Switch) findViewById(R.id.wfc_provisioned_switch);
+        mEabProvisionedSwitch = (Switch) findViewById(R.id.eab_provisioned_switch);
+
+        if (!ImsManager.isImsSupportedOnDevice(mPhone.getContext())) {
+            mImsVolteProvisionedSwitch.setVisibility(View.GONE);
+            mImsVtProvisionedSwitch.setVisibility(View.GONE);
+            mImsWfcProvisionedSwitch.setVisibility(View.GONE);
+            mEabProvisionedSwitch.setVisibility(View.GONE);
+        }
+
+        mCbrsDataSwitch = (Switch) findViewById(R.id.cbrs_data_switch);
+        mCbrsDataSwitch.setVisibility(isCbrsSupported() ? View.VISIBLE : View.GONE);
+
+        mDsdsSwitch = findViewById(R.id.dsds_switch);
+        if (isDsdsSupported() && !dsdsModeOnly()) {
+            mDsdsSwitch.setVisibility(View.VISIBLE);
+            mDsdsSwitch.setOnClickListener(v -> {
+                if (mTelephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
+                    // Undo the click action until user clicks the confirm dialog.
+                    mDsdsSwitch.toggle();
+                    showDsdsChangeDialog();
+                } else {
+                    performDsdsSwitch();
+                }
+            });
+            mDsdsSwitch.setChecked(isDsdsEnabled());
+        } else {
+            mDsdsSwitch.setVisibility(View.GONE);
+        }
+
+        mRadioPowerOnSwitch = (Switch) findViewById(R.id.radio_power);
+
+        mDownlinkKbps = (TextView) findViewById(R.id.dl_kbps);
+        mUplinkKbps = (TextView) findViewById(R.id.ul_kbps);
+        updateBandwidths(0, 0);
+
+        mPingTestButton = (Button) findViewById(R.id.ping_test);
+        mPingTestButton.setOnClickListener(mPingButtonHandler);
+        mUpdateSmscButton = (Button) findViewById(R.id.update_smsc);
+        mUpdateSmscButton.setOnClickListener(mUpdateSmscButtonHandler);
+        mRefreshSmscButton = (Button) findViewById(R.id.refresh_smsc);
+        mRefreshSmscButton.setOnClickListener(mRefreshSmscButtonHandler);
+        mDnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
+        mDnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
+        mCarrierProvisioningButton = (Button) findViewById(R.id.carrier_provisioning);
+        mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+        mTriggerCarrierProvisioningButton = (Button) findViewById(
+                R.id.trigger_carrier_provisioning);
+        mTriggerCarrierProvisioningButton.setOnClickListener(
+                mTriggerCarrierProvisioningButtonHandler);
+
+        mOemInfoButton = (Button) findViewById(R.id.oem_info);
+        mOemInfoButton.setOnClickListener(mOemInfoButtonHandler);
+        PackageManager pm = getPackageManager();
+        Intent oemInfoIntent = new Intent(OEM_RADIO_INFO_INTENT);
+        List<ResolveInfo> oemInfoIntentList = pm.queryIntentActivities(oemInfoIntent, 0);
+        if (oemInfoIntentList.size() == 0) {
+            mOemInfoButton.setEnabled(false);
+        }
+
+        mCellInfoRefreshRateIndex = 0; //disabled
+        mPreferredNetworkTypeResult = PREFERRED_NETWORK_LABELS.length - 1; //Unknown
+        mSelectedPhoneIndex = 0; //phone 0
+
+        //FIXME: Replace with TelephonyManager call
+        mPhone.getPreferredNetworkType(
+                mHandler.obtainMessage(EVENT_QUERY_PREFERRED_TYPE_DONE));
+
+        restoreFromBundle(icicle);
+    }
+
+    @Override
+    public Intent getParentActivityIntent() {
+        Intent parentActivity = super.getParentActivityIntent();
+        if (parentActivity == null) {
+            parentActivity = (new Intent()).setClassName("com.android.settings",
+                    "com.android.settings.Settings$TestingSettingsActivity");
+        }
+        return parentActivity;
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        log("Started onResume");
+
+        updateAllFields();
+    }
+
+    private void updateAllFields() {
+        updateMessageWaiting();
+        updateCallRedirect();
+        updateDataState();
+        updateDataStats2();
+        updateRadioPowerState();
+        updateImsProvisionedState();
+        updateProperties();
+        updateDnsCheckState();
+        updateNetworkType();
+
+        updateLocation(mCellLocationResult);
+        updateCellInfo(mCellInfoResult);
+        updateSubscriptionIds();
+
+        mPingHostnameV4.setText(mPingHostnameResultV4);
+        mPingHostnameV6.setText(mPingHostnameResultV6);
+        mHttpClientTest.setText(mHttpClientTestResult);
+
+        mCellInfoRefreshRateSpinner.setOnItemSelectedListener(mCellInfoRefreshRateHandler);
+        //set selection after registering listener to force update
+        mCellInfoRefreshRateSpinner.setSelection(mCellInfoRefreshRateIndex);
+
+        //set selection before registering to prevent update
+        mPreferredNetworkType.setSelection(mPreferredNetworkTypeResult, true);
+        mPreferredNetworkType.setOnItemSelectedListener(mPreferredNetworkHandler);
+
+        // set phone index
+        mSelectPhoneIndex.setSelection(mSelectedPhoneIndex, true);
+        mSelectPhoneIndex.setOnItemSelectedListener(mSelectPhoneIndexHandler);
+
+        mRadioPowerOnSwitch.setOnCheckedChangeListener(mRadioPowerOnChangeListener);
+        mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
+        mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
+        mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
+        mEabProvisionedSwitch.setOnCheckedChangeListener(mEabCheckedChangeListener);
+
+        if (isCbrsSupported()) {
+            mCbrsDataSwitch.setChecked(getCbrsDataState());
+            mCbrsDataSwitch.setOnCheckedChangeListener(mCbrsDataSwitchChangeListener);
+        }
+
+        unregisterPhoneStateListener();
+        registerPhoneStateListener();
+        mPhone.registerForPhysicalChannelConfig(mHandler,
+            EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
+
+        mConnectivityManager.registerNetworkCallback(
+                mDefaultNetworkRequest, mNetworkCallback, mHandler);
+
+        mSmsc.clearFocus();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        log("onPause: unregister phone & data intents");
+
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
+        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+
+    }
+
+    private void restoreFromBundle(Bundle b) {
+        if (b == null) {
+            return;
+        }
+
+        mPingHostnameResultV4 = b.getString("mPingHostnameResultV4", "");
+        mPingHostnameResultV6 = b.getString("mPingHostnameResultV6", "");
+        mHttpClientTestResult = b.getString("mHttpClientTestResult", "");
+
+        mPingHostnameV4.setText(mPingHostnameResultV4);
+        mPingHostnameV6.setText(mPingHostnameResultV6);
+        mHttpClientTest.setText(mHttpClientTestResult);
+
+        mPreferredNetworkTypeResult = b.getInt("mPreferredNetworkTypeResult",
+                PREFERRED_NETWORK_LABELS.length - 1);
+
+        mSelectedPhoneIndex = b.getInt("mSelectedPhoneIndex", 0);
+
+        mCellInfoRefreshRateIndex = b.getInt("mCellInfoRefreshRateIndex", 0);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putString("mPingHostnameResultV4", mPingHostnameResultV4);
+        outState.putString("mPingHostnameResultV6", mPingHostnameResultV6);
+        outState.putString("mHttpClientTestResult", mHttpClientTestResult);
+
+        outState.putInt("mPreferredNetworkTypeResult", mPreferredNetworkTypeResult);
+        outState.putInt("mSelectedPhoneIndex", mSelectedPhoneIndex);
+        outState.putInt("mCellInfoRefreshRateIndex", mCellInfoRefreshRateIndex);
+
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        menu.add(0, MENU_ITEM_SELECT_BAND, 0, R.string.radio_info_band_mode_label)
+                .setOnMenuItemClickListener(mSelectBandCallback)
+                .setAlphabeticShortcut('b');
+        menu.add(1, MENU_ITEM_VIEW_ADN, 0,
+                R.string.radioInfo_menu_viewADN).setOnMenuItemClickListener(mViewADNCallback);
+        menu.add(1, MENU_ITEM_VIEW_FDN, 0,
+                R.string.radioInfo_menu_viewFDN).setOnMenuItemClickListener(mViewFDNCallback);
+        menu.add(1, MENU_ITEM_VIEW_SDN, 0,
+                R.string.radioInfo_menu_viewSDN).setOnMenuItemClickListener(mViewSDNCallback);
+        if (ImsManager.isImsSupportedOnDevice(mPhone.getContext())) {
+            menu.add(1, MENU_ITEM_GET_IMS_STATUS,
+                    0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
+        }
+        menu.add(1, MENU_ITEM_TOGGLE_DATA,
+                0, R.string.radio_info_data_connection_disable)
+                .setOnMenuItemClickListener(mToggleData);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        // Get the TOGGLE DATA menu item in the right state.
+        MenuItem item = menu.findItem(MENU_ITEM_TOGGLE_DATA);
+        int state = mTelephonyManager.getDataState();
+        boolean visible = true;
+
+        switch (state) {
+            case TelephonyManager.DATA_CONNECTED:
+            case TelephonyManager.DATA_SUSPENDED:
+                item.setTitle(R.string.radio_info_data_connection_disable);
+                break;
+            case TelephonyManager.DATA_DISCONNECTED:
+                item.setTitle(R.string.radio_info_data_connection_enable);
+                break;
+            default:
+                visible = false;
+                break;
+        }
+        item.setVisible(visible);
+        return true;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mQueuedWork.shutdown();
+    }
+
+    // returns array of string labels for each phone index. The array index is equal to the phone
+    // index.
+    private static String[] getPhoneIndexLabels(TelephonyManager tm) {
+        int phones = tm.getPhoneCount();
+        String[] labels = new String[phones];
+        for (int i = 0; i < phones; i++) {
+            labels[i] = "Phone " + i;
+        }
+        return labels;
+    }
+
+    private void unregisterPhoneStateListener() {
+        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mPhone.unregisterForPhysicalChannelConfig(mHandler);
+
+        // clear all fields so they are blank until the next listener event occurs
+        mOperatorName.setText("");
+        mGprsState.setText("");
+        mDataNetwork.setText("");
+        mVoiceNetwork.setText("");
+        mSent.setText("");
+        mReceived.setText("");
+        mCallState.setText("");
+        mLocation.setText("");
+        mMwiValue = false;
+        mMwi.setText("");
+        mCfiValue = false;
+        mCfi.setText("");
+        mCellInfo.setText("");
+        mDBm.setText("");
+        mGsmState.setText("");
+        mRoamingState.setText("");
+        mPhyChanConfig.setText("");
+    }
+
+    // register mPhoneStateListener for relevant fields using the current TelephonyManager
+    private void registerPhoneStateListener() {
+        mPhoneStateListener = new RadioInfoPhoneStateListener();
+        mTelephonyManager.listen(mPhoneStateListener,
+                  PhoneStateListener.LISTEN_CALL_STATE
+        //b/27803938 - RadioInfo currently cannot read PRECISE_CALL_STATE
+        //      | PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+                | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+                | PhoneStateListener.LISTEN_DATA_ACTIVITY
+                | PhoneStateListener.LISTEN_CELL_LOCATION
+                | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+                | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+                | PhoneStateListener.LISTEN_CELL_INFO
+                | PhoneStateListener.LISTEN_SERVICE_STATE
+                | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+    }
+
+    private void updateDnsCheckState() {
+        //FIXME: Replace with a TelephonyManager call
+        mDnsCheckState.setText(mPhone.isDnsCheckDisabled()
+                ? "0.0.0.0 allowed" : "0.0.0.0 not allowed");
+    }
+
+    private void updateBandwidths(int dlbw, int ulbw) {
+        dlbw = (dlbw < 0 || dlbw == Integer.MAX_VALUE) ? -1 : dlbw;
+        ulbw = (ulbw < 0 || ulbw == Integer.MAX_VALUE) ? -1 : ulbw;
+        mDownlinkKbps.setText(String.format("%-5d", dlbw));
+        mUplinkKbps.setText(String.format("%-5d", ulbw));
+    }
+
+
+    private void updateSignalStrength(SignalStrength signalStrength) {
+        Resources r = getResources();
+
+        int signalDbm = signalStrength.getDbm();
+
+        int signalAsu = signalStrength.getAsuLevel();
+
+        if (-1 == signalAsu) signalAsu = 0;
+
+        mDBm.setText(String.valueOf(signalDbm) + " "
+                + r.getString(R.string.radioInfo_display_dbm) + "   "
+                + String.valueOf(signalAsu) + " "
+                + r.getString(R.string.radioInfo_display_asu));
+    }
+
+    private void updateLocation(CellLocation location) {
+        Resources r = getResources();
+        if (location instanceof GsmCellLocation) {
+            GsmCellLocation loc = (GsmCellLocation) location;
+            int lac = loc.getLac();
+            int cid = loc.getCid();
+            mLocation.setText(r.getString(R.string.radioInfo_lac) + " = "
+                    + ((lac == -1) ? "unknown" : Integer.toHexString(lac))
+                    + "   "
+                    + r.getString(R.string.radioInfo_cid) + " = "
+                    + ((cid == -1) ? "unknown" : Integer.toHexString(cid)));
+        } else if (location instanceof CdmaCellLocation) {
+            CdmaCellLocation loc = (CdmaCellLocation) location;
+            int bid = loc.getBaseStationId();
+            int sid = loc.getSystemId();
+            int nid = loc.getNetworkId();
+            int lat = loc.getBaseStationLatitude();
+            int lon = loc.getBaseStationLongitude();
+            mLocation.setText("BID = "
+                    + ((bid == -1) ? "unknown" : Integer.toHexString(bid))
+                    + "   "
+                    + "SID = "
+                    + ((sid == -1) ? "unknown" : Integer.toHexString(sid))
+                    + "   "
+                    + "NID = "
+                    + ((nid == -1) ? "unknown" : Integer.toHexString(nid))
+                    + "\n"
+                    + "LAT = "
+                    + ((lat == -1) ? "unknown" : Integer.toHexString(lat))
+                    + "   "
+                    + "LONG = "
+                    + ((lon == -1) ? "unknown" : Integer.toHexString(lon)));
+        } else {
+            mLocation.setText("unknown");
+        }
+
+
+    }
+
+    private String getCellInfoDisplayString(int i) {
+        return (i != Integer.MAX_VALUE) ? Integer.toString(i) : "";
+    }
+
+    private String getCellInfoDisplayString(long i) {
+        return (i != Long.MAX_VALUE) ? Long.toString(i) : "";
+    }
+
+    private String getConnectionStatusString(CellInfo ci) {
+        String regStr = "";
+        String connStatStr = "";
+        String connector = "";
+
+        if (ci.isRegistered()) {
+            regStr = "R";
+        }
+        switch (ci.getCellConnectionStatus()) {
+            case CellInfo.CONNECTION_PRIMARY_SERVING: connStatStr = "P"; break;
+            case CellInfo.CONNECTION_SECONDARY_SERVING: connStatStr = "S"; break;
+            case CellInfo.CONNECTION_NONE: connStatStr = "N"; break;
+            case CellInfo.CONNECTION_UNKNOWN: /* Field is unsupported */ break;
+            default: break;
+        }
+        if (!TextUtils.isEmpty(regStr) && !TextUtils.isEmpty(connStatStr)) {
+            connector = "+";
+        }
+
+        return regStr + connector + connStatStr;
+    }
+
+    private String buildCdmaInfoString(CellInfoCdma ci) {
+        CellIdentityCdma cidCdma = ci.getCellIdentity();
+        CellSignalStrengthCdma ssCdma = ci.getCellSignalStrength();
+
+        return String.format("%-3.3s %-5.5s %-5.5s %-5.5s %-6.6s %-6.6s %-6.6s %-6.6s %-5.5s",
+                getConnectionStatusString(ci),
+                getCellInfoDisplayString(cidCdma.getSystemId()),
+                getCellInfoDisplayString(cidCdma.getNetworkId()),
+                getCellInfoDisplayString(cidCdma.getBasestationId()),
+                getCellInfoDisplayString(ssCdma.getCdmaDbm()),
+                getCellInfoDisplayString(ssCdma.getCdmaEcio()),
+                getCellInfoDisplayString(ssCdma.getEvdoDbm()),
+                getCellInfoDisplayString(ssCdma.getEvdoEcio()),
+                getCellInfoDisplayString(ssCdma.getEvdoSnr()));
+    }
+
+    private String buildGsmInfoString(CellInfoGsm ci) {
+        CellIdentityGsm cidGsm = ci.getCellIdentity();
+        CellSignalStrengthGsm ssGsm = ci.getCellSignalStrength();
+
+        return String.format("%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-4.4s %-4.4s\n",
+                getConnectionStatusString(ci),
+                getCellInfoDisplayString(cidGsm.getMcc()),
+                getCellInfoDisplayString(cidGsm.getMnc()),
+                getCellInfoDisplayString(cidGsm.getLac()),
+                getCellInfoDisplayString(cidGsm.getCid()),
+                getCellInfoDisplayString(cidGsm.getArfcn()),
+                getCellInfoDisplayString(cidGsm.getBsic()),
+                getCellInfoDisplayString(ssGsm.getDbm()));
+    }
+
+    private String buildLteInfoString(CellInfoLte ci) {
+        CellIdentityLte cidLte = ci.getCellIdentity();
+        CellSignalStrengthLte ssLte = ci.getCellSignalStrength();
+
+        return String.format(
+                "%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s %-6.6s %-2.2s %-4.4s %-4.4s %-2.2s\n",
+                getConnectionStatusString(ci),
+                getCellInfoDisplayString(cidLte.getMcc()),
+                getCellInfoDisplayString(cidLte.getMnc()),
+                getCellInfoDisplayString(cidLte.getTac()),
+                getCellInfoDisplayString(cidLte.getCi()),
+                getCellInfoDisplayString(cidLte.getPci()),
+                getCellInfoDisplayString(cidLte.getEarfcn()),
+                getCellInfoDisplayString(cidLte.getBandwidth()),
+                getCellInfoDisplayString(ssLte.getDbm()),
+                getCellInfoDisplayString(ssLte.getRsrq()),
+                getCellInfoDisplayString(ssLte.getTimingAdvance()));
+    }
+
+    private String buildWcdmaInfoString(CellInfoWcdma ci) {
+        CellIdentityWcdma cidWcdma = ci.getCellIdentity();
+        CellSignalStrengthWcdma ssWcdma = ci.getCellSignalStrength();
+
+        return String.format("%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-3.3s %-4.4s\n",
+                getConnectionStatusString(ci),
+                getCellInfoDisplayString(cidWcdma.getMcc()),
+                getCellInfoDisplayString(cidWcdma.getMnc()),
+                getCellInfoDisplayString(cidWcdma.getLac()),
+                getCellInfoDisplayString(cidWcdma.getCid()),
+                getCellInfoDisplayString(cidWcdma.getUarfcn()),
+                getCellInfoDisplayString(cidWcdma.getPsc()),
+                getCellInfoDisplayString(ssWcdma.getDbm()));
+    }
+
+    private String buildCellInfoString(List<CellInfo> arrayCi) {
+        String value = new String();
+        StringBuilder cdmaCells = new StringBuilder(),
+                gsmCells = new StringBuilder(),
+                lteCells = new StringBuilder(),
+                wcdmaCells = new StringBuilder();
+
+        if (arrayCi != null) {
+            for (CellInfo ci : arrayCi) {
+
+                if (ci instanceof CellInfoLte) {
+                    lteCells.append(buildLteInfoString((CellInfoLte) ci));
+                } else if (ci instanceof CellInfoWcdma) {
+                    wcdmaCells.append(buildWcdmaInfoString((CellInfoWcdma) ci));
+                } else if (ci instanceof CellInfoGsm) {
+                    gsmCells.append(buildGsmInfoString((CellInfoGsm) ci));
+                } else if (ci instanceof CellInfoCdma) {
+                    cdmaCells.append(buildCdmaInfoString((CellInfoCdma) ci));
+                }
+            }
+            if (lteCells.length() != 0) {
+                value += String.format(
+                        "LTE\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s"
+                                + " %-6.6s %-2.2s %-4.4s %-4.4s %-2.2s\n",
+                        "SRV", "MCC", "MNC", "TAC", "CID", "PCI",
+                        "EARFCN", "BW", "RSRP", "RSRQ", "TA");
+                value += lteCells.toString();
+            }
+            if (wcdmaCells.length() != 0) {
+                value += String.format(
+                        "WCDMA\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-3.3s %-4.4s\n",
+                        "SRV", "MCC", "MNC", "LAC", "CID", "UARFCN", "PSC", "RSCP");
+                value += wcdmaCells.toString();
+            }
+            if (gsmCells.length() != 0) {
+                value += String.format(
+                        "GSM\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-4.4s %-4.4s\n",
+                        "SRV", "MCC", "MNC", "LAC", "CID", "ARFCN", "BSIC", "RSSI");
+                value += gsmCells.toString();
+            }
+            if (cdmaCells.length() != 0) {
+                value += String.format(
+                        "CDMA/EVDO\n%-3.3s %-5.5s %-5.5s %-5.5s"
+                                + " %-6.6s %-6.6s %-6.6s %-6.6s %-5.5s\n",
+                        "SRV", "SID", "NID", "BSID",
+                        "C-RSSI", "C-ECIO", "E-RSSI", "E-ECIO", "E-SNR");
+                value += cdmaCells.toString();
+            }
+        } else {
+            value = "unknown";
+        }
+
+        return value.toString();
+    }
+
+    private void updateCellInfo(List<CellInfo> arrayCi) {
+        mCellInfo.setText(buildCellInfoString(arrayCi));
+    }
+
+    private void updateSubscriptionIds() {
+        mSubscriptionId.setText(Integer.toString(mPhone.getSubId()));
+        mDds.setText(Integer.toString(SubscriptionManager.getDefaultDataSubscriptionId()));
+    }
+
+    private void updateMessageWaiting() {
+        mMwi.setText(String.valueOf(mMwiValue));
+    }
+
+    private void updateCallRedirect() {
+        mCfi.setText(String.valueOf(mCfiValue));
+    }
+
+
+    private void updateServiceState(ServiceState serviceState) {
+        int state = serviceState.getState();
+        Resources r = getResources();
+        String display = r.getString(R.string.radioInfo_unknown);
+
+        switch (state) {
+            case ServiceState.STATE_IN_SERVICE:
+                display = r.getString(R.string.radioInfo_service_in);
+                break;
+            case ServiceState.STATE_OUT_OF_SERVICE:
+            case ServiceState.STATE_EMERGENCY_ONLY:
+                display = r.getString(R.string.radioInfo_service_emergency);
+                break;
+            case ServiceState.STATE_POWER_OFF:
+                display = r.getString(R.string.radioInfo_service_off);
+                break;
+        }
+
+        mGsmState.setText(display);
+
+        if (serviceState.getRoaming()) {
+            mRoamingState.setText(R.string.radioInfo_roaming_in);
+        } else {
+            mRoamingState.setText(R.string.radioInfo_roaming_not);
+        }
+
+        mOperatorName.setText(serviceState.getOperatorAlphaLong());
+    }
+
+    private void updatePhoneState(int state) {
+        Resources r = getResources();
+        String display = r.getString(R.string.radioInfo_unknown);
+
+        switch (state) {
+            case TelephonyManager.CALL_STATE_IDLE:
+                display = r.getString(R.string.radioInfo_phone_idle);
+                break;
+            case TelephonyManager.CALL_STATE_RINGING:
+                display = r.getString(R.string.radioInfo_phone_ringing);
+                break;
+            case TelephonyManager.CALL_STATE_OFFHOOK:
+                display = r.getString(R.string.radioInfo_phone_offhook);
+                break;
+        }
+
+        mCallState.setText(display);
+    }
+
+    private void updateDataState() {
+        int state = mTelephonyManager.getDataState();
+        Resources r = getResources();
+        String display = r.getString(R.string.radioInfo_unknown);
+
+        switch (state) {
+            case TelephonyManager.DATA_CONNECTED:
+                display = r.getString(R.string.radioInfo_data_connected);
+                break;
+            case TelephonyManager.DATA_CONNECTING:
+                display = r.getString(R.string.radioInfo_data_connecting);
+                break;
+            case TelephonyManager.DATA_DISCONNECTED:
+                display = r.getString(R.string.radioInfo_data_disconnected);
+                break;
+            case TelephonyManager.DATA_SUSPENDED:
+                display = r.getString(R.string.radioInfo_data_suspended);
+                break;
+        }
+
+        mGprsState.setText(display);
+    }
+
+    private void updateNetworkType() {
+        if (mPhone != null) {
+            ServiceState ss = mPhone.getServiceState();
+            mDataNetwork.setText(ServiceState.rilRadioTechnologyToString(
+                    mPhone.getServiceState().getRilDataRadioTechnology()));
+            mVoiceNetwork.setText(ServiceState.rilRadioTechnologyToString(
+                    mPhone.getServiceState().getRilVoiceRadioTechnology()));
+        }
+    }
+
+    private void updateProperties() {
+        String s;
+        Resources r = getResources();
+
+        s = mPhone.getDeviceId();
+        if (s == null) s = r.getString(R.string.radioInfo_unknown);
+        mDeviceId.setText(s);
+
+        s = mPhone.getSubscriberId();
+        if (s == null) s = r.getString(R.string.radioInfo_unknown);
+        mSubscriberId.setText(s);
+
+        //FIXME: Replace with a TelephonyManager call
+        s = mPhone.getLine1Number();
+        if (s == null) s = r.getString(R.string.radioInfo_unknown);
+        mLine1Number.setText(s);
+    }
+
+    private void updateDataStats2() {
+        Resources r = getResources();
+
+        long txPackets = TrafficStats.getMobileTxPackets();
+        long rxPackets = TrafficStats.getMobileRxPackets();
+        long txBytes   = TrafficStats.getMobileTxBytes();
+        long rxBytes   = TrafficStats.getMobileRxBytes();
+
+        String packets = r.getString(R.string.radioInfo_display_packets);
+        String bytes   = r.getString(R.string.radioInfo_display_bytes);
+
+        mSent.setText(txPackets + " " + packets + ", " + txBytes + " " + bytes);
+        mReceived.setText(rxPackets + " " + packets + ", " + rxBytes + " " + bytes);
+    }
+
+    /**
+     *  Ping a host name
+     */
+    private void pingHostname() {
+        try {
+            try {
+                Process p4 = Runtime.getRuntime().exec("ping -c 1 www.google.com");
+                int status4 = p4.waitFor();
+                if (status4 == 0) {
+                    mPingHostnameResultV4 = "Pass";
+                } else {
+                    mPingHostnameResultV4 = String.format("Fail(%d)", status4);
+                }
+            } catch (IOException e) {
+                mPingHostnameResultV4 = "Fail: IOException";
+            }
+            try {
+                Process p6 = Runtime.getRuntime().exec("ping6 -c 1 www.google.com");
+                int status6 = p6.waitFor();
+                if (status6 == 0) {
+                    mPingHostnameResultV6 = "Pass";
+                } else {
+                    mPingHostnameResultV6 = String.format("Fail(%d)", status6);
+                }
+            } catch (IOException e) {
+                mPingHostnameResultV6 = "Fail: IOException";
+            }
+        } catch (InterruptedException e) {
+            mPingHostnameResultV4 = mPingHostnameResultV6 = "Fail: InterruptedException";
+        }
+    }
+
+    /**
+     * This function checks for basic functionality of HTTP Client.
+     */
+    private void httpClientTest() {
+        HttpURLConnection urlConnection = null;
+        try {
+            // TODO: Hardcoded for now, make it UI configurable
+            URL url = new URL("https://www.google.com");
+            urlConnection = (HttpURLConnection) url.openConnection();
+            if (urlConnection.getResponseCode() == 200) {
+                mHttpClientTestResult = "Pass";
+            } else {
+                mHttpClientTestResult = "Fail: Code: " + urlConnection.getResponseMessage();
+            }
+        } catch (IOException e) {
+            mHttpClientTestResult = "Fail: IOException";
+        } finally {
+            if (urlConnection != null) {
+                urlConnection.disconnect();
+            }
+        }
+    }
+
+    private void refreshSmsc() {
+        mQueuedWork.execute(new Runnable() {
+            public void run() {
+                //FIXME: Replace with a TelephonyManager call
+                mPhone.getSmscAddress(mHandler.obtainMessage(EVENT_QUERY_SMSC_DONE));
+            }
+        });
+    }
+
+    private void updateAllCellInfo() {
+
+        mCellInfo.setText("");
+        mLocation.setText("");
+
+        final Runnable updateAllCellInfoResults = new Runnable() {
+            public void run() {
+                updateLocation(mCellLocationResult);
+                updateCellInfo(mCellInfoResult);
+            }
+        };
+
+        mQueuedWork.execute(new Runnable() {
+            @Override
+            public void run() {
+                mCellInfoResult = mTelephonyManager.getAllCellInfo();
+                mCellLocationResult = mTelephonyManager.getCellLocation();
+
+                mHandler.post(updateAllCellInfoResults);
+            }
+        });
+    }
+
+    private void updatePingState() {
+        // Set all to unknown since the threads will take a few secs to update.
+        mPingHostnameResultV4 = getResources().getString(R.string.radioInfo_unknown);
+        mPingHostnameResultV6 = getResources().getString(R.string.radioInfo_unknown);
+        mHttpClientTestResult = getResources().getString(R.string.radioInfo_unknown);
+
+        mPingHostnameV4.setText(mPingHostnameResultV4);
+        mPingHostnameV6.setText(mPingHostnameResultV6);
+        mHttpClientTest.setText(mHttpClientTestResult);
+
+        final Runnable updatePingResults = new Runnable() {
+            public void run() {
+                mPingHostnameV4.setText(mPingHostnameResultV4);
+                mPingHostnameV6.setText(mPingHostnameResultV6);
+                mHttpClientTest.setText(mHttpClientTestResult);
+            }
+        };
+
+        Thread hostname = new Thread() {
+            @Override
+            public void run() {
+                pingHostname();
+                mHandler.post(updatePingResults);
+            }
+        };
+        hostname.start();
+
+        Thread httpClient = new Thread() {
+            @Override
+            public void run() {
+                httpClientTest();
+                mHandler.post(updatePingResults);
+            }
+        };
+        httpClient.start();
+    }
+
+    private MenuItem.OnMenuItemClickListener mViewADNCallback =
+            new MenuItem.OnMenuItemClickListener() {
+        public boolean onMenuItemClick(MenuItem item) {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            // XXX We need to specify the component here because if we don't
+            // the activity manager will try to resolve the type by calling
+            // the content provider, which causes it to be loaded in a process
+            // other than the Dialer process, which causes a lot of stuff to
+            // break.
+            intent.setClassName("com.android.phone", "com.android.phone.SimContacts");
+            startActivity(intent);
+            return true;
+        }
+    };
+
+    private MenuItem.OnMenuItemClickListener mViewFDNCallback =
+            new MenuItem.OnMenuItemClickListener() {
+        public boolean onMenuItemClick(MenuItem item) {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            // XXX We need to specify the component here because if we don't
+            // the activity manager will try to resolve the type by calling
+            // the content provider, which causes it to be loaded in a process
+            // other than the Dialer process, which causes a lot of stuff to
+            // break.
+            intent.setClassName("com.android.phone", "com.android.phone.settings.fdn.FdnList");
+            startActivity(intent);
+            return true;
+        }
+    };
+
+    private MenuItem.OnMenuItemClickListener mViewSDNCallback =
+            new MenuItem.OnMenuItemClickListener() {
+        public boolean onMenuItemClick(MenuItem item) {
+            Intent intent = new Intent(
+                    Intent.ACTION_VIEW, Uri.parse("content://icc/sdn"));
+            // XXX We need to specify the component here because if we don't
+            // the activity manager will try to resolve the type by calling
+            // the content provider, which causes it to be loaded in a process
+            // other than the Dialer process, which causes a lot of stuff to
+            // break.
+            intent.setClassName("com.android.phone", "com.android.phone.ADNList");
+            startActivity(intent);
+            return true;
+        }
+    };
+
+    private MenuItem.OnMenuItemClickListener mGetImsStatus =
+            new MenuItem.OnMenuItemClickListener() {
+        public boolean onMenuItemClick(MenuItem item) {
+            boolean isImsRegistered = mPhone.isImsRegistered();
+            boolean availableVolte = mPhone.isVolteEnabled();
+            boolean availableWfc = mPhone.isWifiCallingEnabled();
+            boolean availableVt = mPhone.isVideoEnabled();
+            boolean availableUt = mPhone.isUtEnabled();
+
+            final String imsRegString = isImsRegistered
+                    ? getString(R.string.radio_info_ims_reg_status_registered)
+                    : getString(R.string.radio_info_ims_reg_status_not_registered);
+
+            final String available = getString(R.string.radio_info_ims_feature_status_available);
+            final String unavailable = getString(
+                    R.string.radio_info_ims_feature_status_unavailable);
+
+            String imsStatus = getString(R.string.radio_info_ims_reg_status,
+                    imsRegString,
+                    availableVolte ? available : unavailable,
+                    availableWfc ? available : unavailable,
+                    availableVt ? available : unavailable,
+                    availableUt ? available : unavailable);
+
+            AlertDialog imsDialog = new AlertDialog.Builder(RadioInfo.this)
+                    .setMessage(imsStatus)
+                    .setTitle(getString(R.string.radio_info_ims_reg_status_title))
+                    .create();
+
+            imsDialog.show();
+
+            return true;
+        }
+    };
+
+    private MenuItem.OnMenuItemClickListener mSelectBandCallback =
+            new MenuItem.OnMenuItemClickListener() {
+        public boolean onMenuItemClick(MenuItem item) {
+            Intent intent = new Intent();
+            intent.setClass(RadioInfo.this, BandMode.class);
+            startActivity(intent);
+            return true;
+        }
+    };
+
+    private MenuItem.OnMenuItemClickListener mToggleData =
+            new MenuItem.OnMenuItemClickListener() {
+        public boolean onMenuItemClick(MenuItem item) {
+            int state = mTelephonyManager.getDataState();
+            switch (state) {
+                case TelephonyManager.DATA_CONNECTED:
+                    mTelephonyManager.setDataEnabled(false);
+                    break;
+                case TelephonyManager.DATA_DISCONNECTED:
+                    mTelephonyManager.setDataEnabled(true);
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+            return true;
+        }
+    };
+
+    private boolean isRadioOn() {
+        //FIXME: Replace with a TelephonyManager call
+        return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
+    }
+
+    private void updateRadioPowerState() {
+        //delightful hack to prevent on-checked-changed calls from
+        //actually forcing the radio preference to its transient/current value.
+        mRadioPowerOnSwitch.setOnCheckedChangeListener(null);
+        mRadioPowerOnSwitch.setChecked(isRadioOn());
+        mRadioPowerOnSwitch.setOnCheckedChangeListener(mRadioPowerOnChangeListener);
+    }
+
+    void setImsVolteProvisionedState(boolean state) {
+        Log.d(TAG, "setImsVolteProvisioned state: " + ((state) ? "on" : "off"));
+        setImsConfigProvisionedState(IMS_VOLTE_PROVISIONED_CONFIG_ID, state);
+    }
+
+    void setImsVtProvisionedState(boolean state) {
+        Log.d(TAG, "setImsVtProvisioned() state: " + ((state) ? "on" : "off"));
+        setImsConfigProvisionedState(IMS_VT_PROVISIONED_CONFIG_ID, state);
+    }
+
+    void setImsWfcProvisionedState(boolean state) {
+        Log.d(TAG, "setImsWfcProvisioned() state: " + ((state) ? "on" : "off"));
+        setImsConfigProvisionedState(IMS_WFC_PROVISIONED_CONFIG_ID, state);
+    }
+
+    void setEabProvisionedState(boolean state) {
+        Log.d(TAG, "setEabProvisioned() state: " + ((state) ? "on" : "off"));
+        setImsConfigProvisionedState(EAB_PROVISIONED_CONFIG_ID, state);
+    }
+
+    void setImsConfigProvisionedState(int configItem, boolean state) {
+        if (mPhone != null && mImsManager != null) {
+            mQueuedWork.execute(new Runnable() {
+                public void run() {
+                    try {
+                        mImsManager.getConfigInterface().setProvisionedValue(
+                                configItem, state ? 1 : 0);
+                    } catch (ImsException e) {
+                        Log.e(TAG, "setImsConfigProvisioned() exception:", e);
+                    }
+                }
+            });
+        }
+    }
+
+    OnCheckedChangeListener mRadioPowerOnChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            log("toggle radio power: currently " + (isRadioOn() ? "on" : "off"));
+            mPhone.setRadioPower(isChecked);
+        }
+    };
+
+    private boolean isImsVolteProvisioned() {
+        if (mPhone != null && mImsManager != null) {
+            return mImsManager.isVolteEnabledByPlatform(mPhone.getContext())
+                && mImsManager.isVolteProvisionedOnDevice(mPhone.getContext());
+        }
+        return false;
+    }
+
+    OnCheckedChangeListener mImsVolteCheckedChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            setImsVolteProvisionedState(isChecked);
+        }
+    };
+
+    private boolean isImsVtProvisioned() {
+        if (mPhone != null && mImsManager != null) {
+            return mImsManager.isVtEnabledByPlatform(mPhone.getContext())
+                && mImsManager.isVtProvisionedOnDevice(mPhone.getContext());
+        }
+        return false;
+    }
+
+    OnCheckedChangeListener mImsVtCheckedChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            setImsVtProvisionedState(isChecked);
+        }
+    };
+
+    private boolean isImsWfcProvisioned() {
+        if (mPhone != null && mImsManager != null) {
+            return mImsManager.isWfcEnabledByPlatform(mPhone.getContext())
+                && mImsManager.isWfcProvisionedOnDevice(mPhone.getContext());
+        }
+        return false;
+    }
+
+    OnCheckedChangeListener mImsWfcCheckedChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            setImsWfcProvisionedState(isChecked);
+        }
+    };
+
+    private boolean isEabProvisioned() {
+        return isFeatureProvisioned(EAB_PROVISIONED_CONFIG_ID, false);
+    }
+
+    OnCheckedChangeListener mEabCheckedChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            setEabProvisionedState(isChecked);
+        }
+    };
+
+    private boolean isFeatureProvisioned(int featureId, boolean defaultValue) {
+        boolean provisioned = defaultValue;
+        if (mImsManager != null) {
+            try {
+                ImsConfig imsConfig = mImsManager.getConfigInterface();
+                if (imsConfig != null) {
+                    provisioned =
+                            (imsConfig.getProvisionedValue(featureId)
+                                    == ImsConfig.FeatureValueConstants.ON);
+                }
+            } catch (ImsException ex) {
+                Log.e(TAG, "isFeatureProvisioned() exception:", ex);
+            }
+        }
+
+        log("isFeatureProvisioned() featureId=" + featureId + " provisioned=" + provisioned);
+        return provisioned;
+    }
+
+    private static boolean isEabEnabledByPlatform(Context context) {
+        if (context != null) {
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager != null && configManager.getConfig().getBoolean(
+                        CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void updateImsProvisionedState() {
+        if (!ImsManager.isImsSupportedOnDevice(mPhone.getContext())) {
+            return;
+        }
+        log("updateImsProvisionedState isImsVolteProvisioned()=" + isImsVolteProvisioned());
+        //delightful hack to prevent on-checked-changed calls from
+        //actually forcing the ims provisioning to its transient/current value.
+        mImsVolteProvisionedSwitch.setOnCheckedChangeListener(null);
+        mImsVolteProvisionedSwitch.setChecked(isImsVolteProvisioned());
+        mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
+        mImsVolteProvisionedSwitch.setEnabled(!IS_USER_BUILD
+                && mImsManager.isVolteEnabledByPlatform(mPhone.getContext()));
+
+        mImsVtProvisionedSwitch.setOnCheckedChangeListener(null);
+        mImsVtProvisionedSwitch.setChecked(isImsVtProvisioned());
+        mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
+        mImsVtProvisionedSwitch.setEnabled(!IS_USER_BUILD
+                && mImsManager.isVtEnabledByPlatform(mPhone.getContext()));
+
+        mImsWfcProvisionedSwitch.setOnCheckedChangeListener(null);
+        mImsWfcProvisionedSwitch.setChecked(isImsWfcProvisioned());
+        mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
+        mImsWfcProvisionedSwitch.setEnabled(!IS_USER_BUILD
+                && mImsManager.isWfcEnabledByPlatform(mPhone.getContext()));
+
+        mEabProvisionedSwitch.setOnCheckedChangeListener(null);
+        mEabProvisionedSwitch.setChecked(isEabProvisioned());
+        mEabProvisionedSwitch.setOnCheckedChangeListener(mEabCheckedChangeListener);
+        mEabProvisionedSwitch.setEnabled(!IS_USER_BUILD
+                && isEabEnabledByPlatform(mPhone.getContext()));
+    }
+
+    OnClickListener mDnsCheckButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            //FIXME: Replace with a TelephonyManager call
+            mPhone.disableDnsCheck(!mPhone.isDnsCheckDisabled());
+            updateDnsCheckState();
+        }
+    };
+
+    OnClickListener mOemInfoButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            Intent intent = new Intent(OEM_RADIO_INFO_INTENT);
+            try {
+                startActivity(intent);
+            } catch (android.content.ActivityNotFoundException ex) {
+                log("OEM-specific Info/Settings Activity Not Found : " + ex);
+                // If the activity does not exist, there are no OEM
+                // settings, and so we can just do nothing...
+            }
+        }
+    };
+
+    OnClickListener mPingButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            updatePingState();
+        }
+    };
+
+    OnClickListener mUpdateSmscButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            mUpdateSmscButton.setEnabled(false);
+            mQueuedWork.execute(new Runnable() {
+                public void run() {
+                    mPhone.setSmscAddress(mSmsc.getText().toString(),
+                            mHandler.obtainMessage(EVENT_UPDATE_SMSC_DONE));
+                }
+            });
+        }
+    };
+
+    OnClickListener mRefreshSmscButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            refreshSmsc();
+        }
+    };
+
+    OnClickListener mCarrierProvisioningButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            final Intent intent = new Intent("com.android.settings.CARRIER_PROVISIONING");
+            final ComponentName serviceComponent = ComponentName.unflattenFromString(
+                    "com.android.omadm.service/.DMIntentReceiver");
+            intent.setComponent(serviceComponent);
+            sendBroadcast(intent);
+        }
+    };
+
+    OnClickListener mTriggerCarrierProvisioningButtonHandler = new OnClickListener() {
+        public void onClick(View v) {
+            final Intent intent = new Intent("com.android.settings.TRIGGER_CARRIER_PROVISIONING");
+            final ComponentName serviceComponent = ComponentName.unflattenFromString(
+                    "com.android.omadm.service/.DMIntentReceiver");
+            intent.setComponent(serviceComponent);
+            sendBroadcast(intent);
+        }
+    };
+
+    AdapterView.OnItemSelectedListener mPreferredNetworkHandler =
+            new AdapterView.OnItemSelectedListener() {
+
+        public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+            if (mPreferredNetworkTypeResult != pos && pos >= 0
+                    && pos <= PREFERRED_NETWORK_LABELS.length - 2) {
+                mPreferredNetworkTypeResult = pos;
+
+                // TODO: Possibly migrate this to TelephonyManager.setPreferredNetworkType()
+                // which today still has some issues (mostly that the "set" is conditional
+                // on a successful modem call, which is not what we want). Instead we always
+                // want this setting to be set, so that if the radio hiccups and this setting
+                // is for some reason unsuccessful, future calls to the radio will reflect
+                // the users's preference which is set here.
+                final int subId = mPhone.getSubId();
+                if (SubscriptionManager.isUsableSubIdValue(subId)) {
+                    Settings.Global.putInt(mPhone.getContext().getContentResolver(),
+                            PREFERRED_NETWORK_MODE + subId, mPreferredNetworkTypeResult);
+                }
+                log("Calling setPreferredNetworkType(" + mPreferredNetworkTypeResult + ")");
+                Message msg = mHandler.obtainMessage(EVENT_SET_PREFERRED_TYPE_DONE);
+                mPhone.setPreferredNetworkType(mPreferredNetworkTypeResult, msg);
+            }
+        }
+
+        public void onNothingSelected(AdapterView parent) {
+        }
+    };
+
+    AdapterView.OnItemSelectedListener mSelectPhoneIndexHandler =
+            new AdapterView.OnItemSelectedListener() {
+
+        public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+            if (pos >= 0 && pos <= sPhoneIndexLabels.length - 1) {
+                // the array position is equal to the phone index
+                int phoneIndex = pos;
+                Phone[] phones = PhoneFactory.getPhones();
+                if (phones == null || phones.length <= phoneIndex) {
+                    return;
+                }
+                // getSubId says it takes a slotIndex, but it actually takes a phone index
+                int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+                int[] subIds = SubscriptionManager.getSubId(phoneIndex);
+                if (subIds != null && subIds.length > 0) {
+                    subId = subIds[0];
+                }
+                mSelectedPhoneIndex = phoneIndex;
+
+                updatePhoneIndex(phoneIndex, subId);
+            }
+        }
+
+        public void onNothingSelected(AdapterView parent) {
+        }
+    };
+
+    AdapterView.OnItemSelectedListener mCellInfoRefreshRateHandler  =
+            new AdapterView.OnItemSelectedListener() {
+
+        public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+            mCellInfoRefreshRateIndex = pos;
+            mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[pos]);
+            updateAllCellInfo();
+        }
+
+        public void onNothingSelected(AdapterView parent) {
+        }
+    };
+
+    boolean isCbrsSupported() {
+        return getResources().getBoolean(
+              com.android.internal.R.bool.config_cbrs_supported);
+    }
+
+    void updateCbrsDataState(boolean state) {
+        Log.d(TAG, "setCbrsDataSwitchState() state:" + ((state) ? "on" : "off"));
+        if (mTelephonyManager != null) {
+            mQueuedWork.execute(new Runnable() {
+                public void run() {
+                    mTelephonyManager.setOpportunisticNetworkState(state);
+                    mCbrsDataSwitch.setChecked(getCbrsDataState());
+                }
+            });
+        }
+    }
+
+    boolean getCbrsDataState() {
+        boolean state = false;
+        if (mTelephonyManager != null) {
+            state = mTelephonyManager.isOpportunisticNetworkEnabled();
+        }
+        Log.d(TAG, "getCbrsDataState() state:" + ((state) ? "on" : "off"));
+        return state;
+    }
+
+    OnCheckedChangeListener mCbrsDataSwitchChangeListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            updateCbrsDataState(isChecked);
+        }
+    };
+
+    private void showDsdsChangeDialog() {
+        final AlertDialog confirmDialog = new Builder(RadioInfo.this)
+                .setTitle(R.string.dsds_dialog_title)
+                .setMessage(R.string.dsds_dialog_message)
+                .setPositiveButton(R.string.dsds_dialog_confirm, mOnDsdsDialogConfirmedListener)
+                .setNegativeButton(R.string.dsds_dialog_cancel, mOnDsdsDialogConfirmedListener)
+                .create();
+        confirmDialog.show();
+    }
+
+    private static boolean isDsdsSupported() {
+        return (TelephonyManager.getDefault().isMultiSimSupported()
+            == TelephonyManager.MULTISIM_ALLOWED);
+    }
+
+    private static boolean isDsdsEnabled() {
+        return TelephonyManager.getDefault().getPhoneCount() > 1;
+    }
+
+    private void performDsdsSwitch() {
+        mTelephonyManager.switchMultiSimConfig(mDsdsSwitch.isChecked() ? 2 : 1);
+    }
+
+    /**
+     * @return {@code True} if the device is only supported dsds mode.
+     */
+    private boolean dsdsModeOnly() {
+        String dsdsMode = SystemProperties.get(DSDS_MODE_PROPERTY);
+        return !TextUtils.isEmpty(dsdsMode) && Integer.parseInt(dsdsMode) == ALWAYS_ON_DSDS_MODE;
+    }
+
+    DialogInterface.OnClickListener mOnDsdsDialogConfirmedListener =
+            new DialogInterface.OnClickListener() {
+        @Override
+        public void onClick(DialogInterface dialog, int which) {
+            if (which == DialogInterface.BUTTON_POSITIVE) {
+                mDsdsSwitch.toggle();
+                performDsdsSwitch();
+            }
+        }
+    };
+}
diff --git a/src/com/android/phone/settings/TtyModeListPreference.java b/src/com/android/phone/settings/TtyModeListPreference.java
index 89cac47..ba415c5 100644
--- a/src/com/android/phone/settings/TtyModeListPreference.java
+++ b/src/com/android/phone/settings/TtyModeListPreference.java
@@ -18,17 +18,23 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
 
+import java.util.Arrays;
+
 public class TtyModeListPreference extends ListPreference
         implements Preference.OnPreferenceChangeListener {
     private static final String LOG_TAG = TtyModeListPreference.class.getSimpleName();
@@ -44,6 +50,19 @@
         int settingsTtyMode = Settings.Secure.getInt(getContext().getContentResolver(),
                 Settings.Secure.PREFERRED_TTY_MODE,
                 TelecomManager.TTY_MODE_OFF);
+        if (shouldHideHcoAndVco()) {
+            setEntries(Arrays.copyOfRange(
+                    getContext().getResources().getTextArray(R.array.tty_mode_entries), 0, 2));
+            setEntryValues(Arrays.copyOfRange(
+                    getContext().getResources().getTextArray(R.array.tty_mode_values), 0, 2));
+            if (settingsTtyMode == TelecomManager.TTY_MODE_HCO
+                    || settingsTtyMode == TelecomManager.TTY_MODE_VCO) {
+                // If the persisted setting is HCO or VCO, set it to full here
+                settingsTtyMode = TelecomManager.TTY_MODE_FULL;
+                Settings.Secure.putInt(getContext().getContentResolver(), Secure.PREFERRED_TTY_MODE,
+                        settingsTtyMode);
+            }
+        }
         setValue(Integer.toString(settingsTtyMode));
         updatePreferredTtyModeSummary(settingsTtyMode);
     }
@@ -101,6 +120,18 @@
         }
     }
 
+    private boolean shouldHideHcoAndVco() {
+        CarrierConfigManager carrierConfigManager =
+                getContext().getSystemService(CarrierConfigManager.class);
+        PersistableBundle config = carrierConfigManager.getConfigForSubId(
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
+        boolean carrierShouldHideHcoVco = config != null && config.getBoolean(
+                CarrierConfigManager.KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+        boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr.isRttSupported(
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
+        return carrierShouldHideHcoVco && isRttSupported;
+    }
+
     private static void log(String msg) {
         Log.d(LOG_TAG, msg);
     }
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index c358e27..0eda140 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PersistableBundle;
 import android.provider.ContactsContract.CommonDataKinds;
 import android.telephony.PhoneNumberUtils;
 import android.text.Editable;
@@ -52,6 +53,7 @@
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
 import com.android.phone.SubscriptionInfoHelper;
+import android.telephony.CarrierConfigManager;
 
 /**
  * Activity to let the user add or edit an FDN contact.
@@ -100,6 +102,7 @@
     }
     /** flag to track saving state */
     private boolean mDataBusy;
+    private int mFdnNumberLimitLength = 20;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -111,6 +114,17 @@
         setContentView(R.layout.edit_fdn_contact_screen);
         setupView();
         setTitle(mAddContact ? R.string.add_fdn_contact : R.string.edit_fdn_contact);
+        PersistableBundle b = null;
+        if (mSubscriptionInfoHelper.hasSubId()) {
+            b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+                    mSubscriptionInfoHelper.getSubId());
+        } else {
+            b = PhoneGlobals.getInstance().getCarrierConfig();
+        }
+        if (b != null) {
+            mFdnNumberLimitLength = b.getInt(
+                    CarrierConfigManager.KEY_FDN_NUMBER_LENGTH_LIMIT_INT);
+        }
 
         displayProgress(false);
     }
@@ -294,7 +308,7 @@
       * TODO: Fix this logic.
       */
      private boolean isValidNumber(String number) {
-         return (number.length() <= 20) && (number.length() > 0);
+         return (number.length() <= mFdnNumberLimitLength) && (number.length() > 0);
      }
 
 
@@ -397,7 +411,8 @@
         } else {
             if (DBG) log("handleResult: failed!");
             if (invalidNumber) {
-                showStatus(getResources().getText(R.string.fdn_invalid_number));
+                showStatus(getResources().getString(R.string.fdn_invalid_number,
+                        mFdnNumberLimitLength));
             } else {
                if (PhoneFactory.getDefaultPhone().getIccCard().getIccPin2Blocked()) {
                     showStatus(getResources().getText(R.string.fdn_enable_puk2_requested));
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
index 50ac669..6b60303 100644
--- a/src/com/android/phone/vvm/RemoteVvmTaskManager.java
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.ComponentInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
@@ -38,6 +39,7 @@
 import android.telephony.VisualVoicemailSms;
 import android.text.TextUtils;
 
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.phone.Assert;
 import com.android.phone.R;
 
@@ -167,7 +169,8 @@
             }
             if (info.serviceInfo == null) {
                 VvmLog.w(TAG,
-                        "Component " + info.getComponentInfo() + " is not a service, ignoring");
+                        "Component " + TelephonyUtils.getComponentInfo(info)
+                            + " is not a service, ignoring");
                 continue;
             }
             if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE
@@ -180,7 +183,8 @@
                 VvmLog.w(TAG, "target package " + targetPackage
                         + " is no longer the active VisualVoicemailService, ignoring");
             }
-            return info.getComponentInfo().getComponentName();
+            ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(info);
+            return new ComponentName(componentInfo.packageName, componentInfo.name);
 
         }
         return null;
@@ -199,7 +203,8 @@
         if (info.isEmpty()) {
             return null;
         }
-        return info.get(0).getComponentInfo().getComponentName();
+        ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(info.get(0));
+        return new ComponentName(componentInfo.packageName, componentInfo.name);
     }
 
     @Override
@@ -226,6 +231,11 @@
         Assert.isMainThread();
         mTaskReferenceCount++;
 
+        if (intent == null) {
+            VvmLog.i(TAG, "received intent is null");
+            checkReference();
+            return START_NOT_STICKY;
+        }
         PhoneAccountHandle phoneAccountHandle = intent.getExtras()
                 .getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE);
         int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
diff --git a/src/com/android/phone/vvm/VvmDumpHandler.java b/src/com/android/phone/vvm/VvmDumpHandler.java
index 866927e..82c5bb5 100644
--- a/src/com/android/phone/vvm/VvmDumpHandler.java
+++ b/src/com/android/phone/vvm/VvmDumpHandler.java
@@ -19,7 +19,7 @@
         indentedWriter.println("******* OmtpVvm *******");
         indentedWriter.println("======= Configs =======");
         indentedWriter.increaseIndent();
-        for (PhoneAccountHandle handle : TelecomManager.from(context)
+        for (PhoneAccountHandle handle : context.getSystemService(TelecomManager.class)
                 .getCallCapablePhoneAccounts()) {
             int subId = PhoneAccountHandleConverter.toSubId(handle);
             indentedWriter.println(
diff --git a/src/com/android/services/telephony/CdmaConference.java b/src/com/android/services/telephony/CdmaConference.java
index 69ff2a4..693fd16 100755
--- a/src/com/android/services/telephony/CdmaConference.java
+++ b/src/com/android/services/telephony/CdmaConference.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.PersistableBundle;
-import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.CarrierConfigManager;
@@ -32,7 +31,7 @@
 /**
  * CDMA-based conference call.
  */
-public class CdmaConference extends Conference implements Holdable {
+public class CdmaConference extends TelephonyConferenceBase implements Holdable {
     private int mCapabilities;
     private int mProperties;
     private boolean mIsHoldable;
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 5d987f7..9afcd0a 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -48,7 +48,8 @@
  * the conference from being created for 3 seconds. This is a more pleasant experience for the user.
  */
 final class CdmaConferenceController {
-    private final Connection.Listener mConnectionListener = new Connection.Listener() {
+    private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+            new TelephonyConnection.TelephonyConnectionListener() {
                 @Override
                 public void onStateChanged(Connection c, int state) {
                     recalculateConference();
@@ -139,7 +140,7 @@
 
     private void addInternal(CdmaConnection connection) {
         mCdmaConnections.add(connection);
-        connection.addConnectionListener(mConnectionListener);
+        connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
         recalculateConference();
     }
 
@@ -151,7 +152,7 @@
             return;
         }
 
-        connection.removeConnectionListener(mConnectionListener);
+        connection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
         mCdmaConnections.remove(connection);
         recalculateConference();
     }
@@ -197,14 +198,14 @@
             for (CdmaConnection connection : conferenceConnections) {
                 if (!existingChildConnections.contains(connection)) {
                     Log.i(this, "Adding connection to conference call: %s", connection);
-                    mConference.addConnection(connection);
+                    mConference.addTelephonyConnection(connection);
                 }
                 existingChildConnections.remove(connection);
             }
 
             // 3) Remove any lingering old/disconnected/destroyed connections
             for (Connection oldConnection : existingChildConnections) {
-                mConference.removeConnection(oldConnection);
+                mConference.removeTelephonyConnection(oldConnection);
                 Log.i(this, "Removing connection from conference call: %s", oldConnection);
             }
 
@@ -212,13 +213,13 @@
             if (isNewlyCreated) {
                 Log.d(this, "Adding the conference call");
                 mConference.updateCallRadioTechAfterCreation();
-                mConnectionService.addConference(mConference);
+                mConnectionService.addTelephonyConference(mConference);
             }
         } else if (conferenceConnections.isEmpty()) {
             // There are no more connection so if we still have a conference, lets remove it.
             if (mConference != null) {
                 Log.i(this, "Destroying the CDMA conference connection.");
-                mConference.destroy();
+                mConference.destroyTelephonyConference();
                 mConference = null;
             }
         }
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index ca842b1..bd015e3 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -228,8 +228,8 @@
             } catch (CallStateException e) {
                 Log.e(this, e, "Failed to hangup call waiting call");
             }
-            setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause,
-                    null, getPhone().getPhoneId()));
+            setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                    telephonyDisconnectCause, null, getPhone().getPhoneId()));
         }
     }
 
@@ -325,7 +325,7 @@
     }
 
     @Override
-    protected void close() {
+    public void close() {
         mIsConnectionTimeReset = false;
         if (getPhone() != null) {
             getPhone().unregisterForLineControlInfo(mHandler);
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index b1f1b23..ed92e7f 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -17,15 +17,13 @@
 package com.android.services.telephony;
 
 import android.net.Uri;
-import android.telecom.ConferenceParticipant;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
 
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.ims.internal.ConferenceParticipant;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 
@@ -34,6 +32,8 @@
  */
 public class ConferenceParticipantConnection extends Connection {
 
+    private static final String LOG_TAG = "ConferenceParticipantConnection";
+
     /**
      * The user entity URI For the conference participant.
      */
@@ -88,7 +88,7 @@
      * @param newState The new state.
      */
     public void updateState(int newState) {
-        Log.v(this, "updateState endPoint: %s state: %s", Log.pii(mEndpoint),
+        Log.v(this, "updateState endPoint: %s state: %s", Rlog.pii(LOG_TAG, mEndpoint),
                 Connection.stateToString(newState));
         if (newState == getState()) {
             return;
@@ -203,13 +203,13 @@
         sb.append("[ConferenceParticipantConnection objId:");
         sb.append(System.identityHashCode(this));
         sb.append(" endPoint:");
-        sb.append(Log.pii(mEndpoint));
+        sb.append(Rlog.pii(LOG_TAG, mEndpoint));
         sb.append(" address:");
-        sb.append(Log.pii(getAddress()));
+        sb.append(Rlog.pii(LOG_TAG, getAddress()));
         sb.append(" addressPresentation:");
         sb.append(getAddressPresentation());
         sb.append(" parentConnection:");
-        sb.append(Log.pii(mParentConnection.getAddress()));
+        sb.append(Rlog.pii(LOG_TAG, mParentConnection.getAddress()));
         sb.append(" state:");
         sb.append(Connection.stateToString(getState()));
         sb.append(" connectTime:");
diff --git a/src/com/android/services/telephony/DeviceState.java b/src/com/android/services/telephony/DeviceState.java
new file mode 100644
index 0000000..7cd2d7e
--- /dev/null
+++ b/src/com/android/services/telephony/DeviceState.java
@@ -0,0 +1,60 @@
+/*
+ * 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.services.telephony;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.phone.R;
+
+/**
+ * Abstracts device state and settings for better testability.
+ */
+public class DeviceState {
+
+    public boolean shouldCheckSimStateBeforeOutgoingCall(Context context) {
+        return context.getResources().getBoolean(
+                R.bool.config_checkSimStateBeforeOutgoingCall);
+    }
+
+    public boolean isSuplDdsSwitchRequiredForEmergencyCall(Context context) {
+        return context.getResources().getBoolean(
+                R.bool.config_gnss_supl_requires_default_data_for_emergency);
+    }
+
+    public boolean isRadioPowerDownAllowedOnBluetooth(Context context) {
+        return context.getResources().getBoolean(R.bool.config_allowRadioPowerDownOnBluetooth);
+    }
+
+    public boolean isAirplaneModeOn(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+    }
+
+    public int getCellOnStatus(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(), Settings.Global.CELL_ON,
+                PhoneConstants.CELL_ON_FLAG);
+    }
+
+    public boolean isTtyModeEnabled(Context context) {
+        return android.provider.Settings.Secure.getInt(context.getContentResolver(),
+                android.provider.Settings.Secure.PREFERRED_TTY_MODE,
+                TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF;
+    }
+}
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 94e8c4a..9e32f00 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -117,6 +117,8 @@
     private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause) {
         switch (telephonyDisconnectCause) {
             case android.telephony.DisconnectCause.LOCAL:
+            //  The call was still disconnected locally, so this is not an error condition.
+            case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED:
                 return DisconnectCause.LOCAL;
 
             case android.telephony.DisconnectCause.NORMAL:
@@ -170,6 +172,8 @@
             case android.telephony.DisconnectCause.POWER_OFF:
             case android.telephony.DisconnectCause.LOW_BATTERY:
             case android.telephony.DisconnectCause.DIAL_LOW_BATTERY:
+            case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+            case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
             case android.telephony.DisconnectCause.SERVER_ERROR:
             case android.telephony.DisconnectCause.SERVER_UNREACHABLE:
             case android.telephony.DisconnectCause.TIMED_OUT:
@@ -361,6 +365,12 @@
             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
                 resourceId = R.string.callFailed_otasp_provisioning_in_process;
                 break;
+            case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+                resourceId = R.string.callFailed_emergency_call_over_wfc_not_available;
+                break;
+            case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
+                resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
+                break;
             default:
                 break;
         }
@@ -748,6 +758,12 @@
             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
                 resourceId = R.string.callFailed_otasp_provisioning_in_process;
                 break;
+            case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+                resourceId = R.string.callFailed_emergency_call_over_wfc_not_available;
+                break;
+            case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
+                resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
+                break;
             default:
                 break;
         }
@@ -783,6 +799,8 @@
                 break;
             case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
                 return DisconnectCause.REASON_IMS_ACCESS_BLOCKED;
+            case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED:
+                return DisconnectCause.REASON_EMERGENCY_CALL_PLACED;
         }
 
         // If no specific code-mapping found, then fall back to using the reason.
diff --git a/src/com/android/services/telephony/EmergencyTonePlayer.java b/src/com/android/services/telephony/EmergencyTonePlayer.java
index 8e26349..431f8d7 100644
--- a/src/com/android/services/telephony/EmergencyTonePlayer.java
+++ b/src/com/android/services/telephony/EmergencyTonePlayer.java
@@ -23,7 +23,6 @@
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
-import android.os.SystemVibrator;
 import android.os.Vibrator;
 import android.provider.Settings;
 
@@ -49,11 +48,9 @@
                 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
                 .build();
 
-    // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure that this vibrator
-    // object will be isolated from others.
-    private final Vibrator mVibrator = new SystemVibrator();
     private final Context mContext;
     private final AudioManager mAudioManager;
+    private final Vibrator mVibrator;
 
     private ToneGenerator mToneGenerator;
     private int mSavedInCallVolume;
@@ -62,6 +59,7 @@
     EmergencyTonePlayer(Context context) {
         mContext = context;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
     }
 
     public void start() {
diff --git a/src/com/android/services/telephony/HoldTracker.java b/src/com/android/services/telephony/HoldTracker.java
index 805802f..5032b41 100644
--- a/src/com/android/services/telephony/HoldTracker.java
+++ b/src/com/android/services/telephony/HoldTracker.java
@@ -16,7 +16,6 @@
 
 package com.android.services.telephony;
 
-import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
 
 import java.util.ArrayList;
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4da92e2..4c3f9c9 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -21,20 +21,19 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PersistableBundle;
-import android.telecom.Conference;
-import android.telecom.ConferenceParticipant;
 import android.telecom.Connection;
 import android.telecom.Connection.VideoProvider;
 import android.telecom.DisconnectCause;
-import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
 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;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
@@ -68,7 +67,9 @@
  * connection and is responsible for managing the conference participant connections which represent
  * the participants.
  */
-public class ImsConference extends Conference implements Holdable {
+public class ImsConference extends TelephonyConferenceBase implements Holdable {
+
+    private static final String LOG_TAG = "ImsConference";
 
     /**
      * Abstracts out fetching a feature flag.  Makes testing easier.
@@ -78,143 +79,127 @@
     }
 
     /**
-     * Listener used to respond to changes to conference participants.  At the conference level we
-     * are most concerned with handling destruction of a conference participant.
-     */
-    private final Connection.Listener mParticipantListener = new Connection.Listener() {
-        /**
-         * Participant has been destroyed.  Remove it from the conference.
-         *
-         * @param connection The participant which was destroyed.
-         */
-        @Override
-        public void onDestroyed(Connection connection) {
-            ConferenceParticipantConnection participant =
-                    (ConferenceParticipantConnection) connection;
-            removeConferenceParticipant(participant);
-            updateManageConference();
-        }
-
-    };
-
-    /**
      * Listener used to respond to changes to the underlying radio connection for the conference
      * host connection.  Used to respond to SRVCC changes.
      */
     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
             new TelephonyConnection.TelephonyConnectionListener() {
 
-        @Override
-        public void onOriginalConnectionConfigured(TelephonyConnection c) {
-            if (c == mConferenceHost) {
-               handleOriginalConnectionChange();
-            }
-        }
-    };
+                /**
+                 * Updates the state of the conference based on the new state of the host.
+                 *
+                 * @param c The host connection.
+                 * @param state The new state
+                 */
+                @Override
+                public void onStateChanged(android.telecom.Connection c, int state) {
+                    setState(state);
+                }
 
-    /**
-     * Listener used to respond to changes to the connection to the IMS conference server.
-     */
-    private final android.telecom.Connection.Listener mConferenceHostListener =
-            new android.telecom.Connection.Listener() {
+                /**
+                 * Disconnects the conference when its host connection disconnects.
+                 *
+                 * @param c The host connection.
+                 * @param disconnectCause The host connection disconnect cause.
+                 */
+                @Override
+                public void onDisconnected(android.telecom.Connection c,
+                        DisconnectCause disconnectCause) {
+                    setDisconnected(disconnectCause);
+                }
 
-        /**
-         * Updates the state of the conference based on the new state of the host.
-         *
-         * @param c The host connection.
-         * @param state The new state
-         */
-        @Override
-        public void onStateChanged(android.telecom.Connection c, int state) {
-            setState(state);
-        }
+                @Override
+                public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
+                    Log.d(this, "onVideoStateChanged video state %d", videoState);
+                    setVideoState(c, videoState);
+                }
 
-        /**
-         * Disconnects the conference when its host connection disconnects.
-         *
-         * @param c The host connection.
-         * @param disconnectCause The host connection disconnect cause.
-         */
-        @Override
-        public void onDisconnected(android.telecom.Connection c, DisconnectCause disconnectCause) {
-            setDisconnected(disconnectCause);
-        }
+                @Override
+                public void onVideoProviderChanged(android.telecom.Connection c,
+                        Connection.VideoProvider videoProvider) {
+                    Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+                            videoProvider);
+                    setVideoProvider(c, videoProvider);
+                }
 
-        /**
-         * Handles changes to conference participant data as reported by the conference host
-         * connection.
-         *
-         * @param c The connection.
-         * @param participants The participant information.
-         */
-        @Override
-        public void onConferenceParticipantsChanged(android.telecom.Connection c,
-                List<ConferenceParticipant> participants) {
+                @Override
+                public void onConnectionCapabilitiesChanged(Connection c,
+                        int connectionCapabilities) {
+                    Log.d(this, "onConnectionCapabilitiesChanged: Connection: %s,"
+                            + " connectionCapabilities: %s", c, connectionCapabilities);
+                    int capabilites = ImsConference.this.getConnectionCapabilities();
+                    boolean isVideoConferencingSupported = mConferenceHost == null ? false :
+                            mConferenceHost.isCarrierVideoConferencingSupported();
+                    setConnectionCapabilities(
+                            applyHostCapabilities(capabilites, connectionCapabilities,
+                                    isVideoConferencingSupported));
+                }
 
-            if (c == null || participants == null) {
-                return;
-            }
-            Log.v(this, "onConferenceParticipantsChanged: %d participants", participants.size());
-            TelephonyConnection telephonyConnection = (TelephonyConnection) c;
-            handleConferenceParticipantsUpdate(telephonyConnection, participants);
-        }
+                @Override
+                public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
+                    Log.d(this, "onConnectionPropertiesChanged: Connection: %s,"
+                            + " connectionProperties: %s", c, connectionProperties);
+                    int properties = ImsConference.this.getConnectionProperties();
+                    setConnectionProperties(applyHostProperties(properties, connectionProperties));
+                }
 
-        @Override
-        public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
-            Log.d(this, "onVideoStateChanged video state %d", videoState);
-            setVideoState(c, videoState);
-        }
+                @Override
+                public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
+                    Log.v(this, "onStatusHintsChanged");
+                    updateStatusHints();
+                }
 
-        @Override
-        public void onVideoProviderChanged(android.telecom.Connection c,
-                Connection.VideoProvider videoProvider) {
-            Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
-                    videoProvider);
-            setVideoProvider(c, videoProvider);
-        }
+                @Override
+                public void onExtrasChanged(Connection c, Bundle extras) {
+                    Log.v(this, "onExtrasChanged: c=" + c + " Extras=" + extras);
+                    putExtras(extras);
+                }
 
-        @Override
-        public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {
-            Log.d(this, "onConnectionCapabilitiesChanged: Connection: %s," +
-                    " connectionCapabilities: %s", c, connectionCapabilities);
-            int capabilites = ImsConference.this.getConnectionCapabilities();
-            boolean isVideoConferencingSupported = mConferenceHost == null ? false :
-                    mConferenceHost.isCarrierVideoConferencingSupported();
-            setConnectionCapabilities(applyHostCapabilities(capabilites, connectionCapabilities,
-                    isVideoConferencingSupported));
-        }
+                @Override
+                public void onExtrasRemoved(Connection c, List<String> keys) {
+                    Log.v(this, "onExtrasRemoved: c=" + c + " key=" + keys);
+                    removeExtras(keys);
+                }
 
-        @Override
-        public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
-            Log.d(this, "onConnectionPropertiesChanged: Connection: %s," +
-                    " connectionProperties: %s", c, connectionProperties);
-            int properties = ImsConference.this.getConnectionProperties();
-            setConnectionProperties(applyHostProperties(properties, connectionProperties));
-        }
+                @Override
+                public void onConnectionEvent(Connection c, String event, Bundle extras) {
+                    if (Connection.EVENT_MERGE_START.equals(event)) {
+                        // Do not pass a merge start event on the underlying host connection; only
+                        // indicate a merge has started on the connections which are merged into a
+                        // conference.
+                        return;
+                    }
 
-        @Override
-        public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
-            Log.v(this, "onStatusHintsChanged");
-            updateStatusHints();
-        }
+                    sendConferenceEvent(event, extras);
+                }
 
-        @Override
-        public void onExtrasChanged(Connection c, Bundle extras) {
-            Log.v(this, "onExtrasChanged: c=" + c + " Extras=" + extras);
-            putExtras(extras);
-        }
+                @Override
+                public void onOriginalConnectionConfigured(TelephonyConnection c) {
+                    if (c == mConferenceHost) {
+                        handleOriginalConnectionChange();
+                    }
+                }
 
-        @Override
-        public void onExtrasRemoved(Connection c, List<String> keys) {
-            Log.v(this, "onExtrasRemoved: c=" + c + " key=" + keys);
-            removeExtras(keys);
-        }
+                /**
+                 * Handles changes to conference participant data as reported by the conference host
+                 * connection.
+                 *
+                 * @param c The connection.
+                 * @param participants The participant information.
+                 */
+                @Override
+                public void onConferenceParticipantsChanged(android.telecom.Connection c,
+                        List<ConferenceParticipant> participants) {
 
-        @Override
-        public void onConnectionEvent(Connection c, String event, Bundle extras) {
-            sendConnectionEvent(event, extras);
-        }
-    };
+                    if (c == null || participants == null) {
+                        return;
+                    }
+                    Log.v(this, "onConferenceParticipantsChanged: %d participants",
+                            participants.size());
+                    TelephonyConnection telephonyConnection = (TelephonyConnection) c;
+                    handleConferenceParticipantsUpdate(telephonyConnection, participants);
+                }
+            };
 
     /**
      * The telephony connection service; used to add new participant connections to Telecom.
@@ -345,15 +330,15 @@
 
         conferenceCapabilities = changeBitmask(conferenceCapabilities,
                     Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
-                    can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+                (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0);
 
         if (isVideoConferencingSupported) {
             conferenceCapabilities = changeBitmask(conferenceCapabilities,
                     Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
-                    can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
+                    (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0);
             conferenceCapabilities = changeBitmask(conferenceCapabilities,
                     Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
-                    can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO));
+                    (capabilities & Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO) != 0);
         } else {
             // If video conferencing is not supported, explicitly turn off the remote video
             // capability and the ability to upgrade to video.
@@ -366,7 +351,7 @@
 
         conferenceCapabilities = changeBitmask(conferenceCapabilities,
                 Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
-                can(capabilities, Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO));
+                (capabilities & Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO) != 0);
 
         conferenceCapabilities = changeBitmask(conferenceCapabilities,
                 Connection.CAPABILITY_CAN_PAUSE_VIDEO,
@@ -385,15 +370,15 @@
     private int applyHostProperties(int conferenceProperties, int properties) {
         conferenceProperties = changeBitmask(conferenceProperties,
                 Connection.PROPERTY_HIGH_DEF_AUDIO,
-                can(properties, Connection.PROPERTY_HIGH_DEF_AUDIO));
+                (properties & Connection.PROPERTY_HIGH_DEF_AUDIO) != 0);
 
         conferenceProperties = changeBitmask(conferenceProperties,
                 Connection.PROPERTY_WIFI,
-                can(properties, Connection.PROPERTY_WIFI));
+                (properties & Connection.PROPERTY_WIFI) != 0);
 
         conferenceProperties = changeBitmask(conferenceProperties,
                 Connection.PROPERTY_IS_EXTERNAL_CALL,
-                can(properties, Connection.PROPERTY_IS_EXTERNAL_CALL));
+                (properties & Connection.PROPERTY_IS_EXTERNAL_CALL) != 0);
 
         conferenceProperties = changeBitmask(conferenceProperties,
                 Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
@@ -441,7 +426,7 @@
      * Invoked when the Conference and all its {@link Connection}s should be disconnected.
      * <p>
      * Hangs up the call via the conference host connection.  When the host connection has been
-     * successfully disconnected, the {@link #mConferenceHostListener} listener receives an
+     * successfully disconnected, the {@link #mTelephonyConnectionListener} listener receives an
      * {@code onDestroyed} event, which triggers the conference participant connections to be
      * disconnected.
      */
@@ -619,7 +604,8 @@
      * that the conference is represented appropriately on Bluetooth devices.
      */
     private void updateManageConference() {
-        boolean couldManageConference = can(Connection.CAPABILITY_MANAGE_CONFERENCE);
+        boolean couldManageConference =
+                (getConnectionCapabilities() & Connection.CAPABILITY_MANAGE_CONFERENCE) != 0;
         boolean canManageConference = mFeatureFlagProxy.isUsingSinglePartyCallEmulation()
                 && mIsEmulatingSinglePartyCall
                 ? mConferenceParticipantConnections.size() > 1
@@ -696,7 +682,6 @@
             setConnectionTime(mConferenceHost.getConnectTimeMillis());
         }
 
-        mConferenceHost.addConnectionListener(mConferenceHostListener);
         mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener);
         setConnectionCapabilities(applyHostCapabilities(getConnectionCapabilities(),
                 mConferenceHost.getConnectionCapabilities(),
@@ -744,6 +729,10 @@
             // Determine if the conference event package represents a single party conference.
             // A single party conference is one where there is no other participant other than the
             // conference host and one other participant.
+            // We purposely exclude participants which have a disconnected state in the conference
+            // event package; some carriers are known to keep a disconnected participant around in
+            // subsequent CEP updates with a state of disconnected, even though its no longer part
+            // of the conference.
             // Note: We consider 0 to still be a single party conference since some carriers will
             // send a conference event package with JUST the host in it when the conference is
             // disconnected.  We don't want to change back to conference mode prior to disconnection
@@ -751,7 +740,8 @@
             boolean isSinglePartyConference = participants.stream()
                     .filter(p -> {
                         Pair<Uri, Uri> pIdent = new Pair<>(p.getHandle(), p.getEndpoint());
-                        return !Objects.equals(mHostParticipantIdentity, pIdent);
+                        return !Objects.equals(mHostParticipantIdentity, pIdent)
+                                && p.getState() != Connection.STATE_DISCONNECTED;
                     })
                     .count() <= 1;
 
@@ -766,7 +756,14 @@
                     Pair<Uri, Uri> userEntity = new Pair<>(participant.getHandle(),
                             participant.getEndpoint());
 
-                    participantUserEntities.add(userEntity);
+                    // We will exclude disconnected participants from the hash set of tracked
+                    // participants.  Some carriers are known to leave disconnected participants in
+                    // the conference event package data which would cause them to be present in the
+                    // conference even though they're disconnected.  Removing them from the hash set
+                    // here means we'll clean them up below.
+                    if (participant.getState() != Connection.STATE_DISCONNECTED) {
+                        participantUserEntities.add(userEntity);
+                    }
                     if (!mConferenceParticipantConnections.containsKey(userEntity)) {
                         // Some carriers will also include the conference host in the CEP.  We will
                         // filter that out here.
@@ -786,6 +783,13 @@
                                 "handleConferenceParticipantsUpdate: updateState, participant = %s",
                                 participant);
                         connection.updateState(participant.getState());
+                        if (participant.getState() == Connection.STATE_DISCONNECTED) {
+                            /**
+                             * Per {@link ConferenceParticipantConnection#updateState(int)}, we will
+                             * destroy the connection when its disconnected.
+                             */
+                            handleConnectionDestruction(connection);
+                        }
                         connection.setVideoState(parent.getVideoState());
                     }
                 }
@@ -799,6 +803,13 @@
                                         newParticipant.getHandle(),
                                         newParticipant.getEndpoint()));
                         connection.updateState(newParticipant.getState());
+                        /**
+                         * Per {@link ConferenceParticipantConnection#updateState(int)}, we will
+                         * destroy the connection when its disconnected.
+                         */
+                        if (newParticipant.getState() == Connection.STATE_DISCONNECTED) {
+                            handleConnectionDestruction(connection);
+                        }
                         connection.setVideoState(parent.getVideoState());
                     }
                 }
@@ -814,9 +825,8 @@
                     if (!participantUserEntities.contains(entry.getKey())) {
                         ConferenceParticipantConnection participant = entry.getValue();
                         participant.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
-                        participant.removeConnectionListener(mParticipantListener);
-                        mTelephonyConnectionService.removeConnection(participant);
-                        removeConnection(participant);
+                        removeTelephonyConnection(participant);
+                        participant.destroy();
                         entryIterator.remove();
                         oldParticipantsRemoved = true;
                     }
@@ -933,9 +943,8 @@
             // again anyways.
             entry.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED,
                     DisconnectCause.REASON_EMULATING_SINGLE_CALL));
-            entry.removeConnectionListener(mParticipantListener);
-            mTelephonyConnectionService.removeConnection(entry);
-            removeConnection(entry);
+            removeTelephonyConnection(entry);
+            entry.destroy();
             valueIterator.remove();
         }
 
@@ -943,7 +952,8 @@
         setConferenceState(false);
 
         // Remove manage conference capability.
-        mCouldManageConference = can(Connection.CAPABILITY_MANAGE_CONFERENCE);
+        mCouldManageConference =
+                (getConnectionCapabilities() & Connection.CAPABILITY_MANAGE_CONFERENCE) != 0;
         int currentCapabilities = getConnectionCapabilities();
         currentCapabilities &= ~Connection.CAPABILITY_MANAGE_CONFERENCE;
         setConnectionCapabilities(currentCapabilities);
@@ -967,7 +977,6 @@
         ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
                 parent.getOriginalConnection(), participant,
                 !isConferenceHost() /* isRemotelyHosted */);
-        connection.addConnectionListener(mParticipantListener);
         if (participant.getConnectTime() == 0) {
             connection.setConnectTimeMillis(parent.getConnectTimeMillis());
             connection.setConnectionStartElapsedRealTime(parent.getConnectElapsedTimeMillis());
@@ -989,7 +998,7 @@
 
         mTelephonyConnectionService.addExistingConnection(mConferenceHostPhoneAccountHandle,
                 connection, this);
-        addConnection(connection);
+        addTelephonyConnection(connection);
     }
 
     /**
@@ -1000,12 +1009,11 @@
     private void removeConferenceParticipant(ConferenceParticipantConnection participant) {
         Log.i(this, "removeConferenceParticipant: %s", participant);
 
-        participant.removeConnectionListener(mParticipantListener);
         synchronized(mUpdateSyncRoot) {
             mConferenceParticipantConnections.remove(new Pair<>(participant.getUserEntity(),
                     participant.getEndpoint()));
         }
-        mTelephonyConnectionService.removeConnection(participant);
+        participant.destroy();
     }
 
     /**
@@ -1018,14 +1026,13 @@
             for (ConferenceParticipantConnection connection :
                     mConferenceParticipantConnections.values()) {
 
-                connection.removeConnectionListener(mParticipantListener);
                 // Mark disconnect cause as cancelled to ensure that the call is not logged in the
                 // call log.
                 connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
-                mTelephonyConnectionService.removeConnection(connection);
                 connection.destroy();
             }
             mConferenceParticipantConnections.clear();
+            updateManageConference();
         }
     }
 
@@ -1082,7 +1089,7 @@
             boolean isHost = PhoneNumberUtils.compare(hostNumber, number);
 
             Log.v(this, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
-                    Log.pii(hostNumber), Log.pii(number));
+                    Rlog.pii(LOG_TAG, hostNumber), Rlog.pii(LOG_TAG, number));
 
             if (isHost) {
                 return true;
@@ -1136,7 +1143,7 @@
                         mConferenceHost.isOutgoingCall());
                 // This is a newly created conference connection as a result of SRVCC
                 c.setConferenceSupported(true);
-                c.setConnectionProperties(
+                c.setTelephonyConnectionProperties(
                         c.getConnectionProperties() | Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
                 c.updateState();
                 // Copy the connect time from the conferenceHost
@@ -1145,12 +1152,11 @@
                 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c);
                 mTelephonyConnectionService.addConnectionToConferenceController(c);
             } // CDMA case not applicable for SRVCC
-            mConferenceHost.removeConnectionListener(mConferenceHostListener);
             mConferenceHost.removeTelephonyConnectionListener(mTelephonyConnectionListener);
             mConferenceHost = null;
             setDisconnected(new DisconnectCause(DisconnectCause.OTHER));
             disconnectConferenceParticipants();
-            destroy();
+            destroyTelephonyConference();
         }
 
         updateStatusHints();
@@ -1189,7 +1195,7 @@
                 }
                 setDisconnected(disconnectCause);
                 disconnectConferenceParticipants();
-                destroy();
+                destroyTelephonyConference();
                 break;
             case Connection.STATE_ACTIVE:
                 setActive();
@@ -1206,8 +1212,8 @@
      */
     private boolean isVideoCapable() {
         int capabilities = mConferenceHost.getConnectionCapabilities();
-        return can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
-                && can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
+        return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0
+                && (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
     }
 
     private void updateStatusHints() {
@@ -1320,4 +1326,15 @@
     public boolean isEmulatingSinglePartyCall() {
         return mIsEmulatingSinglePartyCall;
     }
+
+    /**
+     * Handles destruction of a {@link ConferenceParticipantConnection}.
+     * We remove the participant from the list of tracked participants in the conference and
+     * update whether the conference can be managed.
+     * @param participant the conference participant.
+     */
+    private void handleConnectionDestruction(ConferenceParticipantConnection participant) {
+        removeConferenceParticipant(participant);
+        updateManageConference();
+    }
 }
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 9902700..1e10055 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -22,6 +22,7 @@
 import android.telecom.ConnectionService;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.Rlog;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
@@ -38,11 +39,13 @@
  * Manages conferences for IMS connections.
  */
 public class ImsConferenceController {
+    private static final String LOG_TAG = "ImsConferenceController";
 
     /**
      * Conference listener; used to receive notification when a conference has been disconnected.
      */
-    private final Conference.Listener mConferenceListener = new Conference.Listener() {
+    private final TelephonyConferenceBase.TelephonyConferenceListener mConferenceListener =
+            new TelephonyConferenceBase.TelephonyConferenceListener() {
         @Override
         public void onDestroyed(Conference conference) {
             if (Log.VERBOSE) {
@@ -53,28 +56,8 @@
         }
     };
 
-    /**
-     * Ims conference controller connection listener.  Used to respond to changes in state of the
-     * Telephony connections the controller is aware of.
-     */
-    private final Connection.Listener mConnectionListener = new Connection.Listener() {
-        @Override
-        public void onStateChanged(Connection c, int state) {
-            Log.v(this, "onStateChanged: %s", Log.pii(c.getAddress()));
-            recalculate();
-        }
-
-        @Override
-        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
-            Log.v(this, "onDisconnected: %s", Log.pii(c.getAddress()));
-            recalculate();
-        }
-
-        @Override
-        public void onDestroyed(Connection connection) {
-            remove(connection);
-        }
-
+    private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+            new TelephonyConnection.TelephonyConnectionListener() {
         @Override
         public void onConferenceStarted() {
             Log.v(this, "onConferenceStarted");
@@ -86,6 +69,23 @@
             Log.v(this, "onConferenceSupportedChanged");
             recalculate();
         }
+
+        @Override
+        public void onStateChanged(Connection c, int state) {
+            Log.v(this, "onStateChanged: %s", Rlog.pii(LOG_TAG, c.getAddress()));
+            recalculate();
+        }
+
+        @Override
+        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
+            Log.v(this, "onDisconnected: %s", Rlog.pii(LOG_TAG, c.getAddress()));
+            recalculate();
+        }
+
+        @Override
+        public void onDestroyed(Connection connection) {
+            remove(connection);
+        }
     };
 
     /**
@@ -148,8 +148,9 @@
         }
 
         mTelephonyConnections.add(connection);
-        connection.addConnectionListener(mConnectionListener);
+        connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
         recalculateConference();
+        recalculateConferenceable();
     }
 
     /**
@@ -175,7 +176,10 @@
             Log.v(this, "remove connection: %s", connection);
         }
 
-        connection.removeConnectionListener(mConnectionListener);
+        if (connection instanceof TelephonyConnection) {
+            TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+            telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+        }
         mTelephonyConnections.remove(connection);
         recalculateConferenceable();
     }
@@ -379,7 +383,7 @@
         ImsConference conference = new ImsConference(mTelecomAccountRegistry, mConnectionService,
                 conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy);
         conference.setState(conferenceHostConnection.getState());
-        conference.addListener(mConferenceListener);
+        conference.addTelephonyConferenceListener(mConferenceListener);
         conference.updateConferenceParticipantsAfterCreation();
         mConnectionService.addConference(conference);
         conferenceHostConnection.setTelecomCallId(conference.getTelecomCallId());
@@ -387,12 +391,11 @@
         // Cleanup TelephonyConnection which backed the original connection and remove from telecom.
         // Use the "Other" disconnect cause to ensure the call is logged to the call log but the
         // disconnect tone is not played.
-        connection.removeConnectionListener(mConnectionListener);
-        connection.clearOriginalConnection();
-        connection.setDisconnected(new DisconnectCause(DisconnectCause.OTHER,
+        connection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+        connection.setTelephonyConnectionDisconnected(new DisconnectCause(DisconnectCause.OTHER,
                 android.telephony.DisconnectCause.toString(
                         android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY)));
-        connection.destroy();
+        connection.close();
         mImsConferences.add(conference);
         // If one of the participants failed to join the conference, recalculate will set the
         // conferenceable connections for the conference to show merge calls option.
diff --git a/src/com/android/services/telephony/Log.java b/src/com/android/services/telephony/Log.java
index 9941a59..57bf849 100644
--- a/src/com/android/services/telephony/Log.java
+++ b/src/com/android/services/telephony/Log.java
@@ -16,8 +16,6 @@
 
 package com.android.services.telephony;
 
-import android.content.Context;
-
 /**
  * Manages logging for the entire module.
  */
@@ -39,72 +37,80 @@
         return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
     }
 
-    public static void initLogging(Context context) {
-        // Register Telephony with the Telecom Logger.
-        android.telecom.Log.setTag(TAG);
-        android.telecom.Log.setSessionContext(context);
-    }
-
     // Relay log messages to Telecom
     // TODO: Redo namespace of Telephony to use these methods directly.
 
     public static void d(String prefix, String format, Object... args) {
-        android.telecom.Log.d(prefix, format, args);
+        android.util.Log.d(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args));
     }
 
     public static void d(Object objectPrefix, String format, Object... args) {
-        android.telecom.Log.d(objectPrefix, format, args);
+        android.util.Log.d(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
     }
 
     public static void i(String prefix, String format, Object... args) {
-        android.telecom.Log.i(prefix, format, args);
+        android.util.Log.i(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args));
     }
 
     public static void i(Object objectPrefix, String format, Object... args) {
-        android.telecom.Log.i(objectPrefix, format, args);
+        android.util.Log.i(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
     }
 
     public static void v(String prefix, String format, Object... args) {
-        android.telecom.Log.v(prefix, format, args);
+        android.util.Log.v(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args));
     }
 
     public static void v(Object objectPrefix, String format, Object... args) {
-        android.telecom.Log.v(objectPrefix, format, args);
+        android.util.Log.v(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
     }
 
     public static void w(String prefix, String format, Object... args) {
-        android.telecom.Log.w(prefix, format, args);
+        android.util.Log.w(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args));
     }
 
     public static void w(Object objectPrefix, String format, Object... args) {
-        android.telecom.Log.w(objectPrefix, format, args);
+        android.util.Log.w(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
     }
 
     public static void e(String prefix, Throwable tr, String format, Object... args) {
-        android.telecom.Log.e(prefix, tr, format, args);
+        android.util.Log.e(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args), tr);
     }
 
     public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
-        android.telecom.Log.e(objectPrefix, tr, format, args);
+        android.util.Log.e(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args), tr);
     }
 
     public static void wtf(String prefix, Throwable tr, String format, Object... args) {
-        android.telecom.Log.wtf(prefix, tr, format, args);
+        android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args), tr);
     }
 
     public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
-        android.telecom.Log.wtf(objectPrefix, tr, format, args);
+        android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args), tr);
     }
 
     public static void wtf(String prefix, String format, Object... args) {
-        android.telecom.Log.wtf(prefix, format, args);
+        android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+                String.format(prefix + ": " + format, args));
     }
 
     public static void wtf(Object objectPrefix, String format, Object... args) {
-        android.telecom.Log.wtf(objectPrefix, format, args);
+        android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+                String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
     }
 
-    public static String pii(Object pii) {
-        return android.telecom.Log.pii(pii);
+    private static String getPrefixFromObject(Object obj) {
+        return obj == null ? "<null>" : obj.getClass().getSimpleName();
     }
 }
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 223616f..2dfeaed 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -25,6 +25,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.Rlog;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.Call;
@@ -40,8 +41,6 @@
 import com.android.phone.NumberVerificationManager;
 import com.android.phone.PhoneUtils;
 
-import com.google.common.base.Preconditions;
-
 import java.util.Objects;
 
 /**
@@ -49,11 +48,21 @@
  * occurence. One instance of these exists for each of the telephony-based call services.
  */
 final class PstnIncomingCallNotifier {
+    private static final String LOG_TAG = "PstnIncomingCallNotifier";
+
     /** New ringing connection event code. */
     private static final int EVENT_NEW_RINGING_CONNECTION = 100;
     private static final int EVENT_CDMA_CALL_WAITING = 101;
     private static final int EVENT_UNKNOWN_CONNECTION = 102;
 
+    /**
+     * The max amount of time to wait before hanging up a call that was for number verification.
+     *
+     * The delay is so that the remote end has time to hang up the call after receiving the
+     * verification signal so that the call doesn't go to voicemail.
+     */
+    private static final int MAX_NUMBER_VERIFICATION_HANGUP_DELAY_MILLIS = 10000;
+
     /** The phone object to listen to. */
     private final Phone mPhone;
 
@@ -85,7 +94,9 @@
      * @param phone The phone object for listening to incoming calls.
      */
     PstnIncomingCallNotifier(Phone phone) {
-        Preconditions.checkNotNull(phone);
+        if (phone == null) {
+            throw new NullPointerException();
+        }
 
         mPhone = phone;
 
@@ -131,12 +142,16 @@
             if (connection.getAddress() != null) {
                 if (NumberVerificationManager.getInstance()
                         .checkIncomingCall(connection.getAddress())) {
-                    // Disconnect the call if it matches
-                    try {
-                        connection.hangup();
-                    } catch (CallStateException e) {
-                        Log.e(this, e, "Error hanging up potential number verification call");
-                    }
+                    // Disconnect the call if it matches, after a delay
+                    mHandler.postDelayed(() -> {
+                        try {
+                            connection.hangup();
+                        } catch (CallStateException e) {
+                            Log.i(this, "Remote end hung up call verification call");
+                        }
+                        // TODO: use an app-supplied delay (needs new API), not to exceed the
+                        // existing max.
+                    }, MAX_NUMBER_VERIFICATION_HANGUP_DELAY_MILLIS);
                     return;
                 }
             }
@@ -169,7 +184,7 @@
                     // Presentation of the number is allowed, so we ensure the number matches the
                     // one in the call waiting information.
                     Log.i(this, "handleCdmaCallWaiting: inform telecom of waiting call; "
-                            + "number = %s", Log.pii(number));
+                            + "number = %s", Rlog.pii(LOG_TAG, number));
                     sendIncomingCallIntent(connection);
                 } else {
                     Log.w(this, "handleCdmaCallWaiting: presentation or number do not match, not"
@@ -236,7 +251,8 @@
                     // connection already disconnected. Do nothing
                 }
             } else {
-                TelecomManager.from(mPhone.getContext()).addNewUnknownCall(handle, extras);
+                TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
+                tm.addNewUnknownCall(handle, extras);
             }
         } else {
             Log.i(this, "swapped an old connection, new one is: %s", connection);
@@ -271,7 +287,8 @@
                 // connection already disconnected. Do nothing
             }
         } else {
-            TelecomManager.from(mPhone.getContext()).addNewIncomingCall(handle, extras);
+            TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
+            tm.addNewIncomingCall(handle, extras);
         }
     }
 
diff --git a/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java b/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
index b9cbf6e..4038dd1 100644
--- a/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
+++ b/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
@@ -21,7 +21,6 @@
 import android.os.Message;
 
 import com.android.internal.telephony.Phone;
-import com.android.internal.util.Preconditions;
 
 /**
  * Listens to phone's capabilities changed event and notifies Telecomm. One instance of these exists
@@ -55,7 +54,9 @@
 
     /*package*/
     PstnPhoneCapabilitiesNotifier(Phone phone, Listener listener) {
-        Preconditions.checkNotNull(phone);
+        if (phone == null) {
+            throw new NullPointerException();
+        }
 
         mPhone = phone;
         mListener = listener;
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 85f94ab..981dc96 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -49,13 +49,19 @@
     }
 
     private void setupListeners() {
-        if (mListeners != null) {
-            return;
+        if (mListeners == null) {
+            mListeners = new ArrayList<>(2);
         }
-        mListeners = new ArrayList<>(2);
-        for (int i = 0; i < TelephonyManager.getDefault().getMaxPhoneCount(); i++) {
+        int activeModems = TelephonyManager.from(mContext).getActiveModemCount();
+        // Add new listeners if active modem count increased.
+        while (mListeners.size() < activeModems) {
             mListeners.add(new RadioOnStateListener());
         }
+        // Clean up listeners if active modem count decreased.
+        while (mListeners.size() > activeModems) {
+            mListeners.get(mListeners.size() - 1).cleanup();
+            mListeners.remove(mListeners.size() - 1);
+        }
     }
     /**
      * Starts the "turn on radio" sequence. This is the (single) external API of the
@@ -76,7 +82,7 @@
         mCallback = callback;
         mInProgressListeners.clear();
         mIsRadioOnCallingEnabled = false;
-        for (int i = 0; i < TelephonyManager.getDefault().getMaxPhoneCount(); i++) {
+        for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
             Phone phone = PhoneFactory.getPhone(i);
             if (phone == null) {
                 continue;
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 729f6a9..52bd9cf 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -231,7 +231,7 @@
      * Note we don't call this method simply after a successful call to placeCall(), since it's
      * still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
      */
-    private void cleanup() {
+    public void cleanup() {
         Log.d(this, "cleanup()");
 
         // This will send a failure call back if callback has yet to be invoked.  If the callback
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 6bbcdc4..5a1e6a6 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -35,13 +35,13 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
 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;
@@ -72,6 +72,7 @@
  */
 public class TelecomAccountRegistry {
     private static final boolean DBG = false; /* STOP SHIP if true */
+    private static final String LOG_TAG = "TelecomAccountRegistry";
 
     // This icon is the one that is used when the Slot ID that we have for a particular SIM
     // is not supported, i.e. SubscriptionManager.INVALID_SLOT_ID or the 5th SIM in a phone.
@@ -286,7 +287,7 @@
                 mIsRttCapable = false;
             }
 
-            mIsVideoCapable = mPhone.isVideoEnabled() && !mIsRttCapable;
+            mIsVideoCapable = mPhone.isVideoEnabled();
             boolean isVideoEnabledByPlatform = ImsManager.getInstance(mPhone.getContext(),
                     mPhone.getPhoneId()).isVtEnabledByPlatform();
 
@@ -390,7 +391,7 @@
             }
             if(isMergedSim) {
                 groupId = GROUP_PREFIX + line1Number;
-                Log.i(this, "Adding Merged Account with group: " + Log.pii(groupId));
+                Log.i(this, "Adding Merged Account with group: " + Rlog.pii(LOG_TAG, groupId));
             }
 
             PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
@@ -803,9 +804,8 @@
                 Log.i(this, "User changed, re-registering phone accounts.");
 
                 int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                UserHandle currentUserHandle = new UserHandle(userHandleId);
-                mIsPrimaryUser = UserManager.get(mContext).getPrimaryUser().getUserHandle()
-                        .equals(currentUserHandle);
+                UserHandle currentUserHandle = UserHandle.of(userHandleId);
+                mIsPrimaryUser = currentUserHandle.isSystem();
 
                 // Any time the user changes, re-register the accounts.
                 tearDownAccounts();
@@ -820,6 +820,15 @@
         }
     };
 
+    private BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(this, "Locale change; re-registering phone accounts.");
+            tearDownAccounts();
+            setupAccounts();
+        }
+    };
+
     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
@@ -865,7 +874,7 @@
 
     TelecomAccountRegistry(Context context) {
         mContext = context;
-        mTelecomManager = TelecomManager.from(context);
+        mTelecomManager = context.getSystemService(TelecomManager.class);
         mTelephonyManager = TelephonyManager.from(context);
         mSubscriptionManager = SubscriptionManager.from(context);
     }
@@ -1083,6 +1092,11 @@
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         mContext.registerReceiver(mReceiver, filter);
 
+        //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));
+
         // Listen to the RTT system setting so that we update it when the user flips it.
         ContentObserver rttUiSettingObserver = new ContentObserver(
                 new Handler(Looper.getMainLooper())) {
@@ -1132,7 +1146,7 @@
                 R.bool.config_emergency_account_emergency_calls_only);
         List<PhoneAccountHandle> accountHandles = emergencyCallsOnlyEmergencyAccount
                 ? mTelecomManager.getAllPhoneAccountHandles()
-                : mTelecomManager.getCallCapablePhoneAccounts(true /* includeDisabled */);
+                : mTelecomManager.getCallCapablePhoneAccounts();
 
         for (PhoneAccountHandle handle : accountHandles) {
             if (telephonyComponentName.equals(handle.getComponentName()) &&
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index c66d6f2..afbe89d 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -16,7 +16,6 @@
 
 package com.android.services.telephony;
 
-import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.PhoneAccountHandle;
 
@@ -30,7 +29,7 @@
  * TelephonyConnection-based conference call for GSM conferences and IMS conferences (which may
  * be either GSM-based or CDMA-based).
  */
-public class TelephonyConference extends Conference implements Holdable {
+public class TelephonyConference extends TelephonyConferenceBase implements Holdable {
 
     private boolean mIsHoldable;
 
diff --git a/src/com/android/services/telephony/TelephonyConferenceBase.java b/src/com/android/services/telephony/TelephonyConferenceBase.java
new file mode 100644
index 0000000..5e7ecf6
--- /dev/null
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -0,0 +1,191 @@
+/*
+ * 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.services.telephony;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.telecom.Conference;
+import android.telecom.Connection;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.ServiceState;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Base class for the various Telephony {@link Conference} implementations ({@link CdmaConference},
+ * {@link TelephonyConference}, and {@link ImsConference}).  Adds some common listener code which
+ * all of these conferences use.
+ */
+public class TelephonyConferenceBase extends Conference {
+    private static final String TAG = "TelephonyConferenceBase";
+
+    /**
+     * Listener for conference events.
+     */
+    public abstract static class TelephonyConferenceListener {
+        /**
+         * Listener called when a connection is added or removed from a conference.
+         * @param connection The connection.
+         */
+        public void onConferenceMembershipChanged(Connection connection) {}
+
+        /**
+         * Listener called when a conference is destroyed.
+         * @param conference The conference.
+         */
+        public void onDestroyed(Conference conference) {}
+    }
+
+    private final Set<TelephonyConferenceListener> mListeners = Collections.newSetFromMap(
+            new ConcurrentHashMap<>(8, 0.9f, 1));
+
+    /**
+     * Adds a listener to this conference.
+     * @param listener The listener.
+     */
+    public void addTelephonyConferenceListener(@NonNull TelephonyConferenceListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from this conference.
+     * @param listener The listener.
+     */
+    public void removeTelephonyConferenceListener(@NonNull TelephonyConferenceListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
+     *
+     * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
+     */
+    public TelephonyConferenceBase(PhoneAccountHandle phoneAccount) {
+        super(phoneAccount);
+    }
+
+    /**
+     * Adds a connection to this {@link Conference}.
+     * <p>
+     * Should be used in place of {@link Conference#addConnection(Connection)} to ensure
+     * {@link TelephonyConferenceListener}s are informed of the change.
+     *
+     * @param connection The connection.
+     */
+    public void addTelephonyConnection(@NonNull Connection connection) {
+        addConnection(connection);
+        notifyConferenceMembershipChanged(connection);
+    }
+
+    /**
+     * Removes a {@link Connection} from this {@link Conference}.
+     * <p>
+     * Should be used instead of {@link Conference#removeConnection(Connection)} to ensure
+     * {@link TelephonyConferenceListener}s are notified of the change.
+     *
+     * @param connection The connection.
+     */
+    public void removeTelephonyConnection(@NonNull Connection connection) {
+        removeConnection(connection);
+        notifyConferenceMembershipChanged(connection);
+    }
+
+    /**
+     * Destroys the current {@link Conference} and notifies {@link TelephonyConferenceListener}s of
+     * the change to conference membership.
+     * <p>
+     * Should be used instead of {@link Conference#destroy()} to ensure telephony listeners are
+     * notified.
+     */
+    public void destroyTelephonyConference() {
+        // Conference#removeConnection modifies the list of participants, so we need to use an
+        // iterator here to ensure all participants are removed.
+        // Technically Conference#destroy does this, but we want to notify listeners of the state
+        // change so we'll do it here first.
+        Iterator<Connection> connectionIterator = getConnections().iterator();
+        while (connectionIterator.hasNext()) {
+            removeTelephonyConnection(connectionIterator.next());
+        }
+        destroy();
+        notifyDestroyed();
+    }
+
+    /**
+     * Updates RIL voice radio technology used for current conference after its creation.
+     */
+    public void updateCallRadioTechAfterCreation() {
+        final Connection primaryConnection = getPrimaryConnection();
+        if (primaryConnection != null && primaryConnection instanceof TelephonyConnection) {
+            TelephonyConnection telephonyConnection = (TelephonyConnection) primaryConnection;
+            Bundle newExtras = new Bundle();
+            newExtras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                    ServiceState.rilRadioTechnologyToNetworkType(
+                            telephonyConnection.getCallRadioTech()));
+            putExtras(newExtras);
+        } else {
+            Log.w(TAG, "No primary connection found while updateCallRadioTechAfterCreation");
+        }
+    }
+
+    /**
+     * Removes the specified capability from the set of capabilities of this {@code Conference}.
+     *
+     * @param capability The capability to remove from the set.
+     */
+    public void removeCapability(int capability) {
+        int newCapabilities = getConnectionCapabilities();
+        newCapabilities &= ~capability;
+
+        setConnectionCapabilities(newCapabilities);
+    }
+
+    /**
+     * Adds the specified capability to the set of capabilities of this {@code Conference}.
+     *
+     * @param capability The capability to add to the set.
+     */
+    public void addCapability(int capability) {
+        int newCapabilities = getConnectionCapabilities();
+        newCapabilities |= capability;
+
+        setConnectionCapabilities(newCapabilities);
+    }
+
+    /**
+     * Notifies {@link TelephonyConferenceListener}s of a connection being added or removed from
+     * the conference.
+     * @param connection The conference.
+     */
+    private void notifyConferenceMembershipChanged(@NonNull Connection connection) {
+        for (TelephonyConferenceListener listener : mListeners) {
+            listener.onConferenceMembershipChanged(connection);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConferenceListener}s of a conference being destroyed
+     */
+    private void notifyDestroyed() {
+        for (TelephonyConferenceListener listener : mListeners) {
+            listener.onDestroyed(this);
+        }
+    }
+}
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index e9eef46..95281b3 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -41,25 +41,26 @@
 final class TelephonyConferenceController {
     private static final int TELEPHONY_CONFERENCE_MAX_SIZE = 5;
 
-    private final Connection.Listener mConnectionListener = new Connection.Listener() {
-        @Override
-        public void onStateChanged(Connection c, int state) {
-            Log.v(this, "onStateChange triggered in Conf Controller : connection = "+ c
-                 + " state = " + state);
-            recalculate();
-        }
+    private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+            new TelephonyConnection.TelephonyConnectionListener() {
+                @Override
+                public void onStateChanged(Connection c, int state) {
+                    Log.v(this, "onStateChange triggered in Conf Controller : connection = " + c
+                            + " state = " + state);
+                    recalculate();
+                }
 
-        /** ${inheritDoc} */
-        @Override
-        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
-            recalculate();
-        }
+                @Override
+                public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
+                    recalculate();
+                }
 
-        @Override
-        public void onDestroyed(Connection connection) {
-            remove(connection);
-        }
-    };
+                @Override
+                public void onDestroyed(Connection connection) {
+                    // Only TelephonyConnections are added.
+                    remove((TelephonyConnection) connection);
+                }
+            };
 
     /** The known connections. */
     private final List<TelephonyConnection> mTelephonyConnections = new ArrayList<>();
@@ -85,18 +86,18 @@
             return;
         }
         mTelephonyConnections.add(connection);
-        connection.addConnectionListener(mConnectionListener);
+        connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
         recalculate();
     }
 
-    void remove(Connection connection) {
+    void remove(TelephonyConnection connection) {
         if (!mTelephonyConnections.contains(connection)) {
             // Debug only since TelephonyConnectionService tries to clean up the connections tracked
             // when the original connection changes.  It does this proactively.
             Log.d(this, "remove - connection not tracked; connection=%s", connection);
             return;
         }
-        connection.removeConnectionListener(mConnectionListener);
+        connection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
         mTelephonyConnections.remove(connection);
         recalculate();
     }
@@ -219,7 +220,7 @@
             // No more connections are conferenced, destroy any existing conference.
             if (mTelephonyConference != null) {
                 Log.d(this, "with a conference to destroy!");
-                mTelephonyConference.destroy();
+                mTelephonyConference.destroyTelephonyConference();
                 mTelephonyConference = null;
             }
         } else {
@@ -229,7 +230,7 @@
                 for (Connection connection : existingConnections) {
                     if (connection instanceof TelephonyConnection &&
                             !conferencedConnections.contains(connection)) {
-                        mTelephonyConference.removeConnection(connection);
+                        mTelephonyConference.removeTelephonyConnection(connection);
                     }
                 }
                 if (allConnInService) {
@@ -237,7 +238,7 @@
                     // Add any new ones
                     for (Connection connection : conferencedConnections) {
                         if (!existingConnections.contains(connection)) {
-                            mTelephonyConference.addConnection(connection);
+                            mTelephonyConference.addTelephonyConnection(connection);
                         }
                     }
                 } else {
@@ -262,7 +263,7 @@
                     for (Connection connection : conferencedConnections) {
                         Log.d(this, "Adding a connection to a conference call: %s %s",
                                 mTelephonyConference, connection);
-                        mTelephonyConference.addConnection(connection);
+                        mTelephonyConference.addTelephonyConnection(connection);
                     }
                     mTelephonyConference.updateCallRadioTechAfterCreation();
                     mConnectionService.addConference(mTelephonyConference);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 84fa40c..fb865b4 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,7 +16,10 @@
 
 package com.android.services.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncResult;
@@ -26,23 +29,30 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.telecom.CallAudioState;
-import android.telecom.ConferenceParticipant;
+import android.telecom.Conference;
 import android.telecom.Connection;
+import android.telecom.ConnectionService;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.StatusHints;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.Annotation.RilRadioTechnology;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
 import android.text.TextUtils;
 import android.util.Pair;
 
 import com.android.ims.ImsCall;
+import com.android.ims.internal.ConferenceParticipant;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallFailCause;
@@ -74,6 +84,8 @@
  * Base class for CDMA and GSM connections.
  */
 abstract class TelephonyConnection extends Connection implements Holdable {
+    private static final String LOG_TAG = "TelephonyConnection";
+
     private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
     private static final int MSG_RINGBACK_TONE = 2;
     private static final int MSG_HANDOVER_STATE_CHANGED = 3;
@@ -100,6 +112,7 @@
     private static final int MSG_HANGUP = 17;
     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 final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
@@ -110,15 +123,25 @@
                     updateState();
                     break;
                 case MSG_HANDOVER_STATE_CHANGED:
-                    Log.v(TelephonyConnection.this, "MSG_HANDOVER_STATE_CHANGED");
+                case MSG_REDIAL_CONNECTION_CHANGED:
+                    String what = (msg.what == MSG_HANDOVER_STATE_CHANGED)
+                            ? "MSG_HANDOVER_STATE_CHANGED" : "MSG_REDIAL_CONNECTION_CHANGED";
+                    Log.v(TelephonyConnection.this, what);
                     AsyncResult ar = (AsyncResult) msg.obj;
                     com.android.internal.telephony.Connection connection =
                          (com.android.internal.telephony.Connection) ar.result;
+                    if (connection == null) {
+                        setDisconnected(DisconnectCauseUtil
+                                .toTelecomDisconnectCause(DisconnectCause.OUT_OF_NETWORK,
+                                        "handover failure, no connection"));
+                        close();
+                        break;
+                    }
                     if (mOriginalConnection != null) {
                         if (connection != null &&
                             ((connection.getAddress() != null &&
                             mOriginalConnection.getAddress() != null &&
-                            mOriginalConnection.getAddress().contains(connection.getAddress())) ||
+                            mOriginalConnection.getAddress().equals(connection.getAddress())) ||
                             connection.getState() == mOriginalConnection.getStateBeforeHandover())) {
                             Log.d(TelephonyConnection.this,
                                     "SettingOriginalConnection " + mOriginalConnection.toString()
@@ -128,7 +151,7 @@
                         }
                     } else {
                         Log.w(TelephonyConnection.this,
-                                "MSG_HANDOVER_STATE_CHANGED: mOriginalConnection==null - invalid state (not cleaned up)");
+                                what + ": mOriginalConnection==null - invalid state (not cleaned up)");
                     }
                     break;
                 case MSG_RINGBACK_TONE:
@@ -173,19 +196,20 @@
 
                 case MSG_SET_VIDEO_STATE:
                     int videoState = (int) msg.obj;
-                    setVideoState(videoState);
+                    setTelephonyVideoState(videoState);
 
                     // A change to the video state of the call can influence whether or not it
                     // can be part of a conference, whether another call can be added, and
                     // whether the call should have the HD audio property set.
                     refreshConferenceSupported();
                     refreshDisableAddCall();
+                    refreshHoldSupported();
                     updateConnectionProperties();
                     break;
 
                 case MSG_SET_VIDEO_PROVIDER:
                     VideoProvider videoProvider = (VideoProvider) msg.obj;
-                    setVideoProvider(videoProvider);
+                    setTelephonyVideoProvider(videoProvider);
                     break;
 
                 case MSG_SET_AUDIO_QUALITY:
@@ -225,9 +249,9 @@
                         // If starting the hold tone, send a connection event to Telecom which will
                         // cause it to play the on hold tone.
                         if (playTone) {
-                            sendConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
+                            sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
                         } else {
-                            sendConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
+                            sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
                         }
                     }
                     break;
@@ -272,7 +296,7 @@
                 case MSG_ON_CONNECTION_EVENT:
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        sendConnectionEvent((String) args.arg1, (Bundle) args.arg2);
+                        sendTelephonyConnectionEvent((String) args.arg1, (Bundle) args.arg2);
 
                     } finally {
                         args.recycle();
@@ -291,7 +315,7 @@
                 ssn.code);
         if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
                 && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
-            sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
+            sendTelephonyConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
         }
         sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
     }
@@ -309,7 +333,8 @@
         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
         extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
                 getSuppServiceMessage(type, code));
-        sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
+        sendTelephonyConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION,
+                extras);
     }
 
     /**
@@ -385,12 +410,26 @@
             }
         }
         if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
-            return getPhone().getContext().getText(messageId);
+            return getResourceText(messageId);
         } else {
             return null;
         }
     }
 
+    @VisibleForTesting
+    public CharSequence getResourceText(int id) {
+        Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(),
+                getPhone().getSubId());
+        return resources.getText(id);
+    }
+
+    @VisibleForTesting
+    public String getResourceString(int id) {
+        Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(),
+                getPhone().getSubId());
+        return resources.getString(id);
+    }
+
     /**
      * @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
      */
@@ -406,6 +445,24 @@
     public abstract static class TelephonyConnectionListener {
         public void onOriginalConnectionConfigured(TelephonyConnection c) {}
         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
+        public void onConferenceParticipantsChanged(Connection c,
+                List<ConferenceParticipant> participants) {}
+        public void onConferenceStarted() {}
+        public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+
+        public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {}
+        public void onConnectionEvent(Connection c, String event, Bundle extras) {}
+        public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {}
+        public void onExtrasChanged(Connection c, Bundle extras) {}
+        public void onExtrasRemoved(Connection c, List<String> keys) {}
+        public void onStateChanged(android.telecom.Connection c, int state) {}
+        public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
+        public void onDestroyed(Connection c) {}
+        public void onDisconnected(android.telecom.Connection c,
+                android.telecom.DisconnectCause disconnectCause) {}
+        public void onVideoProviderChanged(android.telecom.Connection c,
+                Connection.VideoProvider videoProvider) {}
+        public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
     }
 
     private final PostDialListener mPostDialListener = new PostDialListener() {
@@ -465,7 +522,7 @@
          * @param vrat the RIL Voice Radio Technology used for current connection.
          */
         @Override
-        public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) {
+        public void onCallRadioTechChanged(@RilRadioTechnology int vrat) {
             mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
         }
 
@@ -539,7 +596,7 @@
 
             // Inform the InCallService of the fact that the call pull failed (it may choose to
             // display a message informing the user of the pull failure).
-            sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
+            sendTelephonyConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
 
             // Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection
             // which originally represented the call.
@@ -554,7 +611,7 @@
          */
         @Override
         public void onHandoverToWifiFailed() {
-            sendConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
+            sendTelephonyConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
         }
 
         /**
@@ -608,6 +665,7 @@
         @Override
         public void onRttTerminated() {
             updateConnectionProperties();
+            refreshConferenceSupported();
             sendRttSessionRemotelyTerminated();
         }
 
@@ -623,6 +681,7 @@
         }
     };
 
+    private TelephonyConnectionService mTelephonyConnectionService;
     protected com.android.internal.telephony.Connection mOriginalConnection;
     private Call.State mConnectionState = Call.State.IDLE;
     private Bundle mOriginalConnectionExtras = new Bundle();
@@ -724,6 +783,11 @@
     private boolean mShowPreciseFailedCause;
 
     /**
+     * Provides a DisconnectCause associated with a hang up request.
+     */
+    private int mHangupDisconnectCause = DisconnectCause.NOT_VALID;
+
+    /**
      * Listeners to our TelephonyConnection specific callbacks
      */
     private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
@@ -1068,6 +1132,7 @@
 
         if (getConnectionCapabilities() != newCapabilities) {
             setConnectionCapabilities(newCapabilities);
+            notifyConnectionCapabilitiesChanged(newCapabilities);
         }
     }
 
@@ -1104,10 +1169,15 @@
                 isNetworkIdentifiedEmergencyCall());
 
         if (getConnectionProperties() != newProperties) {
-            setConnectionProperties(newProperties);
+            setTelephonyConnectionProperties(newProperties);
         }
     }
 
+    public void setTelephonyConnectionProperties(int newProperties) {
+        setConnectionProperties(newProperties);
+        notifyConnectionPropertiesChanged(newProperties);
+    }
+
     protected final void updateAddress() {
         updateConnectionCapabilities();
         updateConnectionProperties();
@@ -1171,6 +1241,8 @@
                 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
         getPhone().registerForHandoverStateChanged(
                 mHandler, MSG_HANDOVER_STATE_CHANGED, null);
+        getPhone().registerForRedialConnectionChanged(
+                mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
         getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
         getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
         getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
@@ -1180,11 +1252,11 @@
         mOriginalConnection.addListener(mOriginalConnectionListener);
 
         // Set video state and capabilities
-        setVideoState(mOriginalConnection.getVideoState());
+        setTelephonyVideoState(mOriginalConnection.getVideoState());
         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
         setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
-        setVideoProvider(mOriginalConnection.getVideoProvider());
+        setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
         setAudioQuality(mOriginalConnection.getAudioQuality());
         setTechnologyTypeExtra();
 
@@ -1219,8 +1291,8 @@
         } else {
             extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
         }
-        putExtras(extrasToPut);
-        removeExtras(extrasToRemove);
+        putTelephonyExtras(extrasToPut);
+        removeTelephonyExtras(extrasToRemove);
 
         // updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
         // should be executed *after* the above setters have run.
@@ -1265,18 +1337,109 @@
      */
     private void setTechnologyTypeExtra() {
         if (getPhone() != null) {
-            putExtra(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
+            Bundle newExtras = getExtras();
+            if (newExtras == null) {
+                newExtras = new Bundle();
+            }
+            newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
+            putTelephonyExtras(newExtras);
         }
     }
 
+    private void refreshHoldSupported() {
+       if (mOriginalConnection == null) {
+           Log.w(this, "refreshHoldSupported org conn is null");
+           return;
+       }
+
+       if (!mOriginalConnection.shouldAllowHoldingVideoCall() && canHoldImsCalls() !=
+               ((getConnectionCapabilities() & (CAPABILITY_HOLD | CAPABILITY_SUPPORT_HOLD)) != 0)) {
+           updateConnectionCapabilities();
+       }
+    }
+
     private void refreshDisableAddCall() {
         if (shouldSetDisableAddCallExtra()) {
-            putExtra(Connection.EXTRA_DISABLE_ADD_CALL, true);
+            Bundle newExtras = getExtras();
+            if (newExtras == null) {
+                newExtras = new Bundle();
+            }
+            newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
+            putTelephonyExtras(newExtras);
         } else {
             removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
         }
     }
 
+    private void refreshCodecType() {
+        Bundle newExtras = getExtras();
+        if (newExtras == null) {
+            newExtras = new Bundle();
+        }
+        int newCodecType;
+        if (isImsConnection()) {
+            newCodecType = transformCodec(getOriginalConnection().getAudioCodec());
+        } else {
+            // For SRVCC, report AUDIO_CODEC_NONE.
+            newCodecType = Connection.AUDIO_CODEC_NONE;
+        }
+        int oldCodecType = newExtras.getInt(Connection.EXTRA_AUDIO_CODEC,
+                Connection.AUDIO_CODEC_NONE);
+        if (newCodecType != oldCodecType) {
+            newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
+            putTelephonyExtras(newExtras);
+        }
+    }
+
+    private int transformCodec(int codec) {
+        switch (codec) {
+            case ImsStreamMediaProfile.AUDIO_QUALITY_NONE:
+                return Connection.AUDIO_CODEC_NONE;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
+                return Connection.AUDIO_CODEC_AMR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB:
+                return Connection.AUDIO_CODEC_AMR_WB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K:
+                return Connection.AUDIO_CODEC_QCELP13K;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC:
+                return Connection.AUDIO_CODEC_EVRC;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B:
+                return Connection.AUDIO_CODEC_EVRC_B;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB:
+                return Connection.AUDIO_CODEC_EVRC_WB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW:
+                return Connection.AUDIO_CODEC_EVRC_NW;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR:
+                return Connection.AUDIO_CODEC_GSM_EFR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR:
+                return Connection.AUDIO_CODEC_GSM_FR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR:
+                return Connection.AUDIO_CODEC_GSM_HR;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G711U:
+                return Connection.AUDIO_CODEC_G711U;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G723:
+                return Connection.AUDIO_CODEC_G723;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G711A:
+                return Connection.AUDIO_CODEC_G711A;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G722:
+                return Connection.AUDIO_CODEC_G722;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB:
+                return Connection.AUDIO_CODEC_G711AB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_G729:
+                return Connection.AUDIO_CODEC_G729;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB:
+                return Connection.AUDIO_CODEC_EVS_NB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB:
+                return Connection.AUDIO_CODEC_EVS_WB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB:
+                return Connection.AUDIO_CODEC_EVS_SWB;
+            case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB:
+                return Connection.AUDIO_CODEC_EVS_FB;
+            default:
+                return Connection.AUDIO_CODEC_NONE;
+        }
+    }
+
     private boolean shouldSetDisableAddCallExtra() {
         if (mOriginalConnection == null) {
             return false;
@@ -1351,11 +1514,14 @@
     private boolean canHoldImsCalls() {
         PersistableBundle b = getCarrierConfig();
         // Return true if the CarrierConfig is unavailable
-        return !doesDeviceRespectHoldCarrierConfig() || b == null ||
-                b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
+        return (!doesDeviceRespectHoldCarrierConfig() || b == null ||
+                b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL)) &&
+                ((mOriginalConnection != null && mOriginalConnection.shouldAllowHoldingVideoCall())
+                || !VideoProfile.isVideo(getVideoState()));
     }
 
-    private PersistableBundle getCarrierConfig() {
+    @VisibleForTesting
+    public PersistableBundle getCarrierConfig() {
         Phone phone = getPhone();
         if (phone == null) {
             return null;
@@ -1409,6 +1575,7 @@
                 getPhone().unregisterForPreciseCallStateChanged(mHandler);
                 getPhone().unregisterForRingbackTone(mHandler);
                 getPhone().unregisterForHandoverStateChanged(mHandler);
+                getPhone().unregisterForRedialConnectionChanged(mHandler);
                 getPhone().unregisterForDisconnect(mHandler);
                 getPhone().unregisterForSuppServiceNotification(mHandler);
                 getPhone().unregisterForOnHoldTone(mHandler);
@@ -1423,6 +1590,7 @@
 
     protected void hangup(int telephonyDisconnectCode) {
         if (mOriginalConnection != null) {
+            mHangupDisconnectCause = telephonyDisconnectCode;
             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
@@ -1452,7 +1620,7 @@
                 // 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.
-                setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                         android.telephony.DisconnectCause.LOCAL,
                         "Local Disconnect before connection established."));
                 close();
@@ -1554,8 +1722,8 @@
                         for (String key : extras.keySet()) {
                             Object value = extras.get(key);
                             if (value instanceof String) {
-                                Log.d(this, "updateExtras Key=" + Log.pii(key) +
-                                             " value=" + Log.pii((String)value));
+                                Log.d(this, "updateExtras Key=" + Rlog.pii(LOG_TAG, key)
+                                        + " value=" + Rlog.pii(LOG_TAG, value));
                             }
                         }
                     }
@@ -1573,12 +1741,12 @@
                     }
 
                     // Ensure extras are propagated to Telecom.
-                    putExtras(mOriginalConnectionExtras);
+                    putTelephonyExtras(mOriginalConnectionExtras);
                 } else {
                     Log.d(this, "Extras update not required");
                 }
             } else {
-                Log.d(this, "updateExtras extras: " + Log.pii(extras));
+                Log.d(this, "updateExtras extras: " + Rlog.pii(LOG_TAG, extras));
             }
         }
     }
@@ -1642,19 +1810,19 @@
                     setActiveInternal();
                     break;
                 case HOLDING:
-                    setOnHold();
+                    setTelephonyConnectionOnHold();
                     break;
                 case DIALING:
                 case ALERTING:
                     if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
-                        setPulling();
+                        setTelephonyConnectionPulling();
                     } else {
-                        setDialing();
+                        setTelephonyConnectionDialing();
                     }
                     break;
                 case INCOMING:
                 case WAITING:
-                    setRinging();
+                    setTelephonyConnectionRinging();
                     break;
                 case DISCONNECTED:
                     if (shouldTreatAsEmergencyCall()
@@ -1675,11 +1843,19 @@
                             preciseDisconnectCause =
                                     mOriginalConnection.getPreciseDisconnectCause();
                         }
-                        setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
-                                mOriginalConnection.getDisconnectCause(),
-                                preciseDisconnectCause,
-                                mOriginalConnection.getVendorDisconnectCause(),
-                                getPhone().getPhoneId()));
+                        int disconnectCause = mOriginalConnection.getDisconnectCause();
+                        if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID)
+                                && (mHangupDisconnectCause != disconnectCause)) {
+                            Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause
+                                    + " -> " + mHangupDisconnectCause);
+                            disconnectCause = mHangupDisconnectCause;
+                        }
+                        setTelephonyConnectionDisconnected(
+                                DisconnectCauseUtil.toTelecomDisconnectCause(
+                                        disconnectCause,
+                                        preciseDisconnectCause,
+                                        mOriginalConnection.getVendorDisconnectCause(),
+                                        getPhone().getPhoneId()));
                         close();
                     }
                     break;
@@ -1701,6 +1877,7 @@
         updateAddress();
         updateMultiparty();
         refreshDisableAddCall();
+        refreshCodecType();
     }
 
     /**
@@ -1759,8 +1936,8 @@
         // To "optimize", we check here to see if there already exists any active calls.  If so,
         // we issue an update for those calls first to make sure we only have one top-level
         // active call.
-        if (getConnectionService() != null) {
-            for (Connection current : getConnectionService().getAllConnections()) {
+        if (getTelephonyConnectionService() != null) {
+            for (Connection current : getTelephonyConnectionService().getAllConnections()) {
                 if (current != this && current instanceof TelephonyConnection) {
                     TelephonyConnection other = (TelephonyConnection) current;
                     if (other.getState() == STATE_ACTIVE) {
@@ -1769,13 +1946,18 @@
                 }
             }
         }
-        setActive();
+        setTelephonyConnectionActive();
     }
 
-    protected void close() {
+    public void close() {
         Log.v(this, "close");
         clearOriginalConnection();
         destroy();
+        if (mTelephonyConnectionService != null) {
+            removeTelephonyConnectionListener(
+                    mTelephonyConnectionService.getTelephonyConnectionListener());
+        }
+        notifyDestroyed();
     }
 
     /**
@@ -1787,9 +1969,10 @@
      * @return {@code true} if the connection is video capable, {@code false} otherwise.
      */
     private boolean isVideoCapable() {
-        return can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
-                && can(mOriginalConnectionCapabilities,
-                Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
+        return (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
+                == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL
+                && (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
+                == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL;
     }
 
     /**
@@ -1801,7 +1984,8 @@
      * @return {@code true} if the connection is external, {@code false} otherwise.
      */
     private boolean isExternalConnection() {
-        return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION);
+        return (mOriginalConnectionCapabilities
+                & Capability.IS_EXTERNAL_CONNECTION) == Capability.IS_EXTERNAL_CONNECTION;
     }
 
     /**
@@ -1823,8 +2007,10 @@
      * @return {@code true} if the connection is pullable, {@code false} otherwise.
      */
     private boolean isPullable() {
-        return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION)
-                && can(mOriginalConnectionCapabilities, Capability.IS_PULLABLE);
+        return (mOriginalConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION)
+                == Capability.IS_EXTERNAL_CONNECTION
+                && (mOriginalConnectionCapabilities & Capability.IS_PULLABLE)
+                == Capability.IS_PULLABLE;
     }
 
     /**
@@ -1882,17 +2068,20 @@
     public int applyOriginalConnectionCapabilities(int capabilities) {
         // We only support downgrading to audio if both the remote and local side support
         // downgrading to audio.
-        boolean supportsDowngradeToAudio = can(mOriginalConnectionCapabilities,
-                Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
-                        Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE);
+        int supportsDowngrade = Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL
+                | Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE;
+        boolean supportsDowngradeToAudio =
+                (mOriginalConnectionCapabilities & supportsDowngrade) == supportsDowngrade;
         capabilities = changeBitmask(capabilities,
                 CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
 
         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
-                can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
+                (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
+                        == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
 
-        boolean isLocalVideoSupported = can(mOriginalConnectionCapabilities,
-                Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) && !mIsTtyEnabled;
+        boolean isLocalVideoSupported = (mOriginalConnectionCapabilities
+                & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
+                == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled;
         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
                 isLocalVideoSupported);
 
@@ -2119,13 +2308,13 @@
                     : R.string.status_hint_label_wifi_call;
 
             Context context = getPhone().getContext();
-            setStatusHints(new StatusHints(
-                    context.getString(labelId),
+            setTelephonyStatusHints(new StatusHints(
+                    getResourceString(labelId),
                     Icon.createWithResource(
                             context, R.drawable.ic_signal_wifi_4_bar_24dp),
                     null /* extras */));
         } else {
-            setStatusHints(null);
+            setTelephonyStatusHints(null);
         }
     }
 
@@ -2203,7 +2392,8 @@
      * 3. If call is a video call, carrier supports video conference calls.
      * 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
      */
-    private void refreshConferenceSupported() {
+    @VisibleForTesting
+    void refreshConferenceSupported() {
         boolean isVideoCall = VideoProfile.isVideo(getVideoState());
         Phone phone = getPhone();
         if (phone == null) {
@@ -2329,7 +2519,7 @@
         sb.append(" properties:");
         sb.append(propertiesToString(getConnectionProperties()));
         sb.append(" address:");
-        sb.append(Log.pii(getAddress()));
+        sb.append(Rlog.pii(LOG_TAG, getAddress()));
         sb.append(" originalConnection:");
         sb.append(mOriginalConnection);
         sb.append(" partOfConf:");
@@ -2343,4 +2533,346 @@
         sb.append("]");
         return sb.toString();
     }
+
+    public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) {
+        mTelephonyConnectionService = connectionService;
+    }
+
+    public final TelephonyConnectionService getTelephonyConnectionService() {
+        return mTelephonyConnectionService;
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to an active state.
+     * <p>
+     * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified.
+     */
+    public void setTelephonyConnectionActive() {
+        setActive();
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to a ringing state.
+     * <p>
+     * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified.
+     */
+    public void setTelephonyConnectionRinging() {
+        setRinging();
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to an initializing state.
+     * <p>
+     * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are
+     * notified.
+     */
+    public void setTelephonyConnectionInitializing() {
+        setInitializing();
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to a dialing state.
+     * <p>
+     * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified.
+     */
+    public void setTelephonyConnectionDialing() {
+        setDialing();
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to a pulling state.
+     * <p>
+     * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified.
+     */
+    public void setTelephonyConnectionPulling() {
+        setPulling();
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to a held state.
+     * <p>
+     * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified.
+     */
+    public void setTelephonyConnectionOnHold() {
+        setOnHold();
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Set this {@link TelephonyConnection} to a held state.
+     * <p>
+     * Note: This should be used instead of
+     * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
+     */
+    public void setTelephonyConnectionDisconnected(@NonNull
+            android.telecom.DisconnectCause disconnectCause) {
+        setDisconnected(disconnectCause);
+        notifyDisconnected(disconnectCause);
+        notifyStateChanged(getState());
+    }
+
+    /**
+     * Sends a connection event for this {@link TelephonyConnection}.
+     * <p>
+     * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure
+     * listeners are notified.
+     */
+    public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) {
+        sendConnectionEvent(event, extras);
+        notifyTelephonyConnectionEvent(event, extras);
+    }
+
+    /**
+     * Sets the extras associated with this {@link TelephonyConnection}.
+     * <p>
+     * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are
+     * notified.
+     */
+    public void putTelephonyExtras(@NonNull Bundle extras) {
+        putExtras(extras);
+        notifyPutExtras(extras);
+    }
+
+    /**
+     * Removes the specified extras associated with this {@link TelephonyConnection}.
+     * <p>
+     * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are
+     * notified.
+     */
+    public void removeTelephonyExtras(@NonNull List<String> keys) {
+        removeExtras(keys);
+        notifyRemoveExtras(keys);
+    }
+
+    /**
+     * Sets the video state associated with this {@link TelephonyConnection}.
+     * <p>
+     * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are
+     * notified.
+     */
+    public void setTelephonyVideoState(@VideoProfile.VideoState int videoState) {
+        setVideoState(videoState);
+        notifyVideoStateChanged(videoState);
+    }
+
+    /**
+     * Sets the video provider associated with this {@link TelephonyConnection}.
+     * <p>
+     * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure
+     * listeners are notified.
+     */
+    public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) {
+        setVideoProvider(videoProvider);
+        notifyVideoProviderChanged(videoProvider);
+    }
+
+    /**
+     * Sets the status hints associated with this {@link TelephonyConnection}.
+     * <p>
+     * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners
+     * are notified.
+     */
+    public void setTelephonyStatusHints(@Nullable StatusHints statusHints) {
+        setStatusHints(statusHints);
+        notifyStatusHintsChanged(statusHints);
+    }
+
+    /**
+     * Sets RIL voice radio technology used for current connection.
+     * <p>
+     * This property is set by the Telephony {@link ConnectionService}.
+     *
+     * @param vrat the RIL Voice Radio Technology used for current connection,
+     *             see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+     */
+    public final void setCallRadioTech(@RilRadioTechnology int vrat) {
+        Bundle extras = getExtras();
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                ServiceState.rilRadioTechnologyToNetworkType(vrat));
+        putExtras(extras);
+        // Propagates the call radio technology to its parent {@link android.telecom.Conference}
+        // This action only covers non-IMS CS conference calls.
+        // For IMS PS call conference call, it can be updated via its host connection
+        // {@link #Listener.onExtrasChanged} event.
+        if (getConference() != null) {
+            Bundle newExtras = new Bundle();
+            newExtras.putInt(
+                    TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                    ServiceState.rilRadioTechnologyToNetworkType(vrat));
+            getConference().putExtras(newExtras);
+        }
+    }
+
+    /**
+     * Returns RIL voice radio technology used for current connection.
+     * <p>
+     * Used by the Telephony {@link ConnectionService}.
+     *
+     * @return the RIL voice radio technology used for current connection,
+     *         see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+     */
+    public final @RilRadioTechnology int getCallRadioTech() {
+        int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        Bundle extras = getExtras();
+        if (extras != null) {
+            voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        }
+        return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data
+     * received via the {@link ImsConference} (i.e. conference event package).
+     *
+     * @param conferenceParticipants The participants.
+     */
+    private void updateConferenceParticipants(
+            @NonNull List<ConferenceParticipant> conferenceParticipants) {
+        for (TelephonyConnectionListener l : mTelephonyListeners) {
+            l.onConferenceParticipantsChanged(this, conferenceParticipants);
+        }
+    }
+
+    /**
+     * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
+     * operation has started.
+     */
+    protected void notifyConferenceStarted() {
+        for (TelephonyConnectionListener l : mTelephonyListeners) {
+            l.onConferenceStarted();
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection
+     * which impacts its ability to be a part of a conference call.
+     * @param isConferenceSupported {@code true} if the connection supports being part of a
+     *      conference call, {@code false} otherwise.
+     */
+    private void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
+        for (TelephonyConnectionListener l : mTelephonyListeners) {
+            l.onConferenceSupportedChanged(this, isConferenceSupported);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities.
+     * @param newCapabilities the new capabilities.
+     */
+    private void notifyConnectionCapabilitiesChanged(int newCapabilities) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onConnectionCapabilitiesChanged(this, newCapabilities);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties.
+     * @param newProperties the new properties.
+     */
+    private void notifyConnectionPropertiesChanged(int newProperties) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onConnectionPropertiesChanged(this, newProperties);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed.
+     */
+    private void notifyDestroyed() {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onDestroyed(this);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s when a connection disconnects.
+     * @param cause The disconnect cause.
+     */
+    private void notifyDisconnected(android.telecom.DisconnectCause cause) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onDisconnected(this, cause);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of connection state changes.
+     * @param newState The new state.
+     */
+    private void notifyStateChanged(int newState) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onStateChanged(this, newState);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of telephony connection events.
+     * @param event The event.
+     * @param extras Any extras.
+     */
+    private void notifyTelephonyConnectionEvent(String event, Bundle extras) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onConnectionEvent(this, event, extras);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection.
+     * @param extras The new extras.
+     */
+    private void notifyPutExtras(Bundle extras) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onExtrasChanged(this, extras);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection.
+     * @param keys The removed keys.
+     */
+    private void notifyRemoveExtras(List<String> keys) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onExtrasRemoved(this, keys);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection.
+     * @param videoState The new video state.
+     */
+    private void notifyVideoStateChanged(@VideoProfile.VideoState int videoState) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onVideoStateChanged(this, videoState);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
+     * connection.
+     * @param videoProvider The new video provider.
+     */
+    private void notifyVideoProviderChanged(VideoProvider videoProvider) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onVideoProviderChanged(this, videoProvider);
+        }
+    }
+
+    /**
+     * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a
+     * connection.
+     * @param statusHints The new status hints.
+     */
+    private void notifyStatusHintsChanged(StatusHints statusHints) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onStatusHintsChanged(this, statusHints);
+        }
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 0e5a612..40b941e 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -28,7 +28,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
-import android.provider.Settings;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
@@ -103,15 +102,11 @@
         }
         @Override
         public void addConference(TelephonyConference mTelephonyConference) {
-            TelephonyConnectionService.this.addConference(mTelephonyConference);
+            TelephonyConnectionService.this.addTelephonyConference(mTelephonyConference);
         }
         @Override
         public void addConference(ImsConference mImsConference) {
-            TelephonyConnectionService.this.addConference(mImsConference);
-        }
-        @Override
-        public void removeConnection(Connection connection) {
-            TelephonyConnectionService.this.removeConnection(connection);
+            TelephonyConnectionService.this.addTelephonyConference(mImsConference);
         }
         @Override
         public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
@@ -131,13 +126,6 @@
         }
     };
 
-    private final Connection.Listener mConnectionListener = new Connection.Listener() {
-        @Override
-        public void onConferenceChanged(Connection connection, Conference conference) {
-            mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
-        }
-    };
-
     private final BroadcastReceiver mTtyBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -159,12 +147,7 @@
             new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
     private final CdmaConferenceController mCdmaConferenceController =
             new CdmaConferenceController(this);
-    private final ImsConferenceController mImsConferenceController =
-            new ImsConferenceController(TelecomAccountRegistry.getInstance(this),
-                    mTelephonyConnectionServiceProxy,
-                    // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
-                    // TODO: Move to carrier config
-                    () -> true);
+    private ImsConferenceController mImsConferenceController;
 
     private ComponentName mExpectedComponentName = null;
     private RadioOnHelper mRadioOnHelper;
@@ -181,6 +164,7 @@
     public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
     private Handler mDdsSwitchHandler;
     private HandlerThread mHandlerThread;
+    private DeviceState mDeviceState = new DeviceState();
 
     /**
      * Keeps track of the status of a SIM slot.
@@ -193,6 +177,8 @@
         public boolean isLocked = false;
         // Is the emergency number associated with the slot
         public boolean hasDialedEmergencyNumber = false;
+        //SimState
+        public int simState;
 
         public SlotStatus(int slotId, int capabilities) {
             this.slotId = slotId;
@@ -200,7 +186,10 @@
         }
     }
 
-    // SubscriptionManager Proxy interface for testing
+    /**
+     * SubscriptionManager dependencies for testing.
+     */
+    @VisibleForTesting
     public interface SubscriptionManagerProxy {
         int getDefaultVoicePhoneId();
         int getSimStateForSlotIdx(int slotId);
@@ -224,7 +213,9 @@
         }
     };
 
-    // TelephonyManager Proxy interface for testing
+    /**
+     * TelephonyManager dependencies for testing.
+     */
     @VisibleForTesting
     public interface TelephonyManagerProxy {
         int getPhoneCount();
@@ -264,7 +255,9 @@
         }
     }
 
-    //PhoneFactory proxy interface for testing
+    /**
+     * PhoneFactory Dependencies for testing.
+     */
     @VisibleForTesting
     public interface PhoneFactoryProxy {
         Phone getPhone(int index);
@@ -289,22 +282,185 @@
         }
     };
 
+    /**
+     * PhoneUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneUtilsProxy {
+        int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle);
+        PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone);
+        PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
+                boolean isEmergency);
+    }
+
+    private PhoneUtilsProxy mPhoneUtilsProxy = new PhoneUtilsProxy() {
+        @Override
+        public int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle) {
+            return PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
+        }
+
+        @Override
+        public PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
+            return PhoneUtils.makePstnPhoneAccountHandle(phone);
+        }
+
+        @Override
+        public PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
+                boolean isEmergency) {
+            return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(phone, prefix, isEmergency);
+        }
+    };
+
+    /**
+     * PhoneNumberUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneNumberUtilsProxy {
+        String convertToEmergencyNumber(Context context, String number);
+    }
+
+    private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = new PhoneNumberUtilsProxy() {
+        @Override
+        public String convertToEmergencyNumber(Context context, String number) {
+            return PhoneNumberUtils.convertToEmergencyNumber(context, number);
+        }
+    };
+
+    /**
+     * PhoneSwitcher dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface PhoneSwitcherProxy {
+        PhoneSwitcher getPhoneSwitcher();
+    }
+
+    private PhoneSwitcherProxy mPhoneSwitcherProxy = new PhoneSwitcherProxy() {
+        @Override
+        public PhoneSwitcher getPhoneSwitcher() {
+            return PhoneSwitcher.getInstance();
+        }
+    };
+
+    /**
+     * Factory for Handler creation in order to remove flakiness during t esting.
+     */
+    @VisibleForTesting
+    public interface HandlerFactory {
+        HandlerThread createHandlerThread(String name);
+        Handler createHandler(Looper looper);
+    }
+
+    private HandlerFactory mHandlerFactory = new HandlerFactory() {
+        @Override
+        public HandlerThread createHandlerThread(String name) {
+            return new HandlerThread(name);
+        }
+
+        @Override
+        public Handler createHandler(Looper looper) {
+            return new Handler(looper);
+        }
+    };
+
+    /**
+     * DisconnectCause depends on PhoneGlobals in order to get a system context. Mock out
+     * dependency for testing.
+     */
+    @VisibleForTesting
+    public interface DisconnectCauseFactory {
+        DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason);
+        DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+                String reason, int phoneId);
+    }
+
+    private DisconnectCauseFactory mDisconnectCauseFactory = new DisconnectCauseFactory() {
+        @Override
+        public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+                String reason) {
+            return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason);
+        }
+
+        @Override
+        public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason,
+                int phoneId) {
+            return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason,
+                    phoneId);
+        }
+    };
+
+    /**
+     * Overrides SubscriptionManager dependencies for testing.
+     */
     @VisibleForTesting
     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
         mSubscriptionManagerProxy = proxy;
     }
 
+    /**
+     * Overrides TelephonyManager dependencies for testing.
+     */
     @VisibleForTesting
     public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) {
         mTelephonyManagerProxy = proxy;
     }
 
+    /**
+     * Overrides PhoneFactory dependencies for testing.
+     */
     @VisibleForTesting
     public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
         mPhoneFactoryProxy = proxy;
     }
 
     /**
+     * Overrides configuration and settings dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setDeviceState(DeviceState state) {
+        mDeviceState = state;
+    }
+
+    /**
+     * Overrides PhoneSwitcher dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneSwitcherProxy(PhoneSwitcherProxy proxy) {
+        mPhoneSwitcherProxy = proxy;
+    }
+
+    /**
+     * Overrides PhoneNumberUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy proxy) {
+        mPhoneNumberUtilsProxy = proxy;
+    }
+
+    /**
+     * Overrides PhoneUtils dependencies for testing.
+     */
+    @VisibleForTesting
+    public void setPhoneUtilsProxy(PhoneUtilsProxy proxy) {
+        mPhoneUtilsProxy = proxy;
+    }
+
+    /**
+     * Override Handler creation factory for testing.
+     */
+    @VisibleForTesting
+    public void setHandlerFactory(HandlerFactory handlerFactory) {
+        mHandlerFactory = handlerFactory;
+    }
+
+    /**
+     * Override DisconnectCause creation for testing.
+     */
+    @VisibleForTesting
+    public void setDisconnectCauseFactory(DisconnectCauseFactory factory) {
+        mDisconnectCauseFactory = factory;
+    }
+
+    /**
      * A listener to actionable events specific to the TelephonyConnection.
      */
     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -320,25 +476,37 @@
         }
     };
 
+    private final TelephonyConferenceBase.TelephonyConferenceListener mTelephonyConferenceListener =
+            new TelephonyConferenceBase.TelephonyConferenceListener() {
+        @Override
+        public void onConferenceMembershipChanged(Connection connection) {
+            mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
+        }
+    };
+
     @Override
     public void onCreate() {
         super.onCreate();
-        Log.initLogging(this);
+        mImsConferenceController = new ImsConferenceController(
+                TelecomAccountRegistry.getInstance(this),
+                mTelephonyConnectionServiceProxy,
+                // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
+                // TODO: Move to carrier config
+                () -> true);
         setTelephonyManagerProxy(new TelephonyManagerProxyImpl(getApplicationContext()));
         mExpectedComponentName = new ComponentName(this, this.getClass());
         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
         TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
         mHoldTracker = new HoldTracker();
-        mIsTtyEnabled = isTtyModeEnabled(getApplicationContext());
+        mIsTtyEnabled = mDeviceState.isTtyModeEnabled(this);
 
         IntentFilter intentFilter = new IntentFilter(
                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
         registerReceiver(mTtyBroadcastReceiver, intentFilter);
-
-        mHandlerThread = new HandlerThread("DdsSwitchHandlerThread");
+        mHandlerThread = mHandlerFactory.createHandlerThread("DdsSwitchHandlerThread");
         mHandlerThread.start();
         Looper looper = mHandlerThread.getLooper();
-        mDdsSwitchHandler = new Handler(looper);
+        mDdsSwitchHandler = mHandlerFactory.createHandler(looper);
     }
 
     @Override
@@ -358,7 +526,7 @@
         if (handle == null) {
             Log.d(this, "onCreateOutgoingConnection, handle is null");
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
                             "No phone number supplied"));
         }
@@ -373,7 +541,7 @@
             if (phone == null) {
                 Log.d(this, "onCreateOutgoingConnection, phone is null");
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                 "Phone is null"));
             }
@@ -381,7 +549,7 @@
             if (TextUtils.isEmpty(number)) {
                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
                                 "Voicemail scheme provided but no voicemail number set.",
                                 phone.getPhoneId()));
@@ -393,7 +561,7 @@
             if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
                 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.INVALID_NUMBER,
                                 "Handle scheme is not type tel"));
             }
@@ -402,7 +570,7 @@
             if (TextUtils.isEmpty(number)) {
                 Log.d(this, "onCreateOutgoingConnection, unable to parse number");
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.INVALID_NUMBER,
                                 "Unable to parse number"));
             }
@@ -423,7 +591,7 @@
 
                 if (disableActivation) {
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause
                                             .CDMA_ALREADY_ACTIVATED,
                                     "Tried to dial *228",
@@ -447,7 +615,8 @@
             // service.
             if (phone == null || phone.getServiceState().getState()
                     != ServiceState.STATE_IN_SERVICE) {
-                String convertedNumber = PhoneNumberUtils.convertToEmergencyNumber(this, number);
+                String convertedNumber = mPhoneNumberUtilsProxy.convertToEmergencyNumber(this,
+                        number);
                 if (!TextUtils.equals(convertedNumber, number)) {
                     Log.i(this, "onCreateOutgoingConnection, converted to emergency number");
                     number = convertedNumber;
@@ -458,8 +627,7 @@
         final String numberToDial = number;
 
 
-        final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+        final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
 
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
                 || isRadioPowerDownOnBluetooth();
@@ -468,7 +636,7 @@
             final Uri resultHandle = handle;
             // By default, Connection based on the default Phone, since we need to return to Telecom
             // now.
-            final int originalPhoneType = PhoneFactory.getDefaultPhone().getPhoneType();
+            final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
             final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                     isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
             if (mRadioOnHelper == null) {
@@ -585,12 +753,8 @@
      * Whether the cellular radio is power off because the device is on Bluetooth.
      */
     private boolean isRadioPowerDownOnBluetooth() {
-        final Context context = getApplicationContext();
-        final boolean allowed = context.getResources().getBoolean(
-                R.bool.config_allowRadioPowerDownOnBluetooth);
-        final int cellOn = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.CELL_ON,
-                PhoneConstants.CELL_ON_FLAG);
+        final boolean allowed = mDeviceState.isRadioPowerDownAllowedOnBluetooth(this);
+        final int cellOn = mDeviceState.getCellOnStatus(this);
         return (allowed && cellOn == PhoneConstants.CELL_ON_FLAG && !isRadioOn());
     }
 
@@ -628,11 +792,10 @@
 
         } else {
             Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
-            originalConnection.setDisconnected(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+            closeOrDestroyConnection(originalConnection,
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.POWER_OFF,
                             "Failed to turn on radio."));
-            originalConnection.destroy();
         }
     }
 
@@ -655,15 +818,14 @@
             boolean noActiveSimCard = SubscriptionController.getInstance()
                     .getActiveSubInfoCount(phone.getContext().getOpPackageName()) == 0;
             // If there's no active sim card and the device is in emergency mode, use E account.
-            addExistingConnection(PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
+            addExistingConnection(mPhoneUtilsProxy.makePstnPhoneAccountHandleWithPrefix(
                     phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
             // Remove the old connection from Telecom after.
-            connectionToEvaluate.setDisconnected(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+            closeOrDestroyConnection(connectionToEvaluate,
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUTGOING_CANCELED,
                             "Reconnecting outgoing Emergency Call.",
                             phone.getPhoneId()));
-            connectionToEvaluate.destroy();
         } else {
             placeOutgoingConnection((TelephonyConnection) connectionToEvaluate, phone, request);
         }
@@ -689,7 +851,7 @@
 
         if (phone == null) {
             final Context context = getApplicationContext();
-            if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
+            if (mDeviceState.shouldCheckSimStateBeforeOutgoingCall(this)) {
                 // Check SIM card state before the outgoing call.
                 // Start the SIM unlock activity if PIN_REQUIRED.
                 final Phone defaultPhone = mPhoneFactoryProxy.getDefaultPhone();
@@ -714,7 +876,7 @@
                         }
                     }
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                     "SIM_STATE_PIN_REQUIRED"));
                 }
@@ -722,7 +884,7 @@
 
             Log.d(this, "onCreateOutgoingConnection, phone is null");
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
         }
 
@@ -750,7 +912,7 @@
 
             if (!allowNonEmergencyCalls) {
                 return Connection.createFailedConnection(
-                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                        mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY,
                                 "Cannot make non-emergency call in ECM mode.",
                                 phone.getPhoneId()));
@@ -768,7 +930,7 @@
                         break;
                     } else {
                         return Connection.createFailedConnection(
-                                DisconnectCauseUtil.toTelecomDisconnectCause(
+                                mDisconnectCauseFactory.toTelecomDisconnectCause(
                                         android.telephony.DisconnectCause.OUT_OF_SERVICE,
                                         "ServiceState.STATE_OUT_OF_SERVICE",
                                         phone.getPhoneId()));
@@ -779,25 +941,24 @@
                         break;
                     }
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause.POWER_OFF,
                                     "ServiceState.STATE_POWER_OFF",
                                     phone.getPhoneId()));
                 default:
                     Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
                     return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                            mDisconnectCauseFactory.toTelecomDisconnectCause(
                                     android.telephony.DisconnectCause.OUTGOING_FAILURE,
                                     "Unknown service state " + state,
                                     phone.getPhoneId()));
             }
         }
 
-        final Context context = getApplicationContext();
-        final boolean isTtyModeEnabled = isTtyModeEnabled(context);
+        final boolean isTtyModeEnabled = mDeviceState.isTtyModeEnabled(this);
         if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled
                 && !isEmergencyNumber) {
-            return Connection.createFailedConnection(DisconnectCauseUtil.toTelecomDisconnectCause(
+            return Connection.createFailedConnection(mDisconnectCauseFactory.toTelecomDisconnectCause(
                     android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,
                     null, phone.getPhoneId()));
         }
@@ -811,7 +972,7 @@
         // Check roaming status to see if we should block custom call forwarding codes
         if (blockCallForwardingNumberWhileRoaming(phone, number)) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING,
                             "Call forwarding while roaming",
                             phone.getPhoneId()));
@@ -820,17 +981,17 @@
 
         final TelephonyConnection connection =
                 createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
-                        request.getTelecomCallId(), request.getAddress(), request.getVideoState());
+                        request.getTelecomCallId());
         if (connection == null) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUTGOING_FAILURE,
                             "Invalid phone type",
                             phone.getPhoneId()));
         }
         connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
-        connection.setInitializing();
-        connection.setVideoState(request.getVideoState());
+        connection.setTelephonyConnectionInitializing();
+        connection.setTelephonyVideoState(request.getVideoState());
         connection.setRttTextStream(request.getRttTextStream());
         connection.setTtyEnabled(isTtyModeEnabled);
         return connection;
@@ -856,7 +1017,7 @@
                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                             "Phone is null"));
         }
@@ -865,7 +1026,7 @@
         if (!call.getState().isRinging()) {
             Log.i(this, "onCreateIncomingConnection, no ringing call");
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.INCOMING_MISSED,
                             "Found no ringing call",
                             phone.getPhoneId()));
@@ -879,19 +1040,14 @@
             return Connection.createCanceledConnection();
         }
 
-        // We should rely on the originalConnection to get the video state.  The request coming
-        // from Telecom does not know the video state of the incoming call.
-        int videoState = originalConnection != null ? originalConnection.getVideoState() :
-                VideoProfile.STATE_AUDIO_ONLY;
-
         TelephonyConnection connection =
                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
-                        request.getAccountHandle(), request.getTelecomCallId(),
-                        request.getAddress(), videoState);
+                        request.getAccountHandle(), request.getTelecomCallId());
         handleIncomingRtt(request, originalConnection);
         if (connection == null) {
             return Connection.createCanceledConnection();
         } else {
+            connection.setTtyEnabled(mDeviceState.isTtyModeEnabled(getApplicationContext()));
             return connection;
         }
     }
@@ -944,6 +1100,61 @@
     }
 
     @Override
+    public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+            ConnectionRequest request) {
+        Log.i(this, "onCreateIncomingConnectionFailed, request: " + request);
+        // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
+        // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
+        PhoneAccountHandle accountHandle = request.getAccountHandle();
+        boolean isEmergency = false;
+        if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
+                accountHandle.getId())) {
+            Log.w(this, "onCreateIncomingConnectionFailed:Emergency call failed... ");
+            isEmergency = true;
+        }
+        Phone phone = getPhoneForAccount(accountHandle, isEmergency,
+                /* Note: when not an emergency, handle can be null for unknown callers */
+                request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
+        if (phone == null) {
+            Log.w(this, "onCreateIncomingConnectionFailed: can not find corresponding phone.");
+            return;
+        }
+
+        Call call = phone.getRingingCall();
+        if (!call.getState().isRinging()) {
+            Log.w(this, "onCreateIncomingConnectionFailed, no ringing call found for failed call");
+            return;
+        }
+
+        com.android.internal.telephony.Connection originalConnection =
+                call.getState() == Call.State.WAITING
+                        ? call.getLatestConnection() : call.getEarliestConnection();
+        TelephonyConnection knownConnection =
+                getConnectionForOriginalConnection(originalConnection);
+        if (knownConnection != null) {
+            Log.w(this, "onCreateIncomingConnectionFailed, original connection already registered."
+                    + " Hanging it up.");
+            knownConnection.onAbort();
+            return;
+        }
+
+        TelephonyConnection connection =
+                createConnectionFor(phone, originalConnection, false /* isOutgoing */,
+                        request.getAccountHandle(), request.getTelecomCallId());
+        if (connection == null) {
+            Log.w(this, "onCreateIncomingConnectionFailed, TelephonyConnection created as null, "
+                    + "ignoring.");
+            return;
+        }
+
+        // We have to do all of this work because in some cases, hanging up the call maps to
+        // different underlying signaling (CDMA), which is already encapsulated in
+        // TelephonyConnection.
+        connection.onReject();
+        connection.close();
+    }
+
+    @Override
     public void triggerConferenceRecalculate() {
         if (mTelephonyConferenceController.shouldRecalculate()) {
             mTelephonyConferenceController.recalculate();
@@ -969,7 +1180,7 @@
                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
             return Connection.createFailedConnection(
-                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                             "Phone is null"));
         }
@@ -1042,8 +1253,8 @@
         TelephonyConnection connection =
                 createConnectionFor(phone, unknownConnection,
                         !unknownConnection.isIncoming() /* isOutgoing */,
-                        request.getAccountHandle(), request.getTelecomCallId(),
-                        request.getAddress(), videoState);
+                        request.getAccountHandle(), request.getTelecomCallId()
+                );
 
         if (connection == null) {
             return Connection.createCanceledConnection();
@@ -1087,7 +1298,6 @@
     @Override
     public void onConnectionAdded(Connection connection) {
         if (connection instanceof Holdable && !isExternalConnection(connection)) {
-            connection.addConnectionListener(mConnectionListener);
             mHoldTracker.addHoldable(
                     connection.getPhoneAccountHandle(), (Holdable) connection);
         }
@@ -1218,14 +1428,12 @@
         } else {
             // We have run out of Phones to use. Disconnect the call and destroy the connection.
             Log.i(this, "retryOutgoingOriginalConnection, no more Phones to use. Disconnecting.");
-            c.setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
-            c.clearOriginalConnection();
-            c.destroy();
+            closeOrDestroyConnection(c, new DisconnectCause(DisconnectCause.ERROR));
         }
     }
 
     private void updatePhoneAccount(TelephonyConnection connection, Phone phone) {
-        PhoneAccountHandle pHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
+        PhoneAccountHandle pHandle = mPhoneUtilsProxy.makePstnPhoneAccountHandle(phone);
         // For ECall handling on MSIM, until the request reaches here (i.e PhoneApp), we don't know
         // on which phone account ECall can be placed. After deciding, we should notify Telecom of
         // the change so that the proper PhoneAccount can be displayed.
@@ -1249,6 +1457,35 @@
                         phone.getEmergencyNumberTracker().getEmergencyNumber(number);
                 if (emergencyNumber != null) {
                     phone.notifyOutgoingEmergencyCall(emergencyNumber);
+                    if (!getAllConnections().isEmpty()) {
+                        if (!shouldHoldForEmergencyCall(phone)) {
+                            // If we do not support holding ongoing calls for an outgoing
+                            // emergency call, disconnect the ongoing calls.
+                            for (Connection c : getAllConnections()) {
+                                if (!c.equals(connection)
+                                        && c.getState() != Connection.STATE_DISCONNECTED
+                                        && c instanceof TelephonyConnection) {
+                                    ((TelephonyConnection) c).hangup(
+                                            android.telephony.DisconnectCause
+                                                    .OUTGOING_EMERGENCY_CALL_PLACED);
+                                }
+                            }
+                        } else if (!isVideoCallHoldAllowed(phone)) {
+                            // If we do not support holding ongoing video call for an outgoing
+                            // emergency call, disconnect the ongoing video call.
+                            for (Connection c : getAllConnections()) {
+                                if (!c.equals(connection)
+                                        && c.getState() == Connection.STATE_ACTIVE
+                                        && VideoProfile.isVideo(c.getVideoState())
+                                        && c instanceof TelephonyConnection) {
+                                    ((TelephonyConnection) c).hangup(
+                                            android.telephony.DisconnectCause
+                                                    .OUTGOING_EMERGENCY_CALL_PLACED);
+                                    break;
+                                }
+                            }
+                        }
+                    }
                 }
                 originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
                         .setVideoState(videoState)
@@ -1282,10 +1519,10 @@
                     cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
                     break;
             }
-            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
-                    cause, e.getMessage(), phone.getPhoneId()));
-            connection.clearOriginalConnection();
-            connection.destroy();
+            connection.setTelephonyConnectionDisconnected(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(cause, e.getMessage(),
+                            phone.getPhoneId()));
+            connection.close();
             return;
         }
 
@@ -1306,23 +1543,45 @@
                 startActivity(intent);
             }
             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
-            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
-                    telephonyDisconnectCause, "Connection is null", phone.getPhoneId()));
-            connection.clearOriginalConnection();
-            connection.destroy();
+            connection.setTelephonyConnectionDisconnected(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(telephonyDisconnectCause,
+                            "Connection is null", phone.getPhoneId()));
+            connection.close();
         } else {
             connection.setOriginalConnection(originalConnection);
         }
     }
 
+    private boolean isVideoCallHoldAllowed(Phone phone) {
+         CarrierConfigManager cfgManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (cfgManager == null) {
+            // For some reason CarrierConfigManager is unavailable, return default
+            Log.w(this, "isVideoCallHoldAllowed: couldn't get CarrierConfigManager");
+            return true;
+        }
+        return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+                CarrierConfigManager.KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
+    }
+
+    private boolean shouldHoldForEmergencyCall(Phone phone) {
+        CarrierConfigManager cfgManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (cfgManager == null) {
+            // For some reason CarrierConfigManager is unavailable, return default
+            Log.w(this, "shouldHoldForEmergencyCall: couldn't get CarrierConfigManager");
+            return true;
+        }
+        return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+                CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
+    }
+
     private TelephonyConnection createConnectionFor(
             Phone phone,
             com.android.internal.telephony.Connection originalConnection,
             boolean isOutgoing,
             PhoneAccountHandle phoneAccountHandle,
-            String telecomCallId,
-            Uri address,
-            int videoState) {
+            String telecomCallId) {
         TelephonyConnection returnConnection = null;
         int phoneType = phone.getPhoneType();
         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
@@ -1344,21 +1603,27 @@
             returnConnection.setShowPreciseFailedCause(
                     TelecomAccountRegistry.getInstance(this).isShowPreciseFailedCause(
                             phoneAccountHandle));
+            returnConnection.setTelephonyConnectionService(this);
         }
         return returnConnection;
     }
 
     private boolean isOriginalConnectionKnown(
             com.android.internal.telephony.Connection originalConnection) {
+        return (getConnectionForOriginalConnection(originalConnection) != null);
+    }
+
+    private TelephonyConnection getConnectionForOriginalConnection(
+            com.android.internal.telephony.Connection originalConnection) {
         for (Connection connection : getAllConnections()) {
             if (connection instanceof TelephonyConnection) {
                 TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
                 if (telephonyConnection.getOriginalConnection() == originalConnection) {
-                    return true;
+                    return telephonyConnection;
                 }
             }
         }
-        return false;
+        return null;
     }
 
     /**
@@ -1373,7 +1638,7 @@
     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency,
                                      @Nullable String emergencyNumberAddress) {
         Phone chosenPhone = null;
-        int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
+        int subId = mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(accountHandle);
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
             chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
@@ -1403,7 +1668,7 @@
             return possiblyOverrideDefaultDataForEmergencyCall(phone).get(
                     DEFAULT_DATA_SWITCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         } catch (Exception e) {
-            Log.w(this, "onCreateOutgoingConn - delayDialForDdsSwitch exception= "
+            Log.w(this, "delayDialForDdsSwitch - exception= "
                     + e.getMessage());
             return false;
         }
@@ -1423,13 +1688,21 @@
      */
     private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall(
             @NonNull Phone phone) {
-        TelephonyManager telephony = TelephonyManager.from(phone.getContext());
-        int phoneCount = telephony.getPhoneCount();
+        int phoneCount = mTelephonyManagerProxy.getPhoneCount();
         // Do not override DDS if this is a single SIM device.
         if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) {
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
 
+        // Do not switch Default data if this device supports emergency SUPL on non-DDS.
+        final boolean gnssSuplRequiresDefaultData =
+                mDeviceState.isSuplDdsSwitchRequiredForEmergencyCall(this);
+        if (!gnssSuplRequiresDefaultData) {
+            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
+                    + "require DDS switch.");
+            return CompletableFuture.completedFuture(Boolean.TRUE);
+        }
+
         CarrierConfigManager cfgManager = (CarrierConfigManager)
                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (cfgManager == null) {
@@ -1438,34 +1711,41 @@
                     + "CarrierConfigManager");
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
-        // Only override default data if we are IN_SERVICE and on a home network. We don't want to
-        // perform a DDS switch of we are on a roaming network, where SUPL may not be available.
-        boolean isPhoneAvailableForEmergency = isAvailableForEmergencyCalls(phone);
+
+        // Only override default data if we are IN_SERVICE already.
+        if (!isAvailableForEmergencyCalls(phone)) {
+            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS");
+            return CompletableFuture.completedFuture(Boolean.TRUE);
+        }
+
+        // Only override default data if we are not roaming, we do not want to switch onto a network
+        // that only supports data plane only (if we do not know).
         boolean isRoaming = phone.getServiceState().getVoiceRoaming();
-        if (!isPhoneAvailableForEmergency || isRoaming) {
-            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, avail = "
-                    + isPhoneAvailableForEmergency + ", roaming = " + isRoaming);
+        // In some roaming conditions, we know the roaming network doesn't support control plane
+        // fallback even though the home operator does. For these operators we will need to do a DDS
+        // switch anyway to make sure the SUPL request doesn't fail.
+        boolean roamingNetworkSupportsControlPlaneFallback = true;
+        String[] dataPlaneRoamPlmns = cfgManager.getConfigForSubId(phone.getSubId()).getStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY);
+        if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns).contains(
+                phone.getServiceState().getOperatorNumeric())) {
+            roamingNetworkSupportsControlPlaneFallback = false;
+        }
+        if (isRoaming && roamingNetworkSupportsControlPlaneFallback) {
+            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed "
+                    + "to support CP fallback, not switching DDS.");
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
-
-        // Do not switch Default data if this device supports emergency SUPL on non-DDS.
-        final boolean gnssSuplRequiresDefaultData = phone.getContext().getResources().getBoolean(
-                R.bool.config_gnss_supl_requires_default_data_for_emergency);
-        if (!gnssSuplRequiresDefaultData) {
-            Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
-                    + "require DDS switch.");
-            return CompletableFuture.completedFuture(Boolean.TRUE);
-        }
-
+        // Do not try to swap default data if we support CS fallback or it is assumed that the
+        // roaming network supports control plane fallback, we do not want to introduce
+        // a lag in emergency call setup time if possible.
         final boolean supportsCpFallback = cfgManager.getConfigForSubId(phone.getSubId())
                 .getInt(CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                         CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY)
                 != CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY;
-        if (supportsCpFallback) {
+        if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) {
             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier "
                     + "supports CP fallback.");
-            // Do not try to swap default data if we support CS fallback, do not want to introduce
-            // a lag in emergency call setup time if possible.
             return CompletableFuture.completedFuture(Boolean.TRUE);
         }
 
@@ -1473,7 +1753,7 @@
         // CarrierConfig default if format fails.
         int extensionTime = 0;
         try {
-            extensionTime = Integer.valueOf(cfgManager.getConfigForSubId(phone.getSubId())
+            extensionTime = Integer.parseInt(cfgManager.getConfigForSubId(phone.getSubId())
                     .getString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0"));
         } catch (NumberFormatException e) {
             // Just use default.
@@ -1482,12 +1762,13 @@
         try {
             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for "
                     + extensionTime + "seconds");
-            PhoneSwitcher.getInstance().overrideDefaultDataForEmergency(phone.getPhoneId(),
-                    extensionTime, modemResultFuture);
+            mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency(
+                    phone.getPhoneId(), extensionTime, modemResultFuture);
             // Catch all exceptions, we want to continue with emergency call if possible.
         } catch (Exception e) {
             Log.w(this, "possiblyOverrideDefaultDataForEmergencyCall: exception = "
                     + e.getMessage());
+            modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE);
         }
         return modemResultFuture;
     }
@@ -1594,6 +1875,8 @@
             // 4)
             // Report Slot's PIN/PUK lock status for sorting later.
             int simState = mSubscriptionManagerProxy.getSimStateForSlotIdx(i);
+            // Record SimState.
+            status.simState = simState;
             if (simState == TelephonyManager.SIM_STATE_PIN_REQUIRED ||
                     simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
                 status.isLocked = true;
@@ -1639,6 +1922,15 @@
                         if (o1.hasDialedEmergencyNumber && !o2.hasDialedEmergencyNumber) {
                             return 1;
                         }
+                        // Sort by non-absent SIM.
+                        if (o1.simState == TelephonyManager.SIM_STATE_ABSENT
+                                && o2.simState != TelephonyManager.SIM_STATE_ABSENT) {
+                            return -1;
+                        }
+                        if (o2.simState == TelephonyManager.SIM_STATE_ABSENT
+                                && o1.simState != TelephonyManager.SIM_STATE_ABSENT) {
+                            return 1;
+                        }
                         // First start by seeing if either of the phone slots are locked. If they
                         // are, then sort by non-locked SIM first. If they are both locked, sort
                         // by capability instead.
@@ -1716,13 +2008,8 @@
         return true;
     }
 
-    @Override
-    public void removeConnection(Connection connection) {
-        super.removeConnection(connection);
-        if (connection instanceof TelephonyConnection) {
-            TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
-            telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
-        }
+    TelephonyConnection.TelephonyConnectionListener getTelephonyConnectionListener() {
+        return mTelephonyConnectionListener;
     }
 
     /**
@@ -1782,7 +2069,8 @@
 
                     // If the CDMA conference has not been merged, add-call will not work, so fail
                     // this request to add a call.
-                    if (cdmaConf.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
+                    if ((cdmaConf.getConnectionCapabilities()
+                            & Connection.CAPABILITY_MERGE_CONFERENCE) != 0) {
                         return Connection.createFailedConnection(new DisconnectCause(
                                     DisconnectCause.RESTRICTED,
                                     null,
@@ -1796,13 +2084,6 @@
         return null; // null means nothing went wrong, and call should continue.
     }
 
-    private boolean isTtyModeEnabled(Context context) {
-        return (android.provider.Settings.Secure.getInt(
-                context.getContentResolver(),
-                android.provider.Settings.Secure.PREFERRED_TTY_MODE,
-                TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF);
-    }
-
     /**
      * For outgoing dialed calls, potentially send a ConnectionEvent if the user is on WFC and is
      * dialing an international number.
@@ -1823,7 +2104,7 @@
                 // are potentially placing an international call on WFC.
                 Log.i(this, "placeOutgoingConnection - sending international call on WFC " +
                         "confirmation event");
-                telephonyConnection.sendConnectionEvent(
+                telephonyConnection.sendTelephonyConnectionEvent(
                         TelephonyManager.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC, null);
             }
         }
@@ -1839,4 +2120,26 @@
             }
         }
     }
+
+    private void closeOrDestroyConnection(Connection connection, DisconnectCause cause) {
+        if (connection instanceof TelephonyConnection) {
+            TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+            telephonyConnection.setTelephonyConnectionDisconnected(cause);
+            // Close destroys the connection and notifies TelephonyConnection listeners.
+            telephonyConnection.close();
+        } else {
+            connection.setDisconnected(cause);
+            connection.destroy();
+        }
+    }
+
+    /**
+     * 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)}.
+     * @param conference The conference.
+     */
+    public void addTelephonyConference(@NonNull TelephonyConferenceBase conference) {
+        addConference(conference);
+        conference.addTelephonyConferenceListener(mTelephonyConferenceListener);
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
index 1ced34a..6c29bd2 100644
--- a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
+++ b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
@@ -30,7 +30,6 @@
     Collection<Connection> getAllConnections();
     void addConference(TelephonyConference mTelephonyConference);
     void addConference(ImsConference mImsConference);
-    void removeConnection(Connection connection);
     void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
             Connection connection);
     void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
index 4ee9355..5c0416b 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
@@ -24,6 +24,7 @@
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -103,8 +104,8 @@
     }
 
 
-    private final ImsMmTelManager.RegistrationCallback mRegistrationCallback =
-            new ImsMmTelManager.RegistrationCallback() {
+    private final RegistrationManager.RegistrationCallback mRegistrationCallback =
+            new RegistrationManager.RegistrationCallback() {
 
         @Override
         public void onRegistered(int imsRadioTech) {
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
index 96f8bf7..f8d4487 100644
--- a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -50,7 +50,6 @@
         put(PhoneStateListener.LISTEN_DATA_CONNECTION_STATE, "DATA_CONNECTION_STATE");
         put(PhoneStateListener.LISTEN_DATA_ACTIVITY, "DATA_ACTIVITY");
         put(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS, "SIGNAL_STRENGTHS");
-        put(PhoneStateListener.LISTEN_OTASP_CHANGED, "OTASP_CHANGED");
         put(PhoneStateListener.LISTEN_CELL_INFO, "CELL_INFO");
         put(PhoneStateListener.LISTEN_PRECISE_CALL_STATE, "PRECISE_CALL_STATE");
         put(PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..22b40b5
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+android_test {
+    name: "TeleServiceTests",
+
+    srcs: ["src/**/*.java"],
+
+    libs: [
+        "android.test.mock",
+        "android.test.runner",
+        "telephony-common",
+        "android.test.base",
+        "ims-common",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+
+    instrumentation_for: "TeleService",
+
+    static_libs: [
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "androidx.test.espresso.core",
+        "truth-prebuilt",
+    ],
+
+    test_suites: [
+        "device-tests",
+        "mts",
+    ],
+
+}
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 44bf176..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := TeleServiceTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := telephony-common android.test.base
-
-LOCAL_INSTRUMENTATION_FOR := TeleService
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-        androidx.test.rules \
-        mockito-target-minus-junit4 \
-        androidx.test.espresso.core \
-        truth-prebuilt
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index d30ae6b..01267d8 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -16,13 +16,10 @@
 
 package com.android;
 
-import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
 
-import androidx.test.InstrumentationRegistry;
-
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.CountDownLatch;
@@ -33,11 +30,11 @@
  */
 public class TelephonyTestBase {
 
-    protected Context mContext;
+    protected TestContext mContext;
 
     public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
         MockitoAnnotations.initMocks(this);
+        mContext = new TestContext();
         // Set up the looper if it does not exist on the test thread.
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -86,4 +83,8 @@
             Log.e("TelephonyTestBase", "InterruptedException while waiting: " + e);
         }
     }
+
+    protected TestContext getTestContext() {
+        return mContext;
+    }
 }
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
new file mode 100644
index 0000000..776ec6a
--- /dev/null
+++ b/tests/src/com/android/TestContext.java
@@ -0,0 +1,122 @@
+/*
+ * 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;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContext;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class TestContext extends MockContext {
+
+    @Mock CarrierConfigManager mMockCarrierConfigManager;
+    @Mock TelecomManager mMockTelecomManager;
+    @Mock TelephonyManager mMockTelephonyManager;
+    @Mock SubscriptionManager mMockSubscriptionManager;
+
+    private PersistableBundle mCarrierConfig = new PersistableBundle();
+
+    public TestContext() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mCarrierConfig).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
+    }
+
+    @Override
+    public Context getApplicationContext() {
+        return this;
+    }
+
+    @Override
+    public String getPackageName() {
+        return "com.android.phone.tests";
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler, int flags) {
+        return null;
+    }
+
+    @Override
+    public Object getSystemService(String name) {
+        switch (name) {
+            case (Context.CARRIER_CONFIG_SERVICE) : {
+                return mMockCarrierConfigManager;
+            }
+            case (Context.TELECOM_SERVICE) : {
+                return mMockTelecomManager;
+            }
+            case (Context.TELEPHONY_SERVICE) : {
+                return mMockTelephonyManager;
+            }
+            case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+                return mMockSubscriptionManager;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getSystemServiceName(Class<?> serviceClass) {
+        if (serviceClass == CarrierConfigManager.class) {
+            return Context.CARRIER_CONFIG_SERVICE;
+        }
+        if (serviceClass == TelecomManager.class) {
+            return Context.TELECOM_SERVICE;
+        }
+        if (serviceClass == TelephonyManager.class) {
+            return Context.TELEPHONY_SERVICE;
+        }
+        if (serviceClass == SubscriptionManager.class) {
+            return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
+        }
+        return null;
+    }
+
+    public PersistableBundle getCarrierConfig() {
+        return mCarrierConfig;
+    }
+}
diff --git a/tests/src/com/android/phone/CallFeaturesSettingTest.java b/tests/src/com/android/phone/CallFeaturesSettingTest.java
index 78d68e3..8f49b97 100644
--- a/tests/src/com/android/phone/CallFeaturesSettingTest.java
+++ b/tests/src/com/android/phone/CallFeaturesSettingTest.java
@@ -21,8 +21,13 @@
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
+import android.app.KeyguardManager;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.rule.ActivityTestRule;
 
@@ -45,13 +50,16 @@
     IccCard mMockIccCard;
     @Rule
     public ActivityTestRule<CallFeaturesSetting> mRule =
-            new ActivityTestRule<>(CallFeaturesSetting.class);
+            new ActivityTestRule<>(CallFeaturesSetting.class, false, true);
     private CallFeaturesSetting mActivity;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Throwable {
         MockitoAnnotations.initMocks(this);
         mActivity = mRule.getActivity();
+        Context targetContext = InstrumentationRegistry.getTargetContext();
+        doReturn(targetContext).when(mMockPhone).getContext();
+        keepScreenOn(mRule, mActivity);
     }
 
     @FlakyTest
@@ -63,7 +71,7 @@
         when(mMockIccCard.getIccFdnAvailable()).thenReturn(true);
         getField("mPhone").set(mActivity, mMockPhone);
 
-        mActivity.onResume();
+        mActivity.runOnUiThread(() -> mActivity.onResume());
 
         // Check the FDN menu is displayed.
         onView(withText(R.string.fdn)).check(matches(isDisplayed()));
@@ -77,7 +85,7 @@
         when(mMockPhone.getIccCard()).thenReturn(null);
         getField("mPhone").set(mActivity, mMockPhone);
 
-        mActivity.onResume();
+        mActivity.runOnUiThread(() -> mActivity.onResume());
 
         // Check the FDN menu is not displayed.
         onView(withText(R.string.fdn)).check(doesNotExist());
@@ -92,7 +100,7 @@
         when(mMockIccCard.getIccFdnAvailable()).thenReturn(false);
         getField("mPhone").set(mActivity, mMockPhone);
 
-        mActivity.onResume();
+        mActivity.runOnUiThread(() -> mActivity.onResume());
 
         // Check the FDN menu is not displayed.
         onView(withText(R.string.fdn)).check(doesNotExist());
@@ -103,4 +111,19 @@
         field.setAccessible(true);
         return field;
     }
+
+    /**
+     * Automatically wake up device to perform tests.
+     */
+    private static void keepScreenOn(ActivityTestRule activityTestRule,
+            final CallFeaturesSetting activity) throws Throwable {
+        activityTestRule.runOnUiThread(() -> {
+            activity.setTurnScreenOn(true);
+            activity.setShowWhenLocked(true);
+            KeyguardManager keyguardManager =
+                    activity.getSystemService(KeyguardManager.class);
+            keyguardManager.requestDismissKeyguard(activity, null);
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
 }
diff --git a/tests/src/com/android/phone/CnapTest.java b/tests/src/com/android/phone/CnapTest.java
deleted file mode 100644
index 7161ba8..0000000
--- a/tests/src/com/android/phone/CnapTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-// Need to be in this package to access package methods.
-package com.android.phone;
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import com.android.internal.telephony.CallerInfo;
-import com.android.phone.PhoneUtils;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_ALLOWED;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_PAYPHONE;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_RESTRICTED;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_UNKNOWN;
-
-// Test suite for the Caller Name Presentation (CNAP) handling.
-// See AndroidManifest.xml how to run these tests.
-public class CnapTest extends AndroidTestCase {
-    private static final String TAG = "CnapTest";
-    private Context mContext;
-    private CallerInfo mCallerInfo;
-    // TODO: This string should be loaded from the phone package and
-    // not hardcoded.
-    private String mUnknown = "Unknown";
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mContext = getContext();
-        mCallerInfo = new CallerInfo();
-    }
-
-    // Checks the cnap 'ABSENT NUMBER' is mapped to the unknown presentation.
-    @SmallTest
-    public void testAbsentNumberIsMappedToUnknown() throws Exception {
-        String num = modifyForSpecialCnapCases("ABSENT NUMBER", PRESENTATION_ALLOWED);
-        assertIsUnknown(num);
-    }
-
-    // HELPERS
-
-    /**
-     * Checks the number and CallerInfo structure indicate the number
-     * is unknown.
-     */
-    private void assertIsUnknown(String number) {
-        assertEquals(mUnknown, number);
-        assertEquals(PRESENTATION_UNKNOWN, mCallerInfo.numberPresentation);
-        // TODO: cnapName and name presentation should be set to
-        // unknown. At least I cannot see why it shouldn't be the case
-        // assertEquals(mUnknown, mCallerInfo.cnapName);
-        // assertEquals(PRESENTATION_UNKNOWN, mCallerInfo.namePresentation);
-    }
-
-    /**
-     * Shorthand for PhoneUtils.modifyForSpecialCnapCases(mContext, mCallerInfo, ...)
-     */
-    private String modifyForSpecialCnapCases(String number, int presentation) {
-        return PhoneUtils.modifyForSpecialCnapCases(
-            mContext, mCallerInfo, number, presentation);
-    }
-}
diff --git a/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java b/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java
index 4336a20..ede0965 100644
--- a/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java
+++ b/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java
@@ -16,7 +16,7 @@
 
 package com.android.services.telephony;
 
-import static android.telecom.ConferenceParticipant.getParticipantAddress;
+import static com.android.ims.internal.ConferenceParticipant.getParticipantAddress;
 
 import static org.junit.Assert.assertEquals;
 
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
index aa832aa..edfc34f 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -16,14 +16,13 @@
 
 package com.android.services.telephony;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.times;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
 
 import android.os.Looper;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -32,7 +31,6 @@
 
 import org.junit.Before;
 import org.junit.Test;
-
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -85,7 +83,7 @@
         mControllerTest.add(mTestTelephonyConnectionA);
 
         mTestTelephonyConnectionA.setActive();
-        mTestTelephonyConnectionB.setOnHold();
+        mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
 
         assertTrue(mTestTelephonyConnectionA.getConferenceables()
                 .contains(mTestTelephonyConnectionB));
@@ -125,10 +123,9 @@
         mControllerTest.add(mTestTelephonyConnectionA);
 
         mTestTelephonyConnectionA.setActive();
-        mTestTelephonyConnectionB.setOnHold();
+        mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
 
         verify(mMockTelephonyConnectionServiceProxy, times(2))
                 .addConference(any(ImsConference.class));
-
     }
 }
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 568549b..7251402 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -31,11 +31,12 @@
 import android.os.Looper;
 import android.telecom.Call;
 import android.telecom.Conference;
-import android.telecom.ConferenceParticipant;
 import android.telecom.Connection;
 import android.telecom.PhoneAccountHandle;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.ims.internal.ConferenceParticipant;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -97,8 +98,6 @@
         imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
                 Arrays.asList(participant1));
         assertEquals(0, imsConference.getNumberOfParticipants());
-        verify(mMockTelephonyConnectionServiceProxy, times(2)).removeConnection(
-                any(Connection.class));
         reset(mMockTelephonyConnectionServiceProxy);
 
         // Back to 2!
@@ -111,6 +110,181 @@
     }
 
     /**
+     * Tests CEPs with disconnected participants present with disconnected state.
+     */
+    @Test
+    @SmallTest
+    public void testDisconnectParticipantViaDisconnectState() {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+
+        // Start off with 3 participants.
+        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);
+
+        ConferenceParticipant participant3 = new ConferenceParticipant(
+                Uri.parse("tel:6505551214"),
+                "A",
+                Uri.parse("sip:6505551214@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2, participant3));
+        assertEquals(3, imsConference.getNumberOfParticipants());
+        verify(mMockTelephonyConnectionServiceProxy, times(3)).addExistingConnection(
+                any(PhoneAccountHandle.class), any(Connection.class),
+                eq(imsConference));
+
+
+        // Mark one participant as disconnected.
+        ConferenceParticipant participant3Disconnected = new ConferenceParticipant(
+                Uri.parse("tel:6505551214"),
+                "A",
+                Uri.parse("sip:6505551214@testims.com"),
+                Connection.STATE_DISCONNECTED,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2, participant3Disconnected));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+        reset(mMockTelephonyConnectionServiceProxy);
+
+        // Now remove it from another CEP update; should still be the same number of participants
+        // and no updates.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+        verify(mMockTelephonyConnectionServiceProxy, never()).addExistingConnection(
+                any(PhoneAccountHandle.class), any(Connection.class),
+                any(Conference.class));
+    }
+
+    /**
+     * Tests CEPs with removed participants.
+     */
+    @Test
+    @SmallTest
+    public void testDisconnectParticipantViaRemoval() {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+
+        // Start off with 3 participants.
+        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);
+
+        ConferenceParticipant participant3 = new ConferenceParticipant(
+                Uri.parse("tel:6505551214"),
+                "A",
+                Uri.parse("sip:6505551214@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2, participant3));
+        assertEquals(3, imsConference.getNumberOfParticipants());
+        verify(mMockTelephonyConnectionServiceProxy, times(3)).addExistingConnection(
+                any(PhoneAccountHandle.class), any(Connection.class),
+                eq(imsConference));
+        reset(mMockTelephonyConnectionServiceProxy);
+
+        // Remove one from the CEP (don't disconnect first); should have 2 participants now.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+        verify(mMockTelephonyConnectionServiceProxy, never()).addExistingConnection(
+                any(PhoneAccountHandle.class), any(Connection.class),
+                any(Conference.class));
+    }
+
+    /**
+     * Typically when a participant disconnects from a conference it is either:
+     * 1. Removed from a subsequent CEP update.
+     * 2. Marked as disconnected in a CEP update, and then removed from another CEP update.
+     *
+     * When a participant disconnects from a conference, some carriers will mark the disconnected
+     * participant as disconnected, but fail to send another CEP update with it removed.
+     *
+     * This test verifies that we can still enter single party emulation in this case.
+     */
+    @Test
+    @SmallTest
+    public void testSinglePartyEmulationEnterOnDisconnectParticipant() {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+
+        // Setup the initial conference state with 2 participants.
+        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());
+        verify(mMockTelephonyConnectionServiceProxy, times(2)).addExistingConnection(
+                any(PhoneAccountHandle.class), any(Connection.class),
+                eq(imsConference));
+
+        // Some carriers keep disconnected participants around in the CEP; this will cause problems
+        // when we want to enter single party conference mode. Verify that this case is handled.
+        ConferenceParticipant participant2Disconnected = new ConferenceParticipant(
+                Uri.parse("tel:6505551213"),
+                "A",
+                Uri.parse("sip:6505551213@testims.com"),
+                Connection.STATE_DISCONNECTED,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2Disconnected));
+        assertEquals(0, imsConference.getNumberOfParticipants());
+        reset(mMockTelephonyConnectionServiceProxy);
+
+        // Pretend to merge someone else into the conference.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+        verify(mMockTelephonyConnectionServiceProxy, times(2)).addExistingConnection(
+                any(PhoneAccountHandle.class), any(Connection.class),
+                eq(imsConference));
+    }
+
+    /**
      * We have seen a scenario on a carrier where a conference event package comes in just prior to
      * the call disconnecting with only the conference host in it.  This caused a problem because
      * it triggered exiting single party conference mode (due to a bug) and caused the call to not
@@ -127,14 +301,14 @@
                 null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
 
         final boolean[] isConferenceState = new boolean[1];
-        Conference.Listener conferenceListener = new Conference.Listener() {
+        TelephonyConferenceBase.TelephonyConferenceListener conferenceListener =
+                new TelephonyConferenceBase.TelephonyConferenceListener() {
             @Override
-            public void onConferenceStateChanged(Conference c, boolean isConference) {
-                super.onConferenceStateChanged(c, isConference);
-                isConferenceState[0] = isConference;
+            public void onConferenceMembershipChanged(Connection connection) {
+                isConferenceState[0] = connection.getConference() != null;
             }
         };
-        imsConference.addListener(conferenceListener);
+        imsConference.addTelephonyConferenceListener(conferenceListener);
 
         ConferenceParticipant participant1 = new ConferenceParticipant(
                 Uri.parse("tel:6505551212"),
@@ -159,8 +333,6 @@
         imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
                 Arrays.asList(participant1));
         assertEquals(0, imsConference.getNumberOfParticipants());
-        verify(mMockTelephonyConnectionServiceProxy, times(2)).removeConnection(
-                any(Connection.class));
 
         // Emulate a pre-disconnect conference event package; there will be zero participants.
         imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index 275bcc6..cfdc2fd 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -16,6 +16,13 @@
 
 package com.android.services.telephony;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.os.Looper;
 import android.telecom.Conference;
 import android.telecom.Connection;
@@ -23,18 +30,9 @@
 
 import org.junit.Before;
 import org.junit.Test;
-
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.ArgumentCaptor;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.any;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -50,7 +48,7 @@
     private TelephonyConnectionServiceProxy mMockTelephonyConnectionServiceProxy;
 
     @Mock
-    private Conference.Listener mMockListener;
+    private TelephonyConferenceBase.TelephonyConferenceListener mMockListener;
 
     private TestTelephonyConnection mTestTelephonyConnectionA;
     private TestTelephonyConnection mTestTelephonyConnectionB;
@@ -94,8 +92,8 @@
         // add telephony connection A
         mControllerTest.add(mTestTelephonyConnectionA);
 
-        mTestTelephonyConnectionA.setActive();
-        mTestTelephonyConnectionB.setOnHold();
+        mTestTelephonyConnectionA.setTelephonyConnectionActive();
+        mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
 
         assertTrue(mTestTelephonyConnectionA.getConferenceables()
                 .contains(mTestTelephonyConnectionB));
@@ -146,8 +144,8 @@
         // add telephony connection A
         mControllerTest.add(mTestTelephonyConnectionA);
 
-        mTestTelephonyConnectionA.setActive();
-        mTestTelephonyConnectionB.setOnHold();
+        mTestTelephonyConnectionA.setTelephonyConnectionActive();
+        mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
 
         assertTrue(mTestTelephonyConnectionA.getConferenceables()
                 .contains(mTestTelephonyConnectionB));
@@ -160,7 +158,7 @@
         verify(mMockTelephonyConnectionServiceProxy).addConference(argumentCaptor.capture());
 
         // add a listener to the added conference
-        argumentCaptor.getValue().addListener(mMockListener);
+        argumentCaptor.getValue().addTelephonyConferenceListener(mMockListener);
 
         verify(mMockListener, never()).onDestroyed(any(Conference.class));
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 1329a77..6e11e51 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -17,35 +17,48 @@
 package com.android.services.telephony;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.Context;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 
 import org.junit.After;
@@ -56,6 +69,8 @@
 import org.mockito.Mock;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -69,19 +84,72 @@
     private static final int SLOT_0_PHONE_ID = 0;
     private static final int SLOT_1_PHONE_ID = 1;
 
+    private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+            "com.android.phone.tests", TelephonyConnectionServiceTest.class.getName());
+    private static final String TEST_ACCOUNT_ID1 = "id1";
+    private static final String TEST_ACCOUNT_ID2 = "id2";
+    private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_1 = new PhoneAccountHandle(
+            TEST_COMPONENT_NAME, TEST_ACCOUNT_ID1);
+    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");
+
     @Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
     @Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
     @Mock TelephonyConnectionService.PhoneFactoryProxy mPhoneFactoryProxy;
+    @Mock DeviceState mDeviceState;
+    @Mock TelephonyConnectionService.PhoneSwitcherProxy mPhoneSwitcherProxy;
+    @Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
+    @Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
+    @Mock TelephonyConnectionService.HandlerFactory mHandlerFactory;
+    @Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
+    @Mock Handler mMockHandler;
+    @Mock EmergencyNumberTracker mEmergencyNumberTracker;
+    @Mock PhoneSwitcher mPhoneSwitcher;
 
-    TelephonyConnectionService mTestConnectionService;
+    private static class TestTelephonyConnectionService extends TelephonyConnectionService {
+
+        private final Context mContext;
+
+        TestTelephonyConnectionService(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public void onCreate() {
+            // attach test context.
+            attachBaseContext(mContext);
+            super.onCreate();
+        }
+    }
+
+    private TelephonyConnectionService mTestConnectionService;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mTestConnectionService = new TelephonyConnectionService();
+        mTestConnectionService = new TestTelephonyConnectionService(mContext);
         mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
-        mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
         mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
+        // Set configurations statically
+        doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
+        mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
+        doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+        mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
+        mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
+        HandlerThread mockHandlerThread = mock(HandlerThread.class);
+        doReturn(mockHandlerThread).when(mHandlerFactory).createHandlerThread(anyString());
+        doReturn(null).when(mockHandlerThread).getLooper();
+        doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
+        mTestConnectionService.setHandlerFactory(mHandlerFactory);
+        mTestConnectionService.setDeviceState(mDeviceState);
+        doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
+                .toTelecomDisconnectCause(anyInt(), any());
+        doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
+                .toTelecomDisconnectCause(anyInt(), any(), anyInt());
+        mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
+        mTestConnectionService.onCreate();
+        mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
     }
 
     @After
@@ -278,6 +346,37 @@
 
     /**
      * Prerequisites:
+     * - MSIM Device, only slot 1 inserted and PUK locked
+     * - slot 1 has higher capabilities
+     *
+     * Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is the only one
+     * with a SIM inserted (even if it is PUK locked)
+     */
+    @Test
+    @SmallTest
+    public void testSlot1PinLockedAndSlot0Absent() {
+        Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+                false /*isEmergencyOnly*/);
+        setDefaultPhone(slot0Phone);
+        setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+        setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
+        setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_PIN_REQUIRED);
+        // Slot 1 has more capabilities
+        setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
+        setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
+        // Slot 1 has SIM inserted.
+        setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/);
+        setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/);
+
+        Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+        assertEquals(slot1Phone, resultPhone);
+    }
+
+    /**
+     * Prerequisites:
      * - MSIM Device, two slots with SIMs inserted
      * - Slot 1 is LTE capable, Slot 0 is GSM capable
      *
@@ -522,7 +621,6 @@
      * called.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialTempFailOneSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -531,7 +629,7 @@
         List<Phone> phones = new ArrayList<>(1);
         phones.add(slot0Phone);
         setPhones(phones);
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
 
@@ -554,7 +652,6 @@
      * not called.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialPermFailOneSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -563,7 +660,7 @@
         List<Phone> phones = new ArrayList<>(1);
         phones.add(slot0Phone);
         setPhones(phones);
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
 
@@ -588,7 +685,6 @@
      * PhoneAccount.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -597,11 +693,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
 
@@ -626,7 +726,6 @@
      * PhoneAccount.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -635,11 +734,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
 
@@ -664,7 +767,6 @@
      * notified of this twice.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot_twoFailure() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -673,11 +775,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         // First Temporary failure
         mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
@@ -716,7 +822,6 @@
      * notified of the change to slot 1.
      */
     @Test
-    @FlakyTest
     @SmallTest
     public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot_twoFailure() {
         TestTelephonyConnection c = new TestTelephonyConnection();
@@ -725,11 +830,15 @@
         Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
                 false /*isEmergencyOnly*/);
         setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
-        c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+        c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
         List<Phone> phones = new ArrayList<>(2);
         phones.add(slot0Phone);
         phones.add(slot1Phone);
         setPhones(phones);
+        doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot0Phone);
+        doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+                slot1Phone);
 
         // First Permanent failure
         mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
@@ -767,6 +876,7 @@
         // registration to occur.
         Phone phone = c.getPhone();
         c.setOriginalConnection(c.getOriginalConnection());
+        doReturn(mContext).when(phone).getContext();
 
         // When the registration occurs, we'll capture the handler and message so we can post our
         // own messages to it.
@@ -803,6 +913,239 @@
                 extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
     }
 
+    /**
+     * Test that the TelephonyConnectionService successfully performs a DDS switch before a call
+     * when we are not roaming and the carrier only supports SUPL over the data plane.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_carrierconfig_dds() {
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(false);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
+        delayDialRunnable.run();
+
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+                eq(150) /*extensionTime*/, any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
+     * supports control-plane fallback.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_nocarrierconfig() {
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(false);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+        delayDialRunnable.run();
+
+        verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
+     * supports control-plane fallback.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        // If the non-DDS supports SUPL, dont switch data
+        doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(false);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+        delayDialRunnable.run();
+
+        verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier does
+     * not support control-plane fallback CarrierConfig while roaming.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_roaming_nocarrierconfig() {
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                null);
+        testPhone.getServiceState().setRoaming(true);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+        delayDialRunnable.run();
+
+        verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
+     * supports control-plane fallback CarrierConfig and the roaming partner is configured to look
+     * like a home network.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial_roamingcarrierconfig() {
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        // Setup voice roaming scenario
+        String testRoamingOperator = "001001";
+        // In some roaming conditions, we are not technically "roaming"
+        testPhone.getServiceState().setRoaming(false);
+        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        String[] roamingPlmns = new String[1];
+        roamingPlmns[0] = testRoamingOperator;
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                roamingPlmns);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+        delayDialRunnable.run();
+
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+                eq(0) /*extensionTime*/, any());
+    }
+
+    /**
+     * Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
+     * supports control-plane fallback CarrierConfig if we are roaming and the roaming partner is
+     * configured to use data plane only SUPL.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_delayDial__roaming_roamingcarrierconfig() {
+        Phone testPhone = setupConnectionServiceForDelayDial();
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        // Setup voice roaming scenario
+        String testRoamingOperator = "001001";
+        testPhone.getServiceState().setRoaming(true);
+        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+        // Setup test to not support SUPL on the non-DDS subscription
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        String[] roamingPlmns = new String[1];
+        roamingPlmns[0] = testRoamingOperator;
+        getTestContext().getCarrierConfig().putStringArray(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+                roamingPlmns);
+        getTestContext().getCarrierConfig().putInt(
+                CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+        getTestContext().getCarrierConfig().putString(
+                CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+        delayDialRunnable.run();
+
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+                eq(0) /*extensionTime*/, any());
+    }
+
+    /**
+     * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number.
+     * @return the Phone associated with slot 0.
+     */
+    private Phone setupConnectionServiceForDelayDial() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_OUT_OF_SERVICE,
+                false /*isEmergencyOnly*/);
+        List<Phone> phones = new ArrayList<>(2);
+        doReturn(true).when(testPhone0).isRadioOn();
+        doReturn(true).when(testPhone1).isRadioOn();
+        phones.add(testPhone0);
+        phones.add(testPhone1);
+        setPhones(phones);
+        setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+        setupDeviceConfig(testPhone0, testPhone1, 1);
+        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();
+
+        android.telecom.Connection testConnection = mTestConnectionService
+                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", testConnection);
+
+        return testPhone0;
+    }
+
+    private Runnable verifyRunnablePosted() {
+        ArgumentCaptor<Message> runnableCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockHandler).sendMessageDelayed(runnableCaptor.capture(), anyLong());
+        assertNotNull("Invalid Message created", runnableCaptor.getValue());
+        Runnable runnable = runnableCaptor.getValue().getCallback();
+        assertNotNull("sendMessageDelayed never occurred.", runnableCaptor);
+        return runnable;
+    }
+
+    private EmergencyNumber setupEmergencyNumber(Uri address) {
+        return new EmergencyNumber(address.getSchemeSpecificPart(), "", "",
+        EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+        Collections.emptyList(),
+        EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+        EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+    }
+
+    private void setupHandleToPhoneMap(PhoneAccountHandle handle,  Phone phone) {
+        // use subId 0
+        when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(handle)).thenReturn(0);
+        when(mSubscriptionManagerProxy.getPhoneId(0)).thenReturn(0);
+        when(mPhoneFactoryProxy.getPhone(0)).thenReturn(phone);
+    }
+
     private AsyncResult getSuppServiceNotification(int notificationType, int code) {
         SuppServiceNotification notification = new SuppServiceNotification();
         notification.notificationType = notificationType;
@@ -815,9 +1158,12 @@
         ServiceState testServiceState = new ServiceState();
         testServiceState.setState(serviceState);
         testServiceState.setEmergencyOnly(isEmergencyOnly);
+        when(phone.getContext()).thenReturn(mContext);
         when(phone.getServiceState()).thenReturn(testServiceState);
         when(phone.getPhoneId()).thenReturn(phoneId);
         when(phone.getDefaultPhone()).thenReturn(phone);
+        when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
+        when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
         return phone;
     }
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
new file mode 100644
index 0000000..7d15680
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -0,0 +1,25 @@
+package com.android.services.telephony;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.os.Bundle;
+import android.telecom.Connection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+public class TelephonyConnectionTest {
+
+    @Test
+    public void testCodecInIms() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.updateState();
+        Bundle extras = c.getExtras();
+        int codec = extras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE);
+        assertEquals(codec, Connection.AUDIO_CODEC_AMR);
+    }
+
+}
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index b6e4bf3..5b31c0f 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -19,10 +19,12 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.telecom.PhoneAccountHandle;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -31,6 +33,7 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -56,6 +59,9 @@
     @Mock
     Resources mMockResources;
 
+    @Mock
+    EmergencyNumberTracker mEmergencyNumberTracker;
+
     private Phone mMockPhone;
     private int mNotifyPhoneAccountChangedCount = 0;
     private List<String> mLastConnectionEvents = new ArrayList<>();
@@ -75,11 +81,16 @@
         mOriginalConnection = mock(Connection.class);
         // Set up mMockRadioConnection and mMockPhone to contain an active call
         when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
+        when(mOriginalConnection.getState()).thenReturn(Call.State.ACTIVE);
+        when(mMockRadioConnection.getAudioCodec()).thenReturn(
+                android.telecom.Connection.AUDIO_CODEC_AMR);
         when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
         when(mMockRadioConnection.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
         doNothing().when(mMockRadioConnection).addListener(any(Connection.Listener.class));
         doNothing().when(mMockRadioConnection).addPostDialListener(
                 any(Connection.PostDialListener.class));
+        when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
+        when(mMockPhone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
         when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
         when(mMockPhone.getContext()).thenReturn(mMockContext);
         when(mMockPhone.getCurrentSubscriberUris()).thenReturn(null);
@@ -125,6 +136,28 @@
         // Do nothing since the original connection is mock object
     }
 
+    @Override
+    public PersistableBundle getCarrierConfig() {
+        // Depends on PhoneGlobals for context in TelephonyConnection, do not implement during
+        // testing.
+        return new PersistableBundle();
+    }
+
+    @Override
+    public CharSequence getResourceText(int messageId) {
+        return "TEST";
+    }
+
+    @Override
+    public String getResourceString(int id) {
+        return "TEST";
+    }
+
+    @Override
+    void refreshConferenceSupported() {
+        // Requires ImsManager dependencies, do not implement during testing.
+    }
+
     public int getNotifyPhoneAccountChangedCount() {
         return mNotifyPhoneAccountChangedCount;
     }