Snap for 6001391 from 624edfdec69eeac489e6d52e998575b965aab7c0 to qt-aml-resolv-release

Change-Id: I2c3b3a99e21ef41d4ce01a983ec640180ac52527
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..455634f
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,78 @@
+// 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",
+    ],
+
+    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: [
+        "src/**/*.java",
+        "sip/src/**/*.java",
+        "ecc/proto/**/*.proto",
+        "src/com/android/phone/EventLogTags.logtags",
+    ],
+
+    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/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-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/strings.xml b/res/values/strings.xml
index 3e821dc..689e239 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -977,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
@@ -1196,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 -->
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 320fc24..17a1734 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -202,7 +202,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();
@@ -228,8 +229,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 +239,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);
             }
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 392ab6e..874c412 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -141,6 +141,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;
 
@@ -174,27 +176,21 @@
         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.
@@ -211,7 +207,7 @@
                     // 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) {
                             updateConfigForPhoneId(i);
                         }
@@ -494,6 +490,9 @@
                 case EVENT_SUBSCRIPTION_INFO_UPDATED:
                     broadcastConfigChangedIntent(phoneId);
                     break;
+                case EVENT_MULTI_SIM_CONFIG_CHANGED:
+                    onMultiSimConfigChanged();
+                    break;
             }
         }
     }
@@ -523,7 +522,7 @@
         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];
         mOverrideConfigs = new PersistableBundle[numPhones];
@@ -553,6 +552,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 +582,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));
@@ -910,6 +934,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(
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index f5c14b9..c20281a 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -70,9 +70,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
 
-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;
@@ -221,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
@@ -257,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));
 
@@ -498,10 +491,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 {
@@ -638,10 +627,6 @@
     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);
@@ -745,28 +730,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) {
@@ -779,19 +756,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());
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 9e7121d..186de03 100644
--- a/src/com/android/phone/EmergencyInfoGroup.java
+++ b/src/com/android/phone/EmergencyInfoGroup.java
@@ -153,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.
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index f77595b..9e51e82 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);
     }
@@ -209,15 +198,6 @@
                 }
                 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 (mOnConfirmClickListener != null) {
                     mOnConfirmClickListener.onConfirmClick(this);
                 }
@@ -229,7 +209,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(
@@ -266,8 +245,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();
@@ -282,12 +259,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/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
new file mode 100644
index 0000000..d1ff56f
--- /dev/null
+++ b/src/com/android/phone/ImsRcsController.java
@@ -0,0 +1,128 @@
+/*
+ * 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.ServiceManager;
+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.util.Log;
+
+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);
+    }
+
+    @Override
+    public void registerRcsAvailabilityCallback(IImsCapabilityCallback c) {
+        enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
+    }
+
+    @Override
+    public void unregisterRcsAvailabilityCallback(IImsCapabilityCallback c) {
+        enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
+    }
+
+    @Override
+    public boolean isCapable(int subId,
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        enforceReadPrivilegedPermission("isCapable");
+        return false;
+    }
+
+    @Override
+    public boolean isAvailable(int subId,
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+        enforceReadPrivilegedPermission("isAvailable");
+        return false;
+    }
+
+    @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);
+    }
+}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 3443115..9784edb 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -379,7 +379,7 @@
             for (int i = 0; i < users.size(); i++) {
                 final UserInfo user = users.get(i);
                 final UserHandle userHandle = user.getUserHandle();
-                if (!mUserManager.hasUserRestriction(
+                if (!hasUserRestriction(
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
                         && !user.isManagedProfile()) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
@@ -397,7 +397,7 @@
             for (int i = 0; i < users.size(); i++) {
                 final UserInfo user = users.get(i);
                 final UserHandle userHandle = user.getUserHandle();
-                if (!mUserManager.hasUserRestriction(
+                if (!hasUserRestriction(
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
                         && !user.isManagedProfile()) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
@@ -412,6 +412,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
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 c3c3eb8..b9c6728 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -37,12 +37,11 @@
 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;
@@ -148,6 +147,7 @@
     CallerInfoCache callerInfoCache;
     NotificationMgr notificationMgr;
     public PhoneInterfaceManager phoneMgr;
+    public ImsRcsController imsRcsController;
     CarrierConfigLoader configLoader;
 
     private Phone phoneInEcm;
@@ -187,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);
 
@@ -274,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();
                     }
@@ -347,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.
             //
@@ -361,6 +354,8 @@
 
             phoneMgr = PhoneInterfaceManager.init(this);
 
+            imsRcsController = ImsRcsController.init(this);
+
             configLoader = CarrierConfigLoader.init(this);
 
             // Create the CallNotifier singleton, which handles
@@ -505,19 +500,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;
     }
@@ -568,7 +550,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);
     }
@@ -579,7 +561,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);
     }
 
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 35743ef..b5fca03 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -62,6 +62,7 @@
 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;
@@ -94,11 +95,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;
@@ -106,6 +107,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;
@@ -142,8 +144,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;
@@ -176,7 +176,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;
@@ -1078,11 +1077,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);
@@ -2906,6 +2907,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.
@@ -2924,24 +2929,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 {
@@ -2957,22 +3035,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
@@ -3007,6 +3086,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");
@@ -4473,6 +4590,40 @@
         }
     }
 
+    /**
+     * 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();
 
@@ -5577,6 +5728,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 {
@@ -5665,6 +5831,11 @@
             // 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();
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6306,7 +6477,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);
     }
 
     /**
@@ -6886,75 +7058,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) {
@@ -7302,10 +7405,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..3625733 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -43,10 +43,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 +51,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 +126,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 +179,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;
@@ -662,533 +586,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/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..428c006 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -16,11 +16,15 @@
 
 package com.android.phone;
 
+import android.content.Context;
 import android.os.Binder;
+import android.os.Build;
+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;
@@ -30,6 +34,9 @@
 
 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,9 @@
     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 IMS_SET_CARRIER_SERVICE = "set-ims-service";
     private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
@@ -58,18 +65,63 @@
     // 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
+    }
+
+    // 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 +134,13 @@
             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();
+            }
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -103,13 +155,13 @@
         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("  cc");
+        pw.println("    Carrier Config Commands.");
         onHelpIms();
-        onHelpSms();
         onHelpEmergencyNumber();
+        onHelpCc();
     }
 
     private void onHelpIms() {
@@ -139,18 +191,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");
@@ -176,6 +216,33 @@
         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] 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("    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) {
@@ -501,85 +568,50 @@
         return slotId;
     }
 
-    private int handleSmsCommand() {
-        String arg = getNextArg();
-        if (arg == null) {
-            onHelpSms();
-            return 0;
-        }
-
-        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;
+    // Get the subId from argument SLOT_ID if it was provided. Otherwise use the default
+    // subscription.
+    private int getSubIdFromArgumentSlotId(String tag) {
+        PrintWriter errPw = getErrPrintWriter();
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
         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;
+                        subId = slotStringToSubId(tag, getNextArgRequired());
+                    } catch (IllegalArgumentException e) {
+                        // Missing slot id
+                        errPw.println(tag + "SLOT_ID expected after -s.");
+                        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
                     }
                     break;
                 }
+                default: {
+                    errPw.println(tag + "Unknown option " + opt);
+                    return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+                }
             }
         }
-        return userId;
+        return subId;
     }
 
-    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 +619,435 @@
         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 || Build.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;
+
+        // Get the subId from the SLOT_ID-argument.
+        int subId = getSubIdFromArgumentSlotId(tag);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            errPw.println(tag + "No valid subscription found.");
+            return -1;
+        }
+
+        // Get bundle containing all carrier configuration values.
+        PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(subId);
+        if (bundle == null) {
+            errPw.println(tag + "No carrier config values found for subId " + subId + ".");
+            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 + ": ";
+
+        // Get the subId from the SLOT_ID-argument.
+        int subId = getSubIdFromArgumentSlotId(tag);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            errPw.println(tag + "No valid subscription found.");
+            return -1;
+        }
+
+        // Get bundle containing all current carrier configuration values.
+        PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(subId);
+        if (originalValues == null) {
+            errPw.println(tag + "No carrier config values found for subId " + subId + ".");
+            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(subId, overrideBundle);
+
+        // Find bundle containing all new carrier configuration values after the override.
+        PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(subId);
+        if (newValues == null) {
+            errPw.println(tag + "No carrier config values found for subId " + subId + ".");
+            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 + ": ";
+
+        // Get the subId from the SLOT_ID-argument.
+        int subId = getSubIdFromArgumentSlotId(tag);
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            errPw.println(tag + "No valid subscription found.");
+            return -1;
+        }
+
+        // Clear all values that has previously been set.
+        mCarrierConfigManager.overrideConfig(subId, null);
+        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/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index 7c7b75d..4c85818 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -153,7 +153,7 @@
             mPackageManager.revokeDefaultPermissionsFromLuiApps(luiAppsArray, getUserId());
         } 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/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/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/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/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 1969b10..17549cf 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -21,7 +21,6 @@
 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;
@@ -68,7 +67,7 @@
  * 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 {
 
     /**
      * Abstracts out fetching a feature flag.  Makes testing easier.
@@ -78,149 +77,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);
+                }
 
-        /**
-         * 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) {
+                /**
+                 * 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);
+                }
 
-            if (c == null || participants == null) {
-                return;
-            }
-            Log.v(this, "onConferenceParticipantsChanged: %d participants", participants.size());
-            TelephonyConnection telephonyConnection = (TelephonyConnection) c;
-            handleConferenceParticipantsUpdate(telephonyConnection, participants);
-        }
-    };
+                @Override
+                public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
+                    Log.d(this, "onVideoStateChanged video state %d", videoState);
+                    setVideoState(c, videoState);
+                }
 
-    /**
-     * 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() {
+                @Override
+                public void onVideoProviderChanged(android.telecom.Connection c,
+                        Connection.VideoProvider videoProvider) {
+                    Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+                            videoProvider);
+                    setVideoProvider(c, videoProvider);
+                }
 
-        /**
-         * 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 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));
+                }
 
-        /**
-         * 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 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();
-        }
+                    sendConnectionEvent(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) {
-            if (Connection.EVENT_MERGE_START.equals(event)) {
-                // Do not pass a merge start event on the underlying host connection; we only
-                // indicate a merge has started on the connections which are merged into a
-                // conference.
-                return;
-            }
-            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.
@@ -447,7 +424,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.
      */
@@ -702,7 +679,6 @@
             setConnectionTime(mConferenceHost.getConnectTimeMillis());
         }
 
-        mConferenceHost.addConnectionListener(mConferenceHostListener);
         mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener);
         setConnectionCapabilities(applyHostCapabilities(getConnectionCapabilities(),
                 mConferenceHost.getConnectionCapabilities(),
@@ -804,6 +780,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());
                     }
                 }
@@ -817,6 +800,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());
                     }
                 }
@@ -832,9 +822,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);
                         entryIterator.remove();
                         oldParticipantsRemoved = true;
                     }
@@ -951,9 +940,8 @@
             // again anyways.
             entry.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED,
                     DisconnectCause.REASON_EMULATING_SINGLE_CALL));
-            entry.removeConnectionListener(mParticipantListener);
             mTelephonyConnectionService.removeConnection(entry);
-            removeConnection(entry);
+            removeTelephonyConnection(entry);
             valueIterator.remove();
         }
 
@@ -985,7 +973,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());
@@ -1007,7 +994,7 @@
 
         mTelephonyConnectionService.addExistingConnection(mConferenceHostPhoneAccountHandle,
                 connection, this);
-        addConnection(connection);
+        addTelephonyConnection(connection);
     }
 
     /**
@@ -1018,7 +1005,6 @@
     private void removeConferenceParticipant(ConferenceParticipantConnection participant) {
         Log.i(this, "removeConferenceParticipant: %s", participant);
 
-        participant.removeConnectionListener(mParticipantListener);
         synchronized(mUpdateSyncRoot) {
             mConferenceParticipantConnections.remove(new Pair<>(participant.getUserEntity(),
                     participant.getEndpoint()));
@@ -1036,12 +1022,12 @@
             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();
+                handleConnectionDestruction(connection);
             }
             mConferenceParticipantConnections.clear();
         }
@@ -1154,7 +1140,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
@@ -1163,12 +1149,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();
@@ -1207,7 +1192,7 @@
                 }
                 setDisconnected(disconnectCause);
                 disconnectConferenceParticipants();
-                destroy();
+                destroyTelephonyConference();
                 break;
             case Connection.STATE_ACTIVE:
                 setActive();
@@ -1338,4 +1323,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 b193c7f..b7f7b92 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -66,13 +66,7 @@
             Log.v(this, "onConferenceSupportedChanged");
             recalculate();
         }
-    };
 
-    /**
-     * 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()));
@@ -151,9 +145,9 @@
         }
 
         mTelephonyConnections.add(connection);
-        connection.addConnectionListener(mConnectionListener);
         connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
         recalculateConference();
+        recalculateConferenceable();
     }
 
     /**
@@ -179,7 +173,6 @@
             Log.v(this, "remove connection: %s", connection);
         }
 
-        connection.removeConnectionListener(mConnectionListener);
         if (connection instanceof TelephonyConnection) {
             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
             telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
@@ -395,13 +388,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.removeTelephonyConnectionListener(mTelephonyConnectionListener);
-        connection.clearOriginalConnection();
-        connection.setDisconnected(new DisconnectCause(DisconnectCause.OTHER,
+        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/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 1e681e8..2e923ec 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -803,8 +803,10 @@
                 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()
+                UserHandle currentUserHandle = UserHandle.of(userHandleId);
+                UserManager userManager =
+                        (UserManager) context.getSystemService(Context.USER_SERVICE);
+                mIsPrimaryUser = userManager.getPrimaryUser().getUserHandle()
                         .equals(currentUserHandle);
 
                 // Any time the user changes, re-register the accounts.
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..31d0f56
--- /dev/null
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -0,0 +1,129 @@
+/*
+ * 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.telecom.Conference;
+import android.telecom.Connection;
+import android.telecom.PhoneAccountHandle;
+
+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 {
+    /**
+     * 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) {};
+    }
+
+    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 addListener(@NonNull TelephonyConferenceListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from this conference.
+     * @param listener The listener.
+     */
+    public void removeListener(@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();
+    }
+
+    /**
+     * 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);
+        }
+    }
+}
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 1601f00..c1d2995 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,6 +16,8 @@
 
 package com.android.services.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -102,6 +104,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
@@ -112,10 +115,20 @@
                     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 &&
@@ -130,7 +143,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:
@@ -175,7 +188,7 @@
 
                 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
@@ -187,7 +200,7 @@
 
                 case MSG_SET_VIDEO_PROVIDER:
                     VideoProvider videoProvider = (VideoProvider) msg.obj;
-                    setVideoProvider(videoProvider);
+                    setTelephonyVideoProvider(videoProvider);
                     break;
 
                 case MSG_SET_AUDIO_QUALITY:
@@ -227,9 +240,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;
@@ -274,7 +287,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();
@@ -293,7 +306,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);
     }
@@ -311,7 +324,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);
     }
 
     /**
@@ -412,6 +426,20 @@
                 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() {
@@ -545,7 +573,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.
@@ -560,7 +588,7 @@
          */
         @Override
         public void onHandoverToWifiFailed() {
-            sendConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
+            sendTelephonyConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
         }
 
         /**
@@ -1075,6 +1103,7 @@
 
         if (getConnectionCapabilities() != newCapabilities) {
             setConnectionCapabilities(newCapabilities);
+            notifyConnectionCapabilitiesChanged(newCapabilities);
         }
     }
 
@@ -1111,10 +1140,15 @@
                 isNetworkIdentifiedEmergencyCall());
 
         if (getConnectionProperties() != newProperties) {
-            setConnectionProperties(newProperties);
+            setTelephonyConnectionProperties(newProperties);
         }
     }
 
+    public void setTelephonyConnectionProperties(int newProperties) {
+        setConnectionProperties(newProperties);
+        notifyConnectionPropertiesChanged(newProperties);
+    }
+
     protected final void updateAddress() {
         updateConnectionCapabilities();
         updateConnectionProperties();
@@ -1178,6 +1212,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);
@@ -1187,11 +1223,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();
 
@@ -1226,8 +1262,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.
@@ -1277,7 +1313,7 @@
                 newExtras = new Bundle();
             }
             newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
-            putExtras(newExtras);
+            putTelephonyExtras(newExtras);
         }
     }
 
@@ -1288,7 +1324,7 @@
                 newExtras = new Bundle();
             }
             newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
-            putExtras(newExtras);
+            putTelephonyExtras(newExtras);
         } else {
             removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
         }
@@ -1426,6 +1462,7 @@
                 getPhone().unregisterForPreciseCallStateChanged(mHandler);
                 getPhone().unregisterForRingbackTone(mHandler);
                 getPhone().unregisterForHandoverStateChanged(mHandler);
+                getPhone().unregisterForRedialConnectionChanged(mHandler);
                 getPhone().unregisterForDisconnect(mHandler);
                 getPhone().unregisterForSuppServiceNotification(mHandler);
                 getPhone().unregisterForOnHoldTone(mHandler);
@@ -1469,7 +1506,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();
@@ -1590,7 +1627,7 @@
                     }
 
                     // Ensure extras are propagated to Telecom.
-                    putExtras(mOriginalConnectionExtras);
+                    putTelephonyExtras(mOriginalConnectionExtras);
                 } else {
                     Log.d(this, "Extras update not required");
                 }
@@ -1659,19 +1696,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()
@@ -1692,11 +1729,12 @@
                             preciseDisconnectCause =
                                     mOriginalConnection.getPreciseDisconnectCause();
                         }
-                        setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
-                                mOriginalConnection.getDisconnectCause(),
-                                preciseDisconnectCause,
-                                mOriginalConnection.getVendorDisconnectCause(),
-                                getPhone().getPhoneId()));
+                        setTelephonyConnectionDisconnected(
+                                DisconnectCauseUtil.toTelecomDisconnectCause(
+                                        mOriginalConnection.getDisconnectCause(),
+                                        preciseDisconnectCause,
+                                        mOriginalConnection.getVendorDisconnectCause(),
+                                        getPhone().getPhoneId()));
                         close();
                     }
                     break;
@@ -1786,13 +1824,14 @@
                 }
             }
         }
-        setActive();
+        setTelephonyConnectionActive();
     }
 
-    protected void close() {
+    public void close() {
         Log.v(this, "close");
         clearOriginalConnection();
         destroy();
+        notifyDestroyed();
     }
 
     /**
@@ -2143,13 +2182,13 @@
                     : R.string.status_hint_label_wifi_call;
 
             Context context = getPhone().getContext();
-            setStatusHints(new StatusHints(
+            setTelephonyStatusHints(new StatusHints(
                     context.getString(labelId),
                     Icon.createWithResource(
                             context, R.drawable.ic_signal_wifi_4_bar_24dp),
                     null /* extras */));
         } else {
-            setStatusHints(null);
+            setTelephonyStatusHints(null);
         }
     }
 
@@ -2377,12 +2416,153 @@
     }
 
     /**
-     * Notifies listeners of a change to conference participant(s).
+     * 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);
+    }
+
+    /**
+     * 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.
      */
-    protected final void updateConferenceParticipants(
-            List<ConferenceParticipant> conferenceParticipants) {
+    private void updateConferenceParticipants(
+            @NonNull List<ConferenceParticipant> conferenceParticipants) {
         for (TelephonyConnectionListener l : mTelephonyListeners) {
             l.onConferenceParticipantsChanged(this, conferenceParticipants);
         }
@@ -2391,7 +2571,6 @@
     /**
      * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
      * operation has started.
-     * <p>
      */
     protected void notifyConferenceStarted() {
         for (TelephonyConnectionListener l : mTelephonyListeners) {
@@ -2400,14 +2579,126 @@
     }
 
     /**
-     * Notifies listeners when a change has occurred to the Connection which impacts its ability to
-     * be a part of a conference call.
+     * 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.
      */
-    protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
+    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 5e3a899..7b55bfc 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -103,11 +103,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);
+            TelephonyConnectionService.this.addTelephonyConference(mImsConference);
         }
         @Override
         public void removeConnection(Connection connection) {
@@ -129,11 +129,9 @@
         public void addConnectionToConferenceController(TelephonyConnection connection) {
             TelephonyConnectionService.this.addConnectionToConferenceController(connection);
         }
-    };
 
-    private final Connection.Listener mConnectionListener = new Connection.Listener() {
         @Override
-        public void onConferenceChanged(Connection connection, Conference conference) {
+        public void onConferenceMembershipChanged(Connection connection) {
             mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
         }
     };
@@ -320,6 +318,14 @@
         }
     };
 
+    private final TelephonyConferenceBase.TelephonyConferenceListener mTelephonyConferenceListener =
+            new TelephonyConferenceBase.TelephonyConferenceListener() {
+        @Override
+        public void onConferenceMembershipChanged(Connection connection) {
+            mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
+        }
+    };
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -628,11 +634,10 @@
 
         } else {
             Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
-            originalConnection.setDisconnected(
+            closeOrDestroyConnection(originalConnection,
                     DisconnectCauseUtil.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.POWER_OFF,
                             "Failed to turn on radio."));
-            originalConnection.destroy();
         }
     }
 
@@ -658,12 +663,11 @@
             addExistingConnection(PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
                     phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
             // Remove the old connection from Telecom after.
-            connectionToEvaluate.setDisconnected(
+            closeOrDestroyConnection(connectionToEvaluate,
                     DisconnectCauseUtil.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.OUTGOING_CANCELED,
                             "Reconnecting outgoing Emergency Call.",
                             phone.getPhoneId()));
-            connectionToEvaluate.destroy();
         } else {
             placeOutgoingConnection((TelephonyConnection) connectionToEvaluate, phone, request);
         }
@@ -829,8 +833,8 @@
                             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;
@@ -1087,7 +1091,6 @@
     @Override
     public void onConnectionAdded(Connection connection) {
         if (connection instanceof Holdable && !isExternalConnection(connection)) {
-            connection.addConnectionListener(mConnectionListener);
             mHoldTracker.addHoldable(
                     connection.getPhoneAccountHandle(), (Holdable) connection);
         }
@@ -1218,9 +1221,7 @@
         } 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));
         }
     }
 
@@ -1282,10 +1283,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(
+                    DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
+                            phone.getPhoneId()));
+            connection.close();
             return;
         }
 
@@ -1306,10 +1307,10 @@
                 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(
+                    DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause,
+                            "Connection is null", phone.getPhoneId()));
+            connection.close();
         } else {
             connection.setOriginalConnection(originalConnection);
         }
@@ -1824,7 +1825,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);
             }
         }
@@ -1840,4 +1841,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.addListener(mTelephonyConferenceListener);
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
index 1ced34a..604cf03 100644
--- a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
+++ b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
@@ -36,4 +36,10 @@
     void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
                                Connection connection, Conference conference);
     void addConnectionToConferenceController(TelephonyConnection connection);
+
+    /**
+     * Called when a connection's conference membership changes.
+     * @param connection The connection.
+     */
+    void onConferenceMembershipChanged(Connection connection);
 }
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/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/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/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index 275bcc6..4fc8c02 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;
@@ -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));