Merge changes I5b45f043,I1ebe7fc3 into nyc-mr1-dev

* changes:
  Remove NOOP in VVM connection
  Set message count to 0 while clearing MWI
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 26b0c7a..ddc2dbe 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -541,12 +541,6 @@
         </activity>
 
         <!-- Start SIP -->
-        <service android:name="com.android.services.telephony.sip.SipCallServiceProvider"
-                 android:singleUser="true" >
-            <intent-filter>
-                <action android:name="android.telecom.CallServiceProvider" />
-            </intent-filter>
-        </service>
         <service android:name="com.android.services.telephony.sip.SipConnectionService"
                  android:label="@string/sip_connection_service_label"
                  android:singleUser="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0fbd08d..a18ee87 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1011,6 +1011,8 @@
     <string name="incall_error_supp_service_hold">Can\'t hold calls.</string>
     <!-- In-call screen: call failure message displayed in an error dialog when WFC is enabled, is wifi-only, and not connected to a wireless network. [CHAR_LIMIT=NONE] -->
     <string name="incall_error_wfc_only_no_wireless_network">Connect to a wireless network to make a call.</string>
+    <!-- In-call screen: call failure message displayed in an error dialog when the user is connected to a wireless network, but wifi calling is turned off. [CHAR_LIMIT=NONE] -->
+    <string name="incall_error_promote_wfc">Enable Wi-Fi calling to make a call.</string>
 
     <!-- Dialog title for the "radio enable" UI for emergency calls -->
     <string name="emergency_enable_radio_dialog_title">Emergency call</string>
diff --git a/sip/res/values/strings.xml b/sip/res/values/strings.xml
index 6ac7c8d..d515736 100644
--- a/sip/res/values/strings.xml
+++ b/sip/res/values/strings.xml
@@ -115,8 +115,12 @@
     <!-- Help text of the auth_username field. [CHAR LIMIT=NONE] -->
     <string name="auth_username_summary">Username used for authentication</string>
 
-    <!-- Initial status of the preferences is '<Not set>'. [CHAR LIMIT=NONE] -->
-    <string name="default_preference_summary">&lt;Not set&gt;</string>
+    <!-- Initial status of the username preference is '<Not set>'. [CHAR LIMIT=NONE] -->
+    <string name="default_preference_summary_username">&lt;Not set&gt;</string>
+    <!-- Initial status of the password preference is '<Not set>'. [CHAR LIMIT=NONE] -->
+    <string name="default_preference_summary_password">&lt;Not set&gt;</string>
+    <!-- Initial status of the domain address preference is '<Not set>'. [CHAR LIMIT=NONE] -->
+    <string name="default_preference_summary_domain_address">&lt;Not set&gt;</string>
     <!-- Default value for the display-name preference summary. [CHAR LIMIT=NONE] -->
     <string name="display_name_summary">&lt;Same as username&gt;</string>
     <!-- Default value for the outbound-proxy-address preference summary. [CHAR LIMIT=NONE] -->
diff --git a/sip/res/xml/sip_edit.xml b/sip/res/xml/sip_edit.xml
index 15e5ce1..58a8a92 100644
--- a/sip/res/xml/sip_edit.xml
+++ b/sip/res/xml/sip_edit.xml
@@ -22,7 +22,7 @@
         android:key="@string/username"
         android:title="@string/username_title"
         android:dialogTitle="@string/username_title"
-        android:summary="@string/default_preference_summary"
+        android:summary="@string/default_preference_summary_username"
         android:persistent="false"
         android:singleLine="true"
         android:inputType="textNoSuggestions"/>
@@ -32,7 +32,7 @@
         android:title="@string/password_title"
         android:dialogTitle="@string/password_title"
         android:password="true"
-        android:summary="@string/default_preference_summary"
+        android:summary="@string/default_preference_summary_password"
         android:persistent="false"
         android:singleLine="true"/>
 
@@ -40,7 +40,7 @@
         android:key="@string/domain_address"
         android:title="@string/domain_address_title"
         android:dialogTitle="@string/domain_address_title"
-        android:summary="@string/default_preference_summary"
+        android:summary="@string/default_preference_summary_domain_address"
         android:persistent="false"
         android:singleLine="true"
         android:inputType="textNoSuggestions"/>
diff --git a/sip/src/com/android/services/telephony/sip/SipEditor.java b/sip/src/com/android/services/telephony/sip/SipEditor.java
index 8bc7734..f6e9c22 100644
--- a/sip/src/com/android/services/telephony/sip/SipEditor.java
+++ b/sip/src/com/android/services/telephony/sip/SipEditor.java
@@ -68,9 +68,10 @@
     private SipAccountRegistry mSipAccountRegistry;
 
     enum PreferenceKey {
-        Username(R.string.username, 0, R.string.default_preference_summary),
-        Password(R.string.password, 0, R.string.default_preference_summary),
-        DomainAddress(R.string.domain_address, 0, R.string.default_preference_summary),
+        Username(R.string.username, 0, R.string.default_preference_summary_username),
+        Password(R.string.password, 0, R.string.default_preference_summary_password),
+        DomainAddress(R.string.domain_address, 0,
+                R.string.default_preference_summary_domain_address),
         DisplayName(R.string.display_name, 0, R.string.display_name_summary),
         ProxyAddress(R.string.proxy_address, 0, R.string.optional_summary),
         Port(R.string.port, R.string.default_port, R.string.default_port),
diff --git a/src/com/android/phone/ImsUtil.java b/src/com/android/phone/ImsUtil.java
index 868a0f1..4c9089f 100644
--- a/src/com/android/phone/ImsUtil.java
+++ b/src/com/android/phone/ImsUtil.java
@@ -17,6 +17,9 @@
 package com.android.phone;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.telephony.CarrierConfigManager;
 import android.util.Log;
 
 import com.android.ims.ImsConfig;
@@ -66,4 +69,31 @@
         if (DBG) Log.d(LOG_TAG, "isWfcModeWifiOnly :: isWifiOnlyMode" + isWifiOnlyMode);
         return isWfcEnabled(context) && isWifiOnlyMode;
     }
+
+    /**
+     * When a call cannot be placed, determines if the use of WFC should be promoted, per the
+     * carrier config.  Use of WFC is promoted to the user if the device is connected to a WIFI
+     * network, WFC is disabled, and the carrier config indicates that the features should be
+     * promoted.
+     *
+     * @return {@code true} if use of WFC should be promoted, {@code false} otherwise.
+     */
+    public static boolean shouldPromoteWfc(Context context) {
+        CarrierConfigManager cfgManager = (CarrierConfigManager) context
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (cfgManager == null || !cfgManager.getConfig()
+                .getBoolean(CarrierConfigManager.KEY_CARRIER_PROMOTE_WFC_ON_CALL_FAIL_BOOL)) {
+            return false;
+        }
+
+        ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            NetworkInfo ni = cm.getActiveNetworkInfo();
+            if (ni != null && ni.isConnected()) {
+                return ni.getType() == ConnectivityManager.TYPE_WIFI && !isWfcEnabled(context);
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 546f0f2..d050576 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -3308,4 +3308,28 @@
         }
         DumpsysHandler.dump(mPhone.getContext(), fd, writer, args);
     }
+
+    /**
+     * Get aggregated video call data usage from all subscriptions since boot.
+     * @return total data usage in bytes
+     * {@hide}
+     */
+    @Override
+    public long getVtDataUsage() {
+        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_NETWORK_USAGE_HISTORY,
+                null);
+
+        // NetworkStatsService keeps tracking the active network interface and identity. It will
+        // record the delta with the corresponding network identity. What we need to do here is
+        // returning total video call data usage from all subscriptions since boot.
+
+        // TODO: Add sub id support in the future. We'll need it when we support DSDA and
+        // simultaneous VT calls.
+        final Phone[] phones = PhoneFactory.getPhones();
+        long total = 0;
+        for (Phone phone : phones) {
+            total += phone.getVtDataUsage();
+        }
+        return total;
+    }
 }
diff --git a/src/com/android/phone/common/mail/store/ImapConnection.java b/src/com/android/phone/common/mail/store/ImapConnection.java
index 0360e3e..6945b94 100644
--- a/src/com/android/phone/common/mail/store/ImapConnection.java
+++ b/src/com/android/phone/common/mail/store/ImapConnection.java
@@ -30,6 +30,7 @@
 import com.android.phone.common.mail.store.imap.ImapUtility;
 import com.android.phone.common.mail.utils.LogUtils;
 import com.android.phone.vvm.omtp.OmtpEvents;
+import com.android.phone.vvm.omtp.VvmLog;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -110,7 +111,7 @@
             // The server should greet us with something like
             // * OK IMAP4rev1 Server
             // consume the response before doing anything else.
-            ImapResponse response = mParser.readResponse();
+            ImapResponse response = mParser.readResponse(false);
             if (!response.isOk()) {
                 mImapStore.getImapHelper()
                         .handleEvent(OmtpEvents.DATA_INVALID_INITIAL_SERVER_RESPONSE);
@@ -138,11 +139,26 @@
         }
     }
 
+    void logout() {
+        try {
+            sendCommand(ImapConstants.LOGOUT, false);
+            if (!mParser.readResponse(true).is(0, ImapConstants.BYE)) {
+                VvmLog.e(TAG, "Server did not respond LOGOUT with BYE");
+            }
+            if (!mParser.readResponse(false).isOk()) {
+                VvmLog.e(TAG, "Server did not respond OK after LOGOUT");
+            }
+        } catch (IOException | MessagingException e) {
+            VvmLog.e(TAG, "Error while logging out:" + e);
+        }
+    }
+
     /**
      * Closes the connection and releases all resources. This connection can not be used again
      * until {@link #setStore(ImapStore)} is called.
      */
     void close() {
+        logout();
         if (mTransport != null) {
             mTransport.close();
             mTransport = null;
@@ -323,7 +339,7 @@
     }
 
     public ImapResponse readResponse() throws IOException, MessagingException {
-        return mParser.readResponse();
+        return mParser.readResponse(false);
     }
 
     public List<ImapResponse> executeSimpleCommand(String command)
@@ -377,11 +393,12 @@
      * @throws IOException
      * @throws MessagingException
      */
-    List<ImapResponse> getCommandResponses() throws IOException, MessagingException {
+    List<ImapResponse> getCommandResponses()
+            throws IOException, MessagingException {
         final List<ImapResponse> responses = new ArrayList<ImapResponse>();
         ImapResponse response;
         do {
-            response = mParser.readResponse();
+            response = mParser.readResponse(false);
             responses.add(response);
         } while (!(response.isTagged() || response.isContinuationRequest()));
 
diff --git a/src/com/android/phone/common/mail/store/ImapFolder.java b/src/com/android/phone/common/mail/store/ImapFolder.java
index cfbb1bc..4abb7f5 100644
--- a/src/com/android/phone/common/mail/store/ImapFolder.java
+++ b/src/com/android/phone/common/mail/store/ImapFolder.java
@@ -145,7 +145,6 @@
         }
         mMessageCount = -1;
         synchronized (this) {
-            mStore.closeConnection();
             mConnection = null;
         }
     }
diff --git a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
index a6d2df6..6fc5abe 100644
--- a/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
+++ b/src/com/android/phone/common/mail/store/imap/ImapResponseParser.java
@@ -136,10 +136,12 @@
      * is stored in the internal storage.  When the {@link ImapResponse} is no longer used
      * {@link #destroyResponses} should be called to destroy all the responses in the array.
      *
+     * @param byeExpected is a untagged BYE response expected? If not proper cleanup will be done
+     * and {@link ByeException} will be thrown.
      * @return the parsed {@link ImapResponse} object.
-     * @exception ByeException when detects BYE.
+     * @exception ByeException when detects BYE and <code>byeExpected</code> is false.
      */
-    public ImapResponse readResponse() throws IOException, MessagingException {
+    public ImapResponse readResponse(boolean byeExpected) throws IOException, MessagingException {
         ImapResponse response = null;
         try {
             response = parseResponse();
@@ -154,7 +156,7 @@
         }
 
         // Handle this outside of try-catch.  We don't have to dump protocol log when getting BYE.
-        if (response.is(0, ImapConstants.BYE)) {
+        if (!byeExpected && response.is(0, ImapConstants.BYE)) {
             Log.w(TAG, ByeException.MESSAGE);
             response.destroy();
             throw new ByeException();
diff --git a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
index e4230ff..3411228 100644
--- a/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
+++ b/src/com/android/phone/settings/VoicemailChangePinDialogPreference.java
@@ -175,8 +175,7 @@
         @Override
         public void onAvailable(Network network) {
             super.onAvailable(network);
-            ImapHelper helper = new ImapHelper(getContext(), mPhoneAccountHandle, network);
-            try {
+            try (ImapHelper helper = new ImapHelper(getContext(), mPhoneAccountHandle, network)) {
                 @ChangePinResult int result =
                         helper.changePin(mOldPin.getText().toString(),
                                 mNewPin.getText().toString());
diff --git a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
index fe3911c..d267e68 100644
--- a/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
+++ b/src/com/android/phone/vvm/omtp/fetch/FetchVoicemailReceiver.java
@@ -159,19 +159,21 @@
                 try {
                     while (mRetryCount > 0) {
                         VvmLog.i(TAG, "fetching voicemail, retry count=" + mRetryCount);
-                        ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount, network);
-                        if (!imapHelper.isSuccessfullyInitialized()) {
-                            VvmLog.w(TAG, "Can't retrieve Imap credentials.");
-                            return;
-                        }
+                        try (ImapHelper imapHelper = new ImapHelper(mContext, mPhoneAccount,
+                                network)) {
+                            if (!imapHelper.isSuccessfullyInitialized()) {
+                                VvmLog.w(TAG, "Can't retrieve Imap credentials.");
+                                return;
+                            }
 
-                        boolean success = imapHelper.fetchVoicemailPayload(
-                                new VoicemailFetchedCallback(mContext, mUri), mUid);
-                        if (!success && mRetryCount > 0) {
-                            VvmLog.i(TAG, "fetch voicemail failed, retrying");
-                            mRetryCount--;
-                        } else {
-                            return;
+                            boolean success = imapHelper.fetchVoicemailPayload(
+                                    new VoicemailFetchedCallback(mContext, mUri), mUid);
+                            if (!success && mRetryCount > 0) {
+                                VvmLog.i(TAG, "fetch voicemail failed, retrying");
+                                mRetryCount--;
+                            } else {
+                                return;
+                            }
                         }
                     }
                 } finally {
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index 216b6a4..908d0f7 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -57,6 +57,7 @@
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -66,7 +67,7 @@
 /**
  * A helper interface to abstract commands sent across IMAP interface for a given account.
  */
-public class ImapHelper {
+public class ImapHelper implements Closeable {
 
     private static final String TAG = "ImapHelper";
 
@@ -126,6 +127,11 @@
                 VoicemailContract.Status.QUOTA_UNAVAILABLE);
     }
 
+    @Override
+    public void close() {
+        mImapStore.closeConnection();
+    }
+
     /**
      * If mImapStore is null, this means that there was a missing or badly formatted port number,
      * which means there aren't sufficient credentials for login. If mImapStore is succcessfully
diff --git a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
index 306006d..b238c8d 100644
--- a/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
+++ b/src/com/android/phone/vvm/omtp/protocol/Vvm3Protocol.java
@@ -148,9 +148,7 @@
             super.onAvailable(network);
             VvmLog.i(TAG, "new user: network available");
             ImapHelper helper = new ImapHelper(mContext, mPhoneAccount, network);
-
             try {
-
                 // VVM3 has inconsistent error language code to OMTP. Just issue a raw command
                 // here.
                 // TODO(b/29082671): use LocaleList
@@ -176,7 +174,10 @@
             } catch (MessagingException | IOException e) {
                 helper.handleEvent(OmtpEvents.VVM3_NEW_USER_SETUP_FAILED);
                 VvmLog.e(TAG, e.toString());
+            } finally {
+                helper.close();
             }
+
         }
 
         private boolean setPin(ImapHelper helper) throws IOException, MessagingException {
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index 74b1f66..9884e9d 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -235,35 +235,36 @@
         int retryCount = NETWORK_RETRY_COUNT;
         try {
             while (retryCount > 0) {
-                ImapHelper imapHelper = new ImapHelper(this, phoneAccount, network);
-                if (!imapHelper.isSuccessfullyInitialized()) {
-                    VvmLog.w(TAG, "Can't retrieve Imap credentials.");
-                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
-                            phoneAccount);
-                    return;
-                }
+                try (ImapHelper imapHelper = new ImapHelper(this, phoneAccount, network)) {
+                    if (!imapHelper.isSuccessfullyInitialized()) {
+                        VvmLog.w(TAG, "Can't retrieve Imap credentials.");
+                        VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
+                                phoneAccount);
+                        return;
+                    }
 
-                boolean success = true;
-                if (voicemail == null) {
-                    success = syncAll(action, imapHelper, phoneAccount);
-                } else {
-                    success = syncOne(imapHelper, voicemail, phoneAccount);
-                }
-                imapHelper.updateQuota();
+                    boolean success = true;
+                    if (voicemail == null) {
+                        success = syncAll(action, imapHelper, phoneAccount);
+                    } else {
+                        success = syncOne(imapHelper, voicemail, phoneAccount);
+                    }
+                    imapHelper.updateQuota();
 
-                // Need to check again for whether visual voicemail is enabled because it could have
-                // been disabled while waiting for the response from the network.
-                if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount) &&
-                        !success) {
-                    retryCount--;
-                    VvmLog.v(TAG, "Retrying " + action);
-                } else {
-                    // Nothing more to do here, just exit.
-                    VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
-                            phoneAccount);
+                    // Need to check again for whether visual voicemail is enabled because it could
+                    // have been disabled while waiting for the response from the network.
+                    if (VisualVoicemailSettingsUtil.isVisualVoicemailEnabled(this, phoneAccount) &&
+                            !success) {
+                        retryCount--;
+                        VvmLog.v(TAG, "Retrying " + action);
+                    } else {
+                        // Nothing more to do here, just exit.
+                        VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
+                                phoneAccount);
 
-                    imapHelper.handleEvent(OmtpEvents.DATA_IMAP_OPERATION_COMPLETED);
-                    return;
+                        imapHelper.handleEvent(OmtpEvents.DATA_IMAP_OPERATION_COMPLETED);
+                        return;
+                    }
                 }
             }
         } finally {
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index f8fd918..2965968 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -125,6 +125,7 @@
             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_SS:
             case android.telephony.DisconnectCause.DIAL_MODIFIED_TO_DIAL:
             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
+            case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
                 return DisconnectCause.ERROR;
 
             case android.telephony.DisconnectCause.DIALED_MMI:
@@ -140,6 +141,9 @@
             case android.telephony.DisconnectCause.CALL_PULLED:
                 return DisconnectCause.CALL_PULLED;
 
+            case android.telephony.DisconnectCause.ANSWERED_ELSEWHERE:
+                return DisconnectCause.ANSWERED_ELSEWHERE;
+
             default:
                 Log.w("DisconnectCauseUtil.toTelecomDisconnectCauseCode",
                         "Unrecognized Telephony DisconnectCause "
@@ -220,6 +224,9 @@
             case android.telephony.DisconnectCause.CALL_PULLED:
                 resourceId = R.string.callEnded_pulled;
 
+            case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
+                resourceId = R.string.callFailed_maximum_reached;
+
             default:
                 break;
         }
@@ -287,7 +294,9 @@
                 // TODO: Offer the option to turn the radio on, and automatically retry the call
                 // once network registration is complete.
 
-                if (ImsUtil.isWfcModeWifiOnly(context)) {
+                if (ImsUtil.shouldPromoteWfc(context)) {
+                    resourceId = R.string.incall_error_promote_wfc;
+                } else if (ImsUtil.isWfcModeWifiOnly(context)) {
                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
                 } else if (ImsUtil.isWfcEnabled(context)) {
                     resourceId = R.string.incall_error_power_off_wfc;
@@ -309,7 +318,9 @@
 
             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
                 // No network connection.
-                if (ImsUtil.isWfcModeWifiOnly(context)) {
+                if (ImsUtil.shouldPromoteWfc(context)) {
+                    resourceId = R.string.incall_error_promote_wfc;
+                } else if (ImsUtil.isWfcModeWifiOnly(context)) {
                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
                 } else if (ImsUtil.isWfcEnabled(context)) {
                     resourceId = R.string.incall_error_out_of_service_wfc;
@@ -339,6 +350,9 @@
                 resourceId = R.string.callEnded_pulled;
                 break;
 
+            case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
+                resourceId = R.string.callFailed_maximum_reached;
+
             case android.telephony.DisconnectCause.OUTGOING_CANCELED:
                 // We don't want to show any dialog for the canceled case since the call was
                 // either canceled by the user explicitly (end-call button pushed immediately)
diff --git a/src/com/android/services/telephony/EmergencyCallStateListener.java b/src/com/android/services/telephony/EmergencyCallStateListener.java
index 2346a7f..036872d 100644
--- a/src/com/android/services/telephony/EmergencyCallStateListener.java
+++ b/src/com/android/services/telephony/EmergencyCallStateListener.java
@@ -24,6 +24,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SubscriptionController;
@@ -159,30 +160,19 @@
             onComplete(true);
             cleanup();
         } else {
-            // The service state changed, but we're still not ready to call yet. (This probably was
-            // the transition from STATE_POWER_OFF to STATE_OUT_OF_SERVICE, which happens
-            // immediately after powering-on the radio.)
-            //
-            // So just keep waiting; we'll probably get to either STATE_IN_SERVICE or
-            // STATE_EMERGENCY_ONLY very shortly. (Or even if that doesn't happen, we'll at least do
-            // another retry when the RETRY_TIMEOUT event fires.)
+            // The service state changed, but we're still not ready to call yet.
             Log.d(this, "onServiceStateChanged: not ready to call yet, keep waiting.");
         }
     }
 
+    /**
+     * We currently only look to make sure that the radio is on before dialing. We should be able to
+     * make emergency calls at any time after the radio has been powered on and isn't in the
+     * UNAVAILABLE state, even if it is reporting the OUT_OF_SERVICE state.
+     */
     private boolean isOkToCall(int serviceState) {
-        // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY, it's finally OK to place
-        // the emergency call.
-        return ((mPhone.getState() == PhoneConstants.State.OFFHOOK)
-                || (serviceState == ServiceState.STATE_IN_SERVICE)
-                || (serviceState == ServiceState.STATE_EMERGENCY_ONLY))
-                // STATE_EMERGENCY_ONLY currently is not used, so we must also check the service
-                // state for emergency only calling.
-                || (serviceState == ServiceState.STATE_OUT_OF_SERVICE &&
-                        mPhone.getServiceState().isEmergencyOnly())
-                // Allow STATE_OUT_OF_SERVICE if we are at the max number of retries.
-                || (mNumRetriesSoFar == MAX_NUM_RETRIES &&
-                        serviceState == ServiceState.STATE_OUT_OF_SERVICE);
+        return (mPhone.getState() == PhoneConstants.State.OFFHOOK) ||
+                mPhone.getServiceStateTracker().isRadioOn();
     }
 
     /**
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 62bbfe2..fcee589 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -571,8 +571,12 @@
 
         mConferenceHost.addConnectionListener(mConferenceHostListener);
         mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener);
-        setState(mConferenceHost.getState());
+        setConnectionCapabilities(applyHostCapabilities(getConnectionCapabilities(),
+                mConferenceHost.getConnectionCapabilities()));
+        setConnectionProperties(applyHostProperties(getConnectionProperties(),
+                mConferenceHost.getConnectionProperties()));
 
+        setState(mConferenceHost.getState());
         updateStatusHints();
     }
 
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index a874674..d75481b 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -117,6 +117,13 @@
      * @param connection
      */
     void add(TelephonyConnection connection) {
+        // DO NOT add external calls; we don't want to consider them as a potential conference
+        // member.
+        if ((connection.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
+                Connection.PROPERTY_IS_EXTERNAL_CALL) {
+            return;
+        }
+
         // Note: Wrap in Log.VERBOSE to avoid calling connection.toString if we are not going to be
         // outputting the value.
         if (Log.VERBOSE) {
@@ -134,6 +141,12 @@
      * @param connection
      */
     void remove(Connection connection) {
+        // External calls are not part of the conference controller, so don't remove them.
+        if ((connection.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
+                Connection.PROPERTY_IS_EXTERNAL_CALL) {
+            return;
+        }
+
         if (Log.VERBOSE) {
             Log.v(this, "remove connection: %s", connection);
         }
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index b5b23b4..a90bc02 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -60,6 +60,7 @@
     // This icon is the one that is used when the Slot ID that we have for a particular SIM
     // is not supported, i.e. SubscriptionManager.INVALID_SLOT_ID or the 5th SIM in a phone.
     private final static int DEFAULT_SIM_ICON =  R.drawable.ic_multi_sim;
+    private final static String GROUP_PREFIX = "group_";
 
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
@@ -101,6 +102,7 @@
 
             // Populate the phone account data.
             int subId = mPhone.getSubId();
+            String subscriberId = mPhone.getSubscriberId();
             int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
             int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
             String line1Number = mTelephonyManager.getLine1Number(subId);
@@ -216,6 +218,23 @@
                 icon = Icon.createWithBitmap(bitmap);
             }
 
+            // Check to see if the newly registered account should replace the old account.
+            String groupId = "";
+            String[] mergedImsis = mTelephonyManager.getMergedSubscriberIds();
+            boolean isMergedSim = false;
+            if (mergedImsis != null && subscriberId != null && !isEmergency) {
+                for (String imsi : mergedImsis) {
+                    if (imsi.equals(subscriberId)) {
+                        isMergedSim = true;
+                        break;
+                    }
+                }
+            }
+            if(isMergedSim) {
+                groupId = GROUP_PREFIX + line1Number;
+                Log.i(this, "Adding Merged Account with group: " + Log.pii(groupId));
+            }
+
             PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
                     .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, line1Number, null))
                     .setSubscriptionAddress(
@@ -227,6 +246,7 @@
                     .setSupportedUriSchemes(Arrays.asList(
                             PhoneAccount.SCHEME_TEL, PhoneAccount.SCHEME_VOICEMAIL))
                     .setExtras(instantLetteringExtras)
+                    .setGroupId(groupId)
                     .build();
 
             // Register with Telecom and put into the account entry.
@@ -622,7 +642,9 @@
                 for (Phone phone : phones) {
                     int subscriptionId = phone.getSubId();
                     Log.d(this, "Phone with subscription id %d", subscriptionId);
-                    if (subscriptionId >= 0) {
+                    // setupAccounts can be called multiple times during service changes. Don't add an
+                    // account if the Icc has not been set yet.
+                    if (subscriptionId >= 0 && phone.getFullIccSerialNumber() != null) {
                         mAccounts.add(new AccountEntry(phone, false /* emergency */,
                                 false /* isDummy */));
                     }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 07cd7b5..c14208f 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -34,6 +34,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.util.Pair;
 
+import com.android.ims.ImsCall;
 import com.android.ims.ImsCallProfile;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
@@ -44,6 +45,7 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
 
@@ -137,6 +139,7 @@
                     if (isMultiParty) {
                         notifyConferenceStarted();
                     }
+                    break;
                 case MSG_CONFERENCE_MERGE_FAILED:
                     notifyConferenceMergeFailed();
                     break;
@@ -164,8 +167,9 @@
                     setVideoState(videoState);
 
                     // A change to the video state of the call can influence whether or not it
-                    // can be part of a conference.
+                    // can be part of a conference and whether another call can be added.
                     refreshConferenceSupported();
+                    refreshDisableAddCall();
                     break;
 
                 case MSG_SET_VIDEO_PROVIDER:
@@ -779,10 +783,10 @@
             extrasToRemove.add(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
         }
 
-        if (!mOriginalConnection.shouldAllowAddCallDuringVideoCall()) {
-            extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL_DURING_VIDEO_CALL, true);
+        if (shouldSetDisableAddCallExtra()) {
+            extrasToPut.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
         } else {
-            extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL_DURING_VIDEO_CALL);
+            extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
         }
         putExtras(extrasToPut);
         removeExtras(extrasToRemove);
@@ -807,6 +811,48 @@
         }
     }
 
+    private void refreshDisableAddCall() {
+        if (shouldSetDisableAddCallExtra()) {
+            putExtra(Connection.EXTRA_DISABLE_ADD_CALL, true);
+        } else {
+            removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
+        }
+    }
+
+    private boolean shouldSetDisableAddCallExtra() {
+        boolean carrierShouldAllowAddCall = mOriginalConnection.shouldAllowAddCallDuringVideoCall();
+        if (carrierShouldAllowAddCall) {
+            return false;
+        }
+        Phone phone = getPhone();
+        if (phone == null) {
+            return false;
+        }
+        boolean isCurrentVideoCall = false;
+        boolean wasVideoCall = false;
+        boolean isWifiCall = false;
+        boolean isVowifiEnabled = false;
+        if (phone instanceof ImsPhone) {
+            ImsPhone imsPhone = (ImsPhone) phone;
+            if (imsPhone.getForegroundCall() != null
+                    && imsPhone.getForegroundCall().getImsCall() != null) {
+                ImsCall call = imsPhone.getForegroundCall().getImsCall();
+                isCurrentVideoCall = call.isVideoCall();
+                wasVideoCall = call.wasVideoCall();
+                isWifiCall = call.isWifiCall();
+            }
+
+            isVowifiEnabled = ((ImsPhoneCallTracker) imsPhone.getCallTracker()).isVowifiEnabled();
+        }
+
+        if (isCurrentVideoCall) {
+            return true;
+        } else if (wasVideoCall && isWifiCall && !isVowifiEnabled) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Whether the connection should be treated as an emergency.
      * @return {@code true} if the connection should be treated as an emergency call based
@@ -1047,7 +1093,11 @@
                     break;
                 case DIALING:
                 case ALERTING:
-                    setDialing();
+                    if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
+                        setPulling();
+                    } else {
+                        setDialing();
+                    }
                     break;
                 case INCOMING:
                 case WAITING: