Merge AU_LINUX_ANDROID_LA.BF.1.1.3_RB1.06.00.00.181.006 on remote branch

Change-Id: I74253fbfd328059923715b7a7ad51ba585049bac
diff --git a/Android.mk b/Android.mk
index a300de7..8d00183 100644
--- a/Android.mk
+++ b/Android.mk
@@ -3,7 +3,7 @@
 # Build the Telecom service.
 include $(CLEAR_VARS)
 
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common ims-common
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += \
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f4ae711..b894891 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -195,6 +195,7 @@
                 <action android:name="com.android.server.telecom.ACTION_CLEAR_MISSED_CALLS" />
                 <action android:name="com.android.server.telecom.ACTION_CALL_BACK_FROM_NOTIFICATION" />
                 <action android:name="com.android.server.telecom.ACTION_SEND_SMS_FROM_NOTIFICATION" />
+                <action android:name="org.codeaurora.ims.ACTION_CALL_PULL" />
             </intent-filter>
         </receiver>
 
diff --git a/res/drawable-hdpi/video_icon.png b/res/drawable-hdpi/video_icon.png
new file mode 100644
index 0000000..b4a4857
--- /dev/null
+++ b/res/drawable-hdpi/video_icon.png
Binary files differ
diff --git a/res/drawable-hdpi/volte_icon.png b/res/drawable-hdpi/volte_icon.png
new file mode 100644
index 0000000..0679d7b
--- /dev/null
+++ b/res/drawable-hdpi/volte_icon.png
Binary files differ
diff --git a/res/drawable-mdpi/video_icon.png b/res/drawable-mdpi/video_icon.png
new file mode 100644
index 0000000..b4a4857
--- /dev/null
+++ b/res/drawable-mdpi/video_icon.png
Binary files differ
diff --git a/res/drawable-mdpi/volte_icon.png b/res/drawable-mdpi/volte_icon.png
new file mode 100644
index 0000000..0679d7b
--- /dev/null
+++ b/res/drawable-mdpi/volte_icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/video_icon.png b/res/drawable-xhdpi/video_icon.png
new file mode 100644
index 0000000..b4a4857
--- /dev/null
+++ b/res/drawable-xhdpi/video_icon.png
Binary files differ
diff --git a/res/drawable-xhdpi/volte_icon.png b/res/drawable-xhdpi/volte_icon.png
new file mode 100644
index 0000000..0679d7b
--- /dev/null
+++ b/res/drawable-xhdpi/volte_icon.png
Binary files differ
diff --git a/res/drawable-xxhdpi/video_icon.png b/res/drawable-xxhdpi/video_icon.png
new file mode 100644
index 0000000..b4a4857
--- /dev/null
+++ b/res/drawable-xxhdpi/video_icon.png
Binary files differ
diff --git a/res/drawable-xxhdpi/volte_icon.png b/res/drawable-xxhdpi/volte_icon.png
new file mode 100644
index 0000000..0679d7b
--- /dev/null
+++ b/res/drawable-xxhdpi/volte_icon.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/video_icon.png b/res/drawable-xxxhdpi/video_icon.png
new file mode 100644
index 0000000..b4a4857
--- /dev/null
+++ b/res/drawable-xxxhdpi/video_icon.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/volte_icon.png b/res/drawable-xxxhdpi/volte_icon.png
new file mode 100644
index 0000000..0679d7b
--- /dev/null
+++ b/res/drawable-xxxhdpi/volte_icon.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 2cf0fd8..5f5d386 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Foonoproepbestuur"</string>
-    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Foon"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Bel"</string>
     <string name="unknown" msgid="6878797917991465859">"Onbekend"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Gemiste oproep"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Gemiste oproepe"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index ec359db..37f3dea 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Гар утасны Дуудлагын Удирдлага"</string>
-    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Гар утас"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Утас"</string>
     <string name="unknown" msgid="6878797917991465859">"Тодорхойгүй"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Аваагүй дуудлага"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Аваагүй дуудлагууд"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 06dc193..db7f713 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -39,7 +39,7 @@
     <string name="enable_account_preference_title" msgid="2021848090086481720">"Oproepaccounts"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="8504993498756056279">"Alleen noodoproepen zijn toegestaan door de apparaateigenaar."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Deze app kan geen uitgaande oproepen starten zonder telefoonrechten."</string>
-    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Als u wilt bellen, moet u een geldig nummer invoeren."</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Als je wilt bellen, moet je een geldig nummer invoeren."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Oproep kan momenteel niet worden toegevoegd."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Schakel de TTY-modus uit om videogesprekken te voeren."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Voicemailnummer ontbreekt"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b3dfff6..abfbb3d 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gestão de chamadas do telemóvel"</string>
-    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telemóvel"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefone"</string>
     <string name="unknown" msgid="6878797917991465859">"Desconhecido"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Chamada não atendida"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Chamadas não atendidas"</string>
diff --git a/res/values/qtistrings.xml b/res/values/qtistrings.xml
new file mode 100644
index 0000000..9a21620
--- /dev/null
+++ b/res/values/qtistrings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2015, The Linux Foundation. All rights reserved.
+  ~
+  ~ Redistribution and use in source and binary forms, with or without
+  ~ modification, are permitted provided that the following conditions are
+  ~ met:
+  ~      Redistributions of source code must retain the above copyright
+  ~       notice, this list of conditions and the following disclaimer.
+  ~      Redistributions in binary form must reproduce the above
+  ~       copyright notice, this list of conditions and the following
+  ~       disclaimer in the documentation and/or other materials provided
+  ~       with the distribution.
+  ~      Neither the name of The Linux Foundation nor the names of its
+  ~       contributors may be used to endorse or promote products derived
+  ~       from this software without specific prior written permission.
+  ~
+  ~ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+  ~ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+  ~ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+  ~ ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+  ~ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  ~ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  ~ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+  ~ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  ~ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  ~ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  ~ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+  ~
+  -->
+
+<!-- The xml contains Qti specific resource strings neede for any value added features. -->
+<resources>
+    <!-- Title of the VICE notifications -->
+    <string name="notification_pullcall">Calls on Secondary</string>
+    <!-- Message for "Call Pull"" Action, which is displayed in the Vice Notification
+         The user will be able to call back to the person or the phone number. -->
+    <string name="pull_to_call_back">Tap to pull</string>
+</resources>
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index 678ad94..d7bcdce 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -938,8 +938,12 @@
             }
 
             int numActiveCalls = activeCall == null ? 0 : 1;
-            int numHeldCalls = callsManager.getNumHeldCalls();
-            boolean callsSwitched = (numHeldCalls == 2);
+            int numHeldCalls = mCallsManager.getNumHeldCalls();
+            // Intermediate state for GSM calls which are in the process of being swapped.
+            // TODO: Should we be hardcoding this value to 2 or should we check if all top level calls
+            //       are held?
+            boolean callsPendingSwitch = (numHeldCalls == 2);
+
             // For conference calls which support swapping the active call within the conference
             // (namely CDMA calls) we need to expose that as a held call in order for the BT device
             // to show "swap" and "merge" functionality.
@@ -947,8 +951,8 @@
             if (activeCall != null && activeCall.isConference() &&
                 !activeCall.can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
                 if (activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
-                    // Indicate that BT device should show SWAP command by indicating that there
-                    // is a call on hold, but only if the conference wasn't previously merged.
+                    // Indicate that BT device should show SWAP command by indicating that there is a
+                    // call on hold, but only if the conference wasn't previously merged.
                     numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1;
                 } else if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
                     numHeldCalls = 1;  // Merge is available, so expose via numHeldCalls.
@@ -966,13 +970,14 @@
             }
 
             if (mBluetoothHeadset != null &&
-                    (numActiveCalls != mNumActiveCalls ||
-                    numHeldCalls != mNumHeldCalls ||
-                    bluetoothCallState != mBluetoothCallState ||
-                    !TextUtils.equals(ringingAddress, mRingingAddress) ||
-                    ringingAddressType != mRingingAddressType ||
-                    (heldCall != mOldHeldCall && !ignoreHeldCallChange) ||
-                    force) && !callsSwitched) {
+                    (force ||
+                            (!callsPendingSwitch &&
+                                    (numActiveCalls != mNumActiveCalls ||
+                                     numHeldCalls != mNumHeldCalls ||
+                                     bluetoothCallState != mBluetoothCallState ||
+                                     !TextUtils.equals(ringingAddress, mRingingAddress) ||
+                                     ringingAddressType != mRingingAddressType ||
+                                     (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) {
 
                 // If the call is transitioning into the alerting state, send DIALING first.
                 // Some devices expect to see a DIALING state prior to seeing an ALERTING state
@@ -1203,13 +1208,13 @@
             case CallState.NEW:
             case CallState.ABORTED:
             case CallState.DISCONNECTED:
-            case CallState.CONNECTING:
-            case CallState.SELECT_PHONE_ACCOUNT:
                 return CALL_STATE_IDLE;
 
             case CallState.ACTIVE:
                 return CALL_STATE_ACTIVE;
 
+            case CallState.CONNECTING:
+            case CallState.SELECT_PHONE_ACCOUNT:
             case CallState.DIALING:
                 // Yes, this is correctly returning ALERTING.
                 // "Dialing" for BT means that we have sent information to the service provider
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 7be5847..e72c32c 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -369,7 +369,6 @@
         mContactsAsyncHelper = contactsAsyncHelper;
         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
         setHandle(handle);
-        setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
         mGatewayInfo = gatewayInfo;
         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
         setTargetPhoneAccount(targetPhoneAccountHandle);
@@ -638,8 +637,12 @@
                 }
             }
 
-            mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(mContext,
-                    mHandle.getSchemeSpecificPart());
+            // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
+            // call, it will remain so for the rest of it's lifetime.
+            if (!mIsEmergencyCall) {
+                mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(
+                        mContext, mHandle.getSchemeSpecificPart());
+            }
             startCallerInfoLookup();
             for (Listener l : mListeners) {
                 l.onHandleChanged(this);
@@ -1693,4 +1696,13 @@
         }
         return CallState.DISCONNECTED;
     }
+
+    /**
+     * Determines if this call is in disconnected state and waiting to be destroyed.
+     *
+     * @return {@code true} if this call is disconected.
+     */
+    public boolean isDisconnected() {
+        return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
+    }
 }
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 72a8158..ac6b01c 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -16,11 +16,19 @@
 
 package com.android.server.telecom;
 
+import android.app.ActivityManagerNative;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.media.AudioManager;
+import android.media.IAudioService;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.telecom.CallAudioState;
 
 import com.android.internal.util.IndentingPrintWriter;
@@ -86,9 +94,27 @@
                 case MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE: {
                     boolean mute = (msg.arg1 != 0);
                     if (mute != mAudioManager.isMicrophoneMute()) {
-                        Log.i(this, "changing microphone mute state to: %b", mute);
-                        mAudioManager.setMicrophoneMute(mute);
+                        IAudioService audio = getAudioService();
+                        Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
+                                mute, audio == null);
+                        if (audio != null) {
+                            try {
+                                // We use the audio service directly here so that we can specify
+                                // the current user. Telecom runs in the system_server process which
+                                // may run as a separate user from the foreground user. If we
+                                // used AudioManager directly, we would change mute for the system's
+                                // user and not the current foreground, which we want to avoid.
+                                audio.setMicrophoneMute(
+                                        mute, mContext.getOpPackageName(), getCurrentUserId());
+
+                            } catch (RemoteException e) {
+                                Log.e(this, e, "Remote exception while toggling mute.");
+                            }
+                            // TODO: Check microphone state after attempting to set to ensure that
+                            // our state corroborates AudioManager's state.
+                        }
                     }
+
                     break;
                 }
                 case MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL: {
@@ -457,7 +483,6 @@
 
         // Audio route.
         if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
-            turnOnSpeaker(false);
             turnOnBluetooth(true);
         } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
             turnOnBluetooth(false);
@@ -514,8 +539,8 @@
                 Log.v(this, "updateAudioStreamAndMode : no foreground, speeding up MT audio.");
                 requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL,
                                                          AudioManager.MODE_IN_CALL);
-            } else if (foregroundCall != null && waitingForAccountSelectionCall == null
-                    && (foregroundCall.getState() != CallState.DISCONNECTED)) {
+            } else if (foregroundCall != null && !foregroundCall.isDisconnected() &&
+                    waitingForAccountSelectionCall == null) {
                 // In the case where there is a call that is waiting for account selection,
                 // this will fall back to abandonAudioFocus() below, which temporarily exits
                 // the in-call audio mode. This is to allow TalkBack to speak the "Call with"
@@ -533,9 +558,12 @@
                 Log.v(this, "updateAudioStreamAndMode : tone playing");
                 requestAudioFocusAndSetMode(
                         AudioManager.STREAM_VOICE_CALL, mMostRecentlyUsedMode);
-            } else if (call == null) {
+            } else if (!hasRingingForegroundCall() && mCallsManager.hasOnlyDisconnectedCalls()) {
                 Log.v(this, "updateAudioStreamAndMode : no ringing call");
-                abandonAudioFocus();
+                // Request to set audio mode normal. Here confirm if any call exist.
+                if (!hasAnyCalls()) {
+                    abandonAudioFocus();
+                }
             } else {
                 // mIsRinging is false, but there is a foreground ringing call present. Don't
                 // abandon audio focus immediately to prevent audio focus from getting lost between
@@ -695,6 +723,27 @@
         return mAudioFocusStreamType != STREAM_NONE;
     }
 
+    private IAudioService getAudioService() {
+        return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
+    }
+
+    private int getCurrentUserId() {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+            return currentUser.id;
+        } catch (RemoteException e) {
+            // Activity manager not running, nothing we can do assume user 0.
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return UserHandle.USER_OWNER;
+    }
+
+    private boolean hasAnyCalls() {
+        return mCallsManager.hasAnyCalls();
+    }
+
     /**
      * Translates an {@link AudioManager} stream type to a human-readable string description.
      *
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index 7d7de17..3913de6 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -9,6 +9,7 @@
 import android.os.Bundle;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.telecom.Connection;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -117,10 +118,23 @@
                 VideoProfile.STATE_AUDIO_ONLY);
         clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
 
+        boolean isCallPull = intent.getBooleanExtra(TelephonyProperties.EXTRA_IS_CALL_PULL, false);
+        Log.d(CallIntentProcessor.class, "processOutgoingCallIntent callPull = " + isCallPull);
+        if (isCallPull) {
+            clientExtras.putBoolean(TelephonyProperties.EXTRA_IS_CALL_PULL, isCallPull);
+        }
+
         Log.i(CallIntentProcessor.class, " processOutgoingCallIntent handle = " + handle
                 + ",scheme = " + scheme + ", uriString = " + uriString
                 + ", isSkipSchemaParsing = " + isSkipSchemaParsing
-                + ", isAddParticipant = " + isAddParticipant);
+                + ", isAddParticipant = " + isAddParticipant
+                + ", isCallPull = " + isCallPull);
+
+        // Ensure call subject is passed on to the connection service.
+        if (intent.hasExtra(TelecomManager.EXTRA_CALL_SUBJECT)) {
+            String callsubject = intent.getStringExtra(TelecomManager.EXTRA_CALL_SUBJECT);
+            clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
+        }
 
         final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
 
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 2007f6e..1fe491e 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -148,7 +148,8 @@
         // TODO(vt): Once data usage is available, wire it up here.
         int callFeatures = getCallFeatures(call.getVideoStateHistory());
         logCall(call.getCallerInfo(), logNumber, call.getHandlePresentation(),
-                callLogType, callFeatures, accountHandle, creationTime, age, null);
+                callLogType, callFeatures, accountHandle, creationTime, age, null,
+                call.isEmergencyCall());
     }
 
     /**
@@ -162,6 +163,7 @@
      * @param start The start time of the call, in milliseconds.
      * @param duration The duration of the call, in milliseconds.
      * @param dataUsage The data usage for the call, null if not applicable.
+     * @param isEmergency {@code true} if this is an emergency call, {@code false} otherwise.
      */
     private void logCall(
             CallerInfo callerInfo,
@@ -172,8 +174,8 @@
             PhoneAccountHandle accountHandle,
             long start,
             long duration,
-            Long dataUsage) {
-        boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mContext, number);
+            Long dataUsage,
+            boolean isEmergency) {
 
         // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
         // emergency calls to the Call Log.  (This behavior is set on a per-product basis, based
@@ -182,7 +184,7 @@
                 mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
 
         // Don't log emergency numbers if the device doesn't allow it.
-        final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
+        final boolean isOkToLogThisCall = !isEmergency || okToLogEmergencyNumber;
 
         sendAddCallBroadcast(callType, duration);
 
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 1f2b2dc..ea2dc56 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -44,6 +44,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.telecom.ui.ViceNotificationImpl;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -90,6 +91,7 @@
     private static final int MAXIMUM_LIVE_CALLS = 1;
     private static final int MAXIMUM_HOLD_CALLS = 1;
     private static final int MAXIMUM_RINGING_CALLS = 1;
+    private static final int MAXIMUM_DIALING_CALLS = 1;
     private static final int MAXIMUM_OUTGOING_CALLS = 1;
     private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
     private static final int MAXIMUM_DSDA_LIVE_CALLS = 2;
@@ -138,6 +140,7 @@
     private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
     private final MissedCallNotifier mMissedCallNotifier;
+    private final ViceNotificationImpl mViceNotificationImpl;
     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
     /* Handler tied to thread in which CallManager was initialized. */
@@ -180,7 +183,8 @@
             PhoneAccountRegistrar phoneAccountRegistrar,
             HeadsetMediaButtonFactory headsetMediaButtonFactory,
             ProximitySensorManagerFactory proximitySensorManagerFactory,
-            InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
+            InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
+            ViceNotifier viceNotifier) {
         mContext = context;
         mLock = lock;
         mContactsAsyncHelper = contactsAsyncHelper;
@@ -205,6 +209,7 @@
         mConnectionServiceRepository =
                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
+        mViceNotificationImpl = viceNotifier.create(mContext, this);
 
         mListeners.add(statusBarNotifier);
         mListeners.add(mCallLogManager);
@@ -218,11 +223,16 @@
         mListeners.add(mDtmfLocalTonePlayer);
         mListeners.add(mHeadsetMediaButton);
         mListeners.add(mProximitySensorManager);
+        mListeners.add(mViceNotificationImpl);
 
         mMissedCallNotifier.updateOnStartup(
                 mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory);
     }
 
+    ViceNotificationImpl getViceNotificationImpl() {
+        return mViceNotificationImpl;
+    }
+
     public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
         if (mRespondViaSmsManager != null) {
             mListeners.remove(mRespondViaSmsManager);
@@ -266,7 +276,7 @@
         Log.d(this, "onSuccessfulIncomingCall");
         setCallState(incomingCall, CallState.RINGING, "successful incoming call");
 
-        if (hasMaximumRingingCalls(incomingCall.getTargetPhoneAccount().getId())) {
+        if (hasMaximumRingingCalls(incomingCall.getTargetPhoneAccount().getId()) || hasMaximumDialingCalls() ) {
             incomingCall.reject(false, null);
             // since the call was not added to the list of calls, we have to call the missed
             // call notifier and the call logger manually.
@@ -465,6 +475,15 @@
         return false;
     }
 
+    boolean hasOnlyDisconnectedCalls() {
+        for (Call call : mCalls) {
+            if (!call.isDisconnected()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     boolean hasVideoCall() {
         for (Call call : mCalls) {
             if (VideoProfile.isVideo(call.getVideoState())) {
@@ -678,13 +697,11 @@
 
         call.setTargetPhoneAccount(phoneAccountHandle);
 
-        boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
-                call.getHandle());
         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
 
         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
         // a call, or cancel this call altogether.
-        if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) {
+        if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, call.isEmergencyCall())) {
             // just cancel at this point.
             Log.i(this, "No remaining room for outgoing call: %s", call);
             if (mCalls.contains(call)) {
@@ -696,7 +713,7 @@
         }
 
         boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
-                !isEmergencyCall;
+                !call.isEmergencyCall();
 
         if (needsAccountSelection) {
             // This is the state where the user is expected to select an account
@@ -758,15 +775,13 @@
                 || mDockManager.isDocked());
         call.setVideoState(videoState);
 
-        boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
-                call.getHandle());
-        if (isEmergencyCall) {
+        if (call.isEmergencyCall()) {
             // Emergency -- CreateConnectionProcessor will choose accounts automatically
             call.setTargetPhoneAccount(null);
         }
 
-        if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
-            if (!isEmergencyCall) {
+        if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
+            if (!call.isEmergencyCall()) {
                 updateLchStatus(call.getTargetPhoneAccount().getId());
             }
             // If the account has been set, proceed to place the outgoing call.
@@ -1307,6 +1322,16 @@
         return count;
     }
 
+    int getNumTopLevelCalls() {
+        int count = 0;
+        for (Call call : mCalls) {
+            if (call.getParentCall() == null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
     Call getOutgoingCall() {
         return getFirstCallWithState(OUTGOING_CALL_STATES);
     }
@@ -1576,7 +1601,7 @@
                 }
 
                 // If only call in call list is held call it's also a foreground call
-                if (mCalls.size() == 1 && call.getState() == CallState.ON_HOLD) {
+                if (getNumTopLevelCalls() == 1 && call.getState() == CallState.ON_HOLD) {
                     newForegroundCall = call;
                 }
 
@@ -1609,7 +1634,7 @@
                 }
 
                 // If only call in call list is held call it's also a foreground call
-                if (mCalls.size() == 1 && call.getState() == CallState.ON_HOLD) {
+                if (getNumTopLevelCalls() == 1 && call.getState() == CallState.ON_HOLD) {
                     newForegroundCall = call;
                 }
 
@@ -1756,6 +1781,10 @@
         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
     }
 
+    private boolean hasMaximumDialingCalls() {
+        return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(CallState.DIALING);
+    }
+
     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
         if (TelephonyManager.getDefault().getMultiSimConfiguration()
                 == TelephonyManager.MultiSimVariants.DSDA) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 63307d3..4cc1045 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -339,6 +339,9 @@
                             // the failure event all the way to InCallUI instead of stopping
                             // it here. That way we can also handle the UI of notifying that
                             // the merged has failed.
+                            Bundle extras = call.getExtras();
+                            extras.putInt("MergeFail", new java.util.Random().nextInt());
+                            call.setExtras(extras);
                             mCallsManager.onMergeFailed(call);
                         } else {
                             Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 6126e66..b846470 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -297,7 +297,7 @@
     // If we are possibly attempting to call a local emergency number, ensure that the
     // plain PSTN connection services are listed, and nothing else.
     private void adjustAttemptsForEmergency()  {
-        if (TelephonyUtil.shouldProcessAsEmergency(mContext, mCall.getHandle())) {
+        if (mCall.isEmergencyCall()) {
             Log.i(this, "Emergency number detected");
             mAttemptRecords.clear();
             List<PhoneAccount> allAccounts = mPhoneAccountRegistrar.getAllPhoneAccounts();
diff --git a/src/com/android/server/telecom/CreateConnectionTimeout.java b/src/com/android/server/telecom/CreateConnectionTimeout.java
index 45305d5..06dc9ed 100644
--- a/src/com/android/server/telecom/CreateConnectionTimeout.java
+++ b/src/com/android/server/telecom/CreateConnectionTimeout.java
@@ -17,14 +17,10 @@
 package com.android.server.telecom;
 
 import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
 import android.os.Handler;
 import android.os.Looper;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
 
 import java.util.Collection;
 import java.util.Objects;
@@ -32,7 +28,7 @@
 /**
  * Registers a timeout for a call and disconnects the call when the timeout expires.
  */
-final class CreateConnectionTimeout extends PhoneStateListener implements Runnable {
+final class CreateConnectionTimeout implements Runnable {
     private final Context mContext;
     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
     private final ConnectionServiceWrapper mConnectionService;
@@ -43,7 +39,6 @@
 
     CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
             ConnectionServiceWrapper service, Call call) {
-        super(Looper.getMainLooper());
         mContext = context;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
         mConnectionService = service;
@@ -53,7 +48,7 @@
     boolean isTimeoutNeededForCall(Collection<PhoneAccountHandle> accounts,
             PhoneAccountHandle currentAccount) {
         // Non-emergency calls timeout automatically at the radio layer. No need for a timeout here.
-        if (!TelephonyUtil.shouldProcessAsEmergency(mContext, mCall.getHandle())) {
+        if (!mCall.isEmergencyCall()) {
             return false;
         }
 
@@ -69,27 +64,27 @@
             return false;
         }
 
-        // To reduce the number of scenarios where a timeout is needed, only use a timeout if
-        // we're connected to Wi-Fi. This ensures that the fallback connection manager has an
-        // alternate route to place the call. TODO: remove this condition or allow connection
-        // managers to specify transports. See http://b/19199181.
-        if (!isConnectedToWifi()) {
+        // Timeout is only supported for SIM call managers that are set by the carrier.
+        if (!Objects.equals(connectionManager.getComponentName(),
+                mPhoneAccountRegistrar.getSystemSimCallManagerComponent())) {
+            Log.d(this, "isTimeoutNeededForCall, not a system sim call manager");
             return false;
         }
 
-        Log.d(this, "isTimeoutNeededForCall, returning true");
+        Log.i(this, "isTimeoutNeededForCall, returning true");
         return true;
     }
 
     void registerTimeout() {
         Log.d(this, "registerTimeout");
         mIsRegistered = true;
-        // First find out the cellular service state. Based on the state we decide whether a timeout
-        // will actually be enforced and if so how long it should be.
-        TelephonyManager telephonyManager =
-            (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
-        telephonyManager.listen(this, 0);
+
+        long timeoutLengthMillis = getTimeoutLengthMillis();
+        if (timeoutLengthMillis <= 0) {
+            Log.d(this, "registerTimeout, timeout set to %d, skipping", timeoutLengthMillis);
+        } else {
+            mHandler.postDelayed(this, timeoutLengthMillis);
+        }
     }
 
     void unregisterTimeout() {
@@ -103,24 +98,9 @@
     }
 
     @Override
-    public void onServiceStateChanged(ServiceState serviceState) {
-        long timeoutLengthMillis = getTimeoutLengthMillis(serviceState);
-        if (!mIsRegistered) {
-            Log.d(this, "onServiceStateChanged, timeout no longer registered, skipping");
-        } else if (timeoutLengthMillis  <= 0) {
-            Log.d(this, "onServiceStateChanged, timeout set to %d, skipping", timeoutLengthMillis);
-        } else if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
-            // If cellular service is available then don't bother with a timeout.
-            Log.d(this, "onServiceStateChanged, cellular service available, skipping");
-        } else {
-            mHandler.postDelayed(this, timeoutLengthMillis);
-        }
-    }
-
-    @Override
     public void run() {
         if (mIsRegistered && isCallBeingPlaced(mCall)) {
-            Log.d(this, "run, call timed out, calling disconnect");
+            Log.i(this, "run, call timed out, calling disconnect");
             mIsCallTimedOut = true;
             mConnectionService.disconnect(mCall);
         }
@@ -133,24 +113,16 @@
             || state == CallState.DIALING;
     }
 
-    private long getTimeoutLengthMillis(ServiceState serviceState) {
+    private long getTimeoutLengthMillis() {
         // If the radio is off then use a longer timeout. This gives us more time to power on the
         // radio.
-        if (serviceState.getState() == ServiceState.STATE_POWER_OFF) {
+        TelephonyManager telephonyManager =
+            (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        if (telephonyManager.isRadioOn()) {
+            return Timeouts.getEmergencyCallTimeoutMillis(mContext.getContentResolver());
+        } else {
             return Timeouts.getEmergencyCallTimeoutRadioOffMillis(
                     mContext.getContentResolver());
-        } else {
-            return Timeouts.getEmergencyCallTimeoutMillis(mContext.getContentResolver());
         }
     }
-
-    private boolean isConnectedToWifi() {
-        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
-            Context.CONNECTIVITY_SERVICE);
-        if (cm != null) {
-          NetworkInfo ni = cm.getActiveNetworkInfo();
-          return ni != null && ni.isConnected() && ni.getType() == ConnectivityManager.TYPE_WIFI;
-        }
-        return false;
-    }
 }
diff --git a/src/com/android/server/telecom/InCallWakeLockController.java b/src/com/android/server/telecom/InCallWakeLockController.java
index b6d3820..a6c63c3 100644
--- a/src/com/android/server/telecom/InCallWakeLockController.java
+++ b/src/com/android/server/telecom/InCallWakeLockController.java
@@ -40,6 +40,7 @@
 
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mFullWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+        mFullWakeLock.setReferenceCounted(false);
 
         callsManager.addListener(this);
     }
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 65847b8..a795d6f 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -286,6 +286,19 @@
         return getSimCallManager(user);
     }
 
+    public ComponentName getSystemSimCallManagerComponent() {
+        String defaultSimCallManager = null;
+        CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle configBundle = configManager.getConfig();
+        if (configBundle != null) {
+            defaultSimCallManager = configBundle.getString(
+                    CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING);
+        }
+        return TextUtils.isEmpty(defaultSimCallManager)
+            ?  null : ComponentName.unflattenFromString(defaultSimCallManager);
+    }
+
     /**
      * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call
      * Manager. SIM Call Manager returned corresponds to the following priority order:
@@ -300,17 +313,7 @@
         String dialerPackage = DefaultDialerManager.getDefaultDialerApplication(mContext, user);
 
         // Check carrier config.
-        String defaultSimCallManager = null;
-        CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
-                Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle configBundle = configManager.getConfig();
-        if (configBundle != null) {
-            defaultSimCallManager = configBundle.getString(
-                    CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING);
-        }
-
-        ComponentName systemSimCallManagerComponent = TextUtils.isEmpty(defaultSimCallManager) ?
-                null : ComponentName.unflattenFromString(defaultSimCallManager);
+        ComponentName systemSimCallManagerComponent = getSystemSimCallManagerComponent();
 
         PhoneAccountHandle dialerSimCallManager = null;
         PhoneAccountHandle systemSimCallManager = null;
diff --git a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
index 89aa2aa..8833527 100644
--- a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
+++ b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserHandle;
+import android.telecom.TelecomManager;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.server.telecom.ui.ViceNotificationImpl;
 
 public final class TelecomBroadcastIntentProcessor {
     /** The action used to send SMS response for the missed call notification. */
@@ -33,6 +36,9 @@
     public static final String ACTION_CLEAR_MISSED_CALLS =
             "com.android.server.telecom.ACTION_CLEAR_MISSED_CALLS";
 
+    public static final String ACTION_CALL_PULL =
+            "org.codeaurora.ims.ACTION_CALL_PULL";
+
     private final Context mContext;
     private final CallsManager mCallsManager;
 
@@ -72,6 +78,21 @@
         // Clear the missed call notification and call log entries.
         } else if (ACTION_CLEAR_MISSED_CALLS.equals(action)) {
             missedCallNotifier.clearMissedCalls();
+        } else if (ACTION_CALL_PULL.equals(action)) {
+            // Close the notification shade and the notification itself.
+            closeSystemDialogs(mContext);
+
+            String dialogId =  intent.getStringExtra("org.codeaurora.ims.VICE_CLEAR");
+            int callType =  intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 0);
+            Log.i(this,"ACTION_CALL_PULL: calltype = " + callType + ", dialogId = " + dialogId);
+
+            Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData());
+            callIntent.putExtra(TelephonyProperties.EXTRA_IS_CALL_PULL, true);
+            callIntent.putExtra(TelephonyProperties.EXTRA_SKIP_SCHEMA_PARSING, true);
+            callIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, callType);
+            callIntent.setFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            mContext.startActivityAsUser(callIntent, UserHandle.CURRENT);
         }
     }
 
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 95be8ff..bb999c7 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -228,7 +228,7 @@
                             .getAllPhoneAccounts();
                     List<PhoneAccount> profilePhoneAccounts = new ArrayList<>(
                             allPhoneAccounts.size());
-                    for (PhoneAccount phoneAccount : profilePhoneAccounts) {
+                    for (PhoneAccount phoneAccount : allPhoneAccounts) {
                         if (isVisibleToCaller(phoneAccount)) {
                             profilePhoneAccounts.add(phoneAccount);
                         }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index c3ab0dc..7d2cf4d 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.telecom.ui.ViceNotificationImpl;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -67,6 +68,7 @@
     private final TelecomBroadcastIntentProcessor mTelecomBroadcastIntentProcessor;
     private final TelecomServiceImpl mTelecomServiceImpl;
     private final ContactsAsyncHelper mContactsAsyncHelper;
+    private final ViceNotifier mViceNotifier;
 
     private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
         @Override
@@ -95,10 +97,12 @@
             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
             HeadsetMediaButtonFactory headsetMediaButtonFactory,
             ProximitySensorManagerFactory proximitySensorManagerFactory,
-            InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
+            InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
+            ViceNotifier vicenotifier) {
         mContext = context.getApplicationContext();
 
         mMissedCallNotifier = missedCallNotifier;
+        mViceNotifier = vicenotifier;
         mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext);
         mContactsAsyncHelper = new ContactsAsyncHelper(mLock);
 
@@ -111,7 +115,8 @@
                 mPhoneAccountRegistrar,
                 headsetMediaButtonFactory,
                 proximitySensorManagerFactory,
-                inCallWakeLockControllerFactory);
+                inCallWakeLockControllerFactory,
+                mViceNotifier);
 
         mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
         mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
diff --git a/src/com/android/server/telecom/ViceNotifier.java b/src/com/android/server/telecom/ViceNotifier.java
new file mode 100644
index 0000000..aea60ab
--- /dev/null
+++ b/src/com/android/server/telecom/ViceNotifier.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux FOundation, Inc. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+import com.android.server.telecom.ui.ViceNotificationImpl;
+
+/**
+ * Creates a notification for calls that are ongoing on secondary device
+ */
+public interface ViceNotifier {
+
+    ViceNotificationImpl create(Context context, CallsManager callsManager);
+
+}
+
diff --git a/src/com/android/server/telecom/WiredHeadsetManager.java b/src/com/android/server/telecom/WiredHeadsetManager.java
index a61dd6e..ef5f38c 100644
--- a/src/com/android/server/telecom/WiredHeadsetManager.java
+++ b/src/com/android/server/telecom/WiredHeadsetManager.java
@@ -39,7 +39,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_HEADSET_PLUG)) {
-                boolean isPluggedIn = intent.getIntExtra("state", 0) == 1;
+                boolean isPluggedIn = mAudioManager.isWiredHeadsetOn();
                 Log.v(WiredHeadsetManager.this, "ACTION_HEADSET_PLUG event, plugged in: %b",
                         isPluggedIn);
                 onHeadsetPluggedInChanged(isPluggedIn);
@@ -48,6 +48,7 @@
     }
 
     private final WiredHeadsetBroadcastReceiver mReceiver;
+    private final AudioManager mAudioManager;
     private boolean mIsPluggedIn;
     /**
      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
@@ -60,8 +61,8 @@
     WiredHeadsetManager(Context context) {
         mReceiver = new WiredHeadsetBroadcastReceiver();
 
-        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        mIsPluggedIn = audioManager.isWiredHeadsetOn();
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mIsPluggedIn = mAudioManager.isWiredHeadsetOn();
 
         // Register for misc other intent broadcasts.
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 7856094..cc31738 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -33,7 +33,9 @@
 import com.android.server.telecom.Log;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.ViceNotifier;
 import com.android.server.telecom.ui.MissedCallNotifierImpl;
+import com.android.server.telecom.ui.ViceNotificationImpl;
 
 /**
  * Implementation of the ITelecom interface.
@@ -101,6 +103,14 @@
                                         CallsManager callsManager) {
                                     return new InCallWakeLockController(context, callsManager);
                                 }
+                            },
+                            new ViceNotifier() {
+                                @Override
+                                public ViceNotificationImpl create(Context context,
+                                        CallsManager callsManager) {
+                                    return new ViceNotificationImpl(
+                                            context.getApplicationContext(), callsManager);
+                                }
                             }));
         }
         if (BluetoothAdapter.getDefaultAdapter() != null) {
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index d3cf7b4..4db6ba5 100644
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -81,7 +81,7 @@
         if (Intent.ACTION_CALL.equals(action) ||
                 Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                 Intent.ACTION_CALL_EMERGENCY.equals(action)) {
-                processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
+            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
         }
     }
 
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index f1d1078..b4392ae 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -50,10 +50,13 @@
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
 import android.text.BidiFormatter;
 import android.text.TextDirectionHeuristics;
 import android.text.TextUtils;
 
+import java.util.Locale;
+
 // TODO: Needed for move to system service: import com.android.internal.R;
 
 /**
@@ -158,7 +161,22 @@
                     mContext.getString(R.string.notification_missedCallsMsg, mMissedCallCount);
         }
 
-        // Create the notification.
+        // Create a public viewable version of the notification, suitable for display when sensitive
+        // notification content is hidden.
+        Notification.Builder publicBuilder = new Notification.Builder(mContext);
+        publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
+                .setColor(mContext.getResources().getColor(R.color.theme_color))
+                .setWhen(call.getCreationTimeMillis())
+                // Show "Phone" for notification title.
+                .setContentTitle(mContext.getText(R.string.userCallActivityLabel))
+                // Notification details shows that there are missed call(s), but does not reveal
+                // the missed caller information.
+                .setContentText(mContext.getText(titleResId))
+                .setContentIntent(createCallLogPendingIntent())
+                .setAutoCancel(true)
+                .setDeleteIntent(createClearMissedCallsPendingIntent());
+
+        // Create the notification suitable for display when sensitive information is showing.
         Notification.Builder builder = new Notification.Builder(mContext);
         builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                 .setColor(mContext.getResources().getColor(R.color.theme_color))
@@ -167,7 +185,11 @@
                 .setContentText(expandedText)
                 .setContentIntent(createCallLogPendingIntent())
                 .setAutoCancel(true)
-                .setDeleteIntent(createClearMissedCallsPendingIntent());
+                .setDeleteIntent(createClearMissedCallsPendingIntent())
+                // Include a public version of the notification to be shown when the missed call
+                // notification is shown on the user's lock screen and they have chosen to hide
+                // sensitive notification information.
+                .setPublicVersion(publicBuilder.build());
 
         Uri handleUri = call.getHandle();
         String handle = handleUri == null ? null : handleUri.getSchemeSpecificPart();
@@ -182,9 +204,11 @@
                         mContext.getString(R.string.notification_missedCall_call_back),
                         createCallBackPendingIntent(handleUri));
 
-                builder.addAction(R.drawable.ic_message_24dp,
-                        mContext.getString(R.string.notification_missedCall_message),
-                        createSendSmsFromNotificationPendingIntent(handleUri));
+                if (canRespondViaSms(call)) {
+                    builder.addAction(R.drawable.ic_message_24dp,
+                            mContext.getString(R.string.notification_missedCall_message),
+                            createSendSmsFromNotificationPendingIntent(handleUri));
+                }
             }
 
             Bitmap photoIcon = call.getPhotoIcon();
@@ -234,6 +258,18 @@
         String handle = call.getHandle() == null ? null : call.getHandle().getSchemeSpecificPart();
         String name = call.getName();
 
+        if (!TextUtils.isEmpty(handle)) {
+            String formattedNumber = PhoneNumberUtils.formatNumber(handle,
+                    getCurrentCountryIso(mContext));
+
+            // The formatted number will be null if there was a problem formatting it, but we can
+            // default to using the unformatted number instead (e.g. a SIP URI may not be able to
+            // be formatted.
+            if (!TextUtils.isEmpty(formattedNumber)) {
+                handle = formattedNumber;
+            }
+        }
+
         if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) {
             return name;
         } else if (!TextUtils.isEmpty(handle)) {
@@ -249,6 +285,26 @@
     }
 
     /**
+     * @return The ISO 3166-1 two letters country code of the country the user is in based on the
+     *      network location.  If the network location does not exist, fall back to the locale
+     *      setting.
+     */
+    private String getCurrentCountryIso(Context context) {
+        // Without framework function calls, this seems to be the most accurate location service
+        // we can rely on.
+        final TelephonyManager telephonyManager =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        String countryIso = telephonyManager.getNetworkCountryIso().toUpperCase();
+
+        if (countryIso == null) {
+            countryIso = Locale.getDefault().getCountry();
+            Log.w(this, "No CountryDetector; falling back to countryIso based on locale: "
+                    + countryIso);
+        }
+        return countryIso;
+    }
+
+    /**
      * Creates a new pending intent that sends the user to the call log.
      *
      * @return The pending intent.
@@ -312,6 +368,12 @@
         notification.defaults |= Notification.DEFAULT_LIGHTS;
     }
 
+    private boolean canRespondViaSms(Call call) {
+        // Only allow respond-via-sms for "tel:" calls.
+        return call.getHandle() != null &&
+                PhoneAccount.SCHEME_TEL.equals(call.getHandle().getScheme());
+    }
+
     /**
      * Adds the missed call notification on startup if there are unread missed calls.
      */
diff --git a/src/com/android/server/telecom/ui/ViceNotificationImpl.java b/src/com/android/server/telecom/ui/ViceNotificationImpl.java
new file mode 100644
index 0000000..ac068d2
--- /dev/null
+++ b/src/com/android/server/telecom/ui/ViceNotificationImpl.java
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux FOundation, Inc. nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server.telecom.ui;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.CallsManagerListenerBase;
+import com.android.server.telecom.R;
+import com.android.server.telecom.Log;
+import com.android.server.telecom.components.TelecomBroadcastReceiver;
+import com.android.server.telecom.TelecomBroadcastIntentProcessor;
+
+import org.codeaurora.ims.qtiims.IQtiImsInterface;
+import org.codeaurora.ims.qtiims.IQtiImsInterfaceListener;
+import org.codeaurora.ims.qtiims.QtiImsInterfaceListenerBaseImpl;
+import org.codeaurora.ims.qtiims.QtiImsInterfaceUtils;
+import org.codeaurora.ims.qtiims.QtiViceInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Handles the VICE notifications on the statusbar
+ * And when statusbar is expanded
+ */
+public class ViceNotificationImpl extends CallsManagerListenerBase {
+    private final Context mContext;
+    private final NotificationManager mNotificationManager;
+    private IQtiImsInterface mQtiImsInterface = null;
+    private boolean mImsServiceBound = false;
+    private Notification.Builder mBuilder = null;
+    private Notification.Builder mPublicNotificationBuilder = null;
+
+    private MyHandler mHandler = new MyHandler();
+
+    /**
+     * Holds Pullable and NonPullable calls passed from IMS Service
+     * Each String[] contains call info in following order
+     * Number, Pullable/NonPullable, CallType, Direction
+     * Ex: For callInfo[] -
+     * callInfo[QtiViceInfo.INDEX_DIALOG_ID] - Holds Unique DialogId
+     * callInfo[QtiViceInfo.INDEX_NUMBER] - Holds number/uri
+     * callInfo[QtiViceInfo.INDEX_ISPULLABLE] - Pullable/NonPullable (true, false)
+     * callInfo[QtiViceInfo.INDEX_CALLTYPE] - CallType
+     *     CallType - volteactive, volteheld, vttxrx, vttx, vtrx, vtheld
+     * callInfo[QtiViceInfo.INDEX_DIRECTION] - Direction of the call (Originator/recipent)
+     */
+    private List<String[]> mQtiViceInfo = null;
+
+    // Holds the callInfo which needs to be displayed after the active call ends
+    private  List<String[]> mBackedUpCallList = null;
+
+    private boolean mWasInCall = false;
+
+    // HashMap that holds Dialog Number & Notification Id used to display on the statusbar
+    private Map<String,Integer> mNotification = new HashMap<String,Integer>();
+
+    private ImsIntentReceiver mImsIntentReceiver = null;
+
+    private static final String IMS_SERVICE_PKG_NAME = "org.codeaurora.ims";
+
+    public ViceNotificationImpl(Context context, CallsManager callsManager) {
+        mContext = context;
+        mNotificationManager =
+                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        Log.i(this,"ViceNotificationImpl");
+        if (!bindImsService()) {
+            //Register for IMS ready intent to re try bind
+            registerImsReceiver();
+        }
+    }
+
+    private class MyHandler extends Handler {
+        static final int MESSAGE_VICE_NOTIFY = 1;
+        static final int MESSAGE_CALL_ENDED = 2;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_VICE_NOTIFY:
+                    Log.i(this,"MESSAGE_VICE_NOTIFY");
+                    resetBeforeProcess();
+                    processNotification();
+                    break;
+
+                case MESSAGE_CALL_ENDED:
+                    if (mWasInCall) {
+                        Log.i(this,"MESSAGE_CALL_ENDED");
+                        resetBeforeProcess();
+                        mQtiViceInfo = mBackedUpCallList;
+                        processNotification();
+                    }
+                    break;
+
+                default:
+                    Log.i(this,"VICE default");
+            }
+        }
+    }
+
+    private class ImsIntentReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i("ViceNotificationImpl", "mImsIntentReceiver: action " + intent.getAction());
+            if (ImsManager.ACTION_IMS_SERVICE_UP.equals(intent.getAction())) {
+                if (bindImsService()) {
+                    unregisterImsReceiver();
+                }
+            }
+        }
+    }
+
+    // Clear the existing notifications on statusbar and
+    // Hashmap whenever new Vice Notification is received
+    private void resetBeforeProcess() {
+        mNotificationManager.cancelAll();
+        mNotification.clear();
+        mBuilder = null;
+        mPublicNotificationBuilder = null;
+        mWasInCall = false;
+    }
+
+    /* Service connection bound to IQtiImsInterface */
+    private ServiceConnection mConnection = new ServiceConnection() {
+
+        /* Below API gets invoked when connection to ImsService is established */
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Log.i("ViceNotificationImpl", "onServiceConnected");
+            /* Retrieve the IQtiImsInterface */
+            mQtiImsInterface = IQtiImsInterface.Stub.asInterface(service);
+
+            /**
+             * If interface is available register for Vice notifications
+             */
+            if (mQtiImsInterface != null) {
+                registerForViceRefreshInfo();
+            } else {
+                /* Request or interface is unavailable, unbind the service */
+                unbindImsService();
+            }
+        }
+
+        /* Below API gets invoked when connection to ImsService is disconnected */
+        public void onServiceDisconnected(ComponentName className) {
+            Log.i("ViceNotificationImpl", "onServiceDisconnected");
+        }
+    };
+
+    /* QtiImsInterfaceListenerBaseImpl instance to handle call backs */
+    private IQtiImsInterfaceListener imsInterfaceListener =
+        new QtiImsInterfaceListenerBaseImpl() {
+
+        @Override
+        public void notifyRefreshViceInfo(QtiViceInfo qtiViceInfo) {
+            mQtiViceInfo = null;
+            processViceCallInfo(qtiViceInfo);
+        }
+    };
+
+    public void registerForViceRefreshInfo() {
+        try {
+            Log.d(this, "registerForViceRefreshInfo");
+            mQtiImsInterface.registerForViceRefreshInfo(imsInterfaceListener);
+        } catch (RemoteException e) {
+            Log.d(this, "registerForViceRefreshInfo exception " + e);
+        }
+    }
+
+    /**
+     * Informs if call deflection interafce is available or not.
+     * Returns true if allowed, false otherwise.
+     */
+    public boolean isQtiImsInterfaceAvailable() {
+        return (mImsServiceBound && (mQtiImsInterface != null));
+    }
+
+    /**
+     * Checks if ims service is bound or not
+     * Returns true when bound, false otherwise.
+     */
+    public boolean isImsServiceAvailable() {
+        return mImsServiceBound;
+    }
+
+    /**
+     * Bind to the ims service
+     * Returns true if bound sucessfully, false otherwise.
+     */
+    public boolean bindImsService() {
+        if (!ImsManager.getInstance(mContext, 0).isServiceAvailable()) {
+            Log.d(this, "bindImsService: IMS service is not available!");
+            return false;
+        }
+        Intent intent = new Intent(IQtiImsInterface.class.getName());
+        intent.setPackage(IMS_SERVICE_PKG_NAME);
+        mImsServiceBound = mContext.bindService(intent,
+                                   mConnection,
+                                   0);
+        Log.d(this, "Getting IQtiImsInterface : " + (mImsServiceBound?"yes":"failed"));
+        return mImsServiceBound;
+    }
+
+    /* Unbind the ims service if was already bound */
+    public void unbindImsService() {
+        if (mImsServiceBound) {
+            Log.d(this, "UnBinding IQtiImsInterface");
+
+            /* When disconnecting, reset the globals variables */
+            mImsServiceBound = false;
+            mContext.unbindService(mConnection);
+        }
+    }
+
+    private void processViceCallInfo(QtiViceInfo qtiViceInfo) {
+        mQtiViceInfo = new ArrayList<String[]>();
+        mQtiViceInfo = qtiViceInfo.callInfo;
+        // Post an event to self handler
+        mHandler.sendEmptyMessage(MyHandler.MESSAGE_VICE_NOTIFY);
+    }
+
+    private void resetBuilders() {
+        mBuilder = null;
+        mPublicNotificationBuilder = null;
+        mBuilder = new Notification.Builder(mContext);
+        mPublicNotificationBuilder = new Notification.Builder(mContext);
+    }
+
+    /**
+     * This function does the following -
+     * - Iterate through the callList
+     * - Update the hashmap for notification
+     * - AddAction for pullable calls to update the button "TAP TO PULL"
+     * - Build notification and show it
+     *
+     * "Tap to Pull" Button should be seen only if -
+     * - Phone is in idle state AND
+     * - CallState received is ACTIVE for Voice calls or ACTIVE/SENDONLY/RECVONLY for VT AND
+     * - If Volte/VT is supported on device AND
+     * - IsPullable is true
+     */
+    private void processNotification() {
+        Random random = new Random();
+        int notifId = 0;
+        boolean isVt = false;
+
+        if ((mQtiViceInfo != null) && !mQtiViceInfo.isEmpty()) {
+            Log.i(this, "processNotification : Number of Calls = "
+                    + mQtiViceInfo.size() + ", notif = " + notifId);
+
+            for (int i = 0; i < mQtiViceInfo.size(); i++) {
+                notifId = random.nextInt(500);
+                String[] callInfo = new String[QtiViceInfo.INDEX_MAX];
+                callInfo = mQtiViceInfo.get(i);
+                Log.i(this, "processNotification callInfo[" + i + "] = "
+                        + ", DialogId = " + callInfo[QtiViceInfo.INDEX_DIALOG_ID]
+                        + ", Number = " + callInfo[QtiViceInfo.INDEX_NUMBER]
+                        + ", Pullable = " + callInfo[QtiViceInfo.INDEX_ISPULLABLE]
+                        + ", CallType = " + callInfo[QtiViceInfo.INDEX_CALLTYPE]
+                        + ", Direction = " + callInfo[QtiViceInfo.INDEX_DIRECTION]
+                        + ", notifId = " + notifId);
+
+                resetBuilders();
+                checkAndUpdateNotification(callInfo, false);
+                Log.i(this, "processNotification isInCall = " + getTelecomManager().isInCall());
+                isVt = isVtCall(callInfo[QtiViceInfo.INDEX_CALLTYPE]);
+
+                // Once the active call ends, we need to display "Tap to pull" option
+                // for the pullable calls. Hence backup the callinfo
+                if (getTelecomManager().isInCall()) {
+                    backupInfoToProcessLater();
+                }
+                // Refer comments in API description
+                if (!(getTelecomManager().isInCall()) &&
+                        callInfo[QtiViceInfo.INDEX_ISPULLABLE].equalsIgnoreCase("true") &&
+                        isDeviceCapableOfPull(callInfo[QtiViceInfo.INDEX_CALLTYPE])) {
+                    addAction(callInfo[QtiViceInfo.INDEX_NUMBER], isVt,
+                            callInfo[QtiViceInfo.INDEX_CALLTYPE],
+                            callInfo[QtiViceInfo.INDEX_DIALOG_ID]);
+                } else {
+                    addAction(null, false, null, null);
+                }
+                updateLargeIconforCallType(isVt);
+
+                showNotification(callInfo[QtiViceInfo.INDEX_NUMBER],
+                        callInfo[QtiViceInfo.INDEX_DIALOG_ID], notifId);
+            }
+        } else {
+            Log.i(this, "processNotification DEP null");
+            resetBeforeProcess();
+        }
+    }
+
+    private boolean isDeviceCapableOfPull(String callType) {
+        return ((isVtCall(callType) && isVTPullAllowed()) ||
+                ((callType != null) && callType.equalsIgnoreCase(
+                QtiViceInfo.CALL_TYPE_VOICE_ACTIVE) && isVoltePullAllowed()));
+    }
+
+    private boolean isVTPullAllowed() {
+        return TelephonyManager.getDefault().isVideoCallingEnabled();
+    }
+
+    private boolean isVoltePullAllowed() {
+        return TelephonyManager.getDefault().isVolteAvailable()
+                || TelephonyManager.getDefault().isWifiCallingAvailable();
+    }
+
+    private boolean isVtCall(String callType) {
+        if ((callType != null) && (callType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_TX_RX) ||
+                callType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_TX) ||
+                callType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_RX) ||
+                callType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_HELD))) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * After the active call disconnects, need to refresh the notification
+     * to show pullable calls. Hence save the call information
+     *
+     */
+    private void backupInfoToProcessLater() {
+        mBackedUpCallList = new ArrayList<String[]>();
+        mBackedUpCallList = mQtiViceInfo;
+        // Call can be ended either through "Tap To Pull" or existing call ending
+        // Reset the variables only for later case
+        // Use this boolean to track the END reason
+        mWasInCall = true;
+    }
+
+    /**
+     * API updates the hashmap in following order :
+     * - if new call : create new entry
+     * - if existing call : cancel existing notification and remove it from hashmap
+     *                      It will get added to hashmap in showNotification()
+     */
+    private void checkAndUpdateNotification(String[] callInfo, boolean clear) {
+        Set<Map.Entry<String, Integer>> call = mNotification.entrySet();
+        if ((call == null) || (mNotification.isEmpty())) {
+            return;
+        }
+
+        Iterator<Map.Entry<String, Integer>> iterator = call.iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<String, Integer> entry = iterator.next();
+            String dialog = entry.getKey();
+            Integer notifId = entry.getValue();
+            if (dialog.equalsIgnoreCase(callInfo[QtiViceInfo.INDEX_DIALOG_ID])) {
+                mNotificationManager.cancel(notifId);
+                call.remove(dialog);
+            }
+        }
+    }
+
+    /**
+     * This API should be invoked only for pullable calls
+     * Responsible to add "TAP to Pull" button, using which call
+     * can be pulled
+     */
+    private void addAction(String uri, boolean isVt, String callType, String dialogId) {
+        Log.i(this, "addAction dialogId = " + dialogId + ", isVt = " + isVt
+                + ", callType = " + callType);
+        if (uri != null) {
+            mBuilder.addAction(R.drawable.ic_phone_24dp,
+                    mContext.getString(R.string.pull_to_call_back),
+                    createCallBackPendingIntent(Uri.parse(uri), isVt, callType, dialogId));
+            mBuilder.setPriority(Notification.PRIORITY_HIGH);
+        }
+    }
+
+    /*
+     * When the notification bar is pulled,
+     * Update - Video icon for VT Calls
+     *        - Phone icon for Volte calls
+     */
+    private void updateLargeIconforCallType(boolean isVt) {
+       Log.i(this, "updateLargeIconforCallType isVt  = " + isVt);
+       Bitmap bitmap;
+       if (isVt) {
+            bitmap = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.video_icon);
+       } else {
+            bitmap = BitmapFactory.decodeResource(mContext.getResources(),
+                    R.drawable.volte_icon);
+       }
+       mBuilder.setLargeIcon(bitmap);
+    }
+
+    /*
+     * When the phone is pattern locked, display only restricted information
+     * for this notification
+     */
+    private void buildPublicNotification() {
+        // Update the text in the public version as well
+        mPublicNotificationBuilder
+                .setContentTitle(mContext.getString(R.string.notification_pullcall))
+                .setAutoCancel(true)
+                .setColor(com.android.internal.R.color.system_notification_accent_color);
+    }
+
+    // Builds & displays the notification on statusbar
+    private void showNotification(String uri, String dialog, int notifId) {
+        PendingIntent contentIntent = PendingIntent.getActivity( mContext,
+                0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
+
+        buildPublicNotification();
+
+        mBuilder.setContentText(uri)
+                .setColor(Color.BLUE)
+                .setVisibility(Notification.VISIBILITY_PRIVATE)
+                .setPublicVersion(mPublicNotificationBuilder.build())
+                .setDeleteIntent(contentIntent);
+
+        // For 1st notification, display the icon on the statusbar
+        // For subsequent notification, dont display the icon
+        Log.i(this," showNotification size = " + mNotification.size() + ", notifid = " + notifId);
+        if (mNotification.size() == 0) {
+            mBuilder.setSmallIcon(R.drawable.ic_phone_24dp);
+        } else {
+            mBuilder.setSmallIcon(android.R.color.transparent);
+        }
+
+        mNotificationManager.notify(notifId, mBuilder.build());
+        // Add the Dialog+Notification ID to hashmap
+        mNotification.put(dialog, notifId);
+    }
+
+    private PendingIntent createCallBackPendingIntent(Uri handle, boolean isVt,
+        String callType, String dialogId) {
+        return createTelecomPendingIntent(
+                TelecomBroadcastIntentProcessor.ACTION_CALL_PULL, handle, isVt, callType, dialogId);
+    }
+
+    /**
+     * Creates generic pending intent from the specified parameters to be received by
+     * {@link TelecomBroadcastIntentProcessor}.
+     *
+     * @param action The intent action.
+     * @param data The intent data.
+     */
+    private PendingIntent createTelecomPendingIntent(String action, Uri data,
+            boolean isVt, String callType, String dialogId) {
+        Intent intent = new Intent(action, data, mContext, TelecomBroadcastReceiver.class);
+        // Add following extras so that Dial request is placed for CallPull
+        // And parsing is avoided for dialstring
+        intent.putExtra("org.codeaurora.ims.VICE_CLEAR", dialogId);
+        if (isVt) {
+            // Extra to start Dial with VT enabled
+            intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+                    covertStringToIntVtType(callType));
+        } else {
+            intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+                    VideoProfile.STATE_AUDIO_ONLY);
+        }
+        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    private int covertStringToIntVtType(String vtType) {
+        if (vtType != null) {
+            if (vtType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_TX_RX)) {
+                return VideoProfile.STATE_BIDIRECTIONAL;
+            } else if (vtType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_TX)) {
+                return VideoProfile.STATE_TX_ENABLED;
+            } else if (vtType.equalsIgnoreCase(QtiViceInfo.CALL_TYPE_VIDEO_RX)) {
+                return VideoProfile.STATE_RX_ENABLED;
+            } else {
+                return VideoProfile.STATE_AUDIO_ONLY;
+            }
+        } else {
+            Log.i(this, "covertStringToIntVtType vttype null!!");
+            return VideoProfile.STATE_AUDIO_ONLY;
+        }
+    }
+
+    private TelecomManager getTelecomManager() {
+        return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+    }
+
+    /**
+     * During any ongoing calls, if DEP is received with pullable calls,
+     * need to change them to non-pullable. But after calls ends, need to
+     * make them as pullable again.
+     */
+    @Override
+    public void onCallStateChanged(Call call, int oldState, int newState) {
+        if ((newState == CallState.ACTIVE) || (newState == CallState.DISCONNECTED)) {
+            Log.i(this, "onCallStateChanged newState = " + newState);
+            backupInfoToProcessLater();
+            mHandler.sendEmptyMessage(MyHandler.MESSAGE_CALL_ENDED);
+        }
+    }
+
+    private void registerImsReceiver() {
+        mImsIntentReceiver = new ImsIntentReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+        mContext.registerReceiver(mImsIntentReceiver, filter);
+    }
+
+    private void unregisterImsReceiver() {
+        if (mImsIntentReceiver != null) {
+            mContext.unregisterReceiver(mImsIntentReceiver);
+            mImsIntentReceiver = null;
+        }
+    }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
index cd0800e..c1ced80 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
@@ -111,7 +111,8 @@
                 .setAddress(Uri.parse("tel:555-TEST"))
                 .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
-                        PhoneAccount.CAPABILITY_VIDEO_CALLING)
+                        PhoneAccount.CAPABILITY_VIDEO_CALLING |
+                        PhoneAccount.CAPABILITY_CALL_SUBJECT)
                 .setIcon(Icon.createWithResource(
                         context.getResources(), R.drawable.stat_sys_phone_call))
                 // TODO: Add icon tint (Color.RED)
@@ -129,7 +130,8 @@
                 .setSubscriptionAddress(Uri.parse("tel:555-TSIM"))
                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
                         PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
-                        PhoneAccount.CAPABILITY_VIDEO_CALLING)
+                        PhoneAccount.CAPABILITY_VIDEO_CALLING |
+                        PhoneAccount.CAPABILITY_CALL_SUBJECT)
                 .setIcon(Icon.createWithResource(
                         context.getResources(), R.drawable.stat_sys_phone_call))
                 // TODO: Add icon tint (Color.GREEN)
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index 45896f4..7964355 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -36,6 +36,7 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.android.server.telecom.testapps.R;
 
@@ -299,6 +300,13 @@
         String gatewayPackage = extras.getString(TelecomManager.GATEWAY_PROVIDER_PACKAGE);
         Uri originalHandle = extras.getParcelable(TelecomManager.GATEWAY_ORIGINAL_ADDRESS);
 
+        if (extras.containsKey(TelecomManager.EXTRA_CALL_SUBJECT)) {
+            String callSubject = extras.getString(TelecomManager.EXTRA_CALL_SUBJECT);
+            log("Got subject: " + callSubject);
+            Toast.makeText(getApplicationContext(), "Got subject :" + callSubject,
+                    Toast.LENGTH_SHORT).show();
+        }
+
         log("gateway package [" + gatewayPackage + "], original handle [" +
                 originalHandle + "]");
 
@@ -354,6 +362,24 @@
                     VideoProfile.STATE_BIDIRECTIONAL :
                     VideoProfile.STATE_AUDIO_ONLY;
             connection.setVideoState(videoState);
+
+            Bundle connectionExtras = connection.getExtras();
+            if (connectionExtras == null) {
+                connectionExtras = new Bundle();
+            }
+
+            // Randomly choose a varying length call subject.
+            int subjectFormat = mRandom.nextInt(3);
+            if (subjectFormat == 0) {
+                connectionExtras.putString(Connection.EXTRA_CALL_SUBJECT,
+                        "This is a test of call subject lines. Subjects for a call can be long " +
+                                " and can go even longer.");
+            } else if (subjectFormat == 1) {
+                connectionExtras.putString(Connection.EXTRA_CALL_SUBJECT,
+                        "This is a test of call subject lines.");
+            }
+            connection.setExtras(connectionExtras);
+
             setAddress(connection, address);
 
             addVideoProvider(connection);
diff --git a/tests/Android.mk b/tests/Android.mk
index 901ba30..4b3eeda 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -37,7 +37,8 @@
 
 LOCAL_JAVA_LIBRARIES := \
         android.test.runner \
-        telephony-common
+        telephony-common \
+        ims-common
 
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 4f96cc7..a3e9926 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -61,7 +61,8 @@
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.TelecomSystem;
-
+import com.android.server.telecom.ViceNotifier;
+import com.android.server.telecom.ui.ViceNotificationImpl;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
@@ -78,6 +79,7 @@
     @Mock HeadsetMediaButton mHeadsetMediaButton;
     @Mock ProximitySensorManager mProximitySensorManager;
     @Mock InCallWakeLockController mInCallWakeLockController;
+    @Mock ViceNotifier mViceNotifier;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -203,7 +205,8 @@
                 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
                 headsetMediaButtonFactory,
                 proximitySensorManagerFactory,
-                inCallWakeLockControllerFactory);
+                inCallWakeLockControllerFactory,
+                mViceNotifier);
 
         verify(headsetMediaButtonFactory).create(
                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),