am e3c72476: (-s ours) am 42f5cb37: Only mark as read voicemails with the "is_read" flag set.

* commit 'e3c72476ff59854db145062c8ec57b0345f7b74b':
diff --git a/res/values/config.xml b/res/values/config.xml
index c0e808d..a4f13f2 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -148,6 +148,11 @@
     <!-- Class name for the default main Dialer activity [DO NOT TRANSLATE] -->
     <string name="dialer_default_class" translatable="false">com.android.dialer.DialtactsActivity</string>
 
+    <!-- Package name for the network operator settings [DO NOT TRANSLATE] -->
+    <string name="network_operator_settings_package" translatable="false">com.android.phone</string>
+    <!-- Class name for the network operator settings activity [DO NOT TRANSLATE] -->
+    <string name="network_operator_settings_class" translatable="false">com.android.phone.NetworkSetting</string>
+
     <!-- CDMA activation goes through HFA -->
     <!-- DEPRECATED: Use CarrierConfigManager#KEY_USE_HFA_FOR_PROVISIONING_BOOL -->
     <bool name="config_use_hfa_for_provisioning">false</bool>
@@ -186,4 +191,17 @@
     <!-- Disables dialing "*228" (OTASP provisioning) on CDMA carriers where it is not supported or
          is potentially harmful by locking the SIM to 3G. -->
     <string name="config_disable_cdma_activation_code" translatable="false">false</string>
+
+    <!-- Flag indicating if SIM state should be checked before making an outgoing call. -->
+    <bool name="config_checkSimStateBeforeOutgoingCall">false</bool>
+    <!-- Package name for the SIM unlock dialog.[DO NOT TRANSLATE] -->
+    <string name="config_simUnlockUiPackage" translatable="false">@null</string>
+    <!-- Class name for the SIM unlock dialog.[DO NOT TRANSLATE] -->
+    <string name="config_simUnlockUiClass" translatable="false">@null</string>
+
+    <!-- Flag indicating whether to allow visual voicemail if available on the device.[DO NOT TRANSLATE] -->
+    <bool name="allow_visual_voicemail">true</bool>
+
+    <!-- Component for custom voicemail notification handling. [DO NOT TRANSLATE] -->
+    <string name="config_customVoicemailComponent">@null</string>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 52e4961..360e9ef 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -273,6 +273,12 @@
     <string name="response_error">Unexpected response from network.</string>
     <!-- Status message displayed in the "Call settings error" dialog -->
     <string name="exception_error">Network or SIM card error.</string>
+    <!-- Status message displayed in the "Call settings error" dialog when
+         current SS request is modified to a different request by STK CC -->
+    <string name="stk_cc_ss_to_dial_error">SS request modified to DIAL request.</string>
+    <string name="stk_cc_ss_to_ussd_error">SS request modified to USSD request.</string>
+    <string name="stk_cc_ss_to_ss_error">SS request modified to new SS request.</string>
+
     <!-- Status message displayed in the "Call settings error" dialog when operation fails due to FDN
          [CHAR LIMIT=NONE] -->
     <string name="fdn_check_failure">Your Phone app\'s Fixed Dialing Numbers setting is turned on. As a result, some call-related features aren\'t working.</string>
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index bc0e584..2b7d2ff 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -206,7 +206,9 @@
 
             case CallStateMonitor.PHONE_DISCONNECT:
                 if (DBG) log("DISCONNECT");
-                onDisconnect((AsyncResult) msg.obj);
+                // Stop any signalInfo tone being played when a call gets ended, the rest of the
+                // disconnect functionality in onDisconnect() is handled in ConnectionService.
+                stopSignalInfoTone();
                 break;
 
             case CallStateMonitor.PHONE_UNKNOWN_CONNECTION_APPEARED:
diff --git a/src/com/android/phone/CallStateMonitor.java b/src/com/android/phone/CallStateMonitor.java
index 16a6f1f..512c30b 100644
--- a/src/com/android/phone/CallStateMonitor.java
+++ b/src/com/android/phone/CallStateMonitor.java
@@ -88,7 +88,7 @@
         //
         //callManager.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null);
         //callManager.registerForPreciseCallStateChanged(this, PHONE_STATE_CHANGED, null);
-        //callManager.registerForDisconnect(this, PHONE_DISCONNECT, null);
+        callManager.registerForDisconnect(this, PHONE_DISCONNECT, null);
         //callManager.registerForUnknownConnection(this, PHONE_UNKNOWN_CONNECTION_APPEARED, null);
         callManager.registerForCdmaOtaStatusChange(this, EVENT_OTA_PROVISION_CHANGE, null);
         //callManager.registerForCallWaiting(this, PHONE_CDMA_CALL_WAITING, null);
@@ -134,7 +134,7 @@
         // Unregister all events from the old obsolete phone
         //callManager.unregisterForNewRingingConnection(this);
         //callManager.unregisterForPreciseCallStateChanged(this);
-        //callManager.unregisterForDisconnect(this);
+        callManager.unregisterForDisconnect(this);
         //callManager.unregisterForUnknownConnection(this);
         //callManager.unregisterForCallWaiting(this);
         callManager.unregisterForDisplayInfo(this);
diff --git a/src/com/android/phone/IccNetworkDepersonalizationPanel.java b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
index 4831423..9dff461 100644
--- a/src/com/android/phone/IccNetworkDepersonalizationPanel.java
+++ b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
@@ -48,6 +48,12 @@
  */
 public class IccNetworkDepersonalizationPanel extends IccPanel {
 
+    /**
+     * Tracks whether there is an instance of the network depersonalization dialog showing or not.
+     * Ensures only a single instance of the dialog is visible.
+     */
+    private static boolean sShowingDialog = false;
+
     //debug constants
     private static final boolean DBG = false;
 
@@ -65,6 +71,21 @@
     private Button       mUnlockButton;
     private Button       mDismissButton;
 
+    /**
+     * Shows the network depersonalization dialog, but only if it is not already visible.
+     */
+    public static void showDialog() {
+        if (sShowingDialog) {
+            Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; skipped already shown.");
+            return;
+        }
+        Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; showing dialog.");
+        sShowingDialog = true;
+        IccNetworkDepersonalizationPanel ndpPanel =
+                new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance());
+        ndpPanel.show();
+    }
+
     //private textwatcher to control text entry.
     private TextWatcher mPinEntryWatcher = new TextWatcher() {
         public void beforeTextChanged(CharSequence buffer, int start, int olen, int nlen) {
@@ -160,6 +181,13 @@
         super.onStart();
     }
 
+    @Override
+    public void onStop() {
+        super.onStop();
+        Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; hiding dialog.");
+        sShowingDialog = false;
+    }
+
     //Mirrors IccPinUnlockPanel.onKeyDown().
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK) {
diff --git a/src/com/android/phone/NetworkQueryService.java b/src/com/android/phone/NetworkQueryService.java
index 1a497b4..84fde87 100644
--- a/src/com/android/phone/NetworkQueryService.java
+++ b/src/com/android/phone/NetworkQueryService.java
@@ -53,6 +53,8 @@
     // error statuses that will be retured in the callback.
     public static final int QUERY_OK = 0;
     public static final int QUERY_EXCEPTION = 1;
+
+    static final String ACTION_LOCAL_BINDER = "com.android.phone.intent.action.LOCAL_BINDER";
     
     /** state of the query service */
     private int mState;
@@ -184,11 +186,12 @@
      */
     @Override
     public IBinder onBind(Intent intent) {
-        // TODO: Currently, return only the LocalBinder instance.  If we
-        // end up requiring support for a remote binder, we will need to 
-        // return mBinder as well, depending upon the intent.
         if (DBG) log("binding service implementation");
-        return mLocalBinder;
+        if (ACTION_LOCAL_BINDER.equals(intent.getAction())) {
+            return mLocalBinder;
+        }
+
+        return mBinder;
     }
 
     /**
diff --git a/src/com/android/phone/NetworkSetting.java b/src/com/android/phone/NetworkSetting.java
index ad2fea3..3d32817 100644
--- a/src/com/android/phone/NetworkSetting.java
+++ b/src/com/android/phone/NetworkSetting.java
@@ -270,8 +270,9 @@
         // we want this service to just stay in the background until it is killed, we
         // don't bother stopping it from our end.
         startService (new Intent(this, NetworkQueryService.class));
-        bindService (new Intent(this, NetworkQueryService.class), mNetworkQueryServiceConnection,
-                Context.BIND_AUTO_CREATE);
+        bindService (new Intent(this, NetworkQueryService.class).setAction(
+                NetworkQueryService.ACTION_LOCAL_BINDER),
+                mNetworkQueryServiceConnection, Context.BIND_AUTO_CREATE);
     }
 
     @Override
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index c579fe0..100a38c 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -93,6 +93,7 @@
 
     private Context mContext;
     private NotificationManager mNotificationManager;
+    private final ComponentName mNotificationComponent;
     private StatusBarManager mStatusBarManager;
     private UserManager mUserManager;
     private Toast mToast;
@@ -125,6 +126,12 @@
         mSubscriptionManager = SubscriptionManager.from(mContext);
         mTelecomManager = TelecomManager.from(mContext);
         mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
+
+        final String notificationComponent = mContext.getString(
+                R.string.config_customVoicemailComponent);
+
+        mNotificationComponent = notificationComponent != null
+                ? ComponentName.unflattenFromString(notificationComponent) : null;
     }
 
     /**
@@ -352,8 +359,10 @@
                 return;
             }
 
+            Integer vmCount = null;
+
             if (TelephonyCapabilities.supportsVoiceMessageCount(phone)) {
-                int vmCount = phone.getVoiceMessageCount();
+                vmCount = phone.getVoiceMessageCount();
                 String titleFormat = mContext.getString(R.string.notification_voicemail_title_count);
                 notificationTitle = String.format(titleFormat, vmCount);
             }
@@ -363,7 +372,9 @@
 
             Intent intent;
             String notificationText;
-            if (TextUtils.isEmpty(vmNumber)) {
+            boolean isSettingsIntent = TextUtils.isEmpty(vmNumber);
+
+            if (isSettingsIntent) {
                 notificationText = mContext.getString(
                         R.string.notification_voicemail_no_vm_number);
 
@@ -423,22 +434,75 @@
                 if (!mUserManager.hasUserRestriction(
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
                         && !user.isManagedProfile()) {
-                    mNotificationManager.notifyAsUser(
-                            Integer.toString(subId) /* tag */,
-                            VOICEMAIL_NOTIFICATION,
-                            notification,
-                            userHandle);
+                    if (!sendNotificationCustomComponent(vmCount, vmNumber, pendingIntent,
+                            isSettingsIntent)) {
+                        mNotificationManager.notifyAsUser(
+                                Integer.toString(subId) /* tag */,
+                                VOICEMAIL_NOTIFICATION,
+                                notification,
+                                userHandle);
+                    }
                 }
             }
         } else {
-            mNotificationManager.cancelAsUser(
-                    Integer.toString(subId) /* tag */,
-                    VOICEMAIL_NOTIFICATION,
-                    UserHandle.ALL);
+            if (!sendNotificationCustomComponent(0, null, null, false)) {
+                mNotificationManager.cancelAsUser(
+                        Integer.toString(subId) /* tag */,
+                        VOICEMAIL_NOTIFICATION,
+                        UserHandle.ALL);
+            }
         }
     }
 
     /**
+     * Sends a broadcast with the voicemail notification information to a custom component to
+     * handle. This method is also used to indicate to the custom component when to clear the
+     * notification. A pending intent can be passed to the custom component to indicate an action to
+     * be taken as it would by a notification produced in this class.
+     * @param count The number of pending voicemail messages to indicate on the notification. A
+     *              Value of 0 is passed here to indicate that the notification should be cleared.
+     * @param number The voicemail phone number if specified.
+     * @param pendingIntent The intent that should be passed as the action to be taken.
+     * @param isSettingsIntent {@code true} to indicate the pending intent is to launch settings.
+     *                         otherwise, {@code false} to indicate the intent launches voicemail.
+     * @return {@code true} if a custom component was notified of the notification.
+     */
+    private boolean sendNotificationCustomComponent(Integer count, String number,
+            PendingIntent pendingIntent, boolean isSettingsIntent) {
+        if (mNotificationComponent != null) {
+            Intent intent = new Intent();
+            intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            intent.setComponent(mNotificationComponent);
+            intent.setAction(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION);
+
+            if (count != null) {
+                intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
+            }
+
+            // Additional information about the voicemail notification beyond the count is only
+            // present when the count not specified or greater than 0. The value of 0 represents
+            // clearing the notification, which does not require additional information.
+            if (count == null || count > 0) {
+                if (!TextUtils.isEmpty(number)) {
+                    intent.putExtra(TelephonyManager.EXTRA_VOICEMAIL_NUMBER, number);
+                }
+
+                if (pendingIntent != null) {
+                    intent.putExtra(isSettingsIntent
+                            ? TelephonyManager.EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
+                            : TelephonyManager.EXTRA_CALL_VOICEMAIL_INTENT,
+                            pendingIntent);
+                }
+            }
+
+            mContext.sendBroadcast(intent);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
      * Updates the message call forwarding indicator notification.
      *
      * @param visible true if there are messages waiting
@@ -571,8 +635,9 @@
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         // Use NetworkSetting to handle the selection intent
-        intent.setComponent(new ComponentName("com.android.phone",
-                "com.android.phone.NetworkSetting"));
+        intent.setComponent(new ComponentName(
+                mContext.getString(R.string.network_operator_settings_package),
+                mContext.getString(R.string.network_operator_settings_class)));
         intent.putExtra(GsmUmtsOptions.EXTRA_SUB_ID, mPhone.getSubId());
         PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
 
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 315929b..fc08c5f 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -250,9 +250,7 @@
                         // The user won't be able to do anything else until
                         // they enter a valid SIM network PIN.
                         Log.i(LOG_TAG, "show sim depersonal panel");
-                        IccNetworkDepersonalizationPanel ndpPanel =
-                                new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance());
-                        ndpPanel.show();
+                        IccNetworkDepersonalizationPanel.showDialog();
                     }
                     break;
 
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ebfab96..7e06fdf 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -2690,7 +2690,7 @@
      * {@hide}
      * Returns the IMS Registration Status
      */
-    public boolean isWifiCallingEnabled() {
+    public boolean isWifiCallingAvailable() {
         return mPhone.isWifiCallingEnabled();
     }
 
@@ -2698,10 +2698,17 @@
      * {@hide}
      * Returns the IMS Registration Status
      */
-    public boolean isVolteEnabled() {
+    public boolean isVolteAvailable() {
         return mPhone.isVolteEnabled();
     }
 
+    /*
+     * {@hide} Returns the IMS Registration Status
+     */
+    public boolean isVideoTelephonyAvailable() {
+        return mPhone.isVideoEnabled();
+    }
+
     private boolean canReadPhoneState(String callingPackage, String message) {
         try {
             mApp.enforceCallingOrSelfPermission(
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index 08a5a95..05b86a5 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -50,6 +50,9 @@
     static final int RESPONSE_ERROR = 400;
     static final int RADIO_OFF_ERROR = 500;
     static final int FDN_CHECK_FAILURE = 600;
+    static final int STK_CC_SS_TO_DIAL_ERROR = 700;
+    static final int STK_CC_SS_TO_USSD_ERROR = 800;
+    static final int STK_CC_SS_TO_SS_ERROR = 900;
 
     private final ArrayList<String> mBusyList = new ArrayList<String>();
 
@@ -77,7 +80,8 @@
         }
 
         if (id == RESPONSE_ERROR || id == RADIO_OFF_ERROR || id == EXCEPTION_ERROR
-                || id == FDN_CHECK_FAILURE) {
+                || id == FDN_CHECK_FAILURE || id == STK_CC_SS_TO_DIAL_ERROR
+                || id == STK_CC_SS_TO_USSD_ERROR || id == STK_CC_SS_TO_SS_ERROR) {
             AlertDialog.Builder builder = new AlertDialog.Builder(this);
 
             int msgId;
@@ -97,6 +101,18 @@
                     msgId = R.string.fdn_check_failure;
                     builder.setPositiveButton(R.string.close_dialog, mDismiss);
                     break;
+                case STK_CC_SS_TO_DIAL_ERROR:
+                    msgId = R.string.stk_cc_ss_to_dial_error;
+                    builder.setPositiveButton(R.string.close_dialog, mDismiss);
+                    break;
+                case STK_CC_SS_TO_USSD_ERROR:
+                    msgId = R.string.stk_cc_ss_to_ussd_error;
+                    builder.setPositiveButton(R.string.close_dialog, mDismiss);
+                    break;
+                case STK_CC_SS_TO_SS_ERROR:
+                    msgId = R.string.stk_cc_ss_to_ss_error;
+                    builder.setPositiveButton(R.string.close_dialog, mDismiss);
+                    break;
                 case EXCEPTION_ERROR:
                 default:
                     msgId = R.string.exception_error;
diff --git a/src/com/android/phone/settings/fdn/FdnSetting.java b/src/com/android/phone/settings/fdn/FdnSetting.java
index 134d8e6..ac43cce 100644
--- a/src/com/android/phone/settings/fdn/FdnSetting.java
+++ b/src/com/android/phone/settings/fdn/FdnSetting.java
@@ -297,6 +297,15 @@
                                         .setMessage(R.string.puk2_requested)
                                         .setCancelable(true)
                                         .setOnCancelListener(FdnSetting.this)
+                                        .setNeutralButton(android.R.string.ok,
+                                                new DialogInterface.OnClickListener() {
+                                                    @Override
+                                                    public void onClick(DialogInterface dialog,
+                                                            int which) {
+                                                        resetPinChangeStateForPUK2();
+                                                        displayPinChangeDialog(0,true);
+                                                    }
+                                                })
                                         .create();
                                     a.getWindow().addFlags(
                                             WindowManager.LayoutParams.FLAG_DIM_BEHIND);
diff --git a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
index 9aff2d7..e467bb3 100644
--- a/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
+++ b/src/com/android/phone/vvm/omtp/SimChangeReceiver.java
@@ -28,6 +28,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.phone.PhoneUtils;
+import com.android.phone.R;
 import com.android.phone.settings.VisualVoicemailSettingsUtil;
 import com.android.phone.vvm.omtp.sync.OmtpVvmSourceManager;
 
@@ -78,8 +79,10 @@
                     boolean isEnabledInSettings =
                             VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(context,
                             phoneAccount);
-                    boolean isEnabled = isUserSet ? isEnabledInSettings :
-                        carrierConfigHelper.isEnabledByDefault();
+                    boolean isSupported =
+                            context.getResources().getBoolean(R.bool.allow_visual_voicemail);
+                    boolean isEnabled = isSupported && (isUserSet ? isEnabledInSettings :
+                        carrierConfigHelper.isEnabledByDefault());
 
                     if (!isUserSet) {
                         // Preserve the previous setting for "isVisualVoicemailEnabled" if it is
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index c142cc3..5406ab5 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -22,6 +22,8 @@
 import android.telecom.Connection;
 import android.telecom.ConferenceParticipant;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.text.TextUtils;
 
 /**
  * Represents a participant in a conference call.
@@ -53,7 +55,7 @@
             ConferenceParticipant participant) {
 
         mParentConnection = parentConnection;
-        setAddress(participant.getHandle(), PhoneConstants.PRESENTATION_ALLOWED);
+        setAddress(getParticipantAddress(participant), PhoneConstants.PRESENTATION_ALLOWED);
         setCallerDisplayName(participant.getDisplayName(), PhoneConstants.PRESENTATION_ALLOWED);
 
         mUserEntity = participant.getHandle();
@@ -140,6 +142,51 @@
     }
 
     /**
+     * Attempts to build a tel: style URI from a conference participant.
+     * Conference event package data contains SIP URIs, so we try to extract the phone number and
+     * format into a typical tel: style URI.
+     *
+     * @param participant The conference participant.
+     * @return The participant's address URI.
+     */
+    private Uri getParticipantAddress(ConferenceParticipant participant) {
+        Uri address = participant.getHandle();
+        if (address == null) {
+            return address;
+        }
+
+        // If the participant's address is already a TEL scheme, just return it as is.
+        if (PhoneAccount.SCHEME_TEL.equals(address.getScheme())) {
+            return address;
+        }
+
+        // Conference event package participants are identified using SIP URIs (see RFC3261).
+        // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+        // Per RFC3261, the "user" can be a telephone number.
+        // For example: sip:1650555121;phone-context=blah.com@host.com
+        // In this case, the phone number is in the user field of the URI, and the parameters can be
+        // ignored.
+        //
+        // A SIP URI can also specify a phone number in a format similar to:
+        // sip:+1-212-555-1212@something.com;user=phone
+        // In this case, the phone number is again in user field and the parameters can be ignored.
+        // We can get the user field in these instances by splitting the string on the @, ;, or :
+        // and looking at the first found item.
+        String number = address.getSchemeSpecificPart();
+        if (TextUtils.isEmpty(number)) {
+            return address;
+        }
+
+        String numberParts[] = number.split("[@;:]");
+        if (numberParts.length == 0) {
+            return address;
+        }
+        number = numberParts[0];
+
+        return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
+    }
+
+    /**
      * Builds a string representation of this conference participant connection.
      *
      * @return String representation of connection.
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 4a92847..81fc7aa 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -120,6 +120,9 @@
             case android.telephony.DisconnectCause.TIMED_OUT:
             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
             case android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING:
+            case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
+            case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
+            case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
                 return DisconnectCause.ERROR;
 
@@ -251,6 +254,18 @@
                 resourceId = R.string.callFailed_dsac_restricted_normal;
                 break;
 
+            case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_USSD:
+                resourceId = R.string.callFailed_dialToUssd;
+                break;
+
+            case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
+                resourceId = R.string.callFailed_dialToSs;
+                break;
+
+            case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
+                resourceId = R.string.callFailed_dialToDial;
+                break;
+
             case android.telephony.DisconnectCause.OUTGOING_FAILURE:
                 // We couldn't successfully place the call; there was some
                 // failure in the telephony layer.
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 3dbf459..4f26a68 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -32,16 +32,19 @@
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Objects;
 
 /**
  * Represents an IMS conference call.
@@ -172,7 +175,7 @@
             Log.d(this, "onCallCapabilitiesChanged: Connection: %s, callCapabilities: %s", c,
                     connectionCapabilities);
             int capabilites = ImsConference.this.getConnectionCapabilities();
-            setConnectionCapabilities(applyVideoCapabilities(capabilites, connectionCapabilities));
+            setConnectionCapabilities(applyHostCapabilities(capabilites, connectionCapabilities));
         }
 
         @Override
@@ -193,13 +196,30 @@
     private TelephonyConnection mConferenceHost;
 
     /**
-     * The known conference participant connections.  The HashMap is keyed by endpoint Uri.
-     * A {@link ConcurrentHashMap} is used as there is a possibility for radio events impacting the
-     * available participants to occur at the same time as an access via the connection service.
+     * The PhoneAccountHandle of the conference host.
      */
-    private final ConcurrentHashMap<Uri, ConferenceParticipantConnection>
-            mConferenceParticipantConnections =
-                    new ConcurrentHashMap<Uri, ConferenceParticipantConnection>(8, 0.9f, 1);
+    private PhoneAccountHandle mConferenceHostPhoneAccountHandle;
+
+    /**
+     * The address of the conference host.
+     */
+    private Uri mConferenceHostAddress;
+
+    /**
+     * The known conference participant connections.  The HashMap is keyed by endpoint Uri.
+     * Access to the hashmap is protected by the {@link #mUpdateSyncRoot}.
+     */
+    private final HashMap<Uri, ConferenceParticipantConnection>
+            mConferenceParticipantConnections = new HashMap<Uri, ConferenceParticipantConnection>();
+
+    /**
+     * Sychronization root used to ensure that updates to the
+     * {@link #mConferenceParticipantConnections} happen atomically are are not interleaved across
+     * threads.  There are some instances where the network will send conference event package
+     * data closely spaced.  If that happens, it is possible that the interleaving of the update
+     * will cause duplicate participant info to be added.
+     */
+    private final Object mUpdateSyncRoot = new Object();
 
     public void updateConferenceParticipantsAfterCreation() {
         if (mConferenceHost != null) {
@@ -238,13 +258,20 @@
 
         int capabilities = Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD |
                 Connection.CAPABILITY_MUTE | Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN;
-
-        capabilities = applyVideoCapabilities(capabilities, mConferenceHost.getConnectionCapabilities());
+        capabilities = applyHostCapabilities(capabilities,
+                mConferenceHost.getConnectionCapabilities());
         setConnectionCapabilities(capabilities);
 
     }
 
-    private int applyVideoCapabilities(int conferenceCapabilities, int capabilities) {
+    /**
+     * Transfers capabilities from the conference host to the conference itself.
+     *
+     * @param conferenceCapabilities The current conference capabilities.
+     * @param capabilities The new conference host capabilities.
+     * @return The merged capabilities to be applied to the conference.
+     */
+    private int applyHostCapabilities(int conferenceCapabilities, int capabilities) {
         if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
             conferenceCapabilities = applyCapability(conferenceCapabilities,
                     Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
@@ -268,6 +295,14 @@
             conferenceCapabilities = removeCapability(conferenceCapabilities,
                     Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO);
         }
+
+        if (can(capabilities, Connection.CAPABILITY_HIGH_DEF_AUDIO)) {
+            conferenceCapabilities = applyCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_HIGH_DEF_AUDIO);
+        } else {
+            conferenceCapabilities = removeCapability(conferenceCapabilities,
+                    Connection.CAPABILITY_HIGH_DEF_AUDIO);
+        }
         return conferenceCapabilities;
     }
 
@@ -490,9 +525,24 @@
         }
 
         mConferenceHost = conferenceHost;
+
+        // Attempt to get the conference host's address (e.g. the host's own phone number).
+        // We need to look at the default phone for the ImsPhone when creating the phone account
+        // for the
+        if (mConferenceHost.getPhone() != null &&  mConferenceHost.getPhone() instanceof ImsPhone) {
+            // Look up the conference host's address; we need this later for filtering out the
+            // conference host in conference event package data.
+            ImsPhone imsPhone = (ImsPhone) mConferenceHost.getPhone();
+            mConferenceHostPhoneAccountHandle =
+                    PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+            mConferenceHostAddress = TelecomAccountRegistry.getInstance(mTelephonyConnectionService)
+                    .getAddress(mConferenceHostPhoneAccountHandle);
+        }
+
         mConferenceHost.addConnectionListener(mConferenceHostListener);
         mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener);
         setState(mConferenceHost.getState());
+
         updateStatusHints();
     }
 
@@ -508,59 +558,70 @@
         if (participants == null) {
             return;
         }
-        boolean newParticipantsAdded = false;
-        boolean oldParticipantsRemoved = false;
-        ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size());
-        HashSet<Uri> participantUserEntities = new HashSet<>(participants.size());
 
-        // Add any new participants and update existing.
-        for (ConferenceParticipant participant : participants) {
-            Uri userEntity = participant.getHandle();
+        // Perform the update in a synchronized manner.  It is possible for the IMS framework to
+        // trigger two onConferenceParticipantsChanged callbacks in quick succession.  If the first
+        // update adds new participants, and the second does something like update the status of one
+        // of the participants, we can get into a situation where the participant is added twice.
+        synchronized (mUpdateSyncRoot) {
+            boolean newParticipantsAdded = false;
+            boolean oldParticipantsRemoved = false;
+            ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size());
+            HashSet<Uri> participantUserEntities = new HashSet<>(participants.size());
 
-            participantUserEntities.add(userEntity);
-            if (!mConferenceParticipantConnections.containsKey(userEntity)) {
-                createConferenceParticipantConnection(parent, participant);
-                newParticipants.add(participant);
-                newParticipantsAdded = true;
-            } else {
-                ConferenceParticipantConnection connection =
-                        mConferenceParticipantConnections.get(userEntity);
-                connection.updateState(participant.getState());
+            // Add any new participants and update existing.
+            for (ConferenceParticipant participant : participants) {
+                Uri userEntity = participant.getHandle();
+
+                participantUserEntities.add(userEntity);
+                if (!mConferenceParticipantConnections.containsKey(userEntity)) {
+                    // Some carriers will also include the conference host in the CEP.  We will
+                    // filter that out here.
+                    if (!isParticipantHost(mConferenceHostAddress, userEntity)) {
+                        createConferenceParticipantConnection(parent, participant);
+                        newParticipants.add(participant);
+                        newParticipantsAdded = true;
+                    }
+                } else {
+                    ConferenceParticipantConnection connection =
+                            mConferenceParticipantConnections.get(userEntity);
+                    connection.updateState(participant.getState());
+                }
             }
-        }
 
-        // Set state of new participants.
-        if (newParticipantsAdded) {
-            // Set the state of the new participants at once and add to the conference
-            for (ConferenceParticipant newParticipant : newParticipants) {
-                ConferenceParticipantConnection connection =
-                        mConferenceParticipantConnections.get(newParticipant.getHandle());
-                connection.updateState(newParticipant.getState());
+            // Set state of new participants.
+            if (newParticipantsAdded) {
+                // Set the state of the new participants at once and add to the conference
+                for (ConferenceParticipant newParticipant : newParticipants) {
+                    ConferenceParticipantConnection connection =
+                            mConferenceParticipantConnections.get(newParticipant.getHandle());
+                    connection.updateState(newParticipant.getState());
+                }
             }
-        }
 
-        // Finally, remove any participants from the conference that no longer exist in the
-        // conference event package data.
-        Iterator<Map.Entry<Uri, ConferenceParticipantConnection>> entryIterator =
-                mConferenceParticipantConnections.entrySet().iterator();
-        while (entryIterator.hasNext()) {
-            Map.Entry<Uri, ConferenceParticipantConnection> entry = entryIterator.next();
+            // Finally, remove any participants from the conference that no longer exist in the
+            // conference event package data.
+            Iterator<Map.Entry<Uri, ConferenceParticipantConnection>> entryIterator =
+                    mConferenceParticipantConnections.entrySet().iterator();
+            while (entryIterator.hasNext()) {
+                Map.Entry<Uri, ConferenceParticipantConnection> entry = entryIterator.next();
 
-            if (!participantUserEntities.contains(entry.getKey())) {
-                ConferenceParticipantConnection participant = entry.getValue();
-                participant.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
-                participant.removeConnectionListener(mParticipantListener);
-                mTelephonyConnectionService.removeConnection(participant);
-                removeConnection(participant);
-                entryIterator.remove();
-                oldParticipantsRemoved = true;
+                if (!participantUserEntities.contains(entry.getKey())) {
+                    ConferenceParticipantConnection participant = entry.getValue();
+                    participant.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
+                    participant.removeConnectionListener(mParticipantListener);
+                    mTelephonyConnectionService.removeConnection(participant);
+                    removeConnection(participant);
+                    entryIterator.remove();
+                    oldParticipantsRemoved = true;
+                }
             }
-        }
 
-        // If new participants were added or old ones were removed, we need to ensure the state of
-        // the manage conference capability is updated.
-        if (newParticipantsAdded || oldParticipantsRemoved) {
-            updateManageConference();
+            // If new participants were added or old ones were removed, we need to ensure the state
+            // of the manage conference capability is updated.
+            if (newParticipantsAdded || oldParticipantsRemoved) {
+                updateManageConference();
+            }
         }
     }
 
@@ -582,15 +643,17 @@
         ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
                 parent.getOriginalConnection(), participant);
         connection.addConnectionListener(mParticipantListener);
+        connection.setConnectTimeMillis(parent.getConnectTimeMillis());
 
         if (Log.VERBOSE) {
             Log.v(this, "createConferenceParticipantConnection: %s", connection);
         }
 
-        mConferenceParticipantConnections.put(participant.getHandle(), connection);
-        PhoneAccountHandle phoneAccountHandle =
-                PhoneUtils.makePstnPhoneAccountHandle(parent.getPhone());
-        mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, connection);
+        synchronized(mUpdateSyncRoot) {
+            mConferenceParticipantConnections.put(participant.getHandle(), connection);
+        }
+        mTelephonyConnectionService.addExistingConnection(mConferenceHostPhoneAccountHandle,
+                connection);
         addConnection(connection);
     }
 
@@ -603,7 +666,10 @@
         Log.d(this, "removeConferenceParticipant: %s", participant);
 
         participant.removeConnectionListener(mParticipantListener);
-        mConferenceParticipantConnections.remove(participant.getUserEntity());
+        synchronized(mUpdateSyncRoot) {
+            mConferenceParticipantConnections.remove(participant.getUserEntity());
+        }
+        mTelephonyConnectionService.removeConnection(participant);
     }
 
     /**
@@ -612,17 +678,63 @@
     private void disconnectConferenceParticipants() {
         Log.v(this, "disconnectConferenceParticipants");
 
-        for (ConferenceParticipantConnection connection :
-                mConferenceParticipantConnections.values()) {
+        synchronized(mUpdateSyncRoot) {
+            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();
+                connection.removeConnectionListener(mParticipantListener);
+                // Mark disconnect cause as cancelled to ensure that the call is not logged in the
+                // call log.
+                connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
+                mTelephonyConnectionService.removeConnection(connection);
+                connection.destroy();
+            }
+            mConferenceParticipantConnections.clear();
         }
-        mConferenceParticipantConnections.clear();
+    }
+
+    /**
+     * Determines if the passed in participant handle is the same as the conference host's handle.
+     * Starts with a simple equality check.  However, the handles from a conference event package
+     * will be a SIP uri, so we need to pull that apart to look for the participant's phone number.
+     *
+     * @param hostHandle The handle of the connection hosting the conference.
+     * @param handle The handle of the conference participant.
+     * @return {@code true} if the host's handle matches the participant's handle, {@code false}
+     *      otherwise.
+     */
+    private boolean isParticipantHost(Uri hostHandle, Uri handle) {
+        // If host and participant handles are the same, bail early.
+        if (Objects.equals(hostHandle, handle)) {
+            return true;
+        }
+
+        // Conference event package participants are identified using SIP URIs (see RFC3261).
+        // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+        // Per RFC3261, the "user" can be a telephone number.
+        // For example: sip:1650555121;phone-context=blah.com@host.com
+        // In this case, the phone number is in the user field of the URI, and the parameters can be
+        // ignored.
+        //
+        // A SIP URI can also specify a phone number in a format similar to:
+        // sip:+1-212-555-1212@something.com;user=phone
+        // In this case, the phone number is again in user field and the parameters can be ignored.
+        // We can get the user field in these instances by splitting the string on the @, ;, or :
+        // and looking at the first found item.
+
+        String number = handle.getSchemeSpecificPart();
+        String numberParts[] = number.split("[@;:]");
+
+        if (numberParts.length == 0) {
+            return false;
+        }
+        number = numberParts[0];
+
+        // The host number will be a tel: uri.  Per RFC3966, the part after tel: is the phone
+        // number.
+        String hostNumber = hostHandle.getSchemeSpecificPart();
+
+        return Objects.equals(hostNumber, number);
     }
 
     /**
@@ -651,11 +763,19 @@
 
             PhoneAccountHandle phoneAccountHandle =
                     PhoneUtils.makePstnPhoneAccountHandle(mConferenceHost.getPhone());
-            mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, mConferenceHost);
+            if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
+                GsmConnection c = new GsmConnection(originalConnection);
+                c.updateState();
+                // Copy the connect time from the conferenceHost
+                c.setConnectTimeMillis(mConferenceHost.getConnectTimeMillis());
+                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();
         }
 
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 7dcb97e..7c45657 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -18,7 +18,6 @@
 
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 
-import android.net.Uri;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionService;
@@ -152,9 +151,10 @@
         List<Conferenceable> backgroundConnections = new ArrayList<>(mTelephonyConnections.size());
 
         // Loop through and collect all calls which are active or holding
-        for (Connection connection : mTelephonyConnections) {
+        for (TelephonyConnection connection : mTelephonyConnections) {
             if (Log.DEBUG) {
-                Log.d(this, "recalc - %s %s", connection.getState(), connection);
+                Log.d(this, "recalc - %s %s supportsConf? %s", connection.getState(), connection,
+                        connection.isConferenceSupported());
             }
 
             // If this connection is a member of a conference hosted on another device, it is not
@@ -166,6 +166,12 @@
                 continue;
             }
 
+            // If this connection does not support being in a conference call, then it is not
+            // conferenceable with any other connection.
+            if (!connection.isConferenceSupported()) {
+                continue;
+            }
+
             switch (connection.getState()) {
                 case Connection.STATE_ACTIVE:
                     activeConnections.add(connection);
@@ -239,8 +245,8 @@
 
             List<Connection> nonConferencedConnections =
                 new ArrayList<>(mTelephonyConnections.size());
-            for (Connection c : mTelephonyConnections) {
-                if (c.getConference() == null) {
+            for (TelephonyConnection c : mTelephonyConnections) {
+                if (c.getConference() == null && c.isConferenceSupported()) {
                     nonConferencedConnections.add(c);
                 }
             }
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index df458fd..154fc33 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -18,10 +18,8 @@
 
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
@@ -69,6 +67,7 @@
         private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
         private boolean mIsVideoCapable;
         private boolean mIsVideoPauseSupported;
+        private boolean mIsMergeCallSupported;
 
         AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
             mPhone = phone;
@@ -168,9 +167,11 @@
             if (mIsVideoCapable) {
                 capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING;
             }
-            if (record != null) {
-                updateVideoPauseSupport(record);
+            mIsVideoPauseSupported = isCarrierVideoPauseSupported();
+            if (isCarrierInstantLetteringSupported()) {
+                capabilities |= PhoneAccount.CAPABILITY_CALL_SUBJECT;
             }
+            mIsMergeCallSupported = isCarrierMergeCallSupported();
 
             if (icon == null) {
                 // TODO: Switch to using Icon.createWithResource() once that supports tinting.
@@ -211,34 +212,37 @@
         }
 
         /**
-         * Updates indicator for this {@link AccountEntry} to determine if the carrier supports
-         * pause/resume signalling for IMS video calls.  The carrier setting is stored in MNC/MCC
-         * configuration files.
+         * Determines from carrier configuration whether pausing of IMS video calls is supported.
          *
-         * @param subscriptionInfo The subscription info.
+         * @return {@code true} if pausing IMS video calls is supported.
          */
-        private void updateVideoPauseSupport(SubscriptionInfo subscriptionInfo) {
-            // Get the configuration for the MNC/MCC specified in the current subscription info.
-            Configuration configuration = new Configuration();
-            if (subscriptionInfo.getMcc() == 0 && subscriptionInfo.getMnc() == 0) {
-                Configuration config = mContext.getResources().getConfiguration();
-                configuration.mcc = config.mcc;
-                configuration.mnc = config.mnc;
-                Log.i(this, "updateVideoPauseSupport -- no mcc/mnc for sub: " + subscriptionInfo +
-                        " using mcc/mnc from main context: " + configuration.mcc + "/" +
-                        configuration.mnc);
-            } else {
-                Log.i(this, "updateVideoPauseSupport -- mcc/mnc for sub: " + subscriptionInfo);
-
-                configuration.mcc = subscriptionInfo.getMcc();
-                configuration.mnc = subscriptionInfo.getMnc();
-            }
-
+        private boolean isCarrierVideoPauseSupported() {
             // Check if IMS video pause is supported.
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            mIsVideoPauseSupported
-                    = b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
+        }
+
+        /**
+         * Determines from carrier config whether instant lettering is supported.
+         *
+         * @return {@code true} if instant lettering is supported, {@code false} otherwise.
+         */
+        private boolean isCarrierInstantLetteringSupported() {
+            PersistableBundle b =
+                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            return b.getBoolean(CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL);
+        }
+
+        /**
+         * Determines from carrier config whether merging calls is supported.
+         *
+         * @return {@code true} if merging calls is supported, {@code false} otherwise.
+         */
+        private boolean isCarrierMergeCallSupported() {
+            PersistableBundle b =
+                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_CONFERENCE_CALL_BOOL);
         }
 
         /**
@@ -260,6 +264,14 @@
         public boolean isVideoPauseSupported() {
             return mIsVideoCapable && mIsVideoPauseSupported;
         }
+
+        /**
+         * Indicates whether this account supports merging calls (i.e. conferencing).
+         * @return {@code true} if the account supports merging calls, {@code false} otherwise.
+         */
+        public boolean isMergeCallSupported() {
+            return mIsMergeCallSupported;
+        }
     }
 
     private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
@@ -335,6 +347,37 @@
     }
 
     /**
+     * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
+     * merging calls.
+     *
+     * @param handle The {@link PhoneAccountHandle}.
+     * @return {@code True} if merging calls is supported.
+     */
+    boolean isMergeCallSupported(PhoneAccountHandle handle) {
+        for (AccountEntry entry : mAccounts) {
+            if (entry.getPhoneAccountHandle().equals(handle)) {
+                return entry.isMergeCallSupported();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the address (e.g. the phone number) associated with a subscription.
+     *
+     * @param handle The phone account handle to find the subscription address for.
+     * @return The address.
+     */
+    Uri getAddress(PhoneAccountHandle handle) {
+        for (AccountEntry entry : mAccounts) {
+            if (entry.getPhoneAccountHandle().equals(handle)) {
+                return entry.mAccount.getAddress();
+            }
+        }
+        return null;
+    }
+
+    /**
      * Sets up all the phone accounts for SIMs on first boot.
      */
     void setupOnBoot() {
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 2af10a6..9724a32 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -17,6 +17,7 @@
 package com.android.services.telephony;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -26,9 +27,11 @@
 import android.net.Uri;
 import android.telecom.Conference;
 import android.telecom.ConferenceParticipant;
+import android.telecom.Conferenceable;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
+import com.android.phone.PhoneUtils;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.gsm.GsmConnection;
@@ -46,6 +49,8 @@
     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();
         }
 
@@ -65,6 +70,7 @@
     private final List<TelephonyConnection> mTelephonyConnections = new ArrayList<>();
 
     private final TelephonyConnectionService mConnectionService;
+    private boolean mTriggerRecalculate = false;
 
     public TelephonyConferenceController(TelephonyConnectionService connectionService) {
         mConnectionService = connectionService;
@@ -73,6 +79,11 @@
     /** The TelephonyConference connection object. */
     private TelephonyConference mTelephonyConference;
 
+    boolean shouldRecalculate() {
+        Log.d(this, "shouldRecalculate is " + mTriggerRecalculate);
+        return mTriggerRecalculate;
+    }
+
     void add(TelephonyConnection connection) {
         mTelephonyConnections.add(connection);
         connection.addConnectionListener(mConnectionListener);
@@ -82,11 +93,10 @@
     void remove(Connection connection) {
         connection.removeConnectionListener(mConnectionListener);
         mTelephonyConnections.remove(connection);
-
         recalculate();
     }
 
-    private void recalculate() {
+    void recalculate() {
         recalculateConference();
         recalculateConferenceable();
     }
@@ -107,13 +117,15 @@
         Log.v(this, "recalculateConferenceable : %d", mTelephonyConnections.size());
 
         List<Connection> activeConnections = new ArrayList<>(mTelephonyConnections.size());
-        List<Connection> backgroundConnections = new ArrayList<>(mTelephonyConnections.size());
+        List<Connection> backgroundConnections = new ArrayList<>(
+                mTelephonyConnections.size());
 
         // Loop through and collect all calls which are active or holding
-        for (Connection connection : mTelephonyConnections) {
-            Log.d(this, "recalc - %s %s", connection.getState(), connection);
+        for (TelephonyConnection connection : mTelephonyConnections) {
+            Log.d(this, "recalc - %s %s supportsConf? %s", connection.getState(), connection,
+                    connection.isConferenceSupported());
 
-            if (!participatesInFullConference(connection)) {
+            if (connection.isConferenceSupported() && !participatesInFullConference(connection)) {
                 switch (connection.getState()) {
                     case Connection.STATE_ACTIVE:
                         activeConnections.add(connection);
@@ -149,7 +161,7 @@
             List<Connection> nonConferencedConnections =
                     new ArrayList<>(mTelephonyConnections.size());
             for (TelephonyConnection c : mTelephonyConnections) {
-                if (c.getConference() == null) {
+                if (c.isConferenceSupported() && c.getConference() == null) {
                     nonConferencedConnections.add(c);
                 }
             }
@@ -183,6 +195,21 @@
         Log.d(this, "Recalculate conference calls %s %s.",
                 mTelephonyConference, conferencedConnections);
 
+        // Check if all conferenced connections are in Connection Service
+        boolean allConnInService = true;
+        Collection<Connection> allConnections = mConnectionService.getAllConnections();
+        for (Connection connection : conferencedConnections) {
+            Log.v (this, "Finding connection in Connection Service for " + connection);
+            if (!allConnections.contains(connection)) {
+                allConnInService = false;
+                Log.v(this, "Finding connection in Connection Service Failed");
+                break;
+            }
+        }
+
+        Log.d(this, "Is there a match for all connections in connection service " +
+            allConnInService);
+
         // If this is a GSM conference and the number of connections drops below 2, we will
         // terminate the conference.
         if (numGsmConnections < 2) {
@@ -204,35 +231,47 @@
                         mTelephonyConference.removeConnection(connection);
                     }
                 }
-
-                // Add any new ones
-                for (Connection connection : conferencedConnections) {
-                    if (!existingConnections.contains(connection)) {
-                        mTelephonyConference.addConnection(connection);
+                if (allConnInService) {
+                    mTriggerRecalculate = false;
+                    // Add any new ones
+                    for (Connection connection : conferencedConnections) {
+                        if (!existingConnections.contains(connection)) {
+                            mTelephonyConference.addConnection(connection);
+                        }
                     }
+                } else {
+                    Log.d(this, "Trigger recalculate later");
+                    mTriggerRecalculate = true;
                 }
             } else {
-                mTelephonyConference = new TelephonyConference(null);
-
-                for (Connection connection : conferencedConnections) {
-                    Log.d(this, "Adding a connection to a conference call: %s %s",
-                            mTelephonyConference, connection);
-                    mTelephonyConference.addConnection(connection);
+                if (allConnInService) {
+                    mTriggerRecalculate = false;
+                    mTelephonyConference = new TelephonyConference(null);
+                    for (Connection connection : conferencedConnections) {
+                        Log.d(this, "Adding a connection to a conference call: %s %s",
+                                mTelephonyConference, connection);
+                        mTelephonyConference.addConnection(connection);
+                    }
+                    mConnectionService.addConference(mTelephonyConference);
+                } else {
+                    Log.d(this, "Trigger recalculate later");
+                    mTriggerRecalculate = true;
                 }
-
-                mConnectionService.addConference(mTelephonyConference);
             }
-
-            // Set the conference state to the same state as its child connections.
-            Connection conferencedConnection = mTelephonyConference.getPrimaryConnection();
-            if (conferencedConnection != null) {
-                switch (conferencedConnection.getState()) {
-                    case Connection.STATE_ACTIVE:
-                        mTelephonyConference.setActive();
-                        break;
-                    case Connection.STATE_HOLDING:
-                        mTelephonyConference.setOnHold();
-                        break;
+            if (mTelephonyConference != null) {
+                Connection conferencedConnection = mTelephonyConference.getPrimaryConnection();
+                Log.v(this, "Primary Conferenced connection is " + conferencedConnection);
+                if (conferencedConnection != null) {
+                    switch (conferencedConnection.getState()) {
+                        case Connection.STATE_ACTIVE:
+                            Log.v(this, "Setting conference to active");
+                            mTelephonyConference.setActive();
+                            break;
+                        case Connection.STATE_HOLDING:
+                            Log.v(this, "Setting conference to hold");
+                            mTelephonyConference.setOnHold();
+                            break;
+                    }
                 }
             }
         }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index cbe7c0a..50f3c38 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,11 +16,11 @@
 
 package com.android.services.telephony;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.telecom.CallAudioState;
@@ -29,16 +29,23 @@
 import android.telecom.PhoneAccount;
 import android.telecom.StatusHints;
 
+import com.android.ims.ImsCallProfile;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection.PostDialListener;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
+
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.phone.R;
 
 import java.lang.Override;
+import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -53,6 +60,14 @@
     private static final int MSG_DISCONNECT = 4;
     private static final int MSG_MULTIPARTY_STATE_CHANGED = 5;
     private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
+    private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
+    /**
+     * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
+     * equivalents defined in {@link android.telecom.Connection}.
+     */
+    private static final Map<String, String> sExtrasMap = createExtrasMap();
+
+    private SuppServiceNotification mSsNotification = null;
 
     private final Handler mHandler = new Handler() {
         @Override
@@ -72,7 +87,7 @@
                             ((connection.getAddress() != null &&
                             mOriginalConnection.getAddress() != null &&
                             mOriginalConnection.getAddress().contains(connection.getAddress())) ||
-                            connection.getStateBeforeHandover() == mOriginalConnection.getState())) {
+                            connection.getState() == mOriginalConnection.getStateBeforeHandover())) {
                             Log.d(TelephonyConnection.this,
                                     "SettingOriginalConnection " + mOriginalConnection.toString()
                                             + " with " + connection.toString());
@@ -108,6 +123,24 @@
                 case MSG_CONFERENCE_MERGE_FAILED:
                     notifyConferenceMergeFailed();
                     break;
+                case MSG_SUPP_SERVICE_NOTIFY:
+                    Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : "
+                            +getPhone().getPhoneId());
+                    if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
+                        mSsNotification =
+                                (SuppServiceNotification)((AsyncResult) msg.obj).result;
+                        if (mOriginalConnection != null && mSsNotification.history != null) {
+                            Bundle extras = getExtras();
+                            if (extras != null) {
+                                Log.v(TelephonyConnection.this,
+                                        "Updating call history info in extras.");
+                                extras.putStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER,
+                                        new ArrayList(Arrays.asList(mSsNotification.history)));
+                                setExtras(extras);
+                            }
+                        }
+                    }
+                    break;
             }
         }
     };
@@ -236,6 +269,7 @@
 
     private com.android.internal.telephony.Connection mOriginalConnection;
     private Call.State mOriginalConnectionState = Call.State.IDLE;
+    private Bundle mOriginalConnectionExtras = new Bundle();
 
     private boolean mWasImsConnection;
 
@@ -284,6 +318,11 @@
     private boolean mIsVideoPauseSupported;
 
     /**
+     * Indicates whether this connection supports being a part of a conference..
+     */
+    private boolean mIsConferenceSupported;
+
+    /**
      * Listeners to our TelephonyConnection specific callbacks
      */
     private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
@@ -554,7 +593,7 @@
     void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
         Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection);
         clearOriginalConnection();
-
+        mOriginalConnectionExtras.clear();
         mOriginalConnection = originalConnection;
         getPhone().registerForPreciseCallStateChanged(
                 mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
@@ -562,6 +601,7 @@
                 mHandler, MSG_HANDOVER_STATE_CHANGED, null);
         getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
         getPhone().registerForDisconnect(mHandler, MSG_DISCONNECT, null);
+        getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
         mOriginalConnection.addPostDialListener(mPostDialListener);
         mOriginalConnection.addListener(mOriginalConnectionListener);
 
@@ -593,6 +633,7 @@
                 getPhone().unregisterForRingbackTone(mHandler);
                 getPhone().unregisterForHandoverStateChanged(mHandler);
                 getPhone().unregisterForDisconnect(mHandler);
+                getPhone().unregisterForSuppServiceNotification(mHandler);
             }
             mOriginalConnection.removePostDialListener(mPostDialListener);
             mOriginalConnection.removeListener(mOriginalConnectionListener);
@@ -707,6 +748,73 @@
         return true;
     }
 
+    protected void updateExtras() {
+        Bundle extras = null;
+        if (mOriginalConnection != null) {
+            extras = mOriginalConnection.getExtras();
+            if (extras != null) {
+                // Check if extras have changed and need updating.
+                if (!areBundlesEqual(mOriginalConnectionExtras, extras)) {
+                    if (Log.DEBUG) {
+                        Log.d(TelephonyConnection.this, "Updating extras:");
+                        for (String key : extras.keySet()) {
+                            Object value = extras.get(key);
+                            if (value instanceof String) {
+                                Log.d(this, "updateExtras Key=" + Log.pii(key) +
+                                             " value=" + Log.pii((String)value));
+                            }
+                        }
+                    }
+                    mOriginalConnectionExtras.clear();
+
+                    mOriginalConnectionExtras.putAll(extras);
+
+                    // Remap any string extras that have a remapping defined.
+                    for (String key : mOriginalConnectionExtras.keySet()) {
+                        if (sExtrasMap.containsKey(key)) {
+                            String newKey = sExtrasMap.get(key);
+                            mOriginalConnectionExtras.putString(newKey, extras.getString(key));
+                            mOriginalConnectionExtras.remove(key);
+                        }
+                    }
+
+                    // Ensure extras are propagated to Telecom.
+                    Bundle connectionExtras = getExtras();
+                    if (connectionExtras == null) {
+                        connectionExtras = new Bundle();
+                    }
+                    connectionExtras.putAll(mOriginalConnectionExtras);
+                    setExtras(connectionExtras);
+                } else {
+                    Log.d(this, "Extras update not required");
+                }
+            } else {
+                Log.d(this, "updateExtras extras: " + Log.pii(extras));
+            }
+        }
+    }
+
+    private static boolean areBundlesEqual(Bundle extras, Bundle newExtras) {
+        if (extras == null || newExtras == null) {
+            return extras == newExtras;
+        }
+
+        if (extras.size() != newExtras.size()) {
+            return false;
+        }
+
+        for(String key : extras.keySet()) {
+            if (key != null) {
+                final Object value = extras.get(key);
+                final Object newValue = newExtras.get(key);
+                if (!Objects.equals(value, newValue)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
     void updateState() {
         if (mOriginalConnection == null) {
             return;
@@ -747,6 +855,7 @@
         updateConnectionCapabilities();
         updateAddress();
         updateMultiparty();
+        updateExtras();
     }
 
     /**
@@ -940,6 +1049,22 @@
     }
 
     /**
+     * Sets whether this connection supports conference calling.
+     * @param isConferenceSupported {@code true} if conference calling is supported by this
+     *                                         connection, {@code false} otherwise.
+     */
+    public void setConferenceSupported(boolean isConferenceSupported) {
+        mIsConferenceSupported = isConferenceSupported;
+    }
+
+    /**
+     * @return {@code true} if this connection supports merging calls into a conference.
+     */
+    public boolean isConferenceSupported() {
+        return mIsConferenceSupported;
+    }
+
+    /**
      * Whether the original connection is an IMS connection.
      * @return {@code True} if the original connection is an IMS connection, {@code false}
      *     otherwise.
@@ -1038,6 +1163,23 @@
         }
     }
 
+
+    /**
+     * Provides a mapping from extras keys which may be found in the
+     * {@link com.android.internal.telephony.Connection} to their equivalents defined in
+     * {@link android.telecom.Connection}.
+     *
+     * @return Map containing key mappings.
+     */
+    private static Map<String, String> createExtrasMap() {
+        Map<String, String> result = new HashMap<String, String>();
+        result.put(ImsCallProfile.EXTRA_CHILD_NUMBER,
+                android.telecom.Connection.EXTRA_CHILD_ADDRESS);
+        result.put(ImsCallProfile.EXTRA_DISPLAY_TEXT,
+                android.telecom.Connection.EXTRA_CALL_SUBJECT);
+        return Collections.unmodifiableMap(result);
+    }
+
     /**
      * Creates a string representation of this {@link TelephonyConnection}.  Primarily intended for
      * use in log statements.
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 9c53898..e3807b8 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -16,11 +16,10 @@
 
 package com.android.services.telephony;
 
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
@@ -30,13 +29,14 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
@@ -49,7 +49,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import java.util.regex.Pattern;
 
 /**
@@ -176,6 +175,38 @@
         // Get the right phone object from the account data passed in.
         final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
         if (phone == null) {
+            final Context context = getApplicationContext();
+            if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
+                // Check SIM card state before the outgoing call.
+                // Start the SIM unlock activity if PIN_REQUIRED.
+                final Phone defaultPhone = PhoneFactory.getDefaultPhone();
+                final IccCard icc = defaultPhone.getIccCard();
+                IccCardConstants.State simState = IccCardConstants.State.UNKNOWN;
+                if (icc != null) {
+                    simState = icc.getState();
+                }
+                if (simState == IccCardConstants.State.PIN_REQUIRED) {
+                    final String simUnlockUiPackage = context.getResources().getString(
+                            R.string.config_simUnlockUiPackage);
+                    final String simUnlockUiClass = context.getResources().getString(
+                            R.string.config_simUnlockUiClass);
+                    if (simUnlockUiPackage != null && simUnlockUiClass != null) {
+                        Intent simUnlockIntent = new Intent().setComponent(new ComponentName(
+                                simUnlockUiPackage, simUnlockUiClass));
+                        simUnlockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        try {
+                            context.startActivity(simUnlockIntent);
+                        } catch (ActivityNotFoundException exception) {
+                            Log.e(this, exception, "Unable to find SIM unlock UI activity.");
+                        }
+                    }
+                    return Connection.createFailedConnection(
+                            DisconnectCauseUtil.toTelecomDisconnectCause(
+                                    android.telephony.DisconnectCause.OUT_OF_SERVICE,
+                                    "SIM_STATE_PIN_REQUIRED"));
+                }
+            }
+
             Log.d(this, "onCreateOutgoingConnection, phone is null");
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
@@ -202,10 +233,15 @@
                 case ServiceState.STATE_EMERGENCY_ONLY:
                     break;
                 case ServiceState.STATE_OUT_OF_SERVICE:
-                    return Connection.createFailedConnection(
-                            DisconnectCauseUtil.toTelecomDisconnectCause(
-                                    android.telephony.DisconnectCause.OUT_OF_SERVICE,
-                                    "ServiceState.STATE_OUT_OF_SERVICE"));
+                    if (phone.isUtEnabled() && number.endsWith("#")) {
+                        Log.d(this, "onCreateOutgoingConnection dial for UT");
+                        break;
+                    } else {
+                        return Connection.createFailedConnection(
+                                DisconnectCauseUtil.toTelecomDisconnectCause(
+                                        android.telephony.DisconnectCause.OUT_OF_SERVICE,
+                                        "ServiceState.STATE_OUT_OF_SERVICE"));
+                    }
                 case ServiceState.STATE_POWER_OFF:
                     return Connection.createFailedConnection(
                             DisconnectCauseUtil.toTelecomDisconnectCause(
@@ -305,6 +341,13 @@
     }
 
     @Override
+    public void triggerConferenceRecalculate() {
+        if (mTelephonyConferenceController.shouldRecalculate()) {
+            mTelephonyConferenceController.recalculate();
+        }
+    }
+
+    @Override
     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         Log.i(this, "onCreateUnknownConnection, request: " + request);
@@ -323,9 +366,16 @@
             allConnections.addAll(ringingCall.getConnections());
         }
         final Call foregroundCall = phone.getForegroundCall();
-        if (foregroundCall.hasConnections()) {
+        if ((foregroundCall.getState() != Call.State.DISCONNECTED)
+                && (foregroundCall.hasConnections())) {
             allConnections.addAll(foregroundCall.getConnections());
         }
+        if (phone.getImsPhone() != null) {
+            final Call imsFgCall = phone.getImsPhone().getForegroundCall();
+            if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall.hasConnections()) {
+                allConnections.addAll(imsFgCall.getConnections());
+            }
+        }
         final Call backgroundCall = phone.getBackgroundCall();
         if (backgroundCall.hasConnections()) {
             allConnections.addAll(phone.getBackgroundCall().getConnections());
@@ -335,6 +385,7 @@
         for (com.android.internal.telephony.Connection telephonyConnection : allConnections) {
             if (!isOriginalConnectionKnown(telephonyConnection)) {
                 unknownConnection = telephonyConnection;
+                Log.d(this, "onCreateUnknownConnection: conn = " + unknownConnection);
                 break;
             }
         }
@@ -425,6 +476,9 @@
             returnConnection.setVideoPauseSupported(
                     TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
                             phoneAccountHandle));
+            returnConnection.setConferenceSupported(
+                    TelecomAccountRegistry.getInstance(this).isMergeCallSupported(
+                            phoneAccountHandle));
         }
         return returnConnection;
     }