Merge commit '9bdcff7bdf72dd69f43c2b3d6fc20474130008c0' into remote

Conflicts:
	AndroidManifest.xml
	InCallUI/src/com/android/incallui/Call.java
	InCallUI/src/com/android/incallui/CallButtonPresenter.java
	InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
	InCallUI/src/com/android/incallui/StatusBarNotifier.java
	InCallUI/src/com/android/incallui/VideoCallPresenter.java

Change-Id: Ice78377f619bcd740170eff4393102329319f43d
diff --git a/Android.mk b/Android.mk
old mode 100644
new mode 100755
index 975d396..2cb292a
--- a/Android.mk
+++ b/Android.mk
@@ -31,12 +31,18 @@
     $(phone_common_dir)/src-N
 
 LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
+LOCAL_SRC_FILES += src/org/codeaurora/presenceserv/IPresenceService.aidl \
+                   src/org/codeaurora/presenceserv/IPresenceServiceCB.aidl
 LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs)) \
     $(support_library_root_dir)/v7/cardview/res \
     $(support_library_root_dir)/v7/recyclerview/res \
     $(support_library_root_dir)/v7/appcompat/res \
     $(support_library_root_dir)/design/res
 
+LOCAL_JAVA_LIBRARIES := telephony-common \
+                        telephony-ext \
+                        ims-common
+
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
     --extra-packages android.support.v7.appcompat \
@@ -57,7 +63,10 @@
     android-support-design \
     com.android.vcard \
     guava \
-    libphonenumber
+    libphonenumber \
+    ims-ext-common \
+    phonebook_wrapper \
+    telephony-common
 
 LOCAL_PACKAGE_NAME := Dialer
 LOCAL_CERTIFICATE := shared
@@ -65,7 +74,7 @@
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags $(incallui_dir)/proguard.flags
 
-LOCAL_SDK_VERSION := current
+# LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6528fdb..7bbd37f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -39,13 +39,17 @@
     <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
     <uses-permission android:name="android.permission.NFC" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="com.qualcomm.permission.USE_PHONE_SERVICE" />
+    <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
@@ -56,6 +60,10 @@
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
     <!-- This tells the activity manager to not delay any of our activity
      start requests, even if they happen immediately after the user
      presses home. -->
@@ -73,6 +81,7 @@
         <meta-data android:name="com.google.android.backup.api_key"
             android:value="AEdPqrEAAAAIBXgtCEKQ6W0PXVnW-ZVia2KmlV2AxsTw3GjAeQ" />
 
+        <uses-library android:name="com.qualcomm.qti.smartsearch" android:required="false" />
         <!-- The entrance point for Phone UI.
              stateAlwaysHidden is set to suppress keyboard show up on
              dialpad screen. -->
@@ -163,6 +172,7 @@
         </activity>
 
         <activity android:name="com.android.dialer.calllog.CallLogActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden"
             android:label="@string/call_log_activity_title"
             android:theme="@style/DialtactsThemeWithoutActionBarOverlay"
             android:icon="@mipmap/ic_launcher_phone">
@@ -230,6 +240,12 @@
             android:theme="@style/BackgroundOnlyTheme"
             android:exported="false"/>
 
+        <activity android:name=".VideoCallWelcomeActivity"
+                  android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+                  android:finishOnCloseSystemDialogs="true"
+                  android:excludeFromRecents="true"
+                  android:exported="false" />
+
         <!-- vCard related -->
         <activity android:name="com.android.contacts.common.vcard.ImportVCardActivity"
                   android:configChanges="orientation|screenSize|keyboardHidden"
@@ -309,10 +325,10 @@
              fullScreenIntent of a notification (for incoming calls.) -->
         <activity android:name="com.android.incallui.InCallActivity"
                   android:theme="@style/Theme.InCallScreen"
-                  android:label="@string/phoneAppLabel"
+                  android:label=""
                   android:excludeFromRecents="true"
                   android:launchMode="singleInstance"
-                  android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden"
+                  android:configChanges="smallestScreenSize|screenLayout|keyboardHidden"
                   android:exported="false"
                   android:screenOrientation="nosensor"
                   android:directBootAware="true"
@@ -360,5 +376,13 @@
             android:exported="false"
             android:multiprocess="false"
             />
+
+        <activity android:name=".SpeedDialListActivity"
+            android:theme="@style/SpeedDialtactsTheme"
+            android:label="@string/speed_dial_settings" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/InCallUI/res/drawable-hdpi/btn_start_record.png b/InCallUI/res/drawable-hdpi/btn_start_record.png
new file mode 100644
index 0000000..f43a572
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/btn_start_record.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/btn_stop_record.png b/InCallUI/res/drawable-hdpi/btn_stop_record.png
new file mode 100644
index 0000000..c1a68f7
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/btn_stop_record.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_hd2_24dp.png b/InCallUI/res/drawable-hdpi/ic_hd2_24dp.png
new file mode 100644
index 0000000..b4cbe6d
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_hd2_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_recording_indicator.png b/InCallUI/res/drawable-hdpi/ic_recording_indicator.png
new file mode 100644
index 0000000..a98b837
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_recording_indicator.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_big.9.png b/InCallUI/res/drawable-hdpi/ic_zoom_big.9.png
new file mode 100644
index 0000000..8c6cdea
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_big.9.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_big_dark.9.png b/InCallUI/res/drawable-hdpi/ic_zoom_big_dark.9.png
new file mode 100644
index 0000000..63ba20e
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_big_dark.9.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_in_holo_dark.png b/InCallUI/res/drawable-hdpi/ic_zoom_in_holo_dark.png
new file mode 100644
index 0000000..89b5f15
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_in_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_in_holo_light.png b/InCallUI/res/drawable-hdpi/ic_zoom_in_holo_light.png
new file mode 100644
index 0000000..9751ca3
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_in_holo_light.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_out_holo_dark.png b/InCallUI/res/drawable-hdpi/ic_zoom_out_holo_dark.png
new file mode 100644
index 0000000..f4a2589
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_out_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_out_holo_light.png b/InCallUI/res/drawable-hdpi/ic_zoom_out_holo_light.png
new file mode 100644
index 0000000..ba094ac
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_out_holo_light.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/ic_zoom_slider.png b/InCallUI/res/drawable-hdpi/ic_zoom_slider.png
new file mode 100644
index 0000000..8427e4d
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_zoom_slider.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/vb_active.png b/InCallUI/res/drawable-hdpi/vb_active.png
new file mode 100644
index 0000000..2a82ca8
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/vb_active.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/vb_disable.png b/InCallUI/res/drawable-hdpi/vb_disable.png
new file mode 100644
index 0000000..c2710b0
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/vb_disable.png
Binary files differ
diff --git a/InCallUI/res/drawable-hdpi/vb_normal.png b/InCallUI/res/drawable-hdpi/vb_normal.png
new file mode 100644
index 0000000..b78c30e
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/vb_normal.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/btn_start_record.png b/InCallUI/res/drawable-mdpi/btn_start_record.png
new file mode 100644
index 0000000..e340a94
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/btn_start_record.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/btn_stop_record.png b/InCallUI/res/drawable-mdpi/btn_stop_record.png
new file mode 100644
index 0000000..7a21c2f
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/btn_stop_record.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_hd2_24dp.png b/InCallUI/res/drawable-mdpi/ic_hd2_24dp.png
new file mode 100644
index 0000000..f4bc997
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_hd2_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_recording_indicator.png b/InCallUI/res/drawable-mdpi/ic_recording_indicator.png
new file mode 100644
index 0000000..2a4c19e
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_recording_indicator.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_big.9.png b/InCallUI/res/drawable-mdpi/ic_zoom_big.9.png
new file mode 100644
index 0000000..f5e31b4
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_big.9.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_big_dark.9.png b/InCallUI/res/drawable-mdpi/ic_zoom_big_dark.9.png
new file mode 100644
index 0000000..919db3f
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_big_dark.9.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_in_holo_dark.png b/InCallUI/res/drawable-mdpi/ic_zoom_in_holo_dark.png
new file mode 100644
index 0000000..4f33278
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_in_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_in_holo_light.png b/InCallUI/res/drawable-mdpi/ic_zoom_in_holo_light.png
new file mode 100644
index 0000000..3238a39
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_in_holo_light.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_out_holo_dark.png b/InCallUI/res/drawable-mdpi/ic_zoom_out_holo_dark.png
new file mode 100644
index 0000000..2631894
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_out_holo_dark.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_out_holo_light.png b/InCallUI/res/drawable-mdpi/ic_zoom_out_holo_light.png
new file mode 100644
index 0000000..8113dab
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_out_holo_light.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/ic_zoom_slider.png b/InCallUI/res/drawable-mdpi/ic_zoom_slider.png
new file mode 100644
index 0000000..16aacf1
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_zoom_slider.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/vb_active.png b/InCallUI/res/drawable-mdpi/vb_active.png
new file mode 100644
index 0000000..a4d5261
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/vb_active.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/vb_disable.png b/InCallUI/res/drawable-mdpi/vb_disable.png
new file mode 100644
index 0000000..e2e4a7d
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/vb_disable.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/vb_normal.png b/InCallUI/res/drawable-mdpi/vb_normal.png
new file mode 100644
index 0000000..702f619
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/vb_normal.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/btn_start_record.png b/InCallUI/res/drawable-xhdpi/btn_start_record.png
new file mode 100644
index 0000000..310b5a5
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/btn_start_record.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/btn_stop_record.png b/InCallUI/res/drawable-xhdpi/btn_stop_record.png
new file mode 100644
index 0000000..a2f735a
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/btn_stop_record.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_hd2_24dp.png b/InCallUI/res/drawable-xhdpi/ic_hd2_24dp.png
new file mode 100644
index 0000000..4ac0961
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_hd2_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/ic_recording_indicator.png b/InCallUI/res/drawable-xhdpi/ic_recording_indicator.png
new file mode 100644
index 0000000..33e6875
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_recording_indicator.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/vb_active.png b/InCallUI/res/drawable-xhdpi/vb_active.png
new file mode 100644
index 0000000..78f80b9
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/vb_active.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/vb_disable.png b/InCallUI/res/drawable-xhdpi/vb_disable.png
new file mode 100644
index 0000000..777b36f
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/vb_disable.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/vb_normal.png b/InCallUI/res/drawable-xhdpi/vb_normal.png
new file mode 100644
index 0000000..7b5468c
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/vb_normal.png
Binary files differ
diff --git a/InCallUI/res/drawable-xxhdpi/ic_hd2_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_hd2_24dp.png
new file mode 100644
index 0000000..f1e6f1f
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/ic_hd2_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable/ic_zoom_in.xml b/InCallUI/res/drawable/ic_zoom_in.xml
new file mode 100644
index 0000000..d441630
--- /dev/null
+++ b/InCallUI/res/drawable/ic_zoom_in.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2012 - 2015, The Linux Foundation. All rights reserved.
+     Not a Contribution, Apache license notifications and license are retained
+     for attribution purposes only.
+
+     Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:drawable="@drawable/ic_zoom_in_holo_light" />
+    <item android:drawable="@drawable/ic_zoom_in_holo_dark" />
+</selector>
+
diff --git a/InCallUI/res/drawable/ic_zoom_out.xml b/InCallUI/res/drawable/ic_zoom_out.xml
new file mode 100644
index 0000000..1211c33
--- /dev/null
+++ b/InCallUI/res/drawable/ic_zoom_out.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2012 - 2105, The Linux Foundation. All rights reserved.
+     Not a Contribution, Apache license notifications and license are retained
+     for attribution purposes only.
+
+     Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:drawable="@drawable/ic_zoom_out_holo_light" />
+    <item android:drawable="@drawable/ic_zoom_out_holo_dark" />
+</selector>
+
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video.xml b/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video.xml
new file mode 100644
index 0000000..5f86565
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+  ~ Not a Contribution.
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/qti_ic_lockscreen_answer_rx_video_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/qti_ic_lockscreen_answer_rx_video_activated_layer" />
+    <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/qti_ic_lockscreen_answer_rx_video_activated_layer" />
+</selector>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video_activated_layer.xml b/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video_activated_layer.xml
new file mode 100644
index 0000000..24b14cc
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video_activated_layer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+  ~ Not a Contribution.
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_red" />
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_rx_videocam"
+            android:tint="@color/glowpad_widget_active_color"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video_normal_layer.xml b/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video_normal_layer.xml
new file mode 100644
index 0000000..affd928
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_answer_rx_video_normal_layer.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+  ~ Not a Contribution.
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="@dimen/incoming_call_widget_circle_size"
+                android:height="@dimen/incoming_call_widget_circle_size" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_rx_videocam"
+            android:tint="@color/glowpad_call_widget_normal_tint"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video.xml b/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video.xml
new file mode 100644
index 0000000..258c1be
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+  ~ Not a Contribution.
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/qti_ic_lockscreen_answer_tx_video_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/qti_ic_lockscreen_answer_tx_video_activated_layer" />
+    <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/qti_ic_lockscreen_answer_tx_video_activated_layer" />
+</selector>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video_activated_layer.xml b/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video_activated_layer.xml
new file mode 100644
index 0000000..42d09f9
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video_activated_layer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+  ~ Not a Contribution.
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_green" />
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_tx_videocam"
+            android:tint="@color/glowpad_widget_active_color"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video_normal_layer.xml b/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video_normal_layer.xml
new file mode 100644
index 0000000..dc208db
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_answer_tx_video_normal_layer.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+  ~ Not a Contribution.
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="@dimen/incoming_call_widget_circle_size"
+                android:height="@dimen/incoming_call_widget_circle_size" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_tx_videocam"
+            android:tint="@color/glowpad_call_widget_normal_tint"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_deflect.xml b/InCallUI/res/drawable/qti_ic_lockscreen_deflect.xml
new file mode 100644
index 0000000..75f6d23
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_deflect.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/qti_ic_lockscreen_deflect_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/qti_ic_lockscreen_deflect_activated_layer" />
+   <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/qti_ic_lockscreen_deflect_activated_layer" />
+</selector>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_deflect_activated_layer.xml b/InCallUI/res/drawable/qti_ic_lockscreen_deflect_activated_layer.xml
new file mode 100644
index 0000000..f22b87e
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_deflect_activated_layer.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_green"/>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/fab_ic_call"
+            android:tint="@color/glowpad_widget_active_color"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/InCallUI/res/drawable/qti_ic_lockscreen_deflect_normal_layer.xml b/InCallUI/res/drawable/qti_ic_lockscreen_deflect_normal_layer.xml
new file mode 100644
index 0000000..31b884f
--- /dev/null
+++ b/InCallUI/res/drawable/qti_ic_lockscreen_deflect_normal_layer.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="@dimen/incoming_call_widget_circle_size"
+                android:height="@dimen/incoming_call_widget_circle_size" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/fab_ic_call"
+            android:tint="@color/glowpad_call_widget_normal_tint"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/InCallUI/res/drawable/vowifi_in_call_fair.xml b/InCallUI/res/drawable/vowifi_in_call_fair.xml
new file mode 100644
index 0000000..6cd5f30
--- /dev/null
+++ b/InCallUI/res/drawable/vowifi_in_call_fair.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<vector android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="588.0" android:viewportWidth="731.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M143.4,436.09998c-90.1,-91.2 -135,-209 -133.6,-322.6c0.2,-16.1 13.3,-29.2 29.4,-29.4l103.1,-1.3c8.5,-0.1 15.6,6.3 16.3,14.8l11.6,134L114.7,294c-2.9,3.2 -3.4,7.9 -1.3,11.7c18.4,33.4 41.4,64.2 68.9,92l0.7,0.6c27.5,27.8 58,51.3 91.2,70c3.7,2.1 8.4,1.7 11.7,-1.2l63.1,-54.7l133.9,13.3c8.4,0.8 14.8,8 14.6,16.5l-2.6,103c-0.4,16.1 -13.6,29.1 -29.7,29.1c-113.7,0 -230.8,-46.4 -320.9,-137.7L143.4,436.09998z"/>
+    <path android:fillAlpha="0.53" android:fillColor="#FFFFFF" android:pathData="M636.2,171.20001l22.3,-25l25.1,-28.1l34.1,-38.3c-71.4,-39.9 -163.3,-64 -263.6,-64s-192.2,24.1 -263.6,64l33.3,38.6l24.5,28.4L270,172c55.4,-18.6 117.9,-29.3 184.1,-29.3C519.6,142.6 581.3,153 636.2,171.20001z"/>
+    <path android:fillColor="#FFFFFF" android:pathData="M636.2,171.20001c-54.9,-18.2 -116.6,-28.6 -182.1,-28.6c-66.3,0 -128.7,10.7 -184.1,29.3l179.7,208.3L636.2,171.20001z"/>
+</vector>
diff --git a/InCallUI/res/drawable/vowifi_in_call_good.xml b/InCallUI/res/drawable/vowifi_in_call_good.xml
new file mode 100644
index 0000000..52d397c
--- /dev/null
+++ b/InCallUI/res/drawable/vowifi_in_call_good.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<vector android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="588.0" android:viewportWidth="731.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M144.4,436.09998c-90.1,-91.2 -135,-209 -133.6,-322.6c0.2,-16.1 13.3,-29.2 29.4,-29.4l103.1,-1.3c8.5,-0.1 15.6,6.3 16.3,14.8l11.6,134L115.7,294c-2.9,3.2 -3.4,7.9 -1.3,11.7c18.4,33.4 41.4,64.2 68.9,92l0.7,0.6c27.5,27.8 58,51.3 91.2,70c3.7,2.1 8.4,1.7 11.7,-1.2l63.1,-54.7l133.9,13.3c8.4,0.8 14.8,8 14.6,16.5l-2.6,103c-0.4,16.1 -13.6,29.1 -29.7,29.1c-113.7,0 -230.8,-46.4 -320.9,-137.7L144.4,436.09998z"/>
+    <path android:fillColor="#FFFFFF" android:pathData="M454.90002,15.5c-100.3,0 -192.2,24.1 -263.6,64l33.3,38.6l24.5,28.4L450.5,380l208.8,-234.1l25.1,-28.1l34.1,-38.3C647.1,39.600006 555.2,15.5 454.90002,15.5z"/>
+</vector>
diff --git a/InCallUI/res/drawable/vowifi_in_call_poor.xml b/InCallUI/res/drawable/vowifi_in_call_poor.xml
new file mode 100644
index 0000000..663ce90
--- /dev/null
+++ b/InCallUI/res/drawable/vowifi_in_call_poor.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016, 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.
+-->
+<vector android:autoMirrored="true" android:height="24dp"
+    android:viewportHeight="588.0" android:viewportWidth="731.0"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M143.4,435.09998c-90.1,-91.2 -135,-209 -133.6,-322.6c0.2,-16.1 13.3,-29.2 29.4,-29.4l103.1,-1.3c8.5,-0.1 15.6,6.3 16.3,14.8l11.6,134L114.7,293c-2.9,3.2 -3.4,7.9 -1.3,11.7c18.4,33.4 41.4,64.2 68.9,92l0.7,0.6c27.5,27.8 58,51.3 91.2,70c3.7,2.1 8.4,1.7 11.7,-1.2l63.1,-54.7l133.9,13.3c8.4,0.8 14.8,8 14.6,16.5l-2.6,103c-0.4,16.1 -13.6,29.1 -29.7,29.1c-113.7,0 -230.8,-46.4 -320.9,-137.7L143.4,435.09998z"/>
+    <path android:fillAlpha="0.53" android:fillColor="#FFFFFF" android:pathData="M537.3,281.4l121.5,-136.2l25.1,-28.1l34.1,-38.3c-71.4,-39.9 -163.3,-64 -263.6,-64s-192.2,24.1 -263.6,64l33.3,38.6l24.5,28.4l117.6,136.3c28.4,-4.2 57.8,-6.6 88.2,-6.6C482.90002,275.6 510.5,277.7 537.3,281.4z"/>
+    <path android:fillColor="#FFFFFF" android:pathData="M537.3,281.4c-26.8,-3.7 -54.4,-5.8 -82.9,-5.8c-30.3,0 -59.7,2.4 -88.2,6.6l83.7,97.1L537.3,281.4z"/>
+</vector>
diff --git a/InCallUI/res/drawable/zoom_slider_bar.xml b/InCallUI/res/drawable/zoom_slider_bar.xml
new file mode 100644
index 0000000..923e4ba
--- /dev/null
+++ b/InCallUI/res/drawable/zoom_slider_bar.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2012 - 2015, The Linux Foundation. All rights reserved.
+     Not a Contribution, Apache license notifications and license are retained
+     for attribution purposes only.
+
+     Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:drawable="@drawable/ic_zoom_big" />
+    <item android:drawable="@drawable/ic_zoom_big_dark" />
+</selector>
+
diff --git a/InCallUI/res/layout-h400dp/call_card_fragment.xml b/InCallUI/res/layout-h400dp/call_card_fragment.xml
index 2ef6e52..ccff385 100644
--- a/InCallUI/res/layout-h400dp/call_card_fragment.xml
+++ b/InCallUI/res/layout-h400dp/call_card_fragment.xml
@@ -116,17 +116,63 @@
                 <ProgressBar
                     android:id="@+id/progress_bar"
                     style="@android:style/Widget.Material.ProgressBar"
-                    android:layout_gravity="center"
+                    android:layout_gravity="left|center_vertical"
                     android:layout_width="48dp"
                     android:layout_height="48dp"
                     android:indeterminate="true" />
 
             </FrameLayout>
 
-
-            <include layout="@layout/manage_conference_call_button"
+            <LinearLayout
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:layout_centerHorizontal="true"
+                android:layout_below="@id/primary_call_info_container">
+
+                <include layout="@layout/manage_conference_call_button"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:elevation="5dp"
+                    android:layout_alignParentBottom="true"/>
+
+                <!-- Volume boost and Volume enhancements in-call UI -->
+                <ImageButton android:id="@+id/volumeBoost"
+                    android:layout_width="80dp"
+                    android:layout_height="80dp"
+                    android:visibility="gone"
+                    android:soundEffectsEnabled="false"
+                    android:background="@drawable/vb_normal"/>
+
+            </LinearLayout>
+
+        <!-- Call recorder infor -->
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_alignParentLeft="true">
+
+            <TextView android:id="@+id/recordingIcon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentLeft="true"
+                android:layout_marginLeft="24dp"
+                android:drawableRight="@drawable/ic_recording_indicator"
+                android:paddingBottom="119dp"
+                android:visibility="invisible"/>
+            <TextView android:id="@+id/recordingTime"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="5dp"
+                android:layout_toRightOf="@id/recordingIcon"
+                android:paddingBottom="120dp"
+                android:singleLine="true"
+                android:text="@string/recording_time_text"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textColor="@color/incall_call_banner_text_color"
+                android:visibility="invisible"/>
+            </RelativeLayout>
 
             <!-- Placeholder for various fragments that are added dynamically underneath the caller info. -->
             <FrameLayout
@@ -169,4 +215,4 @@
     </LinearLayout>
     <!-- Secondary "Call info" block, for the background ("on hold") call. -->
     <include layout="@layout/secondary_call_info" />
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/InCallUI/res/layout-w500dp-land/call_card_fragment.xml b/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
index c71cf07..049f41f 100644
--- a/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
+++ b/InCallUI/res/layout-w500dp-land/call_card_fragment.xml
@@ -16,47 +16,13 @@
   ~ limitations under the License
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="horizontal">
-
-    <LinearLayout
-        android:id="@+id/primary_call_info_container"
-        android:layout_centerVertical="true"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:orientation="vertical"
-        android:elevation="@dimen/primary_call_elevation"
-        android:background="@drawable/rounded_call_card_background"
-        android:paddingTop="@dimen/call_banner_primary_call_container_top_padding"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:alpha="0.9"
-        android:layout_margin="10dp">
-
-        <include layout="@layout/primary_call_info" />
-
-        <fragment android:name="com.android.incallui.CallButtonFragment"
-            android:id="@+id/callButtonFragment"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="bottom|center_horizontal"
-            android:layout_marginBottom="@dimen/call_buttons_bottom_margin" />
-
-        <!-- Secondary "Call info" block, for the background ("on hold") call. -->
-        <include layout="@layout/secondary_call_info"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="bottom" />
-
-    </LinearLayout>
+    android:layout_height="match_parent">
 
     <FrameLayout
         android:layout_height="match_parent"
-        android:layout_width="0dp"
-        android:layout_weight="1">
+        android:layout_width="match_parent">
 
         <FrameLayout
             android:layout_height="match_parent"
@@ -96,11 +62,6 @@
 
         </FrameLayout>
 
-        <include layout="@layout/manage_conference_call_button"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignTop="@id/photoLarge" />
-
         <!-- Progress spinner, useful for indicating pending operations such as upgrade to video. -->
         <FrameLayout
             android:id="@+id/progressSpinner"
@@ -114,7 +75,7 @@
             <ProgressBar
                 android:id="@+id/progress_bar"
                 style="@android:style/Widget.Material.ProgressBar"
-                android:layout_gravity="center"
+                android:layout_gravity="left|center_vertical"
                 android:layout_width="48dp"
                 android:layout_height="48dp"
                 android:indeterminate="true" />
@@ -127,32 +88,121 @@
             android:id="@+id/videoCallFragment"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
+    </FrameLayout>
 
-        <!-- Placeholder for the dialpad which is replaced with the dialpad fragment when shown. -->
-        <FrameLayout
-            android:id="@+id/answer_and_dialpad_container"
-            android:layout_gravity="bottom"
+    <!-- Call recorder infor -->
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentLeft="true">
+
+        <TextView android:id="@+id/recordingIcon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="24dp"
+            android:drawableRight="@drawable/ic_recording_indicator"
+            android:paddingBottom="119dp"
+            android:visibility="invisible"/>
+
+        <TextView android:id="@+id/recordingTime"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="5dp"
+            android:layout_toRightOf="@id/recordingIcon"
+            android:paddingBottom="120dp"
+            android:singleLine="true"
+            android:text="@string/recording_time_text"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="@color/incall_call_banner_text_color"
+            android:visibility="invisible"/>
+    </RelativeLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_centerHorizontal="true">
+
+        <include layout="@layout/manage_conference_call_button"
             android:layout_width="match_parent"
-            android:layout_height="match_parent" />
+            android:layout_height="wrap_content"
+            android:elevation="5dp"
+            android:layout_alignParentBottom="true"/>
+
+        <!-- Volume boost and Volume enhancements in-call UI -->
+        <ImageButton android:id="@+id/volumeBoost"
+            android:layout_width="80dp"
+            android:layout_height="80dp"
+            android:visibility="gone"
+            android:soundEffectsEnabled="false"
+            android:background="@drawable/vb_normal"/>
+    </LinearLayout>
+
+    <!-- Secondary "Call info" block, for the background ("on hold") call. -->
+    <include layout="@layout/secondary_call_info"
+             android:id="@+id/secondary_call_info"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:layout_alignParentBottom="true" />
+
+    <LinearLayout
+        android:id="@+id/primary_call_info_container"
+        android:layout_alignParentStart="true"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_above="@id/secondary_call_info"
+        android:orientation="vertical"
+        android:elevation="@dimen/primary_call_elevation"
+        android:background="@drawable/rounded_call_card_background"
+        android:paddingTop="@dimen/call_banner_primary_call_container_top_padding"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:alpha="0.9"
+        android:layout_margin="10dp">
+
+        <include layout="@layout/primary_call_info" />
 
         <FrameLayout
-            android:id="@+id/floating_end_call_action_button_container"
-            android:layout_width="@dimen/end_call_floating_action_button_diameter"
-            android:layout_height="@dimen/end_call_floating_action_button_diameter"
-            android:background="@drawable/fab_red"
-            android:layout_gravity="bottom|center_horizontal"
-            android:layout_marginBottom="@dimen/end_call_button_margin_bottom">
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent" >
 
-            <ImageButton android:id="@+id/floating_end_call_action_button"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:background="@drawable/end_call_background"
-                android:src="@drawable/fab_ic_end_call"
-                android:scaleType="center"
-                android:contentDescription="@string/onscreenEndCallText" />
+            <fragment android:name="com.android.incallui.CallButtonFragment"
+                android:id="@+id/callButtonFragment"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom" />
 
         </FrameLayout>
 
-    </FrameLayout>
+    </LinearLayout>
 
-</LinearLayout>
+    <!-- Placeholder for the dialpad which is replaced with the dialpad fragment when shown. -->
+    <FrameLayout
+        android:id="@+id/answer_and_dialpad_container"
+        android:layout_toEndOf="@id/primary_call_info_container"
+        android:layout_gravity="end|center_vertical"
+        android:layout_alignParentEnd="true"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+     <FrameLayout
+        android:id="@+id/floating_end_call_action_button_container"
+        android:layout_width="@dimen/end_call_floating_action_button_diameter"
+        android:layout_height="@dimen/end_call_floating_action_button_diameter"
+        android:background="@drawable/fab_red"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentBottom="true"
+        android:layout_marginRight="@dimen/end_call_button_margin_right"
+        android:layout_marginBottom="@dimen/end_call_button_margin_bottom">
+        <ImageButton android:id="@+id/floating_end_call_action_button"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@drawable/end_call_background"
+            android:src="@drawable/fab_ic_end_call"
+            android:scaleType="center"
+            android:contentDescription="@string/onscreenEndCallText" />
+     </FrameLayout>
+
+</RelativeLayout>
diff --git a/InCallUI/res/layout/call_button_fragment.xml b/InCallUI/res/layout/call_button_fragment.xml
index 802e3de..9ed4e56 100644
--- a/InCallUI/res/layout/call_button_fragment.xml
+++ b/InCallUI/res/layout/call_button_fragment.xml
@@ -151,6 +151,37 @@
             android:background="@drawable/btn_merge"
             android:contentDescription="@string/onscreenMergeCallsText"
             android:visibility="gone" />
+        <!-- "Add Participant" -->
+        <ImageButton android:id="@+id/addParticipant"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_addparticipant"
+            android:contentDescription="@string/onscreenAddParticipant"
+            android:visibility="gone" />
+
+        <!-- "Blind transfer" -->
+        <ImageButton android:id="@+id/blindTransfer"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_add"
+            android:contentDescription="@string/qti_ims_onscreenBlindTransfer"
+            android:visibility="gone" />
+        <!-- "Assured transfer" -->
+        <ImageButton android:id="@+id/assuredTransfer"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_add"
+            android:contentDescription="@string/qti_ims_onscreenAssuredTransfer"
+            android:visibility="gone" />
+        <!-- "Consultative transfer" -->
+        <ImageButton android:id="@+id/consultativeTransfer"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_add"
+            android:contentDescription="@string/qti_ims_onscreenConsultativeTransfer"
+            android:visibility="gone" />
+        <!-- "Record Call" -->
+        <ImageButton android:id="@+id/recordButton"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_start_record"
+            android:contentDescription="@string/menu_start_record"
+            android:visibility="gone"/>
 
         <!-- "Overflow" -->
         <ImageButton android:id="@+id/overflowButton"
@@ -166,6 +197,24 @@
             android:contentDescription="@string/onscreenManageConferenceText"
             android:visibility="gone" />
 
+        <ImageButton android:id="@+id/rxtxVideoCallButton"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_change_to_video"
+            android:contentDescription="@string/overflowBothCallMenuItemText"
+            android:visibility="gone" />
+
+        <ImageButton android:id="@+id/rxVedioCallButton"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_change_to_hm_video"
+            android:contentDescription="@string/overflowRXCallMenuItemText"
+            android:visibility="gone" />
+
+        <ImageButton android:id="@+id/volteCallButton"
+            style="@style/InCallButton"
+            android:background="@drawable/btn_compound_audio"
+            android:contentDescription="@string/overflowVOCallMenuItemText"
+            android:visibility="gone" />
+
     </LinearLayout>
 
 </LinearLayout>
diff --git a/InCallUI/res/layout/call_card_fragment.xml b/InCallUI/res/layout/call_card_fragment.xml
index fabde37..2a4c3e2 100644
--- a/InCallUI/res/layout/call_card_fragment.xml
+++ b/InCallUI/res/layout/call_card_fragment.xml
@@ -60,12 +60,6 @@
              android:elevation="4dp"
              android:layout_alignParentBottom="true" />
 
-    <include layout="@layout/manage_conference_call_button"
-             android:layout_width="match_parent"
-             android:layout_height="wrap_content"
-             android:elevation="5dp"
-             android:layout_alignParentBottom="true"/>
-
     <FrameLayout
         android:id="@+id/floating_end_call_action_button_container"
         android:layout_width="@dimen/end_call_floating_action_button_diameter"
@@ -97,13 +91,63 @@
         <ProgressBar
             android:id="@+id/progress_bar"
             style="@android:style/Widget.Material.ProgressBar"
-            android:layout_gravity="center"
+            android:layout_gravity="left|center_vertical"
             android:layout_width="48dp"
             android:layout_height="48dp"
             android:indeterminate="true" />
 
     </FrameLayout>
 
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_centerHorizontal="true"
+        android:layout_below="@id/primary_call_info_container">
+
+        <include layout="@layout/manage_conference_call_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:elevation="5dp"
+            android:layout_alignParentBottom="true"/>
+
+        <!-- Volume boost and Volume enhancements in-call UI -->
+        <ImageButton android:id="@+id/volumeBoost"
+            android:layout_width="80dp"
+            android:layout_height="80dp"
+            android:visibility="gone"
+            android:soundEffectsEnabled="false"
+            android:background="@drawable/vb_normal"/>
+
+    </LinearLayout>
+
+    <!-- Call recorder infor -->
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentLeft="true">
+        <TextView android:id="@+id/recordingIcon"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_alignParentLeft="true"
+                  android:layout_marginLeft="24dp"
+                  android:drawableRight="@drawable/ic_recording_indicator"
+                  android:paddingBottom="119dp"
+                  android:visibility="invisible"/>
+        <TextView android:id="@+id/recordingTime"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_marginLeft="5dp"
+                  android:layout_toRightOf="@id/recordingIcon"
+                  android:paddingBottom="120dp"
+                  android:singleLine="true"
+                  android:text="@string/recording_time_text"
+                  android:textAppearance="?android:attr/textAppearanceSmall"
+                  android:textColor="@color/incall_call_banner_text_color"
+                  android:visibility="invisible"/>
+    </RelativeLayout>
+
     <fragment android:name="com.android.incallui.VideoCallFragment"
               android:layout_alignParentStart="true"
               android:layout_gravity="start|center_vertical"
diff --git a/InCallUI/res/layout/caller_in_conference.xml b/InCallUI/res/layout/caller_in_conference.xml
index ac78096..dd14002 100644
--- a/InCallUI/res/layout/caller_in_conference.xml
+++ b/InCallUI/res/layout/caller_in_conference.xml
@@ -89,6 +89,15 @@
 
     </LinearLayout>
 
+    <TextView android:id="@+id/conferenceCallerState"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:textSize="18sp"
+        android:ellipsize="marquee"
+        android:gravity="center_vertical"
+        android:textColor="@color/conference_call_manager_secondary_text_color"
+        android:singleLine="true"/>
+
     <!-- "Separate" (i.e. "go private") button for this caller -->
     <ImageView android:id="@+id/conferenceCallerSeparate"
         android:src="@drawable/ic_call_split_white_24dp"
diff --git a/InCallUI/res/layout/manage_conference_call_button.xml b/InCallUI/res/layout/manage_conference_call_button.xml
index 01ca1bd..024b5a2 100644
--- a/InCallUI/res/layout/manage_conference_call_button.xml
+++ b/InCallUI/res/layout/manage_conference_call_button.xml
@@ -40,32 +40,22 @@
         android:background="?android:attr/selectableItemBackground">
 
         <!-- Call status of the background call, usually the string "On hold". -->
-        <TextView android:id="@+id/conferenceLabel"
-                  android:layout_width="0dp"
-                  android:layout_height="wrap_content"
-                  android:layout_weight="1"
-                  android:layout_gravity="center_vertical"
-                  android:paddingEnd="18dp"
-                  android:text="@string/onscreenConferenceText"
-                  android:textColor="@color/incall_banner_secondary_text_color"
-                  android:textSize="@dimen/secondary_call_info_text_size"
-                  android:singleLine="true" />
+        <ImageView android:id="@+id/manageConferenceButtonImage"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/ic_group_white_24dp"
+            android:tint="@color/incall_banner_secondary_text_color"
+            android:paddingEnd="16dp"
+            android:importantForAccessibility="no" />
 
-        <ImageView android:id="@+id/manageConferenceImage"
-                   android:layout_width="wrap_content"
-                   android:layout_height="wrap_content"
-                   android:src="@drawable/ic_group_white_24dp"
-                   android:tint="@color/incall_banner_secondary_text_color"
-                   android:paddingEnd="16dp"/>
-
-        <TextView android:id="@+id/manageConferenceLabel"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:textColor="@color/incall_banner_secondary_text_color"
-                  android:textSize="@dimen/secondary_call_info_text_size"
-                  android:textAlignment="viewStart"
-                  android:text="@string/onscreenManageText"
-                  android:singleLine="true"/>
+        <TextView android:id="@+id/manageConferenceButtonLabel"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical"
+            android:textColor="@color/incall_banner_secondary_text_color"
+            android:textSize="@dimen/secondary_call_info_text_size"
+            android:text="@string/onscreenManageConferenceText"
+            android:importantForAccessibility="no" />
 
     </LinearLayout>
 
diff --git a/InCallUI/res/layout/msim_tab_sub_info.xml b/InCallUI/res/layout/msim_tab_sub_info.xml
new file mode 100644
index 0000000..8b33439
--- /dev/null
+++ b/InCallUI/res/layout/msim_tab_sub_info.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2013-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.
+ *
+ */
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tab_sub_info"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="horizontal"
+    android:background="@color/incall_background_color">
+
+    <TextView
+        android:id="@+id/tabSubText"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"/>
+
+</LinearLayout>
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
index 2aa583c..2fa3b94 100644
--- a/InCallUI/res/layout/primary_call_info.xml
+++ b/InCallUI/res/layout/primary_call_info.xml
@@ -23,7 +23,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/primary_call_banner"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingStart="@dimen/call_banner_side_padding"
         android:paddingEnd="@dimen/call_banner_side_padding"
@@ -75,7 +75,6 @@
             android:layout_width="24dp"
             android:layout_height="match_parent"
             android:layout_marginEnd="10dp"
-            android:tint="@color/incall_accent_color"
             android:alpha="0.0"
             android:scaleType="fitCenter"
             android:visibility="gone" />
@@ -100,7 +99,6 @@
             android:textColor="@color/incall_accent_color"
             android:textSize="@dimen/call_status_text_size"
             android:alpha="0.7"
-            android:singleLine="true"
             android:gravity="start"
             android:ellipsize="end"
             ex:resizing_text_min_size="@dimen/call_status_text_min_size" />
@@ -123,6 +121,7 @@
             android:textAppearance="?android:attr/textAppearanceLarge"
             android:textSize="@dimen/call_name_text_size"
             android:singleLine="true"
+            android:ellipsize="end"
             ex:resizing_text_min_size="@dimen/call_name_text_min_size" />
 
         <!-- Contact photo for primary call info -->
@@ -138,7 +137,7 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/in_call_layout_height"
         android:orientation="horizontal"
         android:clipChildren="false"
         android:clipToPadding="false">
@@ -146,7 +145,7 @@
         <ImageView android:id="@+id/hdAudioIcon"
             android:src="@drawable/ic_hd_24dp"
             android:layout_width="24dp"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:layout_marginEnd="8dp"
             android:tint="@color/incall_call_banner_subtext_color"
             android:scaleType="fitCenter"
diff --git a/InCallUI/res/layout/qti_video_call_picture_mode_menu.xml b/InCallUI/res/layout/qti_video_call_picture_mode_menu.xml
new file mode 100644
index 0000000..bcb15d5
--- /dev/null
+++ b/InCallUI/res/layout/qti_video_call_picture_mode_menu.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2016, 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.
+  ~
+  -->
+
+<!-- Video Call Picture mode menu for InCall UI -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="100dp"
+      android:layout_height="wrap_content"
+      android:theme="@style/Theme.InCallScreen"
+      android:orientation="vertical">
+
+    <CheckBox android:id="@+id/preview_video"
+        android:text="@string/video_call_picture_mode_preview_video"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginLeft="10dp"
+    />
+
+    <CheckBox android:id="@+id/incoming_video"
+        android:text="@string/video_call_picture_mode_incoming_video"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/preview_video"
+        android:layout_marginTop="10dp"
+        android:layout_marginLeft="10dp"
+    />
+</LinearLayout>
diff --git a/InCallUI/res/layout/qti_video_call_zoom_control.xml b/InCallUI/res/layout/qti_video_call_zoom_control.xml
new file mode 100644
index 0000000..51bc907
--- /dev/null
+++ b/InCallUI/res/layout/qti_video_call_zoom_control.xml
@@ -0,0 +1,46 @@
+<?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 zoom control resource -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="20dp"
+    android:theme="@style/Theme.InCallScreen" >
+
+    <com.android.incallui.ZoomControlBar
+        android:id="@+id/zoom_control"
+        android:layout_gravity="center|clip_horizontal|clip_vertical"
+        android:layout_width="match_parent"
+        android:visibility="visible"
+        android:layout_height="20dp" />
+</FrameLayout>
diff --git a/InCallUI/res/layout/video_call_views.xml b/InCallUI/res/layout/video_call_views.xml
index d514f6d..1926fcc 100644
--- a/InCallUI/res/layout/video_call_views.xml
+++ b/InCallUI/res/layout/video_call_views.xml
@@ -20,11 +20,15 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
 
-    <TextureView
-        android:id="@+id/incomingVideo"
-        android:layout_gravity="center"
+    <FrameLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
+        android:layout_height="match_parent">
+        <TextureView
+            android:id="@+id/incomingVideo"
+            android:layout_gravity="center"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </FrameLayout>
     <!-- The width and height are replaced at runtime based on the selected camera. -->
     <FrameLayout
         android:id="@+id/previewVideoContainer"
diff --git a/InCallUI/res/values-mcc460-mnc00/qticonfig.xml b/InCallUI/res/values-mcc460-mnc00/qticonfig.xml
new file mode 100644
index 0000000..de8962c
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc00/qticonfig.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ Copyright (c) 2015, 2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide call duration in call detail -->
+    <bool name="call_duration_enabled">false</bool>
+    <!-- Config to enable/disable ims call log -->
+    <bool name="ims_call_type_enabled">true</bool>
+    <!-- Flag indicating if conference dialer  is enabled -->
+    <bool name="config_enable_conference_dialer">true</bool>
+    <!-- Config to enable/disable conference state display -->
+    <bool name="config_conference_call_show_participant_status">true</bool>
+    <!-- Config to enable/disable add multi participants -->
+    <bool name="add_multi_participants_enabled">true</bool>
+    <!-- Config to add participant only in conferencall call-->
+    <bool name="add_participant_only_in_conference">true</bool>
+</resources>
diff --git a/InCallUI/res/values-mcc460-mnc01/qticonfig.xml b/InCallUI/res/values-mcc460-mnc01/qticonfig.xml
new file mode 100644
index 0000000..5f789c5
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc01/qticonfig.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (c) 2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide HD Icon2 -->
+    <bool name="config_show_hd2">true</bool>
+</resources>
diff --git a/InCallUI/res/values-mcc460-mnc02/qticonfig.xml b/InCallUI/res/values-mcc460-mnc02/qticonfig.xml
new file mode 100644
index 0000000..9613155
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc02/qticonfig.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ Copyright (c) 2015,2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide call duration in call detail -->
+    <bool name="call_duration_enabled">false</bool>
+    <!-- Config to enable/disable ims call log -->
+    <bool name="ims_call_type_enabled">true</bool>
+    <!-- Flag indicating if conference dialer  is enabled -->
+    <bool name="config_enable_conference_dialer">true</bool>
+    <!-- Config to enable/disable conference state display -->
+    <bool name="config_conference_call_show_participant_status">true</bool>
+    <!-- Config to enable/disable add multi participants -->
+    <bool name="add_multi_participants_enabled">true</bool>
+    <!-- Config to add participant only in conferencall call-->
+    <bool name="add_participant_only_in_conference">true</bool>
+</resources>
diff --git a/InCallUI/res/values-mcc460-mnc06/qticonfig.xml b/InCallUI/res/values-mcc460-mnc06/qticonfig.xml
new file mode 100644
index 0000000..5f789c5
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc06/qticonfig.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (c) 2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide HD Icon2 -->
+    <bool name="config_show_hd2">true</bool>
+</resources>
diff --git a/InCallUI/res/values-mcc460-mnc07/qticonfig.xml b/InCallUI/res/values-mcc460-mnc07/qticonfig.xml
new file mode 100644
index 0000000..de8962c
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc07/qticonfig.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ Copyright (c) 2015, 2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide call duration in call detail -->
+    <bool name="call_duration_enabled">false</bool>
+    <!-- Config to enable/disable ims call log -->
+    <bool name="ims_call_type_enabled">true</bool>
+    <!-- Flag indicating if conference dialer  is enabled -->
+    <bool name="config_enable_conference_dialer">true</bool>
+    <!-- Config to enable/disable conference state display -->
+    <bool name="config_conference_call_show_participant_status">true</bool>
+    <!-- Config to enable/disable add multi participants -->
+    <bool name="add_multi_participants_enabled">true</bool>
+    <!-- Config to add participant only in conferencall call-->
+    <bool name="add_participant_only_in_conference">true</bool>
+</resources>
diff --git a/InCallUI/res/values-mcc460-mnc08/qticonfig.xml b/InCallUI/res/values-mcc460-mnc08/qticonfig.xml
new file mode 100644
index 0000000..de8962c
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc08/qticonfig.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ Copyright (c) 2015, 2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide call duration in call detail -->
+    <bool name="call_duration_enabled">false</bool>
+    <!-- Config to enable/disable ims call log -->
+    <bool name="ims_call_type_enabled">true</bool>
+    <!-- Flag indicating if conference dialer  is enabled -->
+    <bool name="config_enable_conference_dialer">true</bool>
+    <!-- Config to enable/disable conference state display -->
+    <bool name="config_conference_call_show_participant_status">true</bool>
+    <!-- Config to enable/disable add multi participants -->
+    <bool name="add_multi_participants_enabled">true</bool>
+    <!-- Config to add participant only in conferencall call-->
+    <bool name="add_participant_only_in_conference">true</bool>
+</resources>
diff --git a/InCallUI/res/values-mcc460-mnc09/qticonfig.xml b/InCallUI/res/values-mcc460-mnc09/qticonfig.xml
new file mode 100644
index 0000000..5f789c5
--- /dev/null
+++ b/InCallUI/res/values-mcc460-mnc09/qticonfig.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright (c) 2016, 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.
+  -->
+<resources>
+    <!-- Config to show/hide HD Icon2 -->
+    <bool name="config_show_hd2">true</bool>
+</resources>
diff --git a/InCallUI/res/values-w500dp-land/dimens.xml b/InCallUI/res/values-w500dp-land/dimens.xml
index 112ec5f..01cf165 100644
--- a/InCallUI/res/values-w500dp-land/dimens.xml
+++ b/InCallUI/res/values-w500dp-land/dimens.xml
@@ -32,4 +32,6 @@
     <dimen name="dialpad_elevation">2dp</dimen>
 
     <dimen name="video_preview_margin">20dp</dimen>
+
+    <dimen name="end_call_button_margin_right">80dp</dimen>
 </resources>
diff --git a/InCallUI/res/values-zh-rCN/qtistrings.xml b/InCallUI/res/values-zh-rCN/qtistrings.xml
new file mode 100644
index 0000000..120af0a
--- /dev/null
+++ b/InCallUI/res/values-zh-rCN/qtistrings.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2016 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- In-call screen: status label for an incoming conference call [CHAR LIMIT=25] -->
+    <string name="card_title_incoming_conf_call">"电话会议来电"</string>
+    <!-- In-call screen: status label for an incoming video conference call [CHAR LIMIT=25] -->
+    <string name="card_title_incoming_video_conf_call">"视频会议来电"</string>
+    <!-- The "label" of the in-call Notification for an incoming conference ringing call. [CHAR LIMIT=60] -->
+    <string name="notification_incoming_conf_call">"电话会议来电"</string>
+    <!-- Title displayed in the overlay for incoming conference calls which include the name of the provider.
+         [CHAR LIMIT=40] -->
+    <string name="incoming_conf_via_template">"通过 <xliff:g id="provider_name">%s</xliff:g>会议来电"</string>
+
+    <!-- In-call screen: Modify Call Options for IMS call -->
+    <string name="modify_call_option_title">选择通话类型?</string>
+    <string name="modify_call_option_vt">双向视频</string>
+    <string name="modify_call_option_vt_tx">视频发送</string>
+    <string name="modify_call_option_vt_rx">视频接收</string>
+    <string name="modify_call_option_voice">仅限语音</string>
+    <!-- Call substate label -->
+    <string name="call_substate_label">" 通话质量不佳 - \u000a"</string>
+    <!-- Call substate label for call resumed -->
+    <string name="call_substate_call_resumed">"通话继续\u000a"</string>
+    <!-- Call substate label for call connected suspended (audio) -->
+    <string name="call_substate_connected_suspended_audio">"暂停连接\u000a"</string>
+    <!-- Call substate label for call connected suspended (video) -->
+    <string name="call_substate_connected_suspended_video">"视频电话暂停\u000a"</string>
+    <!-- Call substate label for avp retry -->
+    <string name="call_substate_avp_retry">"AVP 重试\u000a"</string>
+    <!-- Video quality changed message -->
+    <string name="video_quality_changed">" 视频画质变为\u0020"</string>
+    <!-- Video quality High -->
+    <string name="video_quality_high">"高画质"</string>
+    <!-- Video quality Medium -->
+    <string name="video_quality_medium">"中画质"</string>
+    <!-- Video quality Low -->
+    <string name="video_quality_low">"低画质"</string>
+    <!-- Video quality Unknown -->
+    <string name="video_quality_unknown">"未知"</string>
+    <!-- Message indicating that Video Started flowing for IMS-VT calls -->
+    <string name="player_started">"播放器已启动"</string>
+    <!-- Message indicating that Video Stopped flowing for IMS-VT calls -->
+    <string name="player_stopped">"播放暂停"</string>
+    <!-- Message indicating that camera failure has occurred for the selected camera and
+         as result camera is not ready -->
+    <string name="camera_not_ready">"摄像头故障"</string>
+    <!-- Message indicating that camera is ready/available -->
+    <string name="camera_ready">"摄像头就绪"</string>
+    <!-- Message indicating unknown call session event -->
+    <string name="unknown_call_session_event">"未知电话"</string>
+    <!-- Message indicating data usage -->
+    <string name="data_usage_label">"流量使用: "</string>
+    <!-- Modify call error cause -->
+    <string name="modify_call_failed_due_to_low_battery">"由于电池电量不足,无法修改通话。"</string>
+    <!-- Message indicating video calls not allowed if user enabled TTY Mode -->
+    <string name="video_call_not_allowed_if_tty_enabled">"请关闭 TTY 模式以便升级到视频电话。"</string>
+    <!-- Text for the onscreen "Add Participant" button -->
+    <string name="onscreenAddParticipant">"添加参与者"</string>
+    <!-- Description of the deflect target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="qti_description_target_deflect">"转移"</string>
+    <string name="qti_description_deflect_error">"号码未设置。通过 IMS 设置提供号码,然后重试。"</string>
+    <string name="qti_description_deflect_service_error">"不支持呼叫转移服务。"</string>
+    <!-- Message indicating call failed due to handover not feasible -->
+    <string name="call_failed_ho_not_feasible">"无法从 LTE 切换到 3G/2G,通话已结束。"</string>
+    <!-- Title of the IMS to CS redial dialog -->
+    <string name="cs_redial_option">"重拨选项"</string>
+    <!-- Message text of the IMS to CS redial dialog -->
+    <string name="cs_redial_msg">"无法拨打 IMS 视频通话,是否要拨打非 IMS 的语音通话?"</string>
+    <!-- Yes option of the IMS to CS redial dialog -->
+    <string name="cs_redial_yes">"是"</string>
+    <!-- No option of the IMS to CS redial dialog -->
+    <string name="cs_redial_no">"否"</string>
+    <!-- Session modify cause unspecified -->
+    <string name="session_modify_cause_unspecified"></string>
+    <!-- Session modify cause code upgrade local request -->
+    <string name="session_modify_cause_upgrade_local_request">"应用户请求,通话已升级"</string>
+    <!-- Session modify cause code upgrade remote request -->
+    <string name="session_modify_cause_upgrade_remote_request">"应远程用户请求,通话已升级"</string>
+    <!-- Session modify cause code downgrade local request -->
+    <string name="session_modify_cause_downgrade_local_request">"应用户请求,通话已降级"</string>
+    <!-- Session modify cause code downgrade remote request -->
+    <string name="session_modify_cause_downgrade_remote_request">"应远程用户请求,通话已降级"</string>
+    <!-- Session modify cause code downgrade rtp timeout -->
+    <string name="session_modify_cause_downgrade_rtp_timeout">"由于 RTP 超时,通话已降级"</string>
+    <!-- Session modify cause code downgrade qos -->
+    <string name="session_modify_cause_downgrade_qos">"由于服务质量,通话已降级"</string>
+    <!-- Session modify cause code downgrade packet loss -->
+    <string name="session_modify_cause_downgrade_packet_loss">"由于数据包丢失,通话已降级"</string>
+    <!-- Session modify cause code downgrade low thrput -->
+    <string name="session_modify_cause_downgrade_low_thrput">"由于吞吐量低,通话已降级"</string>
+    <!-- Session modify cause code downgrade thermal mitigation -->
+    <string name="session_modify_cause_downgrade_thermal_mitigation">"由于设备过热,通话已降级"</string>
+    <!-- Session modify cause code downgrade lipsync -->
+    <string name="session_modify_cause_downgrade_lipsync">"由于嘴唇同步功能,通话已降级"</string>
+    <!-- Session modify cause code downgrade generic error -->
+    <string name="session_modify_cause_downgrade_generic_error">"由于一般错误,通话已降级"</string>
+    <!-- Title for low battery alert dialogue -->
+    <string name="low_battery">"电池电量不足"</string>
+    <!-- Yes option of the low battery alert dialog -->
+    <string name="low_battery_yes">"是"</string>
+    <!-- No option of the low battery alert dialog -->
+    <string name="low_battery_no">"否"</string>
+    <!-- Message text of the low battery alert dialog in cases when video call can be downgraded to voice call -->
+    <string name="low_battery_downgrade_to_voice_msg">"要将视频通话转换为音频通话吗?"</string>
+    <!-- Message text of the low battery alert dialog in cases video call doesn't have downgrade capabilities -->
+    <string name="low_battery_hangup_msg">"要挂断电话吗?"</string>
+    <!-- Description of the call transfer related strings [CHAR LIMIT=NONE] -->
+    <string name="qti_ims_transfer_num_error">"号码未设置。通过 IMS 设置提供号码,然后重试。"</string>
+    <string name="qti_ims_transfer_request_error">"呼叫转移请求失败。"</string>
+    <string name="qti_ims_transfer_request_success">"呼叫转移请求成功。"</string>
+    <!-- Text for the onscreen "blind transfer" button -->
+    <string name="qti_ims_onscreenBlindTransfer">"无条件转移"</string>
+    <!-- Text for the onscreen "assured transfer" button -->
+    <string name="qti_ims_onscreenAssuredTransfer">"固定转移"</string>
+    <!-- Text for the onscreen "consultative transfer" button -->
+    <string name="qti_ims_onscreenConsultativeTransfer">"询问转移"</string>
+    <!-- Text for operator specific emergency number -->
+    <add-resource type="string" name="emergency_call_dialog_number_for_display_operator"/>
+    <string name="emergency_call_dialog_number_for_display_operator">"紧急呼救"</string>
+</resources>
diff --git a/InCallUI/res/values-zh-rCN/strings.xml b/InCallUI/res/values-zh-rCN/strings.xml
index e51a719..04d7af3 100644
--- a/InCallUI/res/values-zh-rCN/strings.xml
+++ b/InCallUI/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
+<!--
   ~ Copyright (C) 2013 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -167,6 +167,12 @@
     <string name="preference_category_ringtone" msgid="6246687516643676729">"铃声和振动"</string>
     <string name="manageConferenceLabel" msgid="7237614418556336108">"管理电话会议"</string>
     <string name="emergency_call_dialog_number_for_display" msgid="7244995877625769187">"紧急呼救号码"</string>
+    <string name="volume_boost_notify_enabled">"开启音量增强"</string>
+    <string name="volume_boost_notify_disabled">"关闭音量增强"</string>
+    <string name="volume_boost_notify_unavailable">"有线/蓝牙耳机连接时,音效增强不可用。"</string>
+    <string name="menu_start_record">通话录音</string>
+    <string name="menu_stop_record">停止录音</string>
+    <string name="recording_time_text">录音中</string>
     <plurals name="duration_seconds" formatted="false" msgid="2544699588744957418">
       <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> 秒</item>
       <item quantity="one">1 秒</item>
@@ -196,4 +202,5 @@
     <string name="closed_today_at" msgid="4060072663433467233">"已于今天<xliff:g id="CLOSE_TIME">%s</xliff:g>结束营业"</string>
     <string name="open_now" msgid="4615706338669555999">"营业中"</string>
     <string name="closed_now" msgid="2635314668145282080">"现已结束营业"</string>
+    <string name="too_many_recipients">4G会议电话的参与人数上限为6人。</string>
 </resources>
diff --git a/InCallUI/res/values/array.xml b/InCallUI/res/values/array.xml
index 7877ec8..afd9f55 100644
--- a/InCallUI/res/values/array.xml
+++ b/InCallUI/res/values/array.xml
@@ -132,4 +132,38 @@
         <item>@string/description_direction_left</item>
         <item>@null</item>
     </array>
+
+   <array name="enhance_incoming_call_widget_video_without_sms_targets">
+       <item>@drawable/ic_lockscreen_answer</item>
+       <item>@drawable/ic_enhance_answer_rx_video</item>
+       <item>@drawable/ic_lockscreen_decline</item>
+       <item>@drawable/ic_enhance_answer_video</item>
+   </array>
+   <array name="enhance_incoming_call_widget_video_with_sms_targets">
+       <item>@drawable/ic_enhance_answer_video</item>
+       <item>@drawable/ic_lockscreen_text</item>
+       <item>@drawable/ic_lockscreen_decline</item>
+       <item>@drawable/ic_lockscreen_answer</item>
+       <item>@drawable/ic_enhance_answer_rx_video</item>
+       <item>@null</item>
+   </array>
+   <array name="enhance_incoming_call_widget_video_upgrade_request_targets">
+       <item>@drawable/ic_enhance_answer_video</item>
+       <item>@null</item>
+       <item>@drawable/ic_enhance_decline_video</item>
+       <item>@drawable/ic_enhance_answer_rx_video</item>
+   </array>
+   <array name="enhance_incoming_call_bidirectional_video_accept_request_targets">
+       <item>@drawable/ic_enhance_answer_video</item>
+       <item>@drawable/ic_enhance_decline_video</item>
+   </array>
+   <array name="enhance_incoming_call_video_transmit_accept_request_targets">
+       <item>@drawable/ic_enhance_answer_tx_video</item>
+       <item>@drawable/ic_enhance_decline_video</item>
+   </array>
+   <array name="enhance_incoming_call_video_receive_accept_request_targets">
+       <item>@drawable/ic_enhance_answer_rx_video</item>
+       <item>@drawable/ic_enhance_decline_video</item>
+   </array>
+
 </resources>
diff --git a/InCallUI/res/values/config.xml b/InCallUI/res/values/config.xml
index b81ba3c..763b0e2 100644
--- a/InCallUI/res/values/config.xml
+++ b/InCallUI/res/values/config.xml
@@ -24,4 +24,10 @@
     <!-- The number of milliseconds after which a video call will automatically enter fullscreen
          mode (requires video_call_auto_fullscreen to be true). -->
     <integer name="video_call_auto_fullscreen_timeout">5000</integer>
-</resources>
\ No newline at end of file
+    <!-- When international prefix is enabled in CDMA mode, whether with international prefix -->
+    <bool name="phone_number_with_intl_prefix">false</bool>
+
+    <bool name="config_regional_number_patterns_video_call">false</bool>
+     <!-- When set to true, this config enables enhance videocall ui -->
+    <bool name="config_enable_enhance_video_call_ui">false</bool>
+</resources>
diff --git a/InCallUI/res/values/customize.xml b/InCallUI/res/values/customize.xml
new file mode 100644
index 0000000..45cc4aa
--- /dev/null
+++ b/InCallUI/res/values/customize.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+<resources>
+
+    <!--
+        customize the default value to check idp, default value is none,
+        0-none, 1-CDMA, 2-GSM, 3-CDMA/GSM
+    -->
+    <integer name="check_idp" translatable="false">0</integer>
+    <!-- the international_idp array index that check phone roaming
+        state, -1:none-->
+    <integer name="check_idp_roaming_idx" translatable="false">-1</integer>
+    <!-- the international_idp array index that to be reconverted,
+       -1:none-->
+    <integer name="check_idp_reconvert_idx" translatable="false">-1</integer>
+
+</resources>
diff --git a/InCallUI/res/values/dimens.xml b/InCallUI/res/values/dimens.xml
index 59da786..15520e5 100644
--- a/InCallUI/res/values/dimens.xml
+++ b/InCallUI/res/values/dimens.xml
@@ -63,6 +63,9 @@
     <dimen name="primary_call_elevation">0dp</dimen>
     <dimen name="dialpad_elevation">2dp</dimen>
 
+    <!-- layout height -->
+    <dimen name="in_call_layout_height">40dp</dimen>
+
     <!-- The InCallUI dialpad will sometimes want digits sizes that are different from dialer.
          Note: These are the default sizes for small (<600dp height) devices: larger screen sizes
          apply the values in values-h600dp/dimens.xml. -->
diff --git a/InCallUI/res/values/qtiarray.xml b/InCallUI/res/values/qtiarray.xml
new file mode 100644
index 0000000..3e7e1e8
--- /dev/null
+++ b/InCallUI/res/values/qtiarray.xml
@@ -0,0 +1,285 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (c) 2015, 2016 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.
+  -->
+
+<!-- Array resources for the Phone app. -->
+<resources>
+    <!-- "Target" resources for the GlowPadView widget used for incoming calls;
+         see InCallTouchUi.showIncomingCallWidget() and incall_touch_ui.xml.  -->
+
+    <!-- For video calls, if respond via SMS is disabled:
+         - Answer as video call (drag right)
+         - Decline (drag left)
+         - Answer as audio call (drag down) -->
+    <array name="qti_incoming_call_widget_video_without_sms_targets">
+        <item>@drawable/ic_lockscreen_answer_video</item>
+        <item>@null</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/ic_lockscreen_answer</item>
+        <item>@drawable/qti_ic_lockscreen_answer_tx_video</item>
+        <item>@drawable/qti_ic_lockscreen_answer_rx_video</item>
+    </array>
+    <array name="qti_incoming_call_widget_video_without_sms_target_descriptions">
+        <item>@string/description_target_answer_video_call</item>
+        <item>@null</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/description_target_answer_audio_call</item>
+    </array>
+    <array name="qti_incoming_call_widget_video_without_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For video transmit(Tx), if respond via SMS is disabled:
+         - Answer as video transmit(Tx) (drag right)
+         - Decline (drag left)
+         - Answer as audio call (drag down) -->
+    <array name="qti_incoming_call_widget_tx_video_without_sms_targets">
+        <item>@drawable/qti_ic_lockscreen_answer_tx_video</item>
+        <item>@null</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/ic_lockscreen_answer</item>>
+    </array>
+    <array name="qti_incoming_call_widget_tx_video_without_sms_target_descriptions">
+        <item>@string/description_target_answer_video_call</item>
+        <item>@null</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/description_target_answer_audio_call</item>
+    </array>
+    <array name="qti_incoming_call_widget_tx_video_without_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For video receive(Rx), if respond via SMS is disabled:
+         - Answer as video receive(Rx) (drag right)
+         - Decline (drag left)
+         - Answer as audio call (drag down) -->
+    <array name="qti_incoming_call_widget_rx_video_without_sms_targets">
+        <item>@drawable/qti_ic_lockscreen_answer_rx_video</item>
+        <item>@null</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/ic_lockscreen_answer</item>>
+    </array>
+    <array name="qti_incoming_call_widget_rx_video_without_sms_target_descriptions">
+        <item>@string/description_target_answer_video_call</item>
+        <item>@null</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/description_target_answer_audio_call</item>
+    </array>
+    <array name="qti_incoming_call_widget_rx_video_without_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For video calls, if respond via SMS is enabled:
+         - Answer as video call (drag right)
+         - Respond via SMS (drag up)
+         - Decline (drag left)
+         - Answer as audio call (drag down) -->
+    <array name="qti_incoming_call_widget_video_with_sms_targets">
+        <item>@drawable/ic_lockscreen_answer_video</item>
+        <item>@drawable/ic_lockscreen_text</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/ic_lockscreen_answer</item>
+        <item>@drawable/qti_ic_lockscreen_answer_tx_video</item>
+        <item>@drawable/qti_ic_lockscreen_answer_rx_video</item>
+    </array>
+    <array name="qti_incoming_call_widget_video_with_sms_target_descriptions">
+        <item>@string/description_target_answer_video_call</item>
+        <item>@string/description_target_send_sms</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/description_target_answer_audio_call</item>
+    </array>
+    <array name="qti_incoming_call_widget_video_with_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@string/description_direction_up</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For video transmit(Tx) calls, if respond via SMS is enabled:
+         - Answer as video transmit(Tx) call (drag right)
+         - Respond via SMS (drag up)
+         - Decline (drag left)
+         - Answer as audio call (drag down) -->
+    <array name="qti_incoming_call_widget_tx_video_with_sms_targets">
+        <item>@drawable/qti_ic_lockscreen_answer_tx_video</item>
+        <item>@drawable/ic_lockscreen_text</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/ic_lockscreen_answer</item>
+    </array>
+    <array name="qti_incoming_call_widget_tx_video_with_sms_target_descriptions">
+        <item>@string/description_target_answer_video_call</item>
+        <item>@string/description_target_send_sms</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/description_target_answer_audio_call</item>
+    </array>
+    <array name="qti_incoming_call_widget_tx_video_with_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@string/description_direction_up</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For video receive(Rx) calls, if respond via SMS is enabled:
+         - Answer as video receive call (drag right)
+         - Respond via SMS (drag up)
+         - Decline (drag left)
+         - Answer as audio call (drag down) -->
+    <array name="qti_incoming_call_widget_rx_video_with_sms_targets">
+        <item>@drawable/qti_ic_lockscreen_answer_rx_video</item>
+        <item>@drawable/ic_lockscreen_text</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/ic_lockscreen_answer</item>
+    </array>
+    <array name="qti_incoming_call_widget_rx_video_with_sms_target_descriptions">
+        <item>@string/description_target_answer_video_call</item>
+        <item>@string/description_target_send_sms</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/description_target_answer_audio_call</item>
+    </array>
+    <array name="qti_incoming_call_widget_rx_video_with_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@string/description_direction_up</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For accept/reject upgrade to video in active video call
+         - Accept upgrade to video request (drag right)
+         - Decline upgrade to video request (drag left)-->
+    <array name="qti_incoming_call_widget_video_request_targets">
+        <item>@drawable/ic_lockscreen_answer_video</item>
+        <item>@drawable/ic_lockscreen_decline_video</item>
+        <item>@drawable/ic_lockscreen_answer</item>
+        <item>@drawable/qti_ic_lockscreen_answer_tx_video</item>
+        <item>@drawable/qti_ic_lockscreen_answer_rx_video</item>
+    </array>
+
+    <array name="qti_incoming_call_widget_video_request_target_descriptions">
+        <item>@string/description_target_accept_upgrade_to_video_request</item>
+        <item>@null</item>
+        <item>@string/description_target_decline_upgrade_to_video_request</item>
+        <item>@null</item>"
+    </array>
+    <array name="qti_incoming_call_widget_video_request_target_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@null</item>
+    </array>
+
+    <!-- For accept/reject upgrade to video in active video call
+         - Accept upgrade to video request (drag right)
+         - Decline upgrade to video request (drag left)-->
+    <array name="qti_incoming_call_widget_bidirectional_video_accept_reject_request_targets">
+        <item>@drawable/ic_lockscreen_answer_video</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+    </array>
+
+    <!-- For accept/reject upgrade to video transmit in active video call
+         - Accept upgrade to video request (drag right)
+         - Decline upgrade to video request (drag left)-->
+    <array name="qti_incoming_call_widget_video_transmit_accept_reject_request_targets">
+        <item>@drawable/qti_ic_lockscreen_answer_tx_video</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+    </array>
+    <array name="qti_incoming_call_widget_video_transmit_request_target_descriptions">
+        <item>@string/description_target_accept_upgrade_to_video_transmit_request</item>
+        <item>@string/description_target_decline_upgrade_to_video_transmit_request</item>
+    </array>
+
+    <!-- For accept/reject upgrade to video receive in active video call
+         - Accept upgrade to video request (drag right)
+         - Decline upgrade to video request (drag left)-->
+    <array name="qti_incoming_call_widget_video_receive_accept_reject_request_targets">
+        <item>@drawable/qti_ic_lockscreen_answer_rx_video</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+    </array>
+    <array name="qti_incoming_call_widget_video_receive_request_target_descriptions">
+        <item>@string/description_target_accept_upgrade_to_video_receive_request</item>
+        <item>@string/description_target_decline_upgrade_to_video_receive_request</item>
+    </array>
+
+    <!-- For audio calls, if respond via SMS is disabled & call deflection is enabled:
+         - Answer (drag right)
+         - Decline (drag left)
+         - Deflect (drag down) -->
+    <array name="qti_incoming_call_widget_audio_without_sms_targets">
+        <item>@drawable/ic_lockscreen_answer</item>
+        <item>@null</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/qti_ic_lockscreen_deflect</item>
+    </array>
+    <array name="qti_incoming_call_widget_audio_without_sms_target_descriptions">
+        <item>@string/description_target_answer</item>
+        <item>@null</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/qti_description_target_deflect</item>
+    </array>
+    <array name="qti_incoming_call_widget_audio_without_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@null</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+    <!-- For audio calls, if respond via SMS is enabled & call deflection is enabled:
+         - Answer (drag right)
+         - Respond via SMS (drag up)
+         - Decline (drag left)
+         - Deflect (drag down) -->
+    <array name="qti_incoming_call_widget_audio_with_sms_targets">
+        <item>@drawable/ic_lockscreen_answer</item>
+        <item>@drawable/ic_lockscreen_text</item>
+        <item>@drawable/ic_lockscreen_decline</item>
+        <item>@drawable/qti_ic_lockscreen_deflect</item>
+    </array>
+    <array name="qti_incoming_call_widget_audio_with_sms_target_descriptions">
+        <item>@string/description_target_answer</item>
+        <item>@string/description_target_send_sms</item>
+        <item>@string/description_target_decline</item>
+        <item>@string/qti_description_target_deflect</item>
+    </array>
+    <array name="qti_incoming_call_widget_audio_with_sms_direction_descriptions">
+        <item>@string/description_direction_right</item>
+        <item>@string/description_direction_up</item>
+        <item>@string/description_direction_left</item>
+        <item>@string/description_direction_down</item>
+    </array>
+
+</resources>
diff --git a/InCallUI/res/values/qticonfig.xml b/InCallUI/res/values/qticonfig.xml
new file mode 100644
index 0000000..7cf7410
--- /dev/null
+++ b/InCallUI/res/values/qticonfig.xml
@@ -0,0 +1,53 @@
+<!--
+  ~ Copyright (c) 2015, 2016, 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.
+  -->
+<resources>
+    <!--
+    customize the default value to control Clear-Code feature,
+    default value is false, true is for LatamOpenAMX
+    -->
+    <bool name="def_incallui_clearcode_enabled">false</bool>
+    <!-- Config to show/hide call duration in call detail -->
+    <bool name="call_duration_enabled">true</bool>
+    <!-- Config to enable/disable ims call log -->
+    <bool name="ims_call_type_enabled">false</bool>
+    <!-- Flag indicating if conference dialer  is enabled -->
+    <bool name="config_enable_conference_dialer">false</bool>
+    <!-- Config to enable/disable conference state display -->
+    <bool name="config_conference_call_show_participant_status">false</bool>
+    <!-- Config to enable/disable add multi participants -->
+    <bool name="add_multi_participants_enabled">false</bool>
+    <!-- Config to add participant only in conferencall call-->
+    <bool name="add_participant_only_in_conference">false</bool>
+    <!-- Config to if show preview before the receiver accepts a show me upgrade video call -->
+    <bool name="config_enable_modify_call_preview">false</bool>
+    <!-- Config to show/hide HD Icon2 -->
+    <bool name="config_show_hd2">false</bool>
+    <!-- Config to enalbe call record -->
+    <bool name="enable_call_record">false</bool>
+</resources>
diff --git a/InCallUI/res/values/qtistrings.xml b/InCallUI/res/values/qtistrings.xml
new file mode 100644
index 0000000..eba800a
--- /dev/null
+++ b/InCallUI/res/values/qtistrings.xml
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2015, 2016 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Dtermine whether emergency calls should be marked. -->
+    <bool name="mark_emergency_call">true</bool>
+    <!-- Call substate label -->
+    <string name="call_substate_label"> Call substate - \u000a</string>
+    <!-- Call substate label for call resumed -->
+    <string name="call_substate_call_resumed">Resumed \u000a</string>
+    <!-- Call substate label for call connected suspended (audio) -->
+    <string name="call_substate_connected_suspended_audio">Connected Suspended (Audio) \u000a</string>
+    <!-- Call substate label for call connected suspended (video) -->
+    <string name="call_substate_connected_suspended_video">Connected Suspended (Video) \u000a</string>
+    <!-- Call substate label for avp retry -->
+    <string name="call_substate_avp_retry">Avp Retry \u000a</string>
+
+    <!-- Video quality changed message -->
+    <string name="video_quality_changed"> Video quality changed to \u0020</string>
+    <!-- Video quality High -->
+    <string name="video_quality_high">High</string>
+    <!-- Video quality Medium -->
+    <string name="video_quality_medium">Medium</string>
+    <!-- Video quality Low -->
+    <string name="video_quality_low">Low</string>
+    <!-- Video quality Unknown -->
+    <string name="video_quality_unknown">Unknown</string>
+
+    <!-- Message indicating that Video Started flowing for IMS-VT calls -->
+    <string name="player_started">Player Started</string>
+    <!-- Message indicating that Video Stopped flowing for IMS-VT calls -->
+    <string name="player_stopped">Player Stopped</string>
+    <!-- Message indicating that camera failure has occurred for the selected camera and
+         as result camera is not ready -->
+    <string name="camera_not_ready">Camera not ready</string>
+    <!-- Message indicating that camera is ready/available -->
+    <string name="camera_ready">Camera ready</string>
+    <!-- Message indicating unknown call session event -->
+    <string name="unknown_call_session_event">"Unkown call session event"</string>
+
+    <!-- Message indicating data usage -->
+    <string name="wlan_data_usage_label">"Wlan Data usage : "</string>
+    <string name="lte_data_usage_label">"Lte Data usage : "</string>
+
+    <!-- In-call screen: Modify Call Options for IMS call -->
+    <string name="modify_call_option_title">Which type of call?</string>
+    <string name="modify_call_option_vt">Video bidirectional</string>
+    <string name="modify_call_option_vt_tx">Video Transmit</string>
+    <string name="modify_call_option_vt_rx">Video Receive</string>
+    <string name="modify_call_option_voice">Voice Only</string>
+
+    <!-- Modify call error cause -->
+    <string name="modify_call_failed_due_to_low_battery">Modify call failed due to low battery.</string>
+
+    <!-- Description of the call transfer related strings [CHAR LIMIT=NONE] -->
+    <string name="qti_ims_transfer_num_error">Number not set. Provide the number via IMS settings and retry.</string>
+    <string name="qti_ims_transfer_request_error">Call Transfer request had failed.</string>
+    <string name="qti_ims_transfer_request_success">Call Transfer request is successful.</string>
+    <!-- Text for the onscreen "blind transfer" button -->
+    <string name="qti_ims_onscreenBlindTransfer">Blind Transfer</string>
+    <!-- Text for the onscreen "assured transfer" button -->
+    <string name="qti_ims_onscreenAssuredTransfer">Assured Transfer</string>
+    <!-- Text for the onscreen "consultative transfer" button -->
+    <string name="qti_ims_onscreenConsultativeTransfer">Consultative Transfer</string>
+
+    <!-- Message indicating video calls not allowed if user enabled TTY Mode -->
+    <string name="video_call_not_allowed_if_tty_enabled">Please disable TTY Mode to upgrade to video calls.</string>
+
+    <!-- Description of the deflect target in the Slide unlock screen. [CHAR LIMIT=NONE] -->
+    <string name="qti_description_target_deflect">Deflect</string>
+    <string name="qti_description_deflect_error">Number not set. Provide the number via IMS settings and retry.</string>
+    <string name="qti_description_deflect_service_error">Call Deflection service is not supported.</string>
+    <!-- Message indicating call failed due to handover not feasible -->
+    <string name="call_failed_ho_not_feasible">Call was ended as LTE to 3G/2G handover was not feasible.</string>
+    <!-- Text for the onscreen "Add Participant" button -->
+    <string name="onscreenAddParticipant">Add Participant</string>
+
+    <!-- Title of the IMS to CS redial dialog -->
+    <string name="cs_redial_option">Redial Option</string>
+    <!-- Message text of the IMS to CS redial dialog -->
+    <string name="cs_redial_msg">Unable to make an IMS Video call, try to dial as non-IMS voice call
+?</string>
+    <!-- Yes option of the IMS to CS redial dialog -->
+    <string name="cs_redial_yes">Yes</string>
+    <!-- No option of the IMS to CS redial dialog -->
+    <string name="cs_redial_no">No</string>
+    <!-- Session modify cause unspecified -->
+    <string name="session_modify_cause_unspecified"></string>
+    <!-- Session modify cause code upgrade local request -->
+    <string name="session_modify_cause_upgrade_local_request">Call upgraded on user request</string>
+    <!-- Session modify cause code upgrade remote request -->
+    <string name="session_modify_cause_upgrade_remote_request">Call upgraded on remote user request</string>
+    <!-- Session modify cause code downgrade local request -->
+    <string name="session_modify_cause_downgrade_local_request">Call downgraded on user request</string>
+    <!-- Session modify cause code downgrade remote request -->
+    <string name="session_modify_cause_downgrade_remote_request">Call downgraded on remote user request</string>
+    <!-- Session modify cause code downgrade rtp timeout -->
+    <string name="session_modify_cause_downgrade_rtp_timeout">Call downgraded due to RTP timeout</string>
+    <!-- Session modify cause code downgrade qos -->
+    <string name="session_modify_cause_downgrade_qos">Call downgraded due to quality of service</string>
+    <!-- Session modify cause code downgrade packet loss -->
+    <string name="session_modify_cause_downgrade_packet_loss">Call downgraded due to packet loss</string>
+    <!-- Session modify cause code downgrade low thrput -->
+    <string name="session_modify_cause_downgrade_low_thrput">Call downgraded due to low throughput</string>
+    <!-- Session modify cause code downgrade thermal mitigation -->
+    <string name="session_modify_cause_downgrade_thermal_mitigation">Call downgraded due to thermal mitigation</string>
+    <!-- Session modify cause code downgrade lipsync -->
+    <string name="session_modify_cause_downgrade_lipsync">Call downgraded due to lipsync</string>
+    <!-- Session modify cause code downgrade generic error -->
+    <string name="session_modify_cause_downgrade_generic_error">Call downgraded due to generic error</string>
+
+    <!-- In-call screen: status label for an incoming conference call [CHAR LIMIT=25] -->
+    <string name="card_title_incoming_conf_call">Incoming conference call</string>
+    <!-- In-call screen: status label for an incoming video conference call [CHAR LIMIT=25] -->
+    <string name="card_title_incoming_video_conf_call">Incoming video conference</string>
+    <!-- The "label" of the in-call Notification for an incoming conference ringing call. [CHAR LIMIT=60] -->
+    <string name="notification_incoming_conf_call">Incoming conference call</string>
+    <!-- Title displayed in the overlay for incoming conference calls which include the name of the provider.
+         [CHAR LIMIT=40] -->
+    <string name="incoming_conf_via_template">Incoming conference via <xliff:g id="provider_name">%s</xliff:g></string>
+
+    <!-- Message indicating call failed due to low battery -->
+    <string name="call_failed_due_to_low_battery">Call is failed due to low battery</string>
+
+    <!-- VoWifi call quality indicators -->
+    <string name="vowifi_call_quality_good">Good Voice quality</string>
+    <string name="vowifi_call_quality_fair">Fair Voice quality</string>
+    <string name="vowifi_call_quality_poor">Poor Voice quality</string>
+
+    <!-- Title for low battery alert dialogue -->
+    <string name="low_battery">Low Battery Warning</string>
+    <!-- Yes option of the low battery alert dialog -->
+    <string name="low_battery_yes">Yes</string>
+    <!-- No option of the low battery alert dialog for active video calls -->
+    <string name="low_battery_no">No</string>
+    <!-- Message text of the low battery alert dialog in cases video call doesn't have downgrade capabilities -->
+    <string name="low_battery_hangup_msg">Do you want to hangup the call?</string>
+    <!-- Message text of the low battery alert dialog for MO/MT video calls -->
+    <string name="low_battery_msg">Your battery level is below 15%. Do you want to continue with the video call or convert it to Voice call</string>
+    <!-- No option of the low battery alert dialog for MO/MT Video calls -->
+    <string name="low_battery_convert">Convert</string>
+    <!-- International prefix source-->
+    <string-array name="international_idp" translatable="false">
+        <item>+33</item>
+    </string-array>
+
+    <!-- International prefix converted value-->
+    <string-array name="international_idp_values" translatable="false">
+        <item>"0033"</item>
+    </string-array>
+    <string name="video_call_downgrade_without_lte_toast">Move out of LTE coverage area downgrade the call.</string>
+    <string name="video_call">Video Calling</string>
+    <string name="video_call_cannot_upgrade">cannot accept video calls at this time</string>
+
+    <!-- Pop up menu options for picture mode -->
+    <string name="video_call_picture_mode_menu_title">Choose Picture Mode</string>
+    <string name="video_call_picture_mode_preview_video">Camera Preview</string>
+    <string name="video_call_picture_mode_incoming_video">Incoming Video</string>
+    <string name="video_call_picture_mode_cancel_option">Cancel</string>
+    <string name="video_call_picture_mode_save_option">Save</string>
+</resources>
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
index 84eb14c..02e4bcf 100644
--- a/InCallUI/res/values/strings.xml
+++ b/InCallUI/res/values/strings.xml
@@ -450,7 +450,13 @@
         <item>silent</item>
         <item>never</item>
     </string-array>
+    <string name="menu_start_record">Record call</string>
+    <string name="menu_stop_record">Stop recording</string>
+    <string name="recording_time_text">Recording</string>
 
+    <string name="volume_boost_notify_enabled">"Volume boost ON."</string>
+    <string name="volume_boost_notify_disabled">"Volume boost OFF."</string>
+    <string name="volume_boost_notify_unavailable">"Extra volume is not available when Wired/Bluetooth headset is used."</string>
     <!-- Setting option name to pick ringtone (a list dialog comes up). [CHAR LIMIT=30] -->
     <string name="ringtone_title" msgid="5379026328015343686">Phone ringtone</string>
 
@@ -493,6 +499,10 @@
     <!-- Description of the "camera off" icon displayed when the device's camera is disabled during
          a video call. [CHAR LIMIT=NONE] -->
     <string name="camera_off_description">Camera off</string>
+    <!-- Set Subscription screen: label sub 1 -->
+    <string name="sub_1">SUB 1</string>
+    <!-- Set Subscription screen: label sub 2 -->
+    <string name="sub_2">SUB 2</string>
 
     <!-- Used to inform the user that a call was received via a number other than the primary
         phone number associated with their device. [CHAR LIMIT=16] -->
@@ -536,4 +546,12 @@
     <string name="open_now">Open now</string>
     <!-- Displayed when a place is closed. -->
     <string name="closed_now">Closed now</string>
+    <string name="call_failed_due_to_validate_number">Unable to make video call, incorrect number format</string>
+    <string name="call_state_dialing">Dialing</string>
+    <string name="call_state_holding">Holding</string>
+    <string name="call_state_active">Active</string>
+    <string name="call_state_unknown">Unknown</string>
+    <string name="call_state_disconnecting">Disconnecting</string>
+    <string name="call_state_disconnected">Disconnected</string>
+    <string name="too_many_recipients">The maximum allowed participants in a 4G conference call are 6.</string>
 </resources>
diff --git a/InCallUI/res/values/styles.xml b/InCallUI/res/values/styles.xml
index 11d6362..6f3d3d7 100644
--- a/InCallUI/res/values/styles.xml
+++ b/InCallUI/res/values/styles.xml
@@ -76,7 +76,7 @@
          InCallActivity to have the correct Material style. -->
     <style name="Theme.InCallScreen" parent="@android:style/Theme.Material.Light">
         <item name="android:windowAnimationStyle">@null</item>
-        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowIsTranslucent">false</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="dialpad_key_button_touch_tint">@color/incall_dialpad_touch_tint</item>
diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java
index 44ddfcd..5333058 100644
--- a/InCallUI/src/com/android/incallui/AnswerFragment.java
+++ b/InCallUI/src/com/android/incallui/AnswerFragment.java
@@ -51,6 +51,19 @@
     public static final int TARGET_SET_FOR_VIDEO_WITH_SMS = 3;
     public static final int TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST = 4;
 
+    public static final int TARGET_SET_FOR_QTI_VIDEO_WITHOUT_SMS = 1000;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_WITH_SMS = 1001;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_ACCEPT_REJECT_REQUEST = 1003;
+    public static final int TARGET_SET_FOR_QTI_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST = 1004;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST = 1005;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST = 1006;
+    public static final int TARGET_SET_FOR_QTI_AUDIO_WITHOUT_SMS = 1007;
+    public static final int TARGET_SET_FOR_QTI_AUDIO_WITH_SMS = 1008;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_WITHOUT_SMS = 1009;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_WITH_SMS = 1010;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_WITHOUT_SMS = 1011;
+    public static final int TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_WITH_SMS = 1012;
+
     /**
      * This fragment implement no UI at all. Derived class should do it.
      */
@@ -261,6 +274,10 @@
         getPresenter().onText();
     }
 
+    public void onDeflect(Context context) {
+        getPresenter().onDeflect(context);
+    }
+
     /**
      * OnItemClickListener for the "Respond via SMS" popup.
      */
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 883b54f..b8b5185 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -17,6 +17,9 @@
 package com.android.incallui;
 
 import android.content.Context;
+import android.provider.Settings;
+import android.telecom.VideoProfile;
+import android.telephony.SubscriptionManager;
 
 import com.android.dialer.compat.UserManagerCompat;
 import com.android.dialer.util.TelecomUtil;
@@ -24,6 +27,12 @@
 
 import java.util.List;
 
+import org.codeaurora.ims.internal.IQtiImsExt;
+import org.codeaurora.ims.QtiImsException;
+import org.codeaurora.ims.QtiImsExtListenerBaseImpl;
+import org.codeaurora.ims.QtiImsExtManager;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 /**
  * Presenter for the Incoming call widget. The {@link AnswerPresenter} handles the logic during
  * incoming calls. It is also in charge of responding to incoming calls, so there needs to be
@@ -36,51 +45,101 @@
 public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
         implements CallList.CallUpdateListener, InCallPresenter.InCallUiListener,
                 InCallPresenter.IncomingCallListener,
-                CallList.Listener {
+                CallList.Listener, CallList.ActiveSubChangeListener {
 
     private static final String TAG = AnswerPresenter.class.getSimpleName();
 
-    private String mCallId;
-    private Call mCall = null;
+    private String mCallId[] = new String[InCallServiceImpl.sPhoneCount];
+    private Call mCall[] = new Call[InCallServiceImpl.sPhoneCount];
+    private final CallList mCalls = CallList.getInstance();
     private boolean mHasTextMessages = false;
+    // Currently mVideoState is beeing used only for incoming calls.
+    // As there is only one incoming call allowed there is no need of array here.
+    private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
+
+    /* QtiImsExtListenerBaseImpl instance to handle call deflection response */
+    private QtiImsExtListenerBaseImpl imsInterfaceListener =
+            new QtiImsExtListenerBaseImpl() {
+
+        /* Handles call deflect response */
+        @Override
+        public void receiveCallDeflectResponse(int result) {
+            Log.w(this, "receiveCallDeflectResponse: " + result);
+        }
+    };
+    private static final int INVALID_PHONEID = -1;
 
     @Override
     public void onUiShowing(boolean showing) {
         if (showing) {
-            CallList.getInstance().addListener(this);
-            final CallList calls = CallList.getInstance();
+            mCalls.addListener(this);
+            mCalls.addActiveSubChangeListener(this);
             Call call;
-            call = calls.getIncomingCall();
-            if (call != null) {
-                processIncomingCall(call);
+            // Consider incoming/waiting calls on both subscriptions
+            // for DSDA.
+            for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) {
+                int subId = QtiCallUtils.getSubId(i);
+                if (checkSubId(i)) {
+                    call = mCalls.getCallWithState(Call.State.INCOMING, 0, subId);
+                    if (call == null) {
+                        call = mCalls.getCallWithState(Call.State.CALL_WAITING, 0, subId);
+                    }
+                    if (call != null) {
+                        processIncomingCall(call);
+                    }
+                } else {
+                    Log.d(TAG, "No valid sub");
+                }
             }
-            call = calls.getVideoUpgradeRequestCall();
+            call = mCalls.getVideoUpgradeRequestCall();
             Log.d(this, "getVideoUpgradeRequestCall call =" + call);
             if (call != null) {
                 showAnswerUi(true);
                 processVideoUpgradeRequestCall(call);
             }
         } else {
-            CallList.getInstance().removeListener(this);
+            mCalls.removeListener(this);
             // This is necessary because the activity can be destroyed while an incoming call exists.
             // This happens when back button is pressed while incoming call is still being shown.
-            if (mCallId != null) {
-                CallList.getInstance().removeCallUpdateListener(mCallId, this);
+            for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) {
+                int subId = QtiCallUtils.getSubId(i);
+                if (checkSubId(i)) {
+                    Call call = mCalls.getCallWithState(Call.State.INCOMING, 0, subId);
+                    if (call == null) {
+                        call = mCalls.getCallWithState(Call.State.CALL_WAITING, 0, subId);
+                    }
+                    if (call == null) {
+                        call = mCalls.getCallWithState(Call.State.ACTIVE, 0, subId);
+                    }
+                    if (mCallId[i] != null && call == null) {
+                        mCalls.removeCallUpdateListener(mCallId[i], this);
+                        mCalls.removeActiveSubChangeListener(this);
+                    }
+                } else {
+                    Log.d(TAG, "No valid sub");
+                }
             }
         }
     }
 
+    private boolean checkSubId(int phoneId) {
+        int subId = QtiCallUtils.getSubId(phoneId);
+        return (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+    }
+
     @Override
     public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
+        int subId = call.getSubId();
+        int phoneId = QtiCallUtils.getPhoneId(subId);
         Log.d(this, "onIncomingCall: " + this);
-        Call modifyCall = CallList.getInstance().getVideoUpgradeRequestCall();
+        Call modifyCall = mCalls.getVideoUpgradeRequestCall();
         if (modifyCall != null) {
             showAnswerUi(false);
             Log.d(this, "declining upgrade request id: ");
-            CallList.getInstance().removeCallUpdateListener(mCallId, this);
+            mCalls.removeCallUpdateListener(mCallId[phoneId], this);
             InCallPresenter.getInstance().declineUpgradeRequest();
         }
-        if (!call.getId().equals(mCallId)) {
+        if (!call.getId().equals(mCallId[phoneId])) {
             // A new call is coming in.
             processIncomingCall(call);
         }
@@ -97,15 +156,24 @@
     @Override
     public void onDisconnect(Call call) {
         // no-op
+        int subId = call.getSubId();
+        int phoneId = QtiCallUtils.getPhoneId(subId);
+        if (call.equals(mCall[phoneId])) {
+            mCall[phoneId] = null;
+        }
     }
 
-    public void onSessionModificationStateChange(int sessionModificationState) {
+    public void onSessionModificationStateChange(Call call, int sessionModificationState) {
         boolean isUpgradePending = sessionModificationState ==
                 Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
 
         if (!isUpgradePending) {
             // Stop listening for updates.
-            CallList.getInstance().removeCallUpdateListener(mCallId, this);
+            for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) {
+                if (mCallId[i] != null) {
+                    mCalls.removeCallUpdateListener(mCallId[i], this);
+                }
+            }
             showAnswerUi(false);
         }
     }
@@ -143,15 +211,19 @@
     }
 
     private void processIncomingCall(Call call) {
-        mCallId = call.getId();
-        mCall = call;
+        int subId = call.getSubId();
+        int phoneId = QtiCallUtils.getPhoneId(subId);
+        mCallId[phoneId] = call.getId();
+        mCall[phoneId] = call;
 
+        mCalls.addListener(this);
         // Listen for call updates for the current call.
-        CallList.getInstance().addCallUpdateListener(mCallId, this);
+        mCalls.addCallUpdateListener(mCallId[phoneId], this);
 
-        Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this);
+        Log.d(TAG, "Showing incoming for call id: " + mCallId[phoneId] + " " + this);
         if (showAnswerUi(true)) {
-            final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
+            mVideoState = call.getVideoState();
+            final List<String> textMsgs = mCalls.getTextResponses(call.getId());
             configureAnswerTargetsForSms(call, textMsgs);
         }
     }
@@ -171,11 +243,13 @@
 
     private void processVideoUpgradeRequestCall(Call call) {
         Log.d(this, " processVideoUpgradeRequestCall call=" + call);
-        mCallId = call.getId();
-        mCall = call;
+        int subId = call.getSubId();
+        int phoneId = QtiCallUtils.getPhoneId(subId);
+        mCallId[phoneId] = call.getId();
+        mCall[phoneId] = call;
 
         // Listen for call updates for the current call.
-        CallList.getInstance().addCallUpdateListener(mCallId, this);
+        CallList.getInstance().addCallUpdateListener(mCallId[phoneId], this);
 
         final int currentVideoState = call.getVideoState();
         final int modifyToVideoState = call.getRequestedVideoState();
@@ -192,8 +266,9 @@
             return;
         }
         showAnswerUi(true);
-        ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST,
-                modifyToVideoState);
+        ui.showTargets(QtiCallUtils.getSessionModificationOptions(getUi().getContext(),
+                currentVideoState, modifyToVideoState));
+
     }
 
     private boolean isEnabled(int videoState, int mask) {
@@ -205,12 +280,14 @@
         Log.d(this, "onCallStateChange() " + call + " " + this);
         if (call.getState() != Call.State.INCOMING) {
             boolean isUpgradePending = isVideoUpgradePending(call);
+            int subId = call.getSubId();
+            int phoneId = QtiCallUtils.getPhoneId(subId);
             if (!isUpgradePending) {
                 // Stop listening for updates.
-                CallList.getInstance().removeCallUpdateListener(mCallId, this);
+                mCalls.removeCallUpdateListener(mCallId[phoneId], this);
             }
 
-            final Call incall = CallList.getInstance().getIncomingCall();
+            final Call incall = mCalls.getIncomingCall();
             if (incall != null || isUpgradePending) {
                 showAnswerUi(true);
             } else {
@@ -218,26 +295,43 @@
             }
 
             mHasTextMessages = false;
-        } else if (!mHasTextMessages) {
-            final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId());
-            if (textMsgs != null) {
-                configureAnswerTargetsForSms(call, textMsgs);
-            }
+        } else if (!mHasTextMessages || (mVideoState != call.getVideoState())) {
+            final List<String> textMsgs = mCalls.getTextResponses(call.getId());
+            mVideoState = call.getVideoState();
+            configureAnswerTargetsForSms(call, textMsgs);
         }
     }
 
+    // get active phoneId, for which call is visible to user
+    private int getActivePhoneId() {
+        int phoneId = INVALID_PHONEID;
+        if (mCalls.isDsdaEnabled()) {
+            int subId = mCalls.getActiveSubId();
+            phoneId = QtiCallUtils.getPhoneId(subId);
+        } else {
+            for (int i = 0; i < mCall.length; i++) {
+                if (mCall[i] != null) {
+                    phoneId = i;
+                }
+            }
+        }
+        return phoneId;
+    }
+
     public void onAnswer(int videoState, Context context) {
-        if (mCallId == null) {
+        int phoneId = getActivePhoneId();
+        Log.i(this, "onAnswer  mCallId:" + mCallId + "phoneId:" + phoneId);
+        if (mCallId == null || phoneId == INVALID_PHONEID) {
             return;
         }
 
-        if (mCall.getSessionModificationState()
+        if (mCall[phoneId].getSessionModificationState()
                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
             Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState);
             InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context);
         } else {
             Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
-            TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);
+            InCallPresenter.getInstance().answerIncomingCall(context, videoState);
         }
     }
 
@@ -246,12 +340,13 @@
      * reject since it seems to be more prevalent.
      */
     public void onDecline(Context context) {
-        Log.d(this, "onDecline " + mCallId);
-        if (mCall.getSessionModificationState()
+        int phoneId = getActivePhoneId();
+        Log.d(this, "onDecline mCallId:" + mCallId + "phoneId:" + phoneId);
+        if (mCall[phoneId].getSessionModificationState()
                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
             InCallPresenter.getInstance().declineUpgradeRequest(context);
         } else {
-            TelecomAdapter.getInstance().rejectCall(mCall.getId(), false, null);
+            TelecomAdapter.getInstance().rejectCall(mCall[phoneId].getId(), false, null);
         }
     }
 
@@ -262,9 +357,42 @@
         }
     }
 
+    /**
+     * Deflect the incoming call.
+     */
+    public void onDeflect(Context context) {
+        if (mCallId == null) {
+            return;
+        }
+        Log.d(this, "onDeflect " + mCallId);
+
+        String deflectCallNumber = QtiImsExtUtils.getCallDeflectNumber(
+                                           context.getContentResolver());
+        /* If not set properly, inform user via toast */
+        if (deflectCallNumber == null) {
+            Log.w(this, "getCallDeflectNumber is null or Empty.");
+            QtiCallUtils.displayToast(context, R.string.qti_description_deflect_error);
+        } else {
+            int phoneId = 0;
+            try {
+                Log.d(this, "Sending deflect request with Phone id " + phoneId +
+                        " to " + deflectCallNumber);
+                QtiImsExtManager.getInstance().sendCallDeflectRequest(phoneId,
+                        deflectCallNumber, imsInterfaceListener);
+             } catch (QtiImsException e) {
+                 Log.e(this, "sendCallDeflectRequest exception " + e);
+                 QtiCallUtils.displayToast(getUi().getContext(),
+                         R.string.qti_description_deflect_service_error);
+             }
+        }
+    }
+
     public void rejectCallWithMessage(String message) {
-        Log.d(this, "sendTextToDefaultActivity()...");
-        TelecomAdapter.getInstance().rejectCall(mCall.getId(), true, message);
+        int phoneId = getActivePhoneId();
+        Log.i(this, "sendTextToDefaultActivity()...phoneId:" + phoneId);
+        if (phoneId != INVALID_PHONEID) {
+            TelecomAdapter.getInstance().rejectCall(mCall[phoneId].getId(), true, message);
+        }
 
         onDismissDialog();
     }
@@ -284,12 +412,25 @@
 
         // Only present the user with the option to answer as a video call if the incoming call is
         // a bi-directional video call.
-        if (VideoUtils.isBidirectionalVideoCall(call)) {
+        if (VideoUtils.isVideoCall(call)) {
             if (withSms) {
-                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
+                getUi().showTargets(QtiCallUtils.getIncomingCallAnswerOptions(
+                        getUi().getContext(), call.getVideoState(), withSms));
                 getUi().configureMessageDialog(textMsgs);
             } else {
-                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITHOUT_SMS);
+                getUi().showTargets(QtiCallUtils.getIncomingCallAnswerOptions(
+                        getUi().getContext(), call.getVideoState(), withSms));
+            }
+        } else if (isCallDeflectSupported()) {
+            /**
+             * Only present the user with the option to deflect call,
+             * if the incoming call is only an audio call.
+             */
+            if (withSms) {
+                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_QTI_AUDIO_WITH_SMS);
+                getUi().configureMessageDialog(textMsgs);
+            } else {
+                getUi().showTargets(AnswerFragment.TARGET_SET_FOR_QTI_AUDIO_WITHOUT_SMS);
             }
         } else {
             if (withSms) {
@@ -301,6 +442,23 @@
         }
     }
 
+    /**
+     * Checks the Settings to conclude on the call deflect support.
+     * Returns true if call deflect is possible, false otherwise.
+     */
+    private boolean isCallDeflectSupported() {
+        int value = 0;
+        try{
+            value = android.provider.Settings.Global.getInt(
+                    getUi().getContext().getContentResolver(),
+                    QtiImsExtUtils.QTI_IMS_DEFLECT_ENABLED);
+        } catch(Settings.SettingNotFoundException e) {
+            //do Nothing
+            Log.e(this, "isCallDeflectSupported exception " + e);
+        }
+        return (value == 1);
+    }
+
     interface AnswerUi extends Ui {
         public void onShowAnswerUi(boolean shown);
         public void showTargets(int targetSet);
@@ -309,4 +467,23 @@
         public void configureMessageDialog(List<String> textResponses);
         public Context getContext();
     }
+
+    @Override
+    public void onActiveSubChanged(int subId) {
+        final Call call = mCalls.getIncomingCall();
+        int phoneId = QtiCallUtils.getPhoneId(subId);
+        if ((call != null) && (call.getId() == mCallId[phoneId])) {
+            Log.d(this, "Show incoming for call id: " + mCallId[phoneId] + " " + this);
+            if (showAnswerUi(true)) {
+                final List<String> textMsgs = mCalls.getTextResponses(
+                        call.getId());
+                configureAnswerTargetsForSms(call, textMsgs);
+            }
+        } else if ((call == null) && (mCalls.hasAnyLiveCall(subId))) {
+            Log.d(this, "Hide incoming for call id: " + mCallId[phoneId] + " " + this);
+            showAnswerUi(false);
+        } else {
+            Log.d(this, "No incoming call present for sub = " + subId + " " + this);
+        }
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/AudioModeProvider.java b/InCallUI/src/com/android/incallui/AudioModeProvider.java
index ea56dd6..a27371b 100644
--- a/InCallUI/src/com/android/incallui/AudioModeProvider.java
+++ b/InCallUI/src/com/android/incallui/AudioModeProvider.java
@@ -49,6 +49,10 @@
     public void onAudioModeChange(int newMode, boolean muted) {
         if (mAudioMode != newMode) {
             mAudioMode = newMode;
+            InCallActivity inCallActivity = InCallPresenter.getInstance().getActivity();
+            if (inCallActivity != null && inCallActivity.getCallCardFragment() != null) {
+                inCallActivity.getCallCardFragment().updateVbByAudioMode(newMode);
+            }
             for (AudioModeListener l : mListeners) {
                 l.onAudioMode(mAudioMode);
             }
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index d552ecf..42890f2 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -20,7 +20,9 @@
 import android.hardware.camera2.CameraCharacteristics;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.os.Trace;
+import android.os.RemoteException;
 import android.telecom.Call.Details;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
@@ -29,7 +31,9 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.SubscriptionManager;
 import android.telecom.VideoProfile;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 
 import com.android.contacts.common.CallUtil;
@@ -46,6 +50,13 @@
 import java.util.Locale;
 import java.util.Objects;
 
+import org.codeaurora.ims.internal.IQtiImsExt;
+import org.codeaurora.ims.QtiCallConstants;
+import org.codeaurora.ims.QtiImsException;
+import org.codeaurora.ims.QtiImsExtListenerBaseImpl;
+import org.codeaurora.ims.QtiImsExtManager;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 /**
  * Describes a single call and its state.
  */
@@ -359,6 +370,8 @@
             }
     };
 
+    boolean mIsActiveSub = false;
+    public static final String ACTIVE_SUBSCRIPTION = "active_sub";
     private android.telecom.Call mTelecomCall;
     private boolean mIsEmergencyCall;
     private Uri mHandle;
@@ -381,6 +394,7 @@
     private String mLastForwardedNumber;
     private String mCallSubject;
     private PhoneAccountHandle mPhoneAccountHandle;
+    private long mBaseChronometerTime = 0;
 
     /**
      * Indicates whether the phone account associated with this call supports specifying a call
@@ -581,6 +595,9 @@
                 mCallSubject = callSubject;
             }
         }
+        if (callExtras.containsKey(ACTIVE_SUBSCRIPTION)) {
+            mIsActiveSub = callExtras.getBoolean(ACTIVE_SUBSCRIPTION);
+        }
     }
 
     /**
@@ -657,6 +674,10 @@
         }
     }
 
+    public int getTrueState(){
+        return mState;
+    }
+
     public void setState(int state) {
         mState = state;
         if (mState == State.INCOMING) {
@@ -742,9 +763,29 @@
         int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities();
 
         if ((capabilities & android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
+            if (CallList.getInstance().isDsdaEnabled()) {
+                List<android.telecom.Call> conferenceableCalls =
+                        mTelecomCall.getConferenceableCalls();
+                boolean hasConferenceableCall = false;
+                if (!conferenceableCalls.isEmpty()){
+                    int subId = getSubId();
+                    for (android.telecom.Call call : conferenceableCalls) {
+                        PhoneAccountHandle phHandle = call.getDetails().getAccountHandle();
+                        if ((phHandle != null) && ((Integer.parseInt(phHandle.getId())) == subId)) {
+                            hasConferenceableCall = true;
+                            break;
+                        }
+                    }
+                }
+                if (!hasConferenceableCall &&
+                    ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE
+                            & supportedCapabilities) == 0)) {
+                    // Cannot merge calls if there are no calls to merge with.
+                    return false;
+                }
             // We allow you to merge if the capabilities allow it or if it is a call with
             // conferenceable calls.
-            if (mTelecomCall.getConferenceableCalls().isEmpty() &&
+            } else if (mTelecomCall.getConferenceableCalls().isEmpty() &&
                 ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE
                         & supportedCapabilities) == 0)) {
                 // Cannot merge calls if there are no calls to merge with.
@@ -776,6 +817,22 @@
         return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle();
     }
 
+    public int getSubId() {
+        PhoneAccountHandle ph = getAccountHandle();
+        if (ph != null) {
+            try {
+                if (ph.getId() != null) {
+                    return Integer.parseInt(getAccountHandle().getId());
+                }
+            } catch (NumberFormatException e) {
+                Log.w(this, "sub id is not a number" + e);
+            }
+            return SubscriptionManager.getDefaultVoiceSubscriptionId();
+        } else {
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+    }
+
     /**
      * @return The {@link VideoCall} instance associated with the {@link android.telecom.Call}.
      *      Will return {@code null} until {@link #updateFromTelecomCall()} has registered a valid
@@ -855,7 +912,9 @@
      * repeated calls to isEmergencyNumber.
      */
     private void updateEmergencyCallState() {
-        mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
+        Uri handle = mTelecomCall.getDetails().getHandle();
+        mIsEmergencyCall = QtiCallUtils.isEmergencyNumber
+                (handle == null ? "" : handle.getSchemeSpecificPart());
     }
 
     /**
@@ -898,6 +957,37 @@
         return mSessionModificationState;
     }
 
+    /* QtiImsExtListenerBaseImpl instance to handle call transfer response */
+    private QtiImsExtListenerBaseImpl mQtiImsInterfaceListener =
+            new QtiImsExtListenerBaseImpl() {
+
+        /* Handles call transfer response */
+        @Override
+        public void receiveCallTransferResponse(int result) {
+            Log.w(this, "receiveCallTransferResponse: " + result);
+        }
+    };
+
+    public int getTransferCapabilities() {
+        Bundle extras = getExtras();
+        return (extras == null)? 0 :
+                extras.getInt(QtiImsExtUtils.QTI_IMS_TRANSFER_EXTRA_KEY, 0);
+    }
+
+    public boolean sendCallTransferRequest(int type, String number) {
+        int phoneId = 0;
+        try {
+            Log.d(this, "sendCallTransferRequest: Phoneid-" + phoneId + " type-" + type +
+                    " number: " + number);
+            QtiImsExtManager.getInstance().sendCallTransferRequest(phoneId, type, number,
+                    mQtiImsInterfaceListener);
+        } catch (QtiImsException e) {
+            Log.e(this, "sendCallDeflectRequest exception " + e);
+            return false;
+        }
+        return true;
+    }
+
     public LogState getLogState() {
         return mLogState;
     }
@@ -961,7 +1051,8 @@
         }
 
         return String.format(Locale.US, "[%s, %s, %s, %s, children:%s, parent:%s, " +
-                "conferenceable:%s, videoState:%s, mSessionModificationState:%d, VideoSettings:%s]",
+                "conferenceable:%s, videoState:%s, mSessionModificationState:%d, VideoSettings:%s" +
+                ", mIsActivSub:%b]" ,
                 mId,
                 State.toString(getState()),
                 Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()),
@@ -971,10 +1062,38 @@
                 this.mTelecomCall.getConferenceableCalls(),
                 VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()),
                 mSessionModificationState,
-                getVideoSettings());
+                getVideoSettings(), mIsActiveSub);
     }
 
     public String toSimpleString() {
         return super.toString();
     }
+
+    public boolean isIncomingConfCall() {
+        int callState = getState();
+        if (callState == State.INCOMING || callState == State.CALL_WAITING) {
+            Bundle extras = getExtras();
+            boolean incomingConf = (extras == null)? false :
+                    extras.getBoolean(QtiImsExtUtils.QTI_IMS_INCOMING_CONF_EXTRA_KEY, false);
+            Log.d(this, "isIncomingConfCall = " + incomingConf);
+            return incomingConf;
+        }
+        return false;
+    }
+
+    public int getWifiQuality() {
+        Bundle extras = getExtras();
+        return (extras == null)? QtiCallConstants.VOWIFI_QUALITY_NONE :
+                extras.getInt(QtiCallConstants.VOWIFI_CALL_QUALITY_EXTRA_KEY,
+                QtiCallConstants.VOWIFI_QUALITY_NONE);
+    }
+
+    public void triggerCalcBaseChronometerTime() {
+        mBaseChronometerTime = getConnectTimeMillis() - System.currentTimeMillis()
+                + SystemClock.elapsedRealtime();
+    }
+
+    public long getCallDuration() {
+        return SystemClock.elapsedRealtime() - mBaseChronometerTime;
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index 6b633ea..7694c5f 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -28,7 +28,15 @@
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_TRANSFER_ASSURED;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_TRANSFER_BLIND;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_TRANSFER_CONSULTATIVE;
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_RECORD;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_RXTX_VIDEO_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_RX_VIDEO_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_VO_VIDEO_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_PARTICIPANT;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -40,6 +48,8 @@
 import android.graphics.drawable.StateListDrawable;
 import android.os.Bundle;
 import android.telecom.CallAudioState;
+import android.telecom.VideoProfile;
+import android.telephony.PhoneNumberUtils;
 import android.util.SparseIntArray;
 import android.view.ContextThemeWrapper;
 import android.view.HapticFeedbackConstants;
@@ -53,10 +63,14 @@
 import android.widget.PopupMenu;
 import android.widget.PopupMenu.OnDismissListener;
 import android.widget.PopupMenu.OnMenuItemClickListener;
+import android.widget.Toast;
 
+import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
 import com.android.dialer.R;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 /**
  * Fragment for call control buttons
  */
@@ -81,13 +95,21 @@
         public static final int BUTTON_HOLD = 3;
         public static final int BUTTON_SWAP = 4;
         public static final int BUTTON_UPGRADE_TO_VIDEO = 5;
-        public static final int BUTTON_SWITCH_CAMERA = 6;
-        public static final int BUTTON_DOWNGRADE_TO_AUDIO = 7;
+        public static final int BUTTON_DOWNGRADE_TO_AUDIO = 6;
+        public static final int BUTTON_SWITCH_CAMERA = 7;
         public static final int BUTTON_ADD_CALL = 8;
         public static final int BUTTON_MERGE = 9;
         public static final int BUTTON_PAUSE_VIDEO = 10;
         public static final int BUTTON_MANAGE_VIDEO_CONFERENCE = 11;
-        public static final int BUTTON_COUNT = 12;
+        public static final int BUTTON_TRANSFER_BLIND = 12;
+        public static final int BUTTON_TRANSFER_ASSURED = 13;
+        public static final int BUTTON_TRANSFER_CONSULTATIVE = 14;
+        public static final int BUTTON_RECORD = 15;
+        public static final int BUTTON_RXTX_VIDEO_CALL = 16;
+        public static final int BUTTON_RX_VIDEO_CALL = 17;
+        public static final int BUTTON_VO_VIDEO_CALL = 18;
+        public static final int BUTTON_ADD_PARTICIPANT = 19;
+        public static final int BUTTON_COUNT = 20;
     }
 
     private SparseIntArray mButtonVisibilityMap = new SparseIntArray(BUTTON_COUNT);
@@ -105,6 +127,14 @@
     private CompoundButton mPauseVideoButton;
     private ImageButton mOverflowButton;
     private ImageButton mManageVideoCallConferenceButton;
+    private ImageButton mBlindTransferButton;
+    private ImageButton mAssuredTransferButton;
+    private ImageButton mConsultativeTransferButton;
+    private ImageButton mAddParticipantButton;
+    private ImageButton mRecordButton;
+    private ImageButton mRxTxVideoCallButton;
+    private ImageButton mRxVideoCallButton;
+    private ImageButton mVoVideoCallButton;
 
     private PopupMenu mAudioModePopup;
     private boolean mAudioModePopupVisible;
@@ -168,11 +198,27 @@
         mMergeButton.setOnClickListener(this);
         mPauseVideoButton = (CompoundButton) parent.findViewById(R.id.pauseVideoButton);
         mPauseVideoButton.setOnClickListener(this);
+        mBlindTransferButton = (ImageButton) parent.findViewById(R.id.blindTransfer);
+        mBlindTransferButton.setOnClickListener(this);
+        mAssuredTransferButton = (ImageButton) parent.findViewById(R.id.assuredTransfer);
+        mAssuredTransferButton.setOnClickListener(this);
+        mConsultativeTransferButton = (ImageButton) parent.findViewById(R.id.consultativeTransfer);
+        mConsultativeTransferButton.setOnClickListener(this);
+        mAddParticipantButton = (ImageButton) parent.findViewById(R.id.addParticipant);
+        mAddParticipantButton.setOnClickListener(this);
         mOverflowButton = (ImageButton) parent.findViewById(R.id.overflowButton);
         mOverflowButton.setOnClickListener(this);
         mManageVideoCallConferenceButton = (ImageButton) parent.findViewById(
                 R.id.manageVideoCallConferenceButton);
         mManageVideoCallConferenceButton.setOnClickListener(this);
+        mRecordButton = (ImageButton) parent.findViewById(R.id.recordButton);
+        mRecordButton.setOnClickListener(this);
+        mRxTxVideoCallButton = (ImageButton) parent.findViewById(R.id.rxtxVideoCallButton);
+        mRxTxVideoCallButton.setOnClickListener(this);
+        mRxVideoCallButton = (ImageButton) parent.findViewById(R.id.rxVedioCallButton);
+        mRxVideoCallButton.setOnClickListener(this);
+        mVoVideoCallButton = (ImageButton) parent.findViewById(R.id.volteCallButton);
+        mVoVideoCallButton.setOnClickListener(this);
         return parent;
     }
 
@@ -214,6 +260,8 @@
             getPresenter().swapClicked();
         } else if (id == R.id.dialpadButton) {
             getPresenter().showDialpadClicked(!mShowDialpadButton.isSelected());
+        } else if (id == R.id.addParticipant) {
+            getPresenter().addParticipantClicked();
         } else if (id == R.id.changeToVideoButton) {
             getPresenter().changeToVideoClicked();
         } else if (id == R.id.changeToVoiceButton) {
@@ -224,12 +272,33 @@
         } else if (id == R.id.pauseVideoButton) {
             getPresenter().pauseVideoClicked(
                     !mPauseVideoButton.isSelected() /* pause */);
+        } else if (id == R.id.blindTransfer) {
+            getPresenter().callTransferClicked(QtiImsExtUtils.QTI_IMS_BLIND_TRANSFER);
+        } else if (id == R.id.assuredTransfer) {
+            getPresenter().callTransferClicked(QtiImsExtUtils.QTI_IMS_ASSURED_TRANSFER);
+        } else if (id == R.id.consultativeTransfer) {
+            getPresenter().callTransferClicked(QtiImsExtUtils.QTI_IMS_CONSULTATIVE_TRANSFER);
         } else if (id == R.id.overflowButton) {
             if (mOverflowPopup != null) {
+                updateRecordMenu();
                 mOverflowPopup.show();
             }
         } else if (id == R.id.manageVideoCallConferenceButton) {
             onManageVideoCallConferenceClicked();
+        } else if (id == R.id.recordButton) {
+            if (!((InCallActivity) getActivity()).isCallRecording()) {
+                ((InCallActivity) getActivity()).startInCallRecorder();
+                mRecordButton.setBackgroundResource(R.drawable.btn_stop_record);
+            } else {
+                ((InCallActivity) getActivity()).stopInCallRecorder();
+                mRecordButton.setBackgroundResource(R.drawable.btn_start_record);
+            }
+        } else if(id == R.id.rxtxVideoCallButton){
+            getPresenter().changeToVideo(VideoProfile.STATE_BIDIRECTIONAL);
+        } else if(id == R.id.rxVedioCallButton){
+            getPresenter().changeToVideo(VideoProfile.STATE_RX_ENABLED);
+        } else if(id == R.id.volteCallButton){
+            getPresenter().changeToVideo(VideoProfile.STATE_AUDIO_ONLY);
         } else {
             Log.wtf(this, "onClick: unexpected");
             return;
@@ -240,6 +309,14 @@
                 HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
     }
 
+    private void updateRecordMenu() {
+        MenuItem item = mOverflowPopup.getMenu().findItem(BUTTON_RECORD);
+        if (item != null) {
+            item.setTitle(((InCallActivity) getActivity()).isCallRecording() ?
+                    R.string.menu_stop_record : R.string.menu_start_record);
+        }
+    }
+
     public void updateColors() {
         MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
 
@@ -247,7 +324,7 @@
             return;
         }
 
-        View[] compoundButtons = {
+        CompoundButton[] compoundButtons = {
                 mAudioButton,
                 mMuteButton,
                 mShowDialpadButton,
@@ -256,10 +333,18 @@
                 mPauseVideoButton
         };
 
-        for (View button : compoundButtons) {
+        for (CompoundButton button : compoundButtons) {
+            // Before applying background color, uncheck the button and re apply the
+            // saved checked state after background is changed. This is to fix
+            // an issue where button checked state is displayed wrongly after updating colors.
+            boolean isChecked = button.isChecked();
+            if (isChecked) Log.d(this, "updateColors: button:" + button + " is in checked state");
+            button.setChecked(false);
             final LayerDrawable layers = (LayerDrawable) button.getBackground();
             final RippleDrawable btnCompoundDrawable = compoundBackgroundDrawable(themeColors);
             layers.setDrawableByLayerId(R.id.compoundBackgroundItem, btnCompoundDrawable);
+            button.setChecked(isChecked);
+            button.requestLayout();
         }
 
         ImageButton[] normalButtons = {
@@ -268,6 +353,9 @@
                 mChangeToVoiceButton,
                 mAddCallButton,
                 mMergeButton,
+                mBlindTransferButton,
+                mAssuredTransferButton,
+                mConsultativeTransferButton,
                 mOverflowButton
         };
 
@@ -275,6 +363,7 @@
             final LayerDrawable layers = (LayerDrawable) button.getBackground();
             final RippleDrawable btnDrawable = backgroundDrawable(themeColors);
             layers.setDrawableByLayerId(R.id.backgroundItem, btnDrawable);
+            button.requestLayout();
         }
 
         mCurrentThemeColors = themeColors;
@@ -360,8 +449,16 @@
         mAddCallButton.setEnabled(isEnabled);
         mMergeButton.setEnabled(isEnabled);
         mPauseVideoButton.setEnabled(isEnabled);
+        mBlindTransferButton.setEnabled(isEnabled);
+        mAssuredTransferButton.setEnabled(isEnabled);
+        mConsultativeTransferButton.setEnabled(isEnabled);
         mOverflowButton.setEnabled(isEnabled);
         mManageVideoCallConferenceButton.setEnabled(isEnabled);
+        mAddParticipantButton.setEnabled(isEnabled);
+        mRecordButton.setEnabled(isEnabled);
+        mRxTxVideoCallButton.setEnabled(isEnabled);
+        mRxVideoCallButton.setEnabled(isEnabled);
+        mVoVideoCallButton.setEnabled(isEnabled);
     }
 
     @Override
@@ -396,12 +493,28 @@
             return mSwitchCameraButton;
         } else if (id == BUTTON_ADD_CALL) {
             return mAddCallButton;
+        } else if (id == BUTTON_ADD_PARTICIPANT) {
+            return mAddParticipantButton;
         } else if (id == BUTTON_MERGE) {
             return mMergeButton;
         } else if (id == BUTTON_PAUSE_VIDEO) {
             return mPauseVideoButton;
         } else if (id == BUTTON_MANAGE_VIDEO_CONFERENCE) {
             return mManageVideoCallConferenceButton;
+        } else if (id == BUTTON_TRANSFER_BLIND) {
+            return mBlindTransferButton;
+        } else if (id == BUTTON_TRANSFER_ASSURED) {
+            return mAssuredTransferButton;
+        } else if (id == BUTTON_TRANSFER_CONSULTATIVE) {
+            return mConsultativeTransferButton;
+        } else if (id == BUTTON_RECORD) {
+            return mRecordButton;
+        } else if (id == BUTTON_RXTX_VIDEO_CALL) {
+            return mRxTxVideoCallButton;
+        } else if (id == BUTTON_RX_VIDEO_CALL) {
+            return mRxVideoCallButton;
+        } else if (id == BUTTON_VO_VIDEO_CALL) {
+            return mVoVideoCallButton;
         } else {
             Log.w(this, "Invalid button id");
             return null;
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index defafda..8af8835 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -26,17 +26,29 @@
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_TRANSFER_ASSURED;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_TRANSFER_BLIND;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_TRANSFER_CONSULTATIVE;
 import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_RECORD;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_RXTX_VIDEO_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_RX_VIDEO_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_VO_VIDEO_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_PARTICIPANT;
 
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
 import android.telecom.CallAudioState;
 import android.telecom.InCallService.VideoCall;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
+import android.widget.Toast;
 
 import com.android.contacts.common.compat.CallSdkCompat;
 import com.android.contacts.common.compat.SdkVersionOverride;
+import com.android.dialer.util.PresenceHelper;
 import com.android.dialer.compat.UserManagerCompat;
 import com.android.incallui.AudioModeProvider.AudioModeListener;
 import com.android.incallui.InCallCameraManager.Listener;
@@ -46,12 +58,14 @@
 import com.android.incallui.InCallPresenter.InCallStateListener;
 import com.android.incallui.InCallPresenter.IncomingCallListener;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 /**
  * Logic for call buttons.
  */
 public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi>
         implements InCallStateListener, AudioModeListener, IncomingCallListener,
-        InCallDetailsListener, CanAddCallListener, Listener {
+        InCallDetailsListener, CanAddCallListener, CallList.ActiveSubChangeListener, Listener {
 
     private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
     private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
@@ -59,6 +73,12 @@
     private Call mCall;
     private boolean mAutomaticallyMuted = false;
     private boolean mPreviousMuteState = false;
+    private static final int MAX_PARTICIPANTS_LIMIT = 6;
+    private boolean mEnhanceEnable = false;
+
+    // NOTE: Capability constant definition has been duplicated to avoid bundling
+    // the Dialer with Frameworks.
+    private static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
 
     public CallButtonPresenter() {
     }
@@ -67,6 +87,8 @@
     public void onUiReady(CallButtonUi ui) {
         super.onUiReady(ui);
 
+        mEnhanceEnable = ui.getContext().getResources().getBoolean(
+                R.bool.config_enable_enhance_video_call_ui);
         AudioModeProvider.getInstance().addListener(this);
 
         // register for call state changes last
@@ -76,6 +98,7 @@
         inCallPresenter.addDetailsListener(this);
         inCallPresenter.addCanAddCallListener(this);
         inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
+        CallList.getInstance().addActiveSubChangeListener(this);
 
         // Update the buttons state immediately for the current call
         onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(),
@@ -92,6 +115,7 @@
         InCallPresenter.getInstance().removeDetailsListener(this);
         InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
         InCallPresenter.getInstance().removeCanAddCallListener(this);
+        CallList.getInstance().removeActiveSubChangeListener(this);
     }
 
     @Override
@@ -244,15 +268,45 @@
     }
 
     public void mergeClicked() {
+        if (getUi().getContext().getResources().getBoolean(
+                R.bool.add_multi_participants_enabled)){
+            int participantsCount = 0;
+            if (mCall.isConferenceCall()) {
+                participantsCount = mCall.getChildCallIds().size();
+            } else {
+                Call backgroundCall = CallList.getInstance().getBackgroundCall();
+                if (backgroundCall != null && backgroundCall.isConferenceCall()) {
+                    participantsCount = backgroundCall.getChildCallIds().size();
+                }
+            }
+            Log.i(this, "Number of participantsCount is " + participantsCount);
+            if (participantsCount >= MAX_PARTICIPANTS_LIMIT) {
+                Toast.makeText(getUi().getContext(),
+                        R.string.too_many_recipients, Toast.LENGTH_SHORT).show();
+                return;
+            }
+         }
         TelecomAdapter.getInstance().merge(mCall.getId());
+        InCallAudioManager.getInstance().onMergeClicked();
+    }
+
+    public void addParticipantClicked() {
+        if (getUi().getContext().getResources().getBoolean(
+                R.bool.add_multi_participants_enabled)){
+            InCallPresenter.getInstance().sendAddMultiParticipantsIntent();
+            return;
+        }
+        InCallPresenter.getInstance().sendAddParticipantIntent();
     }
 
     public void addCallClicked() {
-        // Automatically mute the current call
-        mAutomaticallyMuted = true;
-        mPreviousMuteState = AudioModeProvider.getInstance().getMute();
-        // Simulate a click on the mute button
-        muteClicked(true);
+        if (!QtiImsExtUtils.isCarrierOneSupported()) {
+            // Automatically mute the current call
+            mAutomaticallyMuted = true;
+            mPreviousMuteState = AudioModeProvider.getInstance().getMute();
+            // Simulate a click on the mute button
+            muteClicked(true);
+        }
         TelecomAdapter.getInstance().addCall();
     }
 
@@ -264,6 +318,11 @@
 
         VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_AUDIO_ONLY);
         videoCall.sendSessionModifyRequest(videoProfile);
+
+        if (QtiCallUtils.useCustomVideoUi(getUi().getContext())) {
+            InCallAudioManager.getInstance().onModifyCallClicked(mCall,
+                    VideoProfile.STATE_AUDIO_ONLY);
+        }
     }
 
     public void showDialpadClicked(boolean checked) {
@@ -272,6 +331,12 @@
     }
 
     public void changeToVideoClicked() {
+        final Context context = getUi().getContext();
+        if (QtiCallUtils.useExt(context)) {
+            QtiCallUtils.displayModifyCallOptions(mCall, context);
+            return;
+        }
+
         VideoCall videoCall = mCall.getVideoCall();
         if (videoCall == null) {
             return;
@@ -283,8 +348,28 @@
         VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
         videoCall.sendSessionModifyRequest(videoProfile);
         mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
+
+        if (QtiCallUtils.useCustomVideoUi(context)) {
+            InCallAudioManager.getInstance().onModifyCallClicked(mCall,
+                    currUnpausedVideoState);
+        }
     }
 
+    public void changeToVideo(int videoState) {
+        final Context context = getUi().getContext();
+        if(mCall == null) {
+            return;
+        }
+
+        if(VideoProfile.isVideo(videoState) &&
+                !PresenceHelper.getVTCapability(mCall.getNumber())) {
+            Toast.makeText(context,context.getString(R.string.video_call_cannot_upgrade),
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
+        final VideoProfile videoProfile = new VideoProfile(videoState);
+        QtiCallUtils.changeToVideoCall(mCall, videoProfile, context);
+    }
     /**
      * Switches the camera between the front-facing and back-facing camera.
      * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or
@@ -340,6 +425,28 @@
         getUi().setVideoPaused(pause);
     }
 
+    public void callTransferClicked(int type) {
+        String number = null;
+        Context mContext = getUi().getContext();
+        if (type != QtiImsExtUtils.QTI_IMS_CONSULTATIVE_TRANSFER) {
+            /**
+             * Since there are no editor options available to provide a number during
+             * blind or assured transfer, for now, making use of the existing
+             * call deflection editor to provide the required number.
+             */
+            number = QtiImsExtUtils.getCallDeflectNumber(mContext.getContentResolver());
+            if (number == null) {
+                 QtiCallUtils.displayToast(mContext, R.string.qti_ims_transfer_num_error);
+                return;
+            }
+        }
+
+        boolean status = mCall.sendCallTransferRequest(type, number);
+        if (!status) {
+            QtiCallUtils.displayToast(mContext, R.string.qti_ims_transfer_request_error);
+        }
+    }
+
     private void updateUi(InCallState state, Call call) {
         Log.d(this, "Updating call UI for call: ", call);
 
@@ -384,9 +491,53 @@
                 && UserManagerCompat.isUserUnlocked(ui.getContext());
         final boolean showMerge = call.can(
                 android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
-        final boolean showUpgradeToVideo = !isVideo && hasVideoCallCapabilities(call);
+        final boolean useExt = QtiCallUtils.useExt(ui.getContext());
+        final boolean useCustomVideoUi =
+                QtiCallUtils.useCustomVideoUi(ui.getContext());
+        final boolean isCallActive = call.getState() == Call.State.ACTIVE;
+
+        final boolean showUpgradeToVideo =
+                /* When useExt is true, show upgrade button for an active/held
+                   call if the call has either voice or video capabilities */
+                ((useExt && QtiCallUtils.hasVoiceOrVideoCapabilities(call)) ||
+                /* When useCustomVideoUi is true, show upgrade button for an active/held
+                   voice call only if the current call has video capabilities */
+                (useCustomVideoUi && !isVideo && hasVideoCallCapabilities(call))
+                && (isCallActive || isCallOnHold)) ||
+                /* When useExt and custom UI are false, default to Google behaviour */
+                (!isVideo && !useExt && !useCustomVideoUi && hasVideoCallCapabilities(call));
+
         final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
+        final int callState = call.getState();
+
+        final boolean showRecord = ((callState == Call.State.ACTIVE
+                || callState == Call.State.ONHOLD)
+                && (ui.getContext().getResources().getBoolean(R.bool.enable_call_record)));
+
         final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
+        int callTransferCapabilities = call.isEmergencyCall()? 0 : call.getTransferCapabilities();
+        boolean showAddParticipant = call.can(CAPABILITY_ADD_PARTICIPANT);
+        if (ui.getContext().getResources().getBoolean(
+            R.bool.add_participant_only_in_conference)) {
+            showAddParticipant = showAddParticipant&&(call.isConferenceCall());
+        }
+
+        boolean showRxTx = false;
+        boolean showRx = false;
+        boolean showVolte = false;
+
+        if (mEnhanceEnable && hasVideoCallCapabilities(call)) {
+            boolean isAudioAndVtCap = (VideoProfile.isAudioOnly(mCall.getVideoState()) &&
+                    PresenceHelper.getVTCapability(call.getNumber()));
+            showRxTx = ((VideoProfile.isReceptionEnabled(mCall.getVideoState()) &&
+                    !VideoProfile.isBidirectional(mCall.getVideoState())) || isAudioAndVtCap);
+            //"hide me" show be show if call is video call or voice call only, "hide me"
+            //is mean that call can upgrade to Rx video call for voice call only.
+            showRx = (VideoProfile.isBidirectional(mCall.getVideoState()) || isAudioAndVtCap);
+            showVolte = VideoProfile.isVideo(mCall.getVideoState());
+            Log.v(this, "updateButtonsState showRxTx = " + showRxTx +
+                    " showRx" + showRx + " showVolte = " + showVolte);
+        }
 
         ui.showButton(BUTTON_AUDIO, true);
         ui.showButton(BUTTON_SWAP, showSwap);
@@ -394,17 +545,42 @@
         ui.setHold(isCallOnHold);
         ui.showButton(BUTTON_MUTE, showMute);
         ui.showButton(BUTTON_ADD_CALL, showAddCall);
-        ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
-        ui.showButton(BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
+        ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo && !mEnhanceEnable);
+        ui.showButton(BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio && !useExt);
         ui.showButton(BUTTON_SWITCH_CAMERA, isVideo);
-        ui.showButton(BUTTON_PAUSE_VIDEO, isVideo);
+        ui.showButton(BUTTON_PAUSE_VIDEO, isVideo && !useExt && !useCustomVideoUi &&
+                !mEnhanceEnable);
         if (isVideo) {
             getUi().setVideoPaused(!VideoUtils.isTransmissionEnabled(call));
         }
         ui.showButton(BUTTON_DIALPAD, true);
         ui.showButton(BUTTON_MERGE, showMerge);
+        ui.showButton(BUTTON_ADD_PARTICIPANT, showAddParticipant && !mEnhanceEnable);
+        ui.showButton(BUTTON_RECORD, showRecord);
+
+        /* Depending on the transfer capabilities, display the corresponding buttons */
+        if ((callTransferCapabilities & QtiImsExtUtils.QTI_IMS_CONSULTATIVE_TRANSFER) != 0) {
+            ui.showButton(BUTTON_TRANSFER_BLIND, true);
+            ui.showButton(BUTTON_TRANSFER_ASSURED, true);
+            ui.showButton(BUTTON_TRANSFER_CONSULTATIVE, true);
+        } else if ((callTransferCapabilities & QtiImsExtUtils.QTI_IMS_BLIND_TRANSFER) != 0) {
+            ui.showButton(BUTTON_TRANSFER_BLIND, true);
+            ui.showButton(BUTTON_TRANSFER_ASSURED, true);
+            ui.showButton(BUTTON_TRANSFER_CONSULTATIVE, false);
+        } else {
+            ui.showButton(BUTTON_TRANSFER_BLIND, false);
+            ui.showButton(BUTTON_TRANSFER_ASSURED, false);
+            ui.showButton(BUTTON_TRANSFER_CONSULTATIVE, false);
+        }
+        if (mEnhanceEnable) {
+            Log.v(this, "Add three new buttons");
+            ui.showButton(BUTTON_RXTX_VIDEO_CALL, showRxTx);
+            ui.showButton(BUTTON_RX_VIDEO_CALL, showRx);
+            ui.showButton(BUTTON_VO_VIDEO_CALL, showVolte);
+        }
 
         ui.updateButtonStates();
+        ui.updateColors();
     }
 
     private boolean hasVideoCallCapabilities(Call call) {
@@ -474,6 +650,7 @@
          */
         void updateButtonStates();
         Context getContext();
+        public void updateColors();
     }
 
     @Override
@@ -483,4 +660,16 @@
         }
         getUi().setCameraSwitched(!isUsingFrontFacingCamera);
     }
+
+    public void onActiveSubChanged(int subId) {
+        InCallState state = InCallPresenter.getInstance()
+                .getPotentialStateFromCallList(CallList.getInstance());
+
+        onStateChange(null, state, CallList.getInstance());
+        Log.d(this, "onActiveSubChanged");
+        CallButtonUi ui = getUi();
+        if (ui != null) {
+            ui.updateColors();
+        }
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 39dd5ea..1c4890b 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -20,25 +20,38 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.graphics.PorterDuff;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.Trace;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.content.ContentResolver;
+import android.media.AudioManager;
+import android.provider.Settings;
 import android.telecom.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
@@ -57,6 +70,8 @@
 import android.widget.Toast;
 
 import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import android.telecom.CallAudioState;
+
 import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
 import com.android.contacts.common.widget.FloatingActionButtonController;
 import com.android.dialer.R;
@@ -112,6 +127,11 @@
      */
     private static final long ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS = 500;
 
+    private static final String RECORD_STATE_CHANGED =
+            "com.qualcomm.qti.phonefeature.RECORD_STATE_CHANGED";
+
+    private static final int MESSAGE_TIMER = 1;
+
     private AnimatorSet mAnimatorSet;
     private int mShrinkAnimationDuration;
     private int mFabNormalDiameter;
@@ -143,6 +163,8 @@
     private ViewGroup mPrimaryCallInfo;
     private View mCallButtonsContainer;
     private ImageView mPhotoSmall;
+    private ImageButton mVbButton;
+    private AudioManager mAudioManager;
 
     // Secondary caller info
     private View mSecondaryCallInfo;
@@ -166,6 +188,10 @@
     // Dark number info bar
     private TextView mInCallMessageLabel;
 
+    private InCallActivity mInCallActivity;
+    private TextView mRecordingTimeLabel;
+    private TextView mRecordingIcon;
+
     private FloatingActionButtonController mFloatingActionButtonController;
     private View mFloatingActionButtonContainer;
     private ImageButton mFloatingActionButton;
@@ -187,11 +213,61 @@
     private boolean mCallStateLabelResetPending = false;
     private Handler mHandler;
 
+    private BroadcastReceiver recorderStateReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!RECORD_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+
+            if (mInCallActivity.isCallRecording()) {
+                recorderHandler.sendEmptyMessage(MESSAGE_TIMER);
+            } else {
+                mRecordingTimeLabel.setVisibility(View.GONE);
+                mRecordingIcon.setVisibility(View.GONE);
+            }
+        }
+    };
+
+    private Handler recorderHandler = new Handler() {
+
+        @Override
+        public void handleMessage(Message msg) {
+
+            switch (msg.what) {
+                case MESSAGE_TIMER:
+                    if (!mInCallActivity.isCallRecording()) {
+                        break;
+                    }
+
+                    String recordingTime = mInCallActivity.getCallRecordingTime();
+
+                    if (!TextUtils.isEmpty(recordingTime)) {
+                        mRecordingTimeLabel.setVisibility(View.VISIBLE);
+                        showCallRecordingElapsedTime(recordingTime);
+                        mRecordingIcon.setVisibility(View.VISIBLE);
+                    }
+
+                    if (!recorderHandler.hasMessages(MESSAGE_TIMER)) {
+                        sendEmptyMessageDelayed(MESSAGE_TIMER, 1000);
+                    }
+
+                    break;
+            }
+        }
+    };
+
     /**
      * Determines if secondary call info is populated in the secondary call info UI.
      */
     private boolean mHasSecondaryCallInfo = false;
 
+    private static final int TTY_MODE_OFF = 0;
+    private static final int TTY_MODE_HCO = 2;
+
+    private static final String VOLUME_BOOST = "volume_boost";
+
     @Override
     public CallCardPresenter.CallCardUi getUi() {
         return this;
@@ -219,6 +295,21 @@
         if (savedInstanceState != null) {
             mIsDialpadShowing = savedInstanceState.getBoolean(IS_DIALPAD_SHOWING_KEY, false);
         }
+        mAudioManager = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(RECORD_STATE_CHANGED);
+        getActivity().registerReceiver(recorderStateReceiver, filter);
+
+        mInCallActivity = (InCallActivity) getActivity();
+        if (mInCallActivity.isCallRecording()) {
+            recorderHandler.sendEmptyMessage(MESSAGE_TIMER);
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        getActivity().unregisterReceiver(recorderStateReceiver);
     }
 
     @Override
@@ -334,6 +425,21 @@
         mPrimaryName.setElegantTextHeight(false);
         mCallStateLabel.setElegantTextHeight(false);
         mCallSubject = (TextView) view.findViewById(R.id.callSubject);
+
+        mVbButton = (ImageButton) view.findViewById(R.id.volumeBoost);
+        if (null != mVbButton) {
+            mVbButton.setOnClickListener(mVbListener);
+        }
+        mRecordingTimeLabel = (TextView) view.findViewById(R.id.recordingTime);
+        mRecordingIcon = (TextView) view.findViewById(R.id.recordingIcon);
+    }
+
+    private void showCallRecordingElapsedTime(String time) {
+        if (mRecordingTimeLabel.getVisibility() != View.VISIBLE) {
+            AnimUtils.fadeIn(mRecordingTimeLabel, AnimUtils.DEFAULT_DURATION);
+        }
+
+        mRecordingTimeLabel.setText(time);
     }
 
     @Override
@@ -399,6 +505,8 @@
         // Determine how much space there is below or to the side of the call card.
         final float spaceBesideCallCard = getSpaceBesideCallCard();
 
+        doActionOnPredraw(visible, isLayoutRtl, videoView, spaceBesideCallCard);
+
         // We need to translate the video surface, but we need to know its position after the layout
         // has occurred so use a {@code ViewTreeObserver}.
         final ViewTreeObserver observer = getView().getViewTreeObserver();
@@ -408,69 +516,73 @@
                 // We don't want to continue getting called.
                 getView().getViewTreeObserver().removeOnPreDrawListener(this);
 
-                float videoViewTranslation = 0f;
-
-                // Translate the call card to its pre-animation state.
-                if (!mIsLandscape) {
-                    mPrimaryCallCardContainer.setTranslationY(visible ?
-                            -mPrimaryCallCardContainer.getHeight() : 0);
-
-                    ViewGroup.LayoutParams p = videoView.getLayoutParams();
-                    videoViewTranslation = p.height / 2 - spaceBesideCallCard / 2;
-                }
-
-                // Perform animation of video view.
-                ViewPropertyAnimator videoViewAnimator = videoView.animate()
-                        .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
-                        .setDuration(mVideoAnimationDuration);
-                if (mIsLandscape) {
-                    videoViewAnimator
-                            .translationX(visible ? videoViewTranslation : 0);
-                } else {
-                    videoViewAnimator
-                            .translationY(visible ? videoViewTranslation : 0);
-                }
-                videoViewAnimator.start();
-
-                // Animate the call card sliding.
-                ViewPropertyAnimator callCardAnimator = mPrimaryCallCardContainer.animate()
-                        .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
-                        .setDuration(mVideoAnimationDuration)
-                        .setListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                super.onAnimationEnd(animation);
-                                if (!visible) {
-                                    mPrimaryCallCardContainer.setVisibility(View.GONE);
-                                }
-                            }
-
-                            @Override
-                            public void onAnimationStart(Animator animation) {
-                                super.onAnimationStart(animation);
-                                if (visible) {
-                                    mPrimaryCallCardContainer.setVisibility(View.VISIBLE);
-                                }
-                            }
-                        });
-
-                if (mIsLandscape) {
-                    float translationX = mPrimaryCallCardContainer.getWidth();
-                    translationX *= isLayoutRtl ? 1 : -1;
-                    callCardAnimator
-                            .translationX(visible ? 0 : translationX)
-                            .start();
-                } else {
-                    callCardAnimator
-                            .translationY(visible ? 0 : -mPrimaryCallCardContainer.getHeight())
-                            .start();
-                }
-
+                doActionOnPredraw(visible, isLayoutRtl, videoView, spaceBesideCallCard);
                 return true;
             }
         });
     }
 
+    private void doActionOnPredraw(final boolean visible, final boolean isLayoutRtl,
+         final View videoView, final float spaceBesideCallCard) {
+        float videoViewTranslation = 0f;
+
+        // Translate the call card to its pre-animation state.
+        if (!mIsLandscape) {
+            mPrimaryCallCardContainer.setTranslationY(visible ?
+                    -mPrimaryCallCardContainer.getHeight() : 0);
+
+            ViewGroup.LayoutParams p = videoView.getLayoutParams();
+            videoViewTranslation = p.height / 2 - spaceBesideCallCard / 2;
+        }
+
+        // Perform animation of video view.
+        ViewPropertyAnimator videoViewAnimator = videoView.animate()
+                .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+                .setDuration(mVideoAnimationDuration);
+        if (mIsLandscape) {
+            videoViewAnimator
+                    .translationX(visible ? videoViewTranslation : 0);
+        } else {
+            videoViewAnimator
+                    .translationY(visible ? videoViewTranslation : 0);
+        }
+        videoViewAnimator.start();
+
+        // Animate the call card sliding.
+        ViewPropertyAnimator callCardAnimator = mPrimaryCallCardContainer.animate()
+                .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+                .setDuration(mVideoAnimationDuration)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        super.onAnimationEnd(animation);
+                        if (!visible) {
+                            mPrimaryCallCardContainer.setVisibility(View.GONE);
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        super.onAnimationStart(animation);
+                        if (visible) {
+                            mPrimaryCallCardContainer.setVisibility(View.VISIBLE);
+                        }
+                    }
+                });
+
+        if (mIsLandscape) {
+            float translationX = mPrimaryCallCardContainer.getWidth();
+            translationX *= isLayoutRtl ? 1 : -1;
+            callCardAnimator
+                    .translationX(visible ? 0 : translationX)
+                    .start();
+        } else {
+            callCardAnimator
+                    .translationY(visible ? 0 : -mPrimaryCallCardContainer.getHeight())
+                    .start();
+        }
+    }
+
     /**
      * Determines the amount of space below the call card for portrait layouts), or beside the
      * call card for landscape layouts.
@@ -638,11 +750,17 @@
     public void setSecondaryInfoVisible(final boolean visible) {
         boolean wasVisible = mSecondaryCallInfo.isShown();
         final boolean isVisible = visible && mHasSecondaryCallInfo;
+        // If hide request is coming when InCallUI is in background, force the view to hide.
+        final boolean needForceHide = !isVisible &&
+                mSecondaryCallInfo.getVisibility() == View.VISIBLE &&
+                (!InCallPresenter.getInstance().isShowingInCallUi() ||
+                InCallPresenter.getInstance().isShowingManageConferenceUi());
         Log.v(this, "setSecondaryInfoVisible: wasVisible = " + wasVisible + " isVisible = "
-                + isVisible);
+                 + isVisible + " isFg = " + InCallPresenter.getInstance().isShowingInCallUi()
+                 + " view visibility = " + mSecondaryCallInfo.getVisibility());
 
         // If visibility didn't change, nothing to do.
-        if (wasVisible == isVisible) {
+        if (wasVisible == isVisible && !needForceHide) {
             return;
         }
 
@@ -728,6 +846,8 @@
                 sessionModificationState, disconnectCause, connectionLabel, isGatewayCall, isWifi,
                 isConference, isWorkCall);
 
+        updateVbByCall(state);
+
         Log.v(this, "setCallState " + callStateLabel.getCallStateLabel());
         Log.v(this, "AutoDismiss " + callStateLabel.isAutoDismissing());
         Log.v(this, "DisconnectCause " + disconnectCause.toString());
@@ -742,9 +862,9 @@
         // Check if the call subject is showing -- if it is, we want to bypass showing the call
         // state.
         boolean isSubjectShowing = mCallSubject.getVisibility() == View.VISIBLE;
-
+        boolean isSubChanged = (callStateIcon !=  mCallStateIcon.getDrawable());
         if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText()) &&
-                !isSubjectShowing) {
+                !isSubjectShowing && !isSubChanged) {
             // Nothing to do if the labels are the same
             if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
                 mCallStateLabel.clearAnimation();
@@ -778,6 +898,18 @@
             mCallStateIcon.setAlpha(1.0f);
             mCallStateIcon.setImageDrawable(callStateIcon);
 
+            MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
+            if (themeColors != null) {
+                // Change the alpha value in the 32 bit color of sim card, because the color of
+                // call background changed with the color of sim card.
+                // Set the tint mode to SCREEN to avoid the slot number in the sim icon to be
+                // covered.
+                int stateIconColor = (themeColors.mPrimaryColor & 0x00ffffff) | 0x7f000000;
+                mCallStateIcon.setImageTintMode(PorterDuff.Mode.SCREEN);
+                mCallStateIcon.setImageTintList(ColorStateList.valueOf(stateIconColor));
+                Log.d(this, "Set tint of call state icon to " + stateIconColor);
+            }
+
             if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED
                     || TextUtils.isEmpty(callStateLabel.getCallStateLabel())) {
                 mCallStateIcon.clearAnimation();
@@ -788,6 +920,9 @@
             if (callStateIcon instanceof AnimationDrawable) {
                 ((AnimationDrawable) callStateIcon).start();
             }
+            if (isSubChanged) {
+                updateColors();
+            }
         } else {
             mCallStateIcon.clearAnimation();
 
@@ -796,6 +931,7 @@
             mCallStateIcon.setAlpha(0.0f);
             mCallStateIcon.setVisibility(View.GONE);
         }
+        mCallStateIcon.requestLayout();
 
         if (VideoUtils.isVideoCall(videoState)
                 || (state == Call.State.ACTIVE && sessionModificationState
@@ -1033,9 +1169,7 @@
             case Call.State.ACTIVE:
                 // We normally don't show a "call state label" at all in this state
                 // (but we can use the call state label to display the provider name).
-                if ((isAccount || isWifi || isConference) && hasSuggestedLabel) {
-                    callStateLabel = label;
-                } else if (sessionModificationState
+                if (sessionModificationState
                         == Call.SessionModificationState.REQUEST_REJECTED) {
                     callStateLabel = context.getString(R.string.card_title_video_call_rejected);
                     isAutoDismissing = true;
@@ -1052,6 +1186,11 @@
                 } else if (VideoUtils.isVideoCall(videoState)) {
                     callStateLabel = context.getString(R.string.card_title_video_call);
                 }
+
+                if ((isAccount || isWifi || isConference) && hasSuggestedLabel) {
+                   label += (callStateLabel != null) ? (" " + callStateLabel) : "";
+                   callStateLabel = label;
+                }
                 break;
             case Call.State.ONHOLD:
                 callStateLabel = context.getString(R.string.card_title_on_hold);
@@ -1069,7 +1208,17 @@
                 break;
             case Call.State.INCOMING:
             case Call.State.CALL_WAITING:
-                if (isWifi && hasSuggestedLabel) {
+                if (isConference) {
+                    if (isAccount) {
+                        callStateLabel = context.getString(
+                                R.string.incoming_conf_via_template, label);
+                    } else if (VideoUtils.isVideoCall(videoState)) {
+                        callStateLabel = context.getString(
+                                R.string.card_title_incoming_video_conf_call);
+                    } else {
+                        callStateLabel = context.getString(R.string.card_title_incoming_conf_call);
+                    }
+                } else if (isWifi && hasSuggestedLabel) {
                     callStateLabel = label;
                 } else if (isAccount) {
                     callStateLabel = context.getString(R.string.incoming_via_template, label);
@@ -1095,6 +1244,13 @@
                 if (TextUtils.isEmpty(callStateLabel)) {
                     callStateLabel = context.getString(R.string.card_title_call_ended);
                 }
+                if (context.getResources().getBoolean(R.bool.def_incallui_clearcode_enabled)) {
+                    String clearText = disconnectCause.getDescription() == null ? ""
+                            : disconnectCause.getDescription().toString();
+                    if (!TextUtils.isEmpty(clearText)) {
+                        Toast.makeText(context, clearText, Toast.LENGTH_SHORT).show();
+                    }
+                }
                 break;
             case Call.State.CONFERENCED:
                 callStateLabel = context.getString(R.string.card_title_conf_call);
@@ -1203,6 +1359,11 @@
      */
     @Override
     public void showHdAudioIndicator(boolean visible) {
+        int subId = CallList.getInstance().getActiveSubId();
+        if (SubscriptionManager.getResourcesForSubId(getContext(), subId)
+                .getBoolean(R.bool.config_show_hd2)) {
+            mHdAudioIcon.setImageResource(R.drawable.ic_hd2_24dp);
+        }
         mHdAudioIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
@@ -1331,6 +1492,7 @@
                 animator.addListener(new AnimatorListenerAdapter() {
                     @Override
                     public void onAnimationEnd(Animator animation) {
+                        updateFabPosition();
                         mPrimaryCallCardContainer.setTag(R.id.view_tag_callcard_actual_height,
                                 null);
                         setViewStatePostAnimation(listener);
@@ -1489,4 +1651,105 @@
             v.setBottom(oldBottom);
         }
     }
+
+    private OnClickListener mVbListener = new OnClickListener() {
+        @Override
+        public void onClick(View arg0) {
+            if (isVbAvailable()) {
+                // Switch Volume Boost status
+                setVolumeBoost(!isVolumeBoostOn());
+            }
+
+            updateVbButton();
+            showVbNotify();
+        }
+    };
+
+    private boolean isVbAvailable() {
+        int mode = AudioModeProvider.getInstance().getAudioMode();
+        final Context context = getContext();
+        int settingsTtyMode = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.PREFERRED_TTY_MODE,
+                TTY_MODE_OFF);
+
+        return (mode == CallAudioState.ROUTE_EARPIECE || mode == CallAudioState.ROUTE_SPEAKER
+                || settingsTtyMode == TTY_MODE_HCO);
+    }
+
+    private void updateVbButton() {
+        if (isVbAvailable()) {
+           if (isVolumeBoostOn()) {
+               mVbButton.setBackgroundResource(R.drawable.vb_active);
+           } else {
+               mVbButton.setBackgroundResource(R.drawable.vb_normal);
+           }
+        } else {
+            mVbButton.setBackgroundResource(R.drawable.vb_disable);
+        }
+    }
+
+    @Override
+    public void showVbButton(boolean show) {
+        mVbButton.setVisibility(show ? View.VISIBLE : View.GONE);
+    }
+
+    private void showVbNotify() {
+        Toast vbnotify;
+        int resId = R.string.volume_boost_notify_unavailable;
+
+        if (isVbAvailable()) {
+            if (isVolumeBoostOn()) {
+                resId = R.string.volume_boost_notify_enabled;
+            } else {
+                resId = R.string.volume_boost_notify_disabled;
+            }
+        }
+
+        vbnotify = Toast.makeText(getView().getContext(), resId, Toast.LENGTH_SHORT);
+        vbnotify.setGravity(Gravity.CENTER, 0, 0);
+        vbnotify.show();
+    }
+
+    private void updateVbByCall(int state) {
+        updateVbButton();
+
+        if (Call.State.ACTIVE == state) {
+            mVbButton.setVisibility(View.VISIBLE);
+        } else if (Call.State.DISCONNECTED == state) {
+            if (!CallList.getInstance().hasLiveCall()
+                    && isVolumeBoostOn()) {
+                mVbButton.setVisibility(View.GONE);
+
+                setVolumeBoost(false);
+            }
+        }
+    }
+
+    public void updateVbByAudioMode(int newMode) {
+        if (!(newMode == CallAudioState.ROUTE_EARPIECE
+                || newMode == CallAudioState.ROUTE_BLUETOOTH
+                || newMode == CallAudioState.ROUTE_WIRED_HEADSET
+                || newMode == CallAudioState.ROUTE_SPEAKER)) {
+            return;
+        }
+
+        if (mAudioManager != null && isVolumeBoostOn()) {
+            setVolumeBoost(false);
+        }
+
+        updateVbButton();
+    }
+
+    private void setVolumeBoost(boolean on){
+        if (on)
+            mAudioManager.setParameters(VOLUME_BOOST + "=on");
+        else
+            mAudioManager.setParameters(VOLUME_BOOST + "=off");
+    }
+
+    private boolean isVolumeBoostOn(){
+
+        return mAudioManager.getParameters(VOLUME_BOOST).contains("=on");
+    }
+
 }
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 10bf5e6..4317a5e 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -35,6 +35,8 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.accessibility.AccessibilityManager;
@@ -75,6 +77,11 @@
     private static final String TAG = CallCardPresenter.class.getSimpleName();
     private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000;
 
+    private static final int IDP_NONE = 0;
+    private static final int IDP_CDMA = 1;
+    private static final int IDP_GSM = 2;
+    private static final int IDP_BOTH = 3;
+
     private final EmergencyCallListener mEmergencyCallListener =
             ObjectFactory.newEmergencyCallListener();
     private DistanceHelper mDistanceHelper;
@@ -135,6 +142,18 @@
         });
     }
 
+    private boolean isGeocoderLocationNeeded(Call call) {
+        Log.d(this, "isGeocoderLocationNeeded getState() = " + call.getState());
+        if (call.getState() == Call.State.INCOMING ||
+                call.getState() == Call.State.CALL_WAITING ||
+                call.getState() == Call.State.DIALING ||
+                call.getState() == Call.State.CONNECTING ||
+                call.getState() == Call.State.SELECT_PHONE_ACCOUNT) {
+            return true;
+        };
+        return false;
+    }
+
     public void init(Context context, Call call) {
         mContext = Preconditions.checkNotNull(context);
         mDistanceHelper = ObjectFactory.newDistanceHelper(mContext, this);
@@ -153,7 +172,7 @@
 
             // start processing lookups right away.
             if (!call.isConferenceCall()) {
-                startContactInfoSearch(call, true, call.getState() == Call.State.INCOMING);
+                startContactInfoSearch(call, true, isGeocoderLocationNeeded(call));
             } else {
                 updateContactEntry(null, true);
             }
@@ -273,7 +292,7 @@
             CallList.getInstance().addCallUpdateListener(mPrimary.getId(), this);
 
             mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext, mPrimary,
-                    mPrimary.getState() == Call.State.INCOMING);
+                    isGeocoderLocationNeeded(mPrimary));
             updatePrimaryDisplayInfo();
             maybeStartSearch(mPrimary, true);
             maybeClearSessionModificationState(mPrimary);
@@ -302,6 +321,7 @@
         // Start/stop timers.
         if (isPrimaryCallActive()) {
             Log.d(this, "Starting the calltime timer");
+            mPrimary.triggerCalcBaseChronometerTime();
             mCallTimer.start(CALL_TIME_UPDATE_INTERVAL_MS);
         } else {
             Log.d(this, "Canceling the calltime timer");
@@ -360,7 +380,7 @@
      * @param sessionModificationState The new session modification state.
      */
     @Override
-    public void onSessionModificationStateChange(int sessionModificationState) {
+    public void onSessionModificationStateChange(Call call, int sessionModificationState) {
         Log.d(this, "onSessionModificationStateChange : sessionModificationState = " +
                 sessionModificationState);
 
@@ -425,11 +445,13 @@
         return null;
     }
 
-    private void updatePrimaryCallState() {
+    public void updatePrimaryCallState() {
         if (getUi() != null && mPrimary != null) {
             boolean isWorkCall = mPrimary.hasProperty(PROPERTY_ENTERPRISE_CALL)
                     || (mPrimaryContactInfo == null ? false
                             : mPrimaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
+            InCallPresenter.getInstance().setThemeColors();
+            boolean isConfCall = mPrimary.isConferenceCall() || mPrimary.isIncomingConfCall();
             getUi().setCallState(
                     mPrimary.getState(),
                     mPrimary.getVideoState(),
@@ -439,7 +461,7 @@
                     getCallStateIcon(),
                     getGatewayNumber(),
                     mPrimary.hasProperty(Details.PROPERTY_WIFI),
-                    mPrimary.isConferenceCall(),
+                    isConfCall,
                     isWorkCall);
 
             maybeShowHdAudioIcon();
@@ -540,9 +562,7 @@
             ui.setPrimaryCallElapsedTime(false, 0);
             mCallTimer.cancel();
         } else {
-            final long callStart = mPrimary.getConnectTimeMillis();
-            final long duration = System.currentTimeMillis() - callStart;
-            ui.setPrimaryCallElapsedTime(true, duration);
+            ui.setPrimaryCallElapsedTime(true, mPrimary.getCallDuration());
         }
     }
 
@@ -567,7 +587,7 @@
     private void maybeStartSearch(Call call, boolean isPrimary) {
         // no need to start search for conference calls which show generic info.
         if (call != null && !call.isConferenceCall()) {
-            startContactInfoSearch(call, isPrimary, call.getState() == Call.State.INCOMING);
+            startContactInfoSearch(call, isPrimary, isGeocoderLocationNeeded(call));
         }
     }
 
@@ -738,6 +758,82 @@
         return retval;
     }
 
+    private boolean checkIdpEnable(int subId) {
+        boolean ret = false;
+        final int checkIdp = mContext.getResources().getInteger(R.integer.check_idp);
+        final int phoneType = TelephonyManager.getDefault().getCurrentPhoneType(subId);
+
+        if (checkIdp == IDP_BOTH ||
+                (checkIdp == IDP_CDMA && phoneType == TelephonyManager.PHONE_TYPE_CDMA) ||
+                (checkIdp == IDP_GSM && phoneType == TelephonyManager.PHONE_TYPE_GSM)) {
+            ret = true;
+        }
+
+        Log.i(this, "checkIdp phone type:" + phoneType + " enabled:" + ret);
+        return ret;
+    }
+
+    private int getSubId() {
+        int subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+
+        PhoneAccountHandle accountHandle = mPrimary.getAccountHandle();
+        if (accountHandle != null) {
+            TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
+            PhoneAccount account = mgr.getPhoneAccount(accountHandle);
+            if (account != null) {
+                subId = TelephonyManager.getDefault().getSubIdForPhoneAccount(account);
+                Log.d(this, "get sub id from phone account = " + subId);
+            }
+        }
+
+        return subId;
+    }
+
+    private String checkIdp(String number, boolean nameIsNumber, boolean isIncoming) {
+        int subId = getSubId();
+        String checkedNumber = number;
+        String[] idp = mContext.getResources().getStringArray(R.array.international_idp);
+        String[] idpValues = mContext.getResources().
+                getStringArray(R.array.international_idp_values);
+        boolean isDataInValid = (idp.length == 0 || idpValues.length == 0 ||
+                idp.length != idpValues.length);
+
+        if (checkIdpEnable(subId) && nameIsNumber && !isDataInValid) {
+            if (!isIncoming) {
+                final int checkRoamingIdx = mContext.getResources().
+                        getInteger(R.integer.check_idp_roaming_idx);
+                final boolean isNetworkRoaming =
+                        TelephonyManager.getDefault().isNetworkRoaming(subId);
+                for (int i = 0; i < idp.length; i++) {
+                    Log.i(this, "checkIdp idp:" + idp[i] + " idp value:" + idpValues[i] +
+                            " roaming idx:" + checkRoamingIdx);
+                    int indexIdp = checkedNumber.indexOf(idp[i]);
+
+                    if (indexIdp != -1) {
+                        if ((checkRoamingIdx == i && !isNetworkRoaming) ||
+                                checkRoamingIdx != i) {
+                            checkedNumber = checkedNumber.substring(0, indexIdp) + idpValues[i] +
+                                    checkedNumber.substring(indexIdp + idp[i].length());
+                        }
+                    }
+                }
+            } else {
+                final int checkReconvertIdx = mContext.getResources().
+                        getInteger(R.integer.check_idp_reconvert_idx);
+                if (checkReconvertIdx >= 0 && checkReconvertIdx < idp.length &&
+                        number.indexOf(idpValues[checkReconvertIdx]) == 0) {
+                    checkedNumber = idp[checkReconvertIdx] +
+                            number.substring(idpValues[checkReconvertIdx].length());
+                }
+            }
+        }
+
+        Log.i(this, "checkIdp number: " + number + " subid:" + subId + " isIncoming:" +
+                isIncoming + " checked number:" + checkedNumber + " inValid:" +
+                isDataInValid + " nameIsNumber" + nameIsNumber);
+        return checkedNumber;
+    }
+
     private void updatePrimaryDisplayInfo() {
         final CallCardUi ui = getUi();
         if (ui == null) {
@@ -806,6 +902,12 @@
             boolean nameIsNumber = name != null && name.equals(mPrimaryContactInfo.number);
             // Call with caller that is a work contact.
             boolean isWorkContact = (mPrimaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
+            boolean isIncoming = mPrimary.getState() == Call.State.INCOMING;
+            boolean isWithPrefix = mContext.getResources().
+                getBoolean(R.bool.phone_number_with_intl_prefix);
+            if(isWithPrefix == true){
+                name = checkIdp(name, nameIsNumber, isIncoming);
+            }
             ui.setPrimary(
                     number,
                     name,
@@ -902,13 +1004,27 @@
         PhoneAccount account = getAccountForCall(call);
         TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
         if (account != null && !TextUtils.isEmpty(account.getLabel())
-                && TelecomManagerCompat.getCallCapablePhoneAccounts(mgr).size() > 1) {
+                && TelecomManagerCompat.getCallCapablePhoneAccounts(mgr).size() >= 1) {
             return account.getLabel().toString();
         }
         return null;
     }
 
     /**
+     * Return the icon to represent the call provider
+     */
+    private Drawable getCallProviderIcon(Call call) {
+        PhoneAccount account = getAccountForCall(call);
+        TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
+        if (account != null && account.getIcon()!= null &&
+                mgr.getCallCapablePhoneAccounts().size() > 1 &&
+                SubscriptionManager.from(mContext).getActiveSubscriptionInfoCount() > 1) {
+            return account.getIcon().loadDrawable(mContext);
+        }
+        return null;
+    }
+
+    /**
      * Returns the label (line of text above the number/name) for any given call.
      * For example, "calling via [Account/Google Voice]" for outgoing calls.
      */
@@ -943,7 +1059,7 @@
             }
         }
 
-        return null;
+        return getCallProviderIcon(mPrimary);
     }
 
     private boolean hasOutgoingGatewayCall() {
@@ -1031,6 +1147,11 @@
         }
         ui.setCallCardVisible(!isFullscreenMode);
         ui.setSecondaryInfoVisible(!isFullscreenMode);
+        final int callState = (mPrimary != null) ? mPrimary.getState() : Call.State.INVALID;
+        ui.setEndCallButtonEnabled(!isFullscreenMode &&
+                shouldShowEndCallButton(mPrimary, callState),
+                callState != Call.State.INCOMING /* animate */);
+        ui.showVbButton(!isFullscreenMode);
         maybeShowManageConferenceCallButton();
     }
 
@@ -1039,6 +1160,15 @@
         // No-op - the Call Card is the origin of this event.
     }
 
+    @Override
+    public void onIncomingVideoAvailabilityChanged(boolean isAvailable) {
+        Log.d(this, "onIncomingVideoAvailabilityChanged: available = " + isAvailable);
+        if (mPrimary == null) {
+            return;
+        }
+        updatePrimaryDisplayInfo();
+    }
+
     private boolean isPrimaryCallActive() {
         return mPrimary != null && mPrimary.getState() == Call.State.ACTIVE;
     }
@@ -1169,5 +1299,6 @@
         void animateForNewOutgoingCall();
         void sendAccessibilityAnnouncement();
         void showNoteSentToast();
+        void showVbButton(boolean show);
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index d0f3c10..059345f 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -28,8 +28,12 @@
 import com.android.incallui.util.TelecomCallUtil;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.SubscriptionManager;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -51,6 +55,7 @@
     private static final int DISCONNECTED_CALL_LONG_TIMEOUT_MS = 5000;
 
     private static final int EVENT_DISCONNECTED_TIMEOUT = 1;
+    private static final int EVENT_NOTIFY_CHANGE = 2;
     private static final long BLOCK_QUERY_TIMEOUT_MS = 1000;
 
     private static CallList sInstance = new CallList();
@@ -70,6 +75,9 @@
     private final Set<Call> mPendingDisconnectCalls = Collections.newSetFromMap(
             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
     private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private final ArrayList<ActiveSubChangeListener> mActiveSubChangeListeners =
+            Lists.newArrayList();
 
     /**
      * Static singleton accessor method.
@@ -130,6 +138,18 @@
      * Called when a single call has changed.
      */
     public void onIncoming(Call call, List<String> textMessages) {
+        Log.d(this, "onIncoming - " + call);
+
+        // Update active subscription from call object. it will be set by
+        // Telecomm service for incoming call and whenever active sub changes.
+        if (call.mIsActiveSub) {
+            int sub = Integer.parseInt(call.getAccountHandle().getId());
+            Log.d(this, "onIncoming - sub:" + sub + " mSubId:" + mSubId);
+            if (sub != mSubId) {
+                setActiveSubId(sub);
+            }
+        }
+
         if (updateCallInMap(call)) {
             Log.i(this, "onIncoming - " + call);
         }
@@ -151,6 +171,19 @@
      */
     public void onUpdate(Call call) {
         Trace.beginSection("onUpdate");
+        PhoneAccountHandle ph = call.getAccountHandle();
+        Log.d(this, "onUpdate - " + call  + " ph:" + ph);
+        try {
+            if (call.mIsActiveSub && ph != null) {
+                int sub = Integer.parseInt(ph.getId());
+                Log.d(this, "onUpdate - sub:" + sub + " mSubId:" + mSubId);
+                if(sub != mSubId) {
+                    setActiveSubId(sub);
+                }
+            }
+        } catch (NumberFormatException e) {
+                Log.w(this,"Sub Id is not a number " + e);
+        }
         onUpdateCall(call);
         notifyGenericListeners();
         Trace.endSection();
@@ -166,7 +199,7 @@
         final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
         if (listeners != null) {
             for (CallUpdateListener listener : listeners) {
-                listener.onSessionModificationStateChange(sessionModificationState);
+                listener.onSessionModificationStateChange(call, sessionModificationState);
             }
         }
     }
@@ -347,6 +380,9 @@
             result = getFirstCallWithState(Call.State.ACTIVE);
         }
         if (result == null) {
+            result = getFirstCallWithState(Call.State.ONHOLD);
+        }
+        if (result == null) {
             result = getDisconnectingCall();
         }
         if (result == null) {
@@ -382,6 +418,10 @@
         return mCallById.get(callId);
     }
 
+    public boolean isDsdaEnabled() {
+        return QtiCallUtils.isDsdaEnabled();
+    }
+
     public Call getCallByTelecomCall(android.telecom.Call telecomCall) {
         return mCallByTelecomCall.get(telecomCall);
     }
@@ -402,6 +442,13 @@
      * TODO: Improve this logic to sort by call time.
      */
     public Call getCallWithState(int state, int positionToFind) {
+        // if DSDA is enabled call getCallWithState with active subscription.
+        if (state != Call.State.SELECT_PHONE_ACCOUNT && getActiveSubId()
+                != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                && isDsdaEnabled()) {
+            return getCallWithState(state, positionToFind, getActiveSubId());
+        }
+
         Call retval = null;
         int position = 0;
         for (Call call : mCallById.values()) {
@@ -608,6 +655,13 @@
                     Log.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj);
                     finishDisconnectedCall((Call) msg.obj);
                     break;
+                case EVENT_NOTIFY_CHANGE:
+                    Log.d(this, "EVENT_NOTIFY_CHANGE: ");
+                    notifyGenericListeners();
+                    for (ActiveSubChangeListener listener : mActiveSubChangeListeners) {
+                        listener.onActiveSubChanged(getActiveSubId());
+                    }
+                    break;
                 default:
                     Log.wtf(this, "Message not expected: " + msg.what);
                     break;
@@ -663,7 +717,7 @@
          *
          * @param sessionModificationState The new session modification state.
          */
-        public void onSessionModificationStateChange(int sessionModificationState);
+        public void onSessionModificationStateChange(Call call, int sessionModificationState);
 
         /**
          * Notifies of a change to the last forwarded number for a call.
@@ -675,4 +729,152 @@
          */
         public void onChildNumberChange();
     }
+
+    /**
+     * Called when active subscription changes.
+     */
+    void onActiveSubChanged(int activeSub) {
+        Log.d(this, "onActiveSubChanged  = " + activeSub);
+        if (hasAnyLiveCall(activeSub)) {
+            setActiveSubId(activeSub);
+        }
+    }
+
+    int getActiveSubId() {
+        return mSubId;
+    }
+
+    /**
+     * Called to update the latest active subscription id, and also it
+     * notifies the registred clients about subscription change information.
+     */
+    void setActiveSubId(int subId) {
+        if (subId != mSubId) {
+            Log.d(this, "setActiveSubId, oldActiveSubId = " + mSubId +
+                    " newActiveSubId = " + subId);
+            mSubId = subId;
+            final Message msg = mHandler.obtainMessage(EVENT_NOTIFY_CHANGE, null);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
+     * Returns true, if any voice call is ACTIVE on the provided subscription.
+     */
+    boolean hasAnyLiveCall(int subId) {
+        for (Call call : mCallById.values()) {
+            PhoneAccountHandle ph = call.getAccountHandle();
+            try {
+                if (!isCallDead(call) && ph != null && (Integer.parseInt(ph.getId()) == subId)) {
+                    Log.d(this, "hasAnyLiveCall sub = " + subId);
+                    return true;
+                }
+            } catch (NumberFormatException e) {
+                Log.w(this,"Sub Id is not a number " + e);
+            }
+        }
+        Log.d(this, "no active call ");
+        return false;
+    }
+
+    /**
+     * Returns true, if any call is ACTIVE
+     */
+    boolean hasAnyLiveCall() {
+        for (Call call : mCallById.values()) {
+            if (!isCallDead(call)) {
+                Log.d(this, "hasAnyLiveCall call = " + call);
+                return true;
+            }
+        }
+        Log.d(this, "no active call ");
+        return false;
+    }
+
+    /**
+     * This method checks whether any other subscription currently has active voice
+     * call other than current active subscription, if yes it makes that other
+     * subscription as active subscription i.e user visible subscription.
+     * @param retainLch  whether to retain the LCH state of the other active sub
+     */
+    boolean switchToOtherActiveSub() {
+        int activeSub = getActiveSubId();
+        boolean subSwitched = false;
+
+        for (int i = 0; i < InCallServiceImpl.sPhoneCount; i++) {
+            int subId = QtiCallUtils.getSubId(i);
+            if ((subId != activeSub) && hasAnyLiveCall(subId)) {
+                Log.d(this, "switchToOtherActiveSub, subId = " + subId);
+                subSwitched = true;
+                QtiCallUtils.switchToActiveSub(subId);
+                setActiveSubId(subId);
+                break;
+            }
+        }
+        return subSwitched;
+    }
+
+    /**
+     * Method to check if there is any live call in a sub other than the one supplied.
+     * @param currentSub  The subscription to exclude while checking for active calls.
+     */
+    boolean isAnyOtherSubActive(int currentSub) {
+        boolean result = false;
+        if(!isDsdaEnabled()) {
+            return false;
+        }
+
+        for (int phoneId = 0; phoneId < InCallServiceImpl.sPhoneCount;
+                phoneId++) {
+            int subId = QtiCallUtils.getSubId(phoneId);
+
+            if ((subId != currentSub) && hasAnyLiveCall(subId)) {
+                Log.d(this, "Live call found on another sub = " + subId);
+                result = true;
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns the [position]th call which belongs to provided subscription and
+     * found in the call map with the specified state.
+     */
+    Call getCallWithState(int state, int positionToFind, int subId) {
+        Call retval = null;
+        int position = 0;
+        for (Call call : mCallById.values()) {
+            PhoneAccountHandle ph = call.getAccountHandle();
+            try {
+                if ((call.getState() == state) && ((ph == null) ||
+                        (ph != null && (ph.getId() != null) &&  ((ph.getId().contains("sip")
+                        || ph.getId().contains("@")) || Integer.parseInt(ph.getId()) == subId)))) {
+                    if (position >= positionToFind) {
+                        retval = call;
+                        break;
+                    } else {
+                        position++;
+                    }
+                }
+            } catch (NumberFormatException e) {
+                   Log.w(this,"Sub Id is not a number " + e);
+            }
+        }
+        return retval;
+    }
+
+    void addActiveSubChangeListener(ActiveSubChangeListener listener) {
+        Preconditions.checkNotNull(listener);
+        mActiveSubChangeListeners.add(listener);
+    }
+
+    void removeActiveSubChangeListener(ActiveSubChangeListener listener) {
+        Preconditions.checkNotNull(listener);
+        mActiveSubChangeListeners.remove(listener);
+    }
+
+    interface ActiveSubChangeListener {
+        public void onActiveSubChanged(int subId);
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/CallSubstateNotifier.java b/InCallUI/src/com/android/incallui/CallSubstateNotifier.java
new file mode 100644
index 0000000..ab2713c
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/CallSubstateNotifier.java
@@ -0,0 +1,158 @@
+/* Copyright (c) 2015, 2016, 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.
+ */
+
+package com.android.incallui;
+
+import org.codeaurora.ims.QtiCallConstants;
+import android.os.Bundle;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.HashMap;
+import java.util.List;
+import com.google.common.base.Preconditions;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+
+/**
+ * This class listens to incoming events from the {@class InCallDetailsListener}.
+ * When call details change, this class is notified and we parse the callExtras from the details to
+ * figure out if call substate has changed and notify the {@class InCallMessageController} to
+ * display the indication on UI.
+ *
+ */
+public class CallSubstateNotifier implements InCallDetailsListener, CallList.Listener {
+
+    private final List<InCallSubstateListener> mCallSubstateListeners =
+            new CopyOnWriteArrayList<>();
+
+    private static CallSubstateNotifier sCallSubstateNotifier;
+    private final HashMap<String, Integer> mCallSubstateMap = new HashMap<>();
+
+    /**
+     * This method returns a singleton instance of {@class CallSubstateNotifier}
+     */
+    public static synchronized CallSubstateNotifier getInstance() {
+        if (sCallSubstateNotifier == null) {
+            sCallSubstateNotifier = new CallSubstateNotifier();
+        }
+        return sCallSubstateNotifier;
+    }
+
+    /**
+     * This method adds a new call substate listener. Users interested in listening to call
+     * substate changes should add a listener of type {@class InCallSubstateListener}
+     */
+    public void addListener(InCallSubstateListener listener) {
+        Preconditions.checkNotNull(listener);
+        mCallSubstateListeners.add(listener);
+    }
+
+    /**
+     * This method removes an existing call substate listener. Users listening to call
+     * substate changes when not interested any more can de-register an existing listener of type
+     * {@class InCallSubstateListener}
+     */
+    public void removeListener(InCallSubstateListener listener) {
+        if (listener != null) {
+            mCallSubstateListeners.remove(listener);
+        } else {
+            Log.e(this, "Can't remove null listener");
+        }
+    }
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private CallSubstateNotifier() {
+    }
+
+    private int getCallSubstate(Bundle callExtras) {
+        return callExtras.getInt(QtiCallConstants.CALL_SUBSTATE_EXTRA_KEY,
+                QtiCallConstants.CALL_SUBSTATE_NONE);
+    }
+
+    /**
+     * This method overrides onDetailsChanged method of {@class InCallDetailsListener}. We are
+     * notified when call details change and extract the call substate from the callExtras, detect
+     * if call substate changed and notify all registered listeners.
+     */
+    @Override
+    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+        Log.d(this, "onDetailsChanged - call: " + call + "details: " + details);
+
+        if (call == null || details == null ||
+                !Call.State.isConnectingOrConnected(call.getState())) {
+            Log.d(this, "onDetailsChanged - Call/details is null/Call is not connected. Return");
+            return;
+        }
+
+        final Bundle callExtras = details.getExtras();
+
+        if (callExtras == null) {
+            return;
+        }
+
+        final String callId = call.getId();
+
+        final int oldCallSubstate = mCallSubstateMap.containsKey(callId) ?
+                mCallSubstateMap.get(callId) : QtiCallConstants.CALL_SUBSTATE_NONE;
+        final int newCallSubstate = getCallSubstate(callExtras);
+
+        if (oldCallSubstate == newCallSubstate) {
+            return;
+        }
+
+        mCallSubstateMap.put(callId, newCallSubstate);
+        Preconditions.checkNotNull(mCallSubstateListeners);
+        for (InCallSubstateListener listener : mCallSubstateListeners) {
+            listener.onCallSubstateChanged(call, newCallSubstate);
+        }
+    }
+
+    /**
+     * This method overrides onDisconnect method of {@interface CallList.Listener}
+     */
+    @Override
+    public void onDisconnect(final Call call) {
+        Log.d(this, "onDisconnect: call: " + call);
+        mCallSubstateMap.remove(call.getId());
+    }
+
+    @Override
+    public void onUpgradeToVideo(Call call) {
+        //NO-OP
+    }
+
+    @Override
+    public void onIncomingCall(Call call) {
+        //NO-OP
+    }
+
+    @Override
+    public void onCallListChange(CallList callList) {
+        //NO-OP
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/CallerInfo.java b/InCallUI/src/com/android/incallui/CallerInfo.java
index f270678..ccee604 100644
--- a/InCallUI/src/com/android/incallui/CallerInfo.java
+++ b/InCallUI/src/com/android/incallui/CallerInfo.java
@@ -402,6 +402,15 @@
         return this;
     }
 
+    /* package */ CallerInfo markAsEmergency(Context context, String number) {
+        number = PhoneNumberUtils.normalizeNumber(number);
+        name = context.getString(R.string.emergency_call_dialog_number_for_display);
+        phoneNumber = number;
+
+        photoResource = R.drawable.img_phone;
+        mIsEmergency = true;
+        return this;
+    }
 
     /**
      * Mark this CallerInfo as a voicemail call. The voicemail label
diff --git a/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java b/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java
index f7f0cbb..fa4d671 100644
--- a/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java
+++ b/InCallUI/src/com/android/incallui/CallerInfoAsyncQuery.java
@@ -261,7 +261,14 @@
                     if (cw.event == EVENT_EMERGENCY_NUMBER) {
                         // Note we're setting the phone number here (refer to javadoc
                         // comments at the top of CallerInfo class).
-                        mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
+                        if (mQueryContext.getResources().getBoolean(R.bool.mark_emergency_call)) {
+                            Log.d(this, "Emergency Number and Mark Emergency Number enabled");
+                            mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext,
+                                    cw.number);
+                        } else {
+                            Log.d(this, "Emergency Number and Mark Emergency Number disabled");
+                            mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
+                        }
                     } else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
                         mCallerInfo = new CallerInfo().markAsVoiceMail(mQueryContext);
                     } else {
@@ -401,7 +408,7 @@
         cw.number = info.phoneNumber;
 
         // check to see if these are recognized numbers, and use shortcuts if we can.
-        if (PhoneNumberUtils.isLocalEmergencyNumber(context, info.phoneNumber)) {
+        if (QtiCallUtils.isLocalEmergencyNumber(info.phoneNumber)){
             cw.event = EVENT_EMERGENCY_NUMBER;
         } else if (info.isVoiceMailNumber()) {
             cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/InCallUI/src/com/android/incallui/CircularRevealFragment.java b/InCallUI/src/com/android/incallui/CircularRevealFragment.java
index 01bd253..f9816de 100644
--- a/InCallUI/src/com/android/incallui/CircularRevealFragment.java
+++ b/InCallUI/src/com/android/incallui/CircularRevealFragment.java
@@ -24,6 +24,7 @@
 import android.graphics.Outline;
 import android.graphics.Point;
 import android.os.Bundle;
+import android.os.Handler;
 import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -39,9 +40,16 @@
 public class CircularRevealFragment extends Fragment {
     static final String TAG = "CircularRevealFragment";
 
+    // This is used to extend the expiration time of Circular reveal
+    // animation. So over all time the handler waits is Animation
+    // duration + REVEAL_ADDITIONAL_EXPIRATION_TIME and notifies
+    // the Circular reveal completion to listeners.
+    private static final int REVEAL_ADDITIONAL_EXPIRATION_TIME = 200;
+
     private Point mTouchPoint;
     private OnCircularRevealCompleteListener mListener;
     private boolean mAnimationStarted;
+    private boolean mAnimationFinished;
 
     interface OnCircularRevealCompleteListener {
         public void onCircularRevealComplete(FragmentManager fm);
@@ -123,6 +131,7 @@
         view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
             @Override
             public boolean onPreDraw() {
+                mAnimationFinished = false;
                 final ViewTreeObserver vto = view.getViewTreeObserver();
                 if (vto.isAlive()) {
                     vto.removeOnPreDrawListener(this);
@@ -132,13 +141,31 @@
                     animator.addListener(new AnimatorListenerAdapter() {
                         @Override
                         public void onAnimationEnd(Animator animation) {
+                            Log.w(TAG, " CircularRevealFragment - onAnimationEnd");
                             view.setClipToOutline(false);
-                            if (mListener != null) {
+                            if (mListener != null && mAnimationFinished == false) {
                                 mListener.onCircularRevealComplete(getFragmentManager());
+                                mAnimationFinished = true;
                             }
                         }
                     });
                     animator.start();
+                    // If somehow the animator is failed to run from lower layers,
+                    // need to wait for certain expiration time and inform the
+                    // listeners with OnCircularComplete callback so that rest of
+                    // the things will not be blocked.
+                    Handler handler = new Handler();
+                    handler.postDelayed(new Runnable() {
+                         @Override
+                         public void run() {
+                             Log.w(TAG, " Failed to complete animation");
+                             if (mListener != null && mAnimationFinished == false) {
+                                 mListener.onCircularRevealComplete(getFragmentManager());
+                                 mAnimationFinished = true;
+                             }
+                         }
+                    }, getResources().getInteger(R.integer.reveal_animation_duration) +
+                            REVEAL_ADDITIONAL_EXPIRATION_TIME);
                 }
                 return false;
             }
diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
index fe941c8..ad417b8 100644
--- a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
+++ b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java
@@ -99,10 +99,13 @@
     public void onVisibilityChanged(boolean isVisible) {
         mIsVisible = isVisible;
         ActionBar actionBar = getActivity().getActionBar();
+        boolean isDsdaEnabled = CallList.getInstance().isDsdaEnabled();
         if (isVisible) {
             actionBar.setTitle(R.string.manageConferenceLabel);
             actionBar.setElevation(mActionBarElevation);
-            actionBar.setHideOffset(0);
+            if (!isDsdaEnabled) {
+                actionBar.setHideOffset(0);
+            }
             actionBar.show();
 
             final CallList calls = CallList.getInstance();
@@ -112,7 +115,9 @@
             mConferenceParticipantList.requestFocus();
         } else {
             actionBar.setElevation(0);
-            actionBar.setHideOffset(actionBar.getHeight());
+            if (!isDsdaEnabled) {
+                actionBar.setHideOffset(actionBar.getHeight());
+            }
         }
     }
 
diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
index 6fb6e5d..c995be0 100644
--- a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
+++ b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java
@@ -126,7 +126,7 @@
         }
 
         Log.d(this, "Number of calls is " + String.valueOf(calls.size()));
-
+        Log.d(this, "update calls" + calls);
         // Users can split out a call from the conference call if either the active call or the
         // holding call is empty. If both are filled, users can not split out another call.
         final boolean hasActiveCall = (callList.getActiveCall() != null);
diff --git a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
index d68ae1f..e1968f2 100644
--- a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
+++ b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
@@ -366,7 +366,7 @@
                         contactCache.nameAlternative, mContactsPreferences),
                 contactCache.number, contactCache.label,
                 contactCache.lookupKey, contactCache.displayPhotoUri, thisRowCanSeparate,
-                thisRowCanDisconnect);
+                thisRowCanDisconnect, getResourceforState(call.getTrueState()));
 
         // Tag the row in the conference participant list with the call id to make it easier to
         // find calls when contact cache information is loaded.
@@ -375,6 +375,33 @@
         return result;
     }
 
+    private static int getResourceforState(int state){
+        int res;
+        switch (state){
+            case Call.State.ACTIVE:
+                res = R.string.call_state_active;
+                break;
+            case Call.State.NEW:
+            case Call.State.IDLE:
+            case Call.State.DIALING:
+            case Call.State.REDIALING:
+                res = R.string.call_state_dialing;
+                break;
+            case Call.State.ONHOLD:
+                res = R.string.call_state_holding;
+                break;
+            case Call.State.DISCONNECTING:
+                res = R.string.call_state_disconnecting;
+                break;
+            case Call.State.DISCONNECTED:
+                res = R.string.call_state_disconnected;
+                break;
+            default:
+                res = R.string.call_state_unknown;
+        }
+        return res;
+    }
+
     /**
      * Replaces the contact info for a participant and triggers a refresh of the UI.
      *
@@ -404,7 +431,7 @@
      */
     private final void setCallerInfoForRow(View view, String callerName, String preferredName,
             String callerNumber, String callerNumberType, String lookupKey, Uri photoUri,
-            boolean thisRowCanSeparate, boolean thisRowCanDisconnect) {
+            boolean thisRowCanSeparate, boolean thisRowCanDisconnect, int state) {
 
         final ImageView photoView = (ImageView) view.findViewById(R.id.callerPhoto);
         final TextView nameTextView = (TextView) view.findViewById(R.id.conferenceCallerName);
@@ -413,7 +440,11 @@
                 R.id.conferenceCallerNumberType);
         final View endButton = view.findViewById(R.id.conferenceCallerDisconnect);
         final View separateButton = view.findViewById(R.id.conferenceCallerSeparate);
-
+        if (mContext.getResources().getBoolean(R.bool.
+                config_conference_call_show_participant_status)){
+            final TextView stateTextView = (TextView) view.findViewById(R.id.conferenceCallerState);
+            stateTextView.setText(state);
+        }
         endButton.setVisibility(thisRowCanDisconnect ? View.VISIBLE : View.GONE);
         if (thisRowCanDisconnect) {
             endButton.setOnClickListener(mDisconnectListener);
@@ -457,6 +488,7 @@
      * @param conferenceParticipants The calls which make up the conference participants.
      */
     private void updateParticipantInfo(List<Call> conferenceParticipants) {
+        Log.d(this, "updateParticipantInfo: " + conferenceParticipants);
         final ContactInfoCache cache = ContactInfoCache.getInstance(mContext);
         boolean newParticipantAdded = false;
         HashSet<String> newCallIds = new HashSet<>(conferenceParticipants.size());
diff --git a/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java b/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java
index 62a8e78..98e797e 100644
--- a/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java
+++ b/InCallUI/src/com/android/incallui/GlowPadAnswerFragment.java
@@ -91,6 +91,8 @@
         final int directionDescriptionsResourceId;
         final int handleDrawableResourceId;
         mGlowpad.setVideoState(videoState);
+        final boolean isEnhanceUIEnabled = getContext().getResources().getBoolean(
+                R.bool.config_enable_enhance_video_call_ui);
 
         switch (targetSet) {
             case TARGET_SET_FOR_AUDIO_WITH_SMS:
@@ -102,7 +104,12 @@
                 handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
                 break;
             case TARGET_SET_FOR_VIDEO_WITHOUT_SMS:
-                targetResourceId = R.array.incoming_call_widget_video_without_sms_targets;
+                if (isEnhanceUIEnabled) {
+                    targetResourceId =
+                            R.array.enhance_incoming_call_widget_video_without_sms_targets;
+                } else {
+                    targetResourceId = R.array.incoming_call_widget_video_without_sms_targets;
+                }
                 targetDescriptionsResourceId =
                         R.array.incoming_call_widget_video_without_sms_target_descriptions;
                 directionDescriptionsResourceId =
@@ -110,7 +117,11 @@
                 handleDrawableResourceId = R.drawable.ic_incall_video_handle;
                 break;
             case TARGET_SET_FOR_VIDEO_WITH_SMS:
-                targetResourceId = R.array.incoming_call_widget_video_with_sms_targets;
+                if (isEnhanceUIEnabled) {
+                    targetResourceId = R.array.enhance_incoming_call_widget_video_with_sms_targets;
+                } else {
+                    targetResourceId = R.array.incoming_call_widget_video_with_sms_targets;
+                }
                 targetDescriptionsResourceId =
                         R.array.incoming_call_widget_video_with_sms_target_descriptions;
                 directionDescriptionsResourceId =
@@ -126,6 +137,140 @@
                         .incoming_call_widget_video_request_target_direction_descriptions;
                 handleDrawableResourceId = R.drawable.ic_incall_video_handle;
                 break;
+            case TARGET_SET_FOR_QTI_VIDEO_WITHOUT_SMS:
+                if (isEnhanceUIEnabled) {
+                    targetResourceId =
+                            R.array.enhance_incoming_call_widget_video_without_sms_targets;
+                } else {
+                    targetResourceId = R.array.qti_incoming_call_widget_video_without_sms_targets;
+                }
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_without_sms_target_descriptions;
+                directionDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_without_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_WITH_SMS:
+                if (isEnhanceUIEnabled) {
+                    targetResourceId = R.array.enhance_incoming_call_widget_video_with_sms_targets;
+                } else {
+                    targetResourceId = R.array.qti_incoming_call_widget_video_with_sms_targets;
+                }
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_with_sms_target_descriptions;
+                directionDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_with_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_WITHOUT_SMS:
+                targetResourceId =
+                    R.array.qti_incoming_call_widget_tx_video_without_sms_targets;
+                targetDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_tx_video_without_sms_target_descriptions;
+                directionDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_tx_video_without_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_WITH_SMS:
+                targetResourceId =
+                    R.array.qti_incoming_call_widget_tx_video_with_sms_targets;
+                targetDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_tx_video_with_sms_target_descriptions;
+                directionDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_tx_video_with_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_WITHOUT_SMS:
+                targetResourceId =
+                    R.array.qti_incoming_call_widget_rx_video_without_sms_targets;
+                targetDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_rx_video_without_sms_target_descriptions;
+                directionDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_rx_video_without_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_WITH_SMS:
+                targetResourceId =
+                    R.array.qti_incoming_call_widget_rx_video_with_sms_targets;
+                targetDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_rx_video_with_sms_target_descriptions;
+                directionDescriptionsResourceId =
+                    R.array.qti_incoming_call_widget_rx_video_with_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_ACCEPT_REJECT_REQUEST:
+                if (isEnhanceUIEnabled) {
+                    targetResourceId =
+                            R.array.enhance_incoming_call_widget_video_upgrade_request_targets;
+                } else {
+                    targetResourceId = R.array.qti_incoming_call_widget_video_request_targets;
+                }
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_request_target_descriptions;
+                directionDescriptionsResourceId = R.array.
+                        qti_incoming_call_widget_video_request_target_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST:
+                if (isEnhanceUIEnabled) {
+                    targetResourceId = R.array.
+                        enhance_incoming_call_bidirectional_video_accept_request_targets;
+                } else {
+                    targetResourceId = R.array.
+                        qti_incoming_call_widget_bidirectional_video_accept_reject_request_targets;
+                }
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_request_target_descriptions;
+                directionDescriptionsResourceId = R.array.
+                        qti_incoming_call_widget_video_request_target_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST:
+                if (isEnhanceUIEnabled) {
+                    targetResourceId =
+                            R.array.enhance_incoming_call_video_transmit_accept_request_targets;
+                } else {
+                    targetResourceId = R.array.
+                           qti_incoming_call_widget_video_transmit_accept_reject_request_targets;
+                }
+                targetDescriptionsResourceId = R.array.
+                        qti_incoming_call_widget_video_transmit_request_target_descriptions;
+                directionDescriptionsResourceId = R.array
+                        .qti_incoming_call_widget_video_request_target_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+            case TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST:
+                if (isEnhanceUIEnabled) {
+                    targetResourceId = R.array.
+                            enhance_incoming_call_video_receive_accept_request_targets;
+                } else {
+                    targetResourceId = R.array.
+                            qti_incoming_call_widget_video_receive_accept_reject_request_targets;
+                }
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_video_receive_request_target_descriptions;
+                directionDescriptionsResourceId = R.array
+                        .qti_incoming_call_widget_video_request_target_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_video_handle;
+                break;
+
+            case TARGET_SET_FOR_QTI_AUDIO_WITH_SMS:
+                targetResourceId = R.array.qti_incoming_call_widget_audio_with_sms_targets;
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_audio_with_sms_target_descriptions;
+                directionDescriptionsResourceId = R.array
+                        .qti_incoming_call_widget_audio_with_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
+                break;
+            case TARGET_SET_FOR_QTI_AUDIO_WITHOUT_SMS:
+                targetResourceId = R.array.qti_incoming_call_widget_audio_without_sms_targets;
+                targetDescriptionsResourceId =
+                        R.array.qti_incoming_call_widget_audio_without_sms_target_descriptions;
+                directionDescriptionsResourceId = R.array
+                        .qti_incoming_call_widget_audio_without_sms_direction_descriptions;
+                handleDrawableResourceId = R.drawable.ic_incall_audio_handle;
+                break;
+
             case TARGET_SET_FOR_AUDIO_WITHOUT_SMS:
             default:
                 targetResourceId = R.array.incoming_call_widget_audio_without_sms_targets;
diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
index 342f6b4..c88a984 100644
--- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java
+++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java
@@ -115,18 +115,32 @@
         if (resId == R.drawable.ic_lockscreen_answer) {
             mAnswerFragment.onAnswer(VideoProfile.STATE_AUDIO_ONLY, getContext());
             mTargetTriggered = true;
-        } else if (resId == R.drawable.ic_lockscreen_decline) {
+        } else if (resId == R.drawable.ic_lockscreen_decline ||
+                resId == R.drawable.ic_enhance_decline_video) {
             mAnswerFragment.onDecline(getContext());
             mTargetTriggered = true;
         } else if (resId == R.drawable.ic_lockscreen_text) {
             mAnswerFragment.onText();
             mTargetTriggered = true;
-        } else if (resId == R.drawable.ic_videocam || resId == R.drawable.ic_lockscreen_answer_video) {
+        } else if (resId == R.drawable.ic_videocam ||
+                resId == R.drawable.ic_lockscreen_answer_video ||
+                resId == R.drawable.ic_enhance_answer_video) {
             mAnswerFragment.onAnswer(mVideoState, getContext());
             mTargetTriggered = true;
         } else if (resId == R.drawable.ic_lockscreen_decline_video) {
             mAnswerFragment.onDeclineUpgradeRequest(getContext());
             mTargetTriggered = true;
+        } else if (resId == R.drawable.qti_ic_lockscreen_answer_tx_video ||
+                resId == R.drawable.ic_enhance_answer_tx_video) {
+            mAnswerFragment.onAnswer(VideoProfile.STATE_TX_ENABLED, getContext());
+            mTargetTriggered = true;
+        } else if (resId == R.drawable.qti_ic_lockscreen_answer_rx_video ||
+                resId == R.drawable.ic_enhance_answer_rx_video) {
+            mAnswerFragment.onAnswer(VideoProfile.STATE_RX_ENABLED, getContext());
+            mTargetTriggered = true;
+        } else if (resId == R.drawable.qti_ic_lockscreen_deflect) {
+            mAnswerFragment.onDeflect(getContext());
+            mTargetTriggered = true;
         } else {
             // Code should never reach here.
             Log.e(this, "Trigger detected on unhandled resource. Skipping.");
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index eca79f8..dd2af36 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -17,6 +17,8 @@
 package com.android.incallui;
 
 import android.app.ActionBar;
+import android.app.FragmentTransaction;
+import android.app.ActionBar.Tab;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
@@ -30,12 +32,17 @@
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.Point;
 import android.hardware.SensorManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.view.KeyEvent;
 import android.view.MenuItem;
@@ -49,6 +56,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import com.android.contacts.common.activity.TransactionSafeActivity;
 import com.android.contacts.common.compat.CompatUtils;
@@ -103,6 +112,16 @@
 
     private AlertDialog mDialog;
     private InCallOrientationEventListener mInCallOrientationEventListener;
+    // Add phone feature uri
+    private static final Uri URI_PHONE_FEATURE = Uri
+            .parse("content://com.qualcomm.qti.phonefeature.FEATURE_PROVIDER");
+
+    private static final String METHOD_START_CALL_RECORD = "start_call_record";
+    private static final String METHOD_STOP_CALL_RECORD = "stop_call_record";
+    private static final String METHOD_IS_CALL_RECORD_RUNNING = "is_call_record_running";
+    private static final String METHOD_IS_CALL_RECORD_AVAILABLE = "is_call_record_available";
+    private static final String METHOD_GET_CALL_RECORD_DURATION = "get_call_record_duration";
+    private static final String EXTRA_RESULT = "result";
 
     /**
      * Used to indicate whether the dialpad should be hidden or shown {@link #onResume}.
@@ -134,6 +153,13 @@
     private Animation mSlideOut;
     private boolean mDismissKeyguard = false;
 
+    private final int TAB_COUNT_ONE = 1;
+    private final int TAB_COUNT_TWO = 2;
+    private final int TAB_POSITION_FIRST = 0;
+
+    private Tab[] mDsdaTab = new Tab[TAB_COUNT_TWO];
+    private boolean[] mDsdaTabAdd = {false, false};
+
     AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() {
         @Override
         public void onAnimationEnd(Animation animation) {
@@ -170,19 +196,28 @@
                 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
 
         getWindow().addFlags(flags);
-
-        // Setup action bar for the conference call manager.
-        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
-        ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setDisplayShowTitleEnabled(true);
-            actionBar.hide();
+        boolean isDsdaEnabled = CallList.getInstance().isDsdaEnabled();
+        if (isDsdaEnabled) {
+            requestWindowFeature(Window.FEATURE_ACTION_BAR);
+            getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+            getActionBar().setDisplayShowTitleEnabled(false);
+            getActionBar().setDisplayShowHomeEnabled(false);
+        } else {
+            requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+            if (getActionBar() != null) {
+                getActionBar().setDisplayHomeAsUpEnabled(true);
+                getActionBar().setDisplayShowTitleEnabled(true);
+                getActionBar().hide();
+            }
         }
 
         // TODO(klp): Do we need to add this back when prox sensor is not available?
         // lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
 
+        // Since activity is created newly, clear full screen flag. This will ensure that
+        // the flag is in sync with actual UI when UI is recreated due to orientation change.
+        InCallPresenter.getInstance().clearFullscreen();
+
         setContentView(R.layout.incall_screen);
 
         internalResolveIntent(getIntent());
@@ -236,7 +271,10 @@
         }
         mInCallOrientationEventListener = new InCallOrientationEventListener(this);
 
-        Log.d(this, "onCreate(): exit");
+        if (isDsdaEnabled ) {
+            initializeDsdaSwitchTab();
+        }
+        Log.d(this, "onCreate(): exit" + isDsdaEnabled);
     }
 
     @Override
@@ -301,6 +339,7 @@
         if (mShowPostCharWaitDialogOnResume) {
             showPostCharWaitDialog(mShowPostCharWaitDialogCallId, mShowPostCharWaitDialogChars);
         }
+        InCallPresenter.getInstance().updatePrimaryCallState();
     }
 
     // onPause is guaranteed to be called when the InCallActivity goes
@@ -331,6 +370,7 @@
     @Override
     protected void onDestroy() {
         Log.d(this, "onDestroy()...  this = " + this);
+        InCallLowBatteryListener.getInstance().onDestroyInCallActivity();
         InCallPresenter.getInstance().unsetActivity(this);
         InCallPresenter.getInstance().updateIsChangingConfigurations();
         super.onDestroy();
@@ -387,8 +427,13 @@
         return isSafeToCommitTransactions();
     }
 
+    /* package */ boolean isManageConferenceVisible() {
+        return (mConferenceManagerFragment != null && mConferenceManagerFragment.isVisible());
+    }
+
     private boolean hasPendingDialogs() {
-        return mDialog != null || (mAnswerFragment != null && mAnswerFragment.hasPendingDialogs());
+        return mDialog != null || (mAnswerFragment != null && mAnswerFragment.hasPendingDialogs())
+                || InCallCsRedialHandler.getInstance().hasPendingDialogs();
     }
 
     @Override
@@ -885,9 +930,16 @@
             mDialog.dismiss();
             mDialog = null;
         }
+        SelectPhoneAccountDialogFragment dialogFragment = (SelectPhoneAccountDialogFragment)
+                getFragmentManager().findFragmentByTag(TAG_SELECT_ACCT_FRAGMENT);
+        if (dialogFragment != null) {
+            dialogFragment.dismiss();
+        }
         if (mAnswerFragment != null) {
             mAnswerFragment.dismissPendingDialogs();
         }
+        InCallCsRedialHandler.getInstance().dismissPendingDialogs();
+        InCallLowBatteryListener.getInstance().dismissPendingDialogs();
     }
 
     /**
@@ -940,6 +992,113 @@
         }
     }
 
+    private void initializeDsdaSwitchTab() {
+        int phoneCount = InCallServiceImpl.sPhoneCount;
+        ActionBar bar = getActionBar();
+        View[] mDsdaTabLayout = new View[phoneCount];
+        int[] subString = {R.string.sub_1, R.string.sub_2};
+
+        Log.d(TAG, "initializeDsdaSwitchTab" + phoneCount);
+        for (int i = 0; i < phoneCount; i++) {
+            mDsdaTabLayout[i] = getLayoutInflater()
+                    .inflate(R.layout.msim_tab_sub_info, null);
+
+            ((TextView)mDsdaTabLayout[i].findViewById(R.id.tabSubText))
+                    .setText(subString[i]);
+
+            mDsdaTab[i] = bar.newTab().setCustomView(mDsdaTabLayout[i])
+                    .setTabListener(new TabListener(i));
+        }
+    }
+
+    public void updateDsdaTab() {
+        int phoneCount = InCallServiceImpl.sPhoneCount;
+        ActionBar bar = getActionBar();
+
+        for (int i = 0; i < phoneCount; i++) {
+            int subId = QtiCallUtils.getSubId(i);
+            if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                    && CallList.getInstance().hasAnyLiveCall(subId)) {
+                if (!mDsdaTabAdd[i]) {
+                    addDsdaTab(i);
+                }
+            } else {
+                removeDsdaTab(i);
+            }
+        }
+
+        updateDsdaTabSelection();
+    }
+
+    private void addDsdaTab(int phoneId) {
+        ActionBar bar = getActionBar();
+        int tabCount = bar.getTabCount();
+
+        if (tabCount < phoneId) {
+            bar.addTab(mDsdaTab[phoneId], false);
+        } else {
+            bar.addTab(mDsdaTab[phoneId], phoneId, false);
+        }
+        mDsdaTabAdd[phoneId] = true;
+        Log.d(this, "addDsdaTab, phoneId = " + phoneId + " tab count = " + tabCount);
+    }
+
+    private void removeDsdaTab(int subId) {
+        ActionBar bar = getActionBar();
+        int tabCount = bar.getTabCount();
+
+        for (int i = 0; i < tabCount; i++) {
+            if (bar.getTabAt(i).equals(mDsdaTab[subId])) {
+                bar.removeTab(mDsdaTab[subId]);
+                mDsdaTabAdd[subId] = false;
+                return;
+            }
+        }
+        Log.d(this, "removeDsdaTab, subId = " + subId + " tab count = " + tabCount);
+    }
+
+    private void updateDsdaTabSelection() {
+        ActionBar bar = getActionBar();
+        int barCount = bar.getTabCount();
+
+        if (barCount == TAB_COUNT_ONE) {
+            bar.selectTab(bar.getTabAt(TAB_POSITION_FIRST));
+        } else if (barCount == TAB_COUNT_TWO) {
+            int phoneId = QtiCallUtils.getPhoneId(CallList
+                    .getInstance().getActiveSubId());
+            bar.selectTab(bar.getTabAt(phoneId));
+        }
+    }
+
+    private class TabListener implements ActionBar.TabListener {
+        int mPhoneId;
+
+        public TabListener(int phoneId) {
+            mPhoneId = phoneId;
+        }
+
+        public void onTabSelected(Tab tab, FragmentTransaction ft) {
+            ActionBar bar = getActionBar();
+            int tabCount = bar.getTabCount();
+                Log.d(this, "onTabSelected mPhoneId:" + mPhoneId);
+            //Don't setActiveSubscription if tab count is 1.This is to avoid
+            //setting active subscription automatically when call on one sub
+            //ends and it's corresponding tab is removed.For such cases active
+            //subscription will be set by InCallPresenter.attemptFinishActivity.
+            int subId = QtiCallUtils.getSubId(mPhoneId);
+            if (tabCount != TAB_COUNT_ONE && CallList.getInstance().hasAnyLiveCall(subId)
+                    && (CallList.getInstance().getActiveSubId() != subId)) {
+                Log.d(this, "Switch to other active sub: " + subId);
+                QtiCallUtils.switchToActiveSub(subId);
+            }
+        }
+
+        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+        }
+
+        public void onTabReselected(Tab tab, FragmentTransaction ft) {
+        }
+    }
 
     public OnTouchListener getDispatchTouchEventListener() {
         return mDispatchTouchEventListener;
@@ -961,4 +1120,58 @@
             mInCallOrientationEventListener.disable();
         }
     }
+
+    public static boolean isPhoneFeatureEnabled(Context context) {
+        return (UserHandle.myUserId() == UserHandle.USER_OWNER &&
+                context.getContentResolver().acquireProvider(URI_PHONE_FEATURE) != null);
+    }
+
+    public static Bundle callBinder(Context context, String method) {
+        if (!isPhoneFeatureEnabled(context)) {
+            return null;
+        }
+        return context.getContentResolver().call(URI_PHONE_FEATURE, method, null, null);
+    }
+
+    public boolean isCallRecording() {
+        boolean isRecording = false;
+        Bundle result = callBinder(InCallActivity.this, METHOD_IS_CALL_RECORD_RUNNING);
+
+        if (result != null) {
+            isRecording = result.getBoolean(EXTRA_RESULT);
+        }
+
+        return isRecording;
+    }
+
+    public boolean isCallRecorderEnabled() {
+        boolean isCallRecorderEnabled = false;
+        Bundle result = callBinder(InCallActivity.this, METHOD_IS_CALL_RECORD_AVAILABLE);
+
+        if (result != null) {
+            isCallRecorderEnabled = result.getBoolean(EXTRA_RESULT);
+        }
+        return isCallRecorderEnabled;
+    }
+
+    public void startInCallRecorder() {
+        callBinder(InCallActivity.this, METHOD_START_CALL_RECORD);
+    }
+
+    public void stopInCallRecorder() {
+        callBinder(InCallActivity.this, METHOD_STOP_CALL_RECORD);
+    }
+
+    public String getCallRecordingTime() {
+        long time = 0;
+        Bundle result = callBinder(InCallActivity.this, METHOD_GET_CALL_RECORD_DURATION);
+
+        if (result != null) {
+            time = result.getLong(EXTRA_RESULT) / 1000;
+        }
+
+        String recordingTime = String.format("%02d:%02d", time / 60, time % 60);
+
+        return recordingTime;
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/InCallAudioManager.java b/InCallUI/src/com/android/incallui/InCallAudioManager.java
new file mode 100644
index 0000000..3f0644e
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallAudioManager.java
@@ -0,0 +1,202 @@
+/* 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.
+ */
+
+package com.android.incallui;
+
+import android.telecom.CallAudioState;
+import android.telecom.VideoProfile;
+
+/**
+ * This class implements QTI audio routing policy for upgrade/downgrade/merge call use cases
+ * based on the audio policy below.
+ *
+ * Audio policy for any user initiated action like making a video call, accepting an upgrade
+ * request or sending an upgrade/downgrade request is as follows : If any of the above user actions
+ * indicate a transition to a video call, we route audio to SPEAKER. If any of the above user
+ * actions indicate a voice call, we route to EARPIECE. For all other non-user initiated actions
+ * like an incoming downgrade request, we don't change the audio path and respect user's choice.
+ * For merge calls, we route to SPEAKER only if one of the calls being merged is a video call.
+ *
+ * Audio path routing for outgoing calls, incoming calls, add call and call waiting are handled
+ * in Telecom layer. Same audio policy is followed for those as well. Any user initiated action
+ * indicating a origin/acceptance of video call routes audio to SPEAKER or to EARPIECE for voice
+ * calls.
+ */
+public class InCallAudioManager {
+
+    private static InCallAudioManager sInCallAudioManager;
+
+    private final static String LOG_TAG = "InCallAudioManager";
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private InCallAudioManager() {
+    }
+
+    /**
+     * This method returns a singleton instance of {@class InCallAudioManager}
+     */
+    public static synchronized InCallAudioManager getInstance() {
+        if (sInCallAudioManager == null) {
+            sInCallAudioManager = new InCallAudioManager();
+        }
+        return sInCallAudioManager;
+    }
+
+    /**
+     * Called when user sends an upgrade/downgrade request. Route audio to speaker if the user
+     * sends an upgrade request to Video (bidirectional, transmit or receive) otherwise route
+     * audio to earpiece if it's a downgrade request.
+     */
+    public void onModifyCallClicked(final Call call,final int newVideoState) {
+        Log.v(this, "onModifyCallClicked: Call = " + call + "new video state = " +
+                newVideoState);
+
+        if (!VideoProfile.isVideo(newVideoState)) {
+            enableEarpiece();
+        } else if (canEnableSpeaker(call.getVideoState(),newVideoState)) {
+            enableSpeaker();
+        }
+    }
+
+    /**
+     * Called when user accepts an upgrade request. Route audio to speaker if the user accepts an
+     * upgrade request to Video (bidirectional, transmit or receive)
+     */
+    public void onAcceptUpgradeRequest(final Call call, final int videoState) {
+        Log.v(this, "onAcceptUpgradeRequest: Call = " + call + "video state = " +
+                videoState);
+        if (canEnableSpeaker(call.getVideoState(), videoState)) {
+            enableSpeaker();
+        }
+    }
+
+    /**
+     * Called when user accepts an incoming call. Route audio to speaker if the user accepts an
+     * incoming call as Video (bidirectional, transmit or receive) or to earpiece is the user
+     * accepts the call as Voice
+      */
+    public void onAnswerIncomingCall(final Call call, final int videoState) {
+        Log.v(this, "onAnswerIncomingCall: Call = " + call + "video state = " +
+                videoState);
+        if (!VideoProfile.isVideo(videoState)) {
+            enableEarpiece();
+        } else {
+            enableSpeaker();
+        }
+    }
+
+    /**
+     * Called when user clicks on merge calls from the UI. Route audio to speaker if one of the
+     * calls being merged is a video call.
+     */
+    public void onMergeClicked() {
+        Log.v(this, "onMergeClicked");
+
+        if (VideoUtils.isVideoCall(CallList.getInstance().getBackgroundCall()) ||
+                VideoUtils.isVideoCall(CallList.getInstance().getActiveCall())) {
+            enableSpeaker();
+        }
+    }
+
+    /**
+     * Determines if the speakerphone should be automatically enabled for the call.  Speakerphone
+     * should be enabled if the call is a video call and if the adb property
+     * "persist.radio.call.audio.output" is true.
+     *
+     * @param videoState The video state of the call.
+     * @return {@code true} if the speakerphone should be enabled.
+     */
+    private static boolean canEnableSpeaker(int oldVideoState, int newVideoState) {
+        return !VideoProfile.isVideo(oldVideoState) &&
+                VideoProfile.isVideo(newVideoState) && isSpeakerEnabledForVideoCalls();
+    }
+
+    /**
+     * Determines if the speakerphone should be automatically enabled for video calls.
+     *
+     * @return {@code true} if the speakerphone should automatically be enabled.
+     */
+    private static boolean isSpeakerEnabledForVideoCalls() {
+        // TODO: we can't access adb properties from InCallUI. Need to refactor this.
+        return true;
+    }
+
+    /**
+     * Routes the call to the earpiece if audio is not being already routed to Earpiece and if
+     * bluetooth or wired headset is not connected.
+     */
+    private static void enableEarpiece() {
+        final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance();
+        if (telecomAdapter == null) {
+            Log.e(LOG_TAG, "enableEarpiece: TelecomAdapter is null");
+            return;
+        }
+
+        final int currentAudioMode = AudioModeProvider.getInstance().getAudioMode();
+        Log.v(LOG_TAG, "enableEarpiece: Current audio mode is - " + currentAudioMode);
+
+        /*
+         * For MO video calls, we mark that audio needs to be routed to speaker during
+         * call setup phase and audio routing then takes place when call state transitions
+         * to DIALING. If user decides to modify low battery MO video call to voice call,
+         * audio route needs to be changed to earpiece for which we need to unmark speaker
+         * audio route selection that was done during video call setup phase. To avoid below
+         * API from being a no operation, do not check for CallAudioState.ROUTE_EARPIECE
+         */
+        if (QtiCallUtils.isNotEnabled(CallAudioState.ROUTE_BLUETOOTH |
+                CallAudioState.ROUTE_WIRED_HEADSET,
+                currentAudioMode)) {
+            Log.v(LOG_TAG, "enableEarpiece: Set audio route to earpiece");
+            telecomAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
+        }
+    }
+
+    /**
+     * Routes the call to the speaker if audio is not being already routed to Speaker and if
+     * bluetooth or wired headset is not connected.
+     */
+    private static void enableSpeaker() {
+        final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance();
+        if (telecomAdapter == null) {
+            Log.e(LOG_TAG, "enableSpeaker: TelecomAdapter is null");
+            return;
+        }
+
+        final int currentAudioMode = AudioModeProvider.getInstance().getAudioMode();
+        Log.v(LOG_TAG, "enableSpeaker: Current audio mode is - " + currentAudioMode);
+
+        if(QtiCallUtils.isNotEnabled(CallAudioState.ROUTE_SPEAKER |
+                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET,
+                currentAudioMode)) {
+            Log.v(LOG_TAG, "enableSpeaker: Set audio route to speaker");
+            telecomAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+        }
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallCsRedialHandler.java b/InCallUI/src/com/android/incallui/InCallCsRedialHandler.java
new file mode 100644
index 0000000..017d636
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallCsRedialHandler.java
@@ -0,0 +1,254 @@
+/* Copyright (c) 2015, 2016, 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.
+ */
+
+package com.android.incallui;
+
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.view.WindowManager;
+import android.os.Bundle;
+
+import org.codeaurora.ims.utils.QtiCallUtils;
+import org.codeaurora.ims.QtiCallConstants;
+
+/*
+ * This class handles redialing a call on CS domain when current call ends with reason
+ * cs retry required
+ */
+public class InCallCsRedialHandler implements CallList.Listener {
+
+    private static InCallCsRedialHandler sInCallCsRedialHandler;
+    private Context mContext;
+    private CallList mCallList = null;
+    private AlertDialog mAlert = null;
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private InCallCsRedialHandler() {
+    }
+
+    /**
+     * Handles set up of the {@class InCallCsRedialHandler}. Instantiates the context needed by
+     * the class and adds a listener to listen to call substate changes.
+     */
+    public void setUp(Context context) {
+        mContext = context;
+        mCallList = CallList.getInstance();
+        mCallList.addListener(this);
+    }
+
+    /**
+     * Handles tear down of the {@class InCallCsRedialHandler}. Sets the context to null and
+     * unregisters it's call substate listener.
+     */
+    public void tearDown() {
+        mContext = null;
+        if (mCallList != null) {
+            mCallList.removeListener(this);
+            mCallList = null;
+        }
+    }
+
+    /**
+     * This method overrides onIncomingCall method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onIncomingCall(Call call) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onCallListChange method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onCallListChange(CallList list) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onUpgradeToVideo method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onUpgradeToVideo(Call call) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onDisconnect method of {@interface CallList.Listener}
+     */
+    @Override
+    public void onDisconnect(Call call) {
+        Log.i(this, "onDisconnect");
+        checkForCsRetry(call);
+    }
+
+    /**
+     * This method returns a singleton instance of {@class InCallCsRedialHandler}
+     */
+    public static synchronized InCallCsRedialHandler getInstance() {
+        if (sInCallCsRedialHandler == null) {
+            sInCallCsRedialHandler = new InCallCsRedialHandler();
+        }
+        return sInCallCsRedialHandler;
+    }
+
+    /*
+     * This method gets fail cause value corresponding to EXTRAS_KEY_CALL_FAIL_EXTRA_CODE key
+     */
+    private int getFailCauseFromExtras(Bundle extras) {
+        int failCause = QtiCallConstants.DISCONNECT_CAUSE_UNSPECIFIED;
+        if (extras != null) {
+            failCause = extras.getInt(QtiCallConstants.EXTRAS_KEY_CALL_FAIL_EXTRA_CODE,
+                    QtiCallConstants.DISCONNECT_CAUSE_UNSPECIFIED);
+        }
+        return failCause;
+    }
+
+    /*
+     * This method checks to see if CS Retry is required or not and if
+     * required, the method further checks the user selection option to decide
+     * whether to CS Redial automatically or based on user confirmation
+     */
+    private void checkForCsRetry(final Call call) {
+        final int failCause = getFailCauseFromExtras(call.getExtras());
+        Log.i(this, "checkForCsRetry failCause: " + failCause);
+        if (failCause != QtiCallConstants.CALL_FAIL_EXTRA_CODE_CALL_CS_RETRY_REQUIRED) {
+            return;
+        }
+
+        if (QtiCallUtils.isCsRetryEnabledByUser(mContext)) {
+            dialCsCall(call.getNumber());
+        } else {
+            showCsRedialDialogOnDisconnect(call.getNumber());
+        }
+    }
+
+    /*
+     * This method initiates a CS call
+     */
+    private void dialCsCall(String number) {
+        Log.i(this, "dialCsCall number: " + number);
+
+        final Uri uri = Uri.fromParts("tel", number, null);
+        final Intent intent = new Intent(Intent.ACTION_CALL, uri);
+        intent.putExtra(QtiCallConstants.EXTRA_CALL_DOMAIN, QtiCallConstants.DOMAIN_CS);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(this, "Activity for dialing new call isn't found.");
+        }
+    }
+
+    /*
+     * If user confirmation is required to retry the call on CS domain, this method
+     * displays a dialog seeking user confirmation
+     */
+    private void showCsRedialDialogOnDisconnect(final String dialString) {
+        final InCallActivity inCallActivity = InCallPresenter.getInstance().getActivity();
+
+        if (inCallActivity == null) {
+            Log.e(this, "showCsRedialDialogOnDisconnect inCallActivity is NULL");
+            return;
+        }
+        inCallActivity.dismissPendingDialogs();
+
+        mAlert = new AlertDialog.Builder(inCallActivity).setTitle(R.string.cs_redial_option)
+                .setMessage(R.string.cs_redial_msg)
+                .setPositiveButton(R.string.cs_redial_yes, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialCsCall(dialString);
+                    }
+                })
+                .setNegativeButton(R.string.cs_redial_no, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        //No implementation. Added for completeness
+                    }
+                })
+                .setOnDismissListener(new OnDismissListener() {
+                    @Override
+                    public void onDismiss(final DialogInterface dialog) {
+                        Log.d(this, "showCsRedialDialogOnDisconnect calling onDialogDismissed");
+                        onDialogDismissed();
+                    }
+                })
+                .create();
+
+        mAlert.setCanceledOnTouchOutside(false);
+        mAlert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+        mAlert.show();
+    }
+
+    /*
+     * This method returns true if dialog is showing else false
+     */
+    private boolean isCsRetryDialogShowing() {
+        return mAlert != null && mAlert.isShowing();
+    }
+
+    /**
+     * A dialog could have prevented in-call screen from being previously finished.
+     * This function checks to see if there should be any UI left and if not attempts
+     * to tear down the UI.
+     */
+    private void onDialogDismissed() {
+        mAlert = null;
+        InCallPresenter.getInstance().onDismissDialog();
+    }
+
+    /*
+     * This method dismisses the CS retry dialog
+     */
+    public void dismissPendingDialogs() {
+        if (isCsRetryDialogShowing()) {
+            mAlert.dismiss();
+            mAlert = null;
+        }
+    }
+
+    /*
+     * This method returns true if the dialog is still visible and waiting for user confirmation
+     * else false
+     */
+    public boolean hasPendingDialogs() {
+        return mAlert != null;
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java b/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
new file mode 100644
index 0000000..bdc46e1
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallLowBatteryListener.java
@@ -0,0 +1,519 @@
+
+/* Copyright (c) 2016, 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.
+ */
+
+package com.android.incallui;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.DialogInterface.OnKeyListener;
+import android.os.Bundle;
+import android.telecom.VideoProfile;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.android.incallui.Call.State;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.InCallUiListener;
+
+import com.google.common.base.Preconditions;
+import org.codeaurora.ims.QtiCallConstants;
+import org.codeaurora.ims.QtiImsException;
+import org.codeaurora.ims.QtiImsExtManager;
+
+
+/**
+ * This class is responsible for processing video call low battery indication.
+ * On detecting a low battery Video call, user shall be notified via a warning
+ * dialog for user to take informed decision (i.e. convert video call to voice
+ * call or hangup the video call etc). This class maintains a low battery map
+ * and entry is added to this map on detecting a low battery video call. The
+ * low battery map holds key value pairs with call object as key and a boolean
+ * as value. The boolean value determines whether low battery indication for the
+ * call can be processed or not. The map holds boolean value TRUE if low battery
+ * indication for the call can be processed and holds FALSE when the low battery
+ * indication processing is deferred or is done. For eg. the map holds FALSE
+ * value on detecting a low battery MT Video call and the handling is deferred until
+ * user decides to answer the call as Video in which case the map holds TRUE value
+ * signalling that low battery indication for the call can be processed now.
+ */
+public class InCallLowBatteryListener implements CallList.Listener, InCallDetailsListener,
+        InCallUiListener {
+
+    private static InCallLowBatteryListener sInCallLowBatteryListener;
+    private PrimaryCallTracker mPrimaryCallTracker;
+    private CallList mCallList = null;
+    private AlertDialog mAlert = null;
+    private Map<Call, Boolean> mLowBatteryMap = new ConcurrentHashMap<Call, Boolean>();
+    private final Boolean PROCESS_LOW_BATTERY = true;
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private InCallLowBatteryListener() {
+    }
+
+    /**
+     * Handles set up of the {@class InCallLowBatteryListener}.
+     */
+    public void setUp(Context context) {
+        mPrimaryCallTracker = new PrimaryCallTracker();
+        mCallList = CallList.getInstance();
+        mCallList.addListener(this);
+        InCallPresenter.getInstance().addListener(mPrimaryCallTracker);
+        InCallPresenter.getInstance().addIncomingCallListener(mPrimaryCallTracker);
+        InCallPresenter.getInstance().addDetailsListener(this);
+        InCallPresenter.getInstance().addInCallUiListener(this);
+    }
+
+    /**
+     * Handles tear down of the {@class InCallLowBatteryListener}.
+     */
+    public void tearDown() {
+        if (mCallList != null) {
+            mCallList.removeListener(this);
+            mCallList = null;
+        }
+        InCallPresenter.getInstance().removeListener(mPrimaryCallTracker);
+        InCallPresenter.getInstance().removeIncomingCallListener(mPrimaryCallTracker);
+        InCallPresenter.getInstance().removeDetailsListener(this);
+        InCallPresenter.getInstance().removeInCallUiListener(this);
+        mLowBatteryMap.clear();
+        mPrimaryCallTracker = null;
+    }
+
+     /**
+     * This method returns a singleton instance of {@class InCallLowBatteryListener}
+     */
+    public static synchronized InCallLowBatteryListener getInstance() {
+        if (sInCallLowBatteryListener == null) {
+            sInCallLowBatteryListener = new InCallLowBatteryListener();
+        }
+        return sInCallLowBatteryListener;
+    }
+
+    /**
+     * This method overrides onIncomingCall method of {@interface CallList.Listener}
+     * @param call The call that is in incoming state
+     */
+    @Override
+    public void onIncomingCall(Call call) {
+        maybeAddToLowBatteryMap(call);
+        // if low battery dialog is already visible to user, dismiss it
+        dismissPendingDialogs();
+    }
+
+    /**
+     * This method overrides onCallListChange method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onCallListChange(CallList list) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onUpgradeToVideo method of {@interface CallList.Listener}
+     * @param call The call for which upgrade request is received
+     */
+    @Override
+    public void onUpgradeToVideo(Call call) {
+        //if low battery dialog is visible to user, dismiss it
+        dismissPendingDialogs();
+    }
+
+    /**
+     * This method overrides onDisconnect method of {@interface CallList.Listener}
+     * @param call The call that is disconnected
+     */
+    @Override
+    public void onDisconnect(Call call) {
+        Log.d(this, "onDisconnect call: " + call);
+        if (call == null) {
+            return;
+        }
+
+        mLowBatteryMap.remove(call);
+    }
+
+    /**
+      * This API handles InCallActivity destroy when low battery dialog is showing
+      */
+    public void onDestroyInCallActivity() {
+        if (dismissPendingDialogs()) {
+            Log.i(this, "onDestroyInCallActivity dismissed low battery dialog");
+
+            /* Activity is destroyed when low battery dialog is showing, possibly
+               by removing the activity from recent tasks list etc. Handle this by
+               dismissing the existing low battery dialog and marking the entry
+               against the call in low battery map that the low battery indication
+               needs to be reprocessed for eg. when user brings back the call to
+               foreground by pulling it from notification bar */
+            Call call = mPrimaryCallTracker.getPrimaryCall();
+            if (call == null) {
+                Log.w(this, "onDestroyInCallActivity call is null");
+                return;
+            }
+            mLowBatteryMap.replace(call, PROCESS_LOW_BATTERY);
+        }
+    }
+
+    /**
+     * This API conveys if incall experience is showing or not.
+     *
+     * @param showing TRUE if incall experience is showing else FALSE
+     */
+    @Override
+    public void onUiShowing(boolean showing) {
+        Call call = mPrimaryCallTracker.getPrimaryCall();
+        Log.d(this, "onUiShowing showing: " + showing + " call = " + call);
+        if (call == null || !showing) {
+            return;
+        }
+
+        maybeProcessLowBatteryIndication(call);
+    }
+
+    /**
+     * When call is answered, this API checks to see if UE is under low battery or not
+     * and accordingly processes the low battery video call and returns TRUE if
+     * user action to answer the call is handled by this API else FALSE.
+     *
+     * @param call The call that is being answered
+     * @param videoState The videoState type with which user answered the MT call
+     */
+    public boolean onAnswerIncomingCall(Call call, int videoState) {
+        Log.d(this, "onAnswerIncomingCall = " + call + " videoState = " + videoState);
+        if (call == null || !mPrimaryCallTracker.isPrimaryCall(call)) {
+            return false;
+        }
+
+        if(!(isLowBatteryVideoCall(call) && VideoProfile.isBidirectional(videoState))) {
+            /* As user didnt accept the call as Video, low battery
+               processing for that call isn't required any more */
+            return false;
+        }
+
+        if (!mLowBatteryMap.containsKey(call)) {
+            Log.w(this, "onAnswerIncomingCall no call in low battery map");
+            return false;
+        }
+
+        /* There can be multiple user attempts to answer the call as Video from
+           HUN (HeadsUp Notification) by pulling the call from notification bar.
+           In such cases, avoid multiple processing of low battery indication */
+        if (!isLowBatteryDialogShowing()) {
+           /* There is user action to answer the call as Video. Update the low battery map
+              to indicate that low battery indication for this call can be processed now */
+            mLowBatteryMap.replace(call, PROCESS_LOW_BATTERY);
+            maybeProcessLowBatteryIndication(call);
+        }
+        return true;
+    }
+
+    private boolean isLowBattery(android.telecom.Call.Details details) {
+        final Bundle extras =  (details != null) ? details.getExtras() : null;
+        final boolean isLowBattery = (extras != null) ? extras.getBoolean(
+                QtiCallConstants.LOW_BATTERY_EXTRA_KEY, false) : false;
+        Log.d(this, "isLowBattery : " + isLowBattery);
+        return isLowBattery;
+    }
+
+    private boolean isActiveUnPausedVideoCall(Call call) {
+        return VideoUtils.isActiveVideoCall(call) && !VideoProfile.isPaused(call.getVideoState());
+    }
+
+    private boolean isLowBatteryVideoCall(Call call) {
+        return call != null && VideoUtils.isVideoCall(call) &&
+                isLowBattery(call.getTelecomCall().getDetails());
+    }
+
+    // Return TRUE if low battery indication for the call can be processed else return FALSE
+    private boolean canProcessLowBatteryIndication(Call call) {
+        /* Low Battery indication can be processed if:
+           1. Value stored against the call in low battery map is true
+           2. InCallActivity is created */
+        if (call == null || InCallPresenter.getInstance().getActivity() == null) {
+            return false;
+        }
+
+        // we get null value if map contains no mapping for the key
+        if (mLowBatteryMap.get(call) != null) {
+            return mLowBatteryMap.get(call);
+        }
+
+        Log.w(this, "canProcessLowBatteryIndication no mapping for call in low battery map");
+        return false;
+    }
+
+    private void maybeAddToLowBatteryMap(Call call) {
+        if (call == null) {
+            return;
+        }
+
+        if(isLowBatteryVideoCall(call)) {
+           /* For MT Video calls, do not mark right away that Low battery indication
+              can to be processed since the handling kicks-in only after user decides
+              to answer the call as Video handled via onAnswerIncomingCall API*/
+            mLowBatteryMap.putIfAbsent(call, !VideoUtils.isIncomingVideoCall(call));
+        }
+    }
+
+    /**
+     * Handles changes to the details of the call.
+     *
+     * @param call The call for which the details changed.
+     * @param details The new call details.
+     */
+    @Override
+    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+        Log.i(this, "onDetailsChanged call = " + call + " details = " + details);
+
+        if (mPrimaryCallTracker.getPrimaryCall() == null) {
+           /* primarycall is null may signal the possibility that there is only a single call and
+              is getting disconnected. So, try to dismiss low battery alert dialogue (if any). This
+              is to handle unintentional dismiss for add VT call use-cases wherein low battery alert
+              dialog is waiting for user input and the held call is remotely disconnected */
+            Log.i(this,"onDetailsChanged: no primary call.Clear the map/dismiss low battery alert");
+            mLowBatteryMap.remove(call);
+            dismissPendingDialogs();
+            return;
+        }
+
+        if (call == null || !mPrimaryCallTracker.isPrimaryCall(call)) {
+            Log.d(this," onDetailsChanged: call is null/Details not for primary call");
+            return;
+        }
+
+        /* Low Battery handling for MT Video call kicks in only when user decides
+           to answer the call as Video call so ignore the incoming video call
+           processing here for now */
+        if (VideoUtils.isIncomingVideoCall(call)) {
+            return;
+        }
+
+        if (!VideoUtils.isVideoCall(call)) {
+            /* There can be chances that Video call gets downgraded when
+               low battery alert dialog is waiting for user confirmation.
+               Handle this by dismissing the dialog (if any) */
+            dismissPendingDialogs();
+            return;
+        }
+
+        //process MO/Active Video call low battery indication
+        maybeAddToLowBatteryMap(call);
+        maybeProcessLowBatteryIndication(call);
+    }
+
+    /**
+      * disconnects MO video call that is waiting for user confirmation on
+      * low battery dialog
+      * @param call The probable call that may need to be disconnected
+      **/
+    private void maybeDisconnectMoCall(Call call) {
+        if (call == null || !isLowBatteryVideoCall(call)) {
+            return;
+        }
+
+        if (call.getState() == Call.State.DIALING) {
+            // dismiss the low battery dialog that is waiting for user input
+            dismissPendingDialogs();
+
+            Log.d(this, "disconnect MO call this is waiting for user input");
+            TelecomAdapter.getInstance().disconnectCall(call.getId());
+        }
+    }
+
+    private void maybeProcessLowBatteryIndication(Call call) {
+        if (!isLowBatteryVideoCall(call)) {
+            return;
+        }
+
+        if (canProcessLowBatteryIndication(call)) {
+            displayLowBatteryAlert(call);
+            //mark against the call that the respective low battery indication is processed
+            mLowBatteryMap.replace(call, !PROCESS_LOW_BATTERY);
+        }
+    }
+
+    /*
+     * This method displays one of below alert dialogs when UE is in low battery
+     * For Active Video Calls:
+     *     1. hangup alert dialog in absence of voice capabilities
+     *     2. downgrade to voice call alert dialog in the presence of voice
+     *        capabilities
+     * For MT Video calls wherein user decided to accept the call as Video and for MO Video Calls:
+     *     1. alert dialog asking user confirmation to convert the video call to voice call or
+     *        to continue the call as video call
+     * For MO Video calls, seek user confirmation to continue the video call as is or convert the
+     * video call to voice call
+     */
+    private void displayLowBatteryAlert(final Call call) {
+        //if low battery dialog is already visible to user, dismiss it
+        dismissPendingDialogs();
+
+        final InCallActivity inCallActivity = InCallPresenter.getInstance().getActivity();
+        if (inCallActivity == null) {
+            Log.w(this, "displayLowBatteryAlert inCallActivity is NULL");
+            return;
+        }
+
+        AlertDialog.Builder alertDialog = new AlertDialog.Builder(inCallActivity);
+        alertDialog.setTitle(R.string.low_battery);
+        alertDialog.setOnDismissListener(new OnDismissListener() {
+            @Override
+            public void onDismiss(final DialogInterface dialog) {
+            }
+        });
+
+        if (VideoUtils.isIncomingVideoCall(call)) {
+            alertDialog.setNegativeButton(R.string.low_battery_convert, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                     Log.d(this, "displayLowBatteryAlert answer as Voice Call");
+                     TelecomAdapter.getInstance().answerCall(call.getId(),
+                             VideoProfile.STATE_AUDIO_ONLY);
+                }
+            });
+
+            alertDialog.setMessage(R.string.low_battery_msg);
+            alertDialog.setPositiveButton(R.string.low_battery_yes, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                     Log.d(this, "displayLowBatteryAlert answer as Video Call");
+                     TelecomAdapter.getInstance().answerCall(call.getId(),
+                             VideoProfile.STATE_BIDIRECTIONAL);
+                }
+            });
+        } else if (VideoUtils.isOutgoingVideoCall(call)) {
+            alertDialog.setNegativeButton(R.string.low_battery_convert, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                     Log.d(this, "displayLowBatteryAlert place Voice Call");
+                     //Change the audio route to earpiece
+                     InCallAudioManager.getInstance().onModifyCallClicked(call,
+                             VideoProfile.STATE_AUDIO_ONLY);
+                     try {
+                         QtiImsExtManager.getInstance().resumePendingCall(
+                                 VideoProfile.STATE_AUDIO_ONLY);
+                     } catch (QtiImsException e) {
+                         Log.e(this, "resumePendingCall exception " + e);
+                     }
+                }
+            });
+
+            alertDialog.setMessage(R.string.low_battery_msg);
+            alertDialog.setPositiveButton(R.string.low_battery_yes, new OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                     Log.d(this, "displayLowBatteryAlert place Video Call");
+                     try {
+                         QtiImsExtManager.getInstance().resumePendingCall(
+                                 VideoProfile.STATE_BIDIRECTIONAL);
+                     } catch (QtiImsException e) {
+                         Log.e(this, "resumePendingCall exception " + e);
+                     }
+                }
+            });
+        } else if (isActiveUnPausedVideoCall(call)) {
+            if (QtiCallUtils.hasVoiceCapabilities(call)) {
+                //active video call can be downgraded to voice
+                alertDialog.setMessage(R.string.low_battery_msg);
+                alertDialog.setPositiveButton(R.string.low_battery_yes, null);
+                alertDialog.setNegativeButton(R.string.low_battery_convert, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        Log.d(this, "displayLowBatteryAlert downgrading to voice call");
+                        QtiCallUtils.downgradeToVoiceCall(call);
+                    }
+                });
+            } else {
+                /* video call doesn't have downgrade capabilities, so alert the user
+                   with a hangup dialog*/
+                alertDialog.setMessage(R.string.low_battery_hangup_msg);
+                alertDialog.setNegativeButton(R.string.low_battery_no, null);
+                alertDialog.setPositiveButton(R.string.low_battery_yes, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        Log.d(this, "displayLowBatteryAlert hanging up the call: " + call);
+                        call.setState(Call.State.DISCONNECTING);
+                        CallList.getInstance().onUpdate(call);
+                        TelecomAdapter.getInstance().disconnectCall(call.getId());
+                    }
+                });
+            }
+        }
+
+        mAlert = alertDialog.create();
+        mAlert.setOnKeyListener(new OnKeyListener() {
+            @Override
+            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+                Log.d(this, "on Alert displayLowBattery keyCode = " + keyCode);
+                if (keyCode == KeyEvent.KEYCODE_BACK) {
+                    // On Back key press, disconnect MO low battery video call
+                    // that is waiting for user input
+                    maybeDisconnectMoCall(call);
+                    return true;
+                }
+                return false;
+            }
+        });
+        mAlert.setCanceledOnTouchOutside(false);
+        mAlert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+        mAlert.show();
+    }
+
+    /*
+     * This method returns true if dialog is showing else false
+     */
+    private boolean isLowBatteryDialogShowing() {
+        return mAlert != null && mAlert.isShowing();
+    }
+
+    /*
+     * This method dismisses the low battery dialog and
+     * returns true if dialog is dimissed else false
+     */
+    public boolean dismissPendingDialogs() {
+        if (isLowBatteryDialogShowing()) {
+            mAlert.dismiss();
+            mAlert = null;
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallMessageController.java b/InCallUI/src/com/android/incallui/InCallMessageController.java
new file mode 100644
index 0000000..aa5a57e
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallMessageController.java
@@ -0,0 +1,521 @@
+/* Copyright (c) 2015, 2016, 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.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallVideoCallCallbackNotifier.VideoEventListener;
+import com.android.incallui.InCallVideoCallCallbackNotifier.SessionModificationListener;
+import org.codeaurora.ims.QtiCallConstants;
+import org.codeaurora.ims.QtiCarrierConfigs;
+import org.codeaurora.ims.QtiVideoCallDataUsage;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
+/**
+ * This class listens to incoming events for the listener classes it implements. It should
+ * handle all UI notification to be shown to the user for any indication that is required to be
+ * shown like call substate indication, video quality indication, etc.
+ * For e.g., this class implements {@class InCallSubstateListener} and when call substate changes,
+ * {@class CallSubstateNotifier} notifies it through the onCallSubstateChanged callback.
+ */
+public class InCallMessageController implements InCallSubstateListener, VideoEventListener,
+        CallList.Listener, SessionModificationListener, InCallSessionModificationCauseListener,
+        InCallDetailsListener {
+
+    private INetworkStatsService mStatsService;
+    private IConnectivityManager mConnManager;
+    private long previousLteUsage;
+    private long previousWlanUsage;
+
+    private static InCallMessageController sInCallMessageController;
+
+    private PrimaryCallTracker mPrimaryCallTracker;
+
+    private Context mContext;
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private InCallMessageController() {
+    }
+
+    /**
+     * Handles set up of the {@class InCallMessageController}. Instantiates the context needed by
+     * the class and adds a listener to listen to call substate changes, video event changes,
+     * session modification cause changes, call state changes.
+     */
+    public void setUp(Context context) {
+        mContext = context;
+        mPrimaryCallTracker = new PrimaryCallTracker();
+        CallSubstateNotifier.getInstance().addListener(this);
+        InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this);
+        CallList.getInstance().addListener(this);
+        SessionModificationCauseNotifier.getInstance().addListener(this);
+        InCallPresenter.getInstance().addListener(mPrimaryCallTracker);
+        InCallVideoCallCallbackNotifier.getInstance().addSessionModificationListener(this);
+        InCallPresenter.getInstance().addDetailsListener(this);
+        mStatsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        mConnManager = IConnectivityManager.Stub.asInterface(
+                ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+    }
+
+    /**
+     * Handles tear down of the {@class InCallMessageController}. Sets the context to null and
+     * unregisters it's call substate,  video event, session modification cause, call state
+     * listeners.
+     */
+    public void tearDown() {
+        mContext = null;
+        CallSubstateNotifier.getInstance().removeListener(this);
+        InCallVideoCallCallbackNotifier.getInstance().removeVideoEventListener(this);
+        CallList.getInstance().removeListener(this);
+        SessionModificationCauseNotifier.getInstance().removeListener(this);
+        InCallPresenter.getInstance().removeListener(mPrimaryCallTracker);
+        InCallVideoCallCallbackNotifier.getInstance().removeSessionModificationListener(this);
+        InCallPresenter.getInstance().removeDetailsListener(this);
+        mPrimaryCallTracker = null;
+        mStatsService = null;
+        mConnManager = null;
+        previousLteUsage = 0;
+        previousWlanUsage = 0;
+    }
+
+    /**
+     * This method returns a singleton instance of {@class InCallMessageController}
+     */
+    public static synchronized InCallMessageController getInstance() {
+        if (sInCallMessageController == null) {
+            sInCallMessageController = new InCallMessageController();
+        }
+        return sInCallMessageController;
+    }
+
+    /**
+     * This method overrides onCallSubstateChanged method of {@interface InCallSubstateListener}
+     * We are notified when call substate changes and display a toast message on the UI.
+     */
+    @Override
+    public void onCallSubstateChanged(final Call call, final int callSubstate) {
+        Log.d(this, "onCallSubstateChanged - Call : " + call + " call substate changed to " +
+                callSubstate);
+
+        if (mContext == null || !mPrimaryCallTracker.isPrimaryCall(call)) {
+            Log.e(this, "onCallSubstateChanged - Context is null/not primary call.");
+            return;
+        }
+
+        String callSubstateChangedText = "";
+
+        if (QtiCallUtils.isEnabled(
+                QtiCallConstants.CALL_SUBSTATE_AUDIO_CONNECTED_SUSPENDED, callSubstate)) {
+            callSubstateChangedText +=
+                    mContext.getResources().getString(
+                    R.string.call_substate_connected_suspended_audio);
+        }
+
+        if (QtiCallUtils.isEnabled(
+                QtiCallConstants.CALL_SUBSTATE_VIDEO_CONNECTED_SUSPENDED, callSubstate)) {
+            callSubstateChangedText +=
+                    mContext.getResources().getString(
+                    R.string.call_substate_connected_suspended_video);
+        }
+
+        if (QtiCallUtils.isEnabled(QtiCallConstants.CALL_SUBSTATE_AVP_RETRY, callSubstate)) {
+            callSubstateChangedText +=
+                    mContext.getResources().getString(R.string.call_substate_avp_retry);
+        }
+
+        if (QtiCallUtils.isNotEnabled(QtiCallConstants.CALL_SUBSTATE_ALL, callSubstate)) {
+            callSubstateChangedText =
+                    mContext.getResources().getString(R.string.call_substate_call_resumed);
+        }
+
+        if (!callSubstateChangedText.isEmpty()) {
+            String callSubstateLabelText = mContext.getResources().getString(
+                    R.string.call_substate_label);
+            QtiCallUtils.displayToast(mContext, callSubstateLabelText + callSubstateChangedText);
+        }
+    }
+
+    /**
+     * This method overrides onVideoQualityChanged method of {@interface VideoEventListener}
+     * We are notified when video quality of the call changed and display a message on the UI.
+     */
+    @Override
+    public void onVideoQualityChanged(final Call call, final int videoQuality) {
+        Log.d(this, "onVideoQualityChanged: - Call : " + call + " Video quality changed to " +
+                videoQuality);
+
+        if (mContext == null || !mPrimaryCallTracker.isPrimaryCall(call)) {
+            Log.e(this, "onVideoQualityChanged - Context is null/not primary call.");
+            return;
+        }
+        if (QtiImsExtUtils.isCarrierConfigEnabled(mContext,
+                QtiCarrierConfigs.SHOW_VIDEO_QUALITY_TOAST)) {
+            final Resources resources = mContext.getResources();
+            final String videoQualityChangedText = resources.getString(
+                    R.string.video_quality_changed) + resources.getString(
+                    QtiCallUtils.getVideoQualityResourceId(videoQuality));
+            QtiCallUtils.displayToast(mContext, videoQualityChangedText);
+        }
+    }
+
+    /**
+     * This method overrides onCallSessionEvent method of {@interface VideoEventListener}
+     * We are notified when a new call session event is sent and display a message on the UI.
+     */
+    @Override
+    public void onCallSessionEvent(final int event) {
+        Log.d(this, "onCallSessionEvent: event = " + event);
+
+        if (mContext == null) {
+            Log.e(this, "onCallSessionEvent - Context is null.");
+            return;
+        }
+        if (QtiImsExtUtils.isCarrierConfigEnabled(mContext,
+                QtiCarrierConfigs.SHOW_CALL_SESSION_EVENT_TOAST)) {
+            QtiCallUtils.displayToast(mContext, QtiCallUtils.getCallSessionResourceId(event));
+        }
+    }
+
+    /**
+     * Handles changes to the details of the call.
+     *
+     * @param call The call for which the details changed.
+     * @param details The new call details.
+     */
+    @Override
+    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+        Log.d(this, " onDetailsChanged call=" + call + " details=" + details);
+
+        if (mContext == null || details == null) {
+            Log.e(this," onDetailsChanged: context/details is null");
+            return;
+        }
+
+        final Bundle extras =  details.getExtras();
+        final QtiVideoCallDataUsage dataUsage = (extras != null) ?
+                (QtiVideoCallDataUsage)extras.getParcelable(
+                QtiCallConstants.VIDEO_CALL_DATA_USAGE_KEY) : null;
+        if (dataUsage == null) {
+            return;
+        }
+
+        long lteUsage = dataUsage.getLteDataUsage();
+        long wlanUsage = dataUsage.getWlanDataUsage();
+        Log.i(this, "onDetailsChanged LTE data value = " + lteUsage + " WiFi data value = " +
+                wlanUsage);
+
+        if (mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_video_call_datausage_enable)) {
+            recordDataUsage(lteUsage, wlanUsage);
+        }
+
+
+        if (QtiImsExtUtils.isCarrierConfigEnabled(mContext,
+                QtiCarrierConfigs.SHOW_DATA_USAGE_TOAST)) {
+            final String dataUsageChangedText = mContext.getResources().getString(
+                    R.string.wlan_data_usage_label) + wlanUsage;
+            QtiCallUtils.displayToast(mContext, dataUsageChangedText);
+        }
+    }
+
+    /**
+     * This method overrides onCallDataUsageChange method of {@interface VideoEventListener}
+     *  We are notified when data usage is changed and display a message on the UI.
+     */
+    @Override
+    public void onCallDataUsageChange(final long dataUsage) {
+        Log.d(this, "onCallDataUsageChange: dataUsage = " + dataUsage);
+        if (QtiImsExtUtils.isCarrierConfigEnabled(mContext,
+                QtiCarrierConfigs.SHOW_DATA_USAGE_TOAST)) {
+            final String dataUsageChangedText = mContext.getResources().getString(
+                    R.string.lte_data_usage_label) + dataUsage;
+            QtiCallUtils.displayToast(mContext, dataUsageChangedText);
+        }
+    }
+
+    /**
+     * This method overrides onPeerPauseStateChanged method of {@interface VideoEventListener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onPeerPauseStateChanged(final Call call, final boolean paused) {
+        //no-op
+    }
+
+    /**
+     * This method overrides onIncomingCall method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onIncomingCall(Call call) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onCallListChange method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onCallListChange(CallList list) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onUpgradeToVideo method of {@interface CallList.Listener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onUpgradeToVideo(Call call) {
+        // no-op
+    }
+
+    /**
+     * This method overrides onDisconnect method of {@interface CallList.Listener}
+     */
+    @Override
+    public void onDisconnect(final Call call) {
+        Bundle extras = call.getExtras();
+        if (extras == null) {
+            Log.w(this, "onDisconnect: null Extras");
+            return;
+        }
+        final int errorCode = extras.getInt(QtiCallConstants.EXTRAS_KEY_CALL_FAIL_EXTRA_CODE,
+                QtiCallConstants.DISCONNECT_CAUSE_UNSPECIFIED);
+        Log.d(this, "onDisconnect: code = " + errorCode);
+        showCallDisconnectInfo(errorCode);
+    }
+
+    /**
+     * This method displays specific disconnect information to user
+     */
+    private void showCallDisconnectInfo(int errorCode) {
+       if(errorCode == QtiCallConstants.DISCONNECT_CAUSE_UNSPECIFIED) {
+          return;
+       }
+
+       switch (errorCode) {
+         case QtiCallConstants.CALL_FAIL_EXTRA_CODE_LTE_3G_HA_FAILED:
+             QtiCallUtils.displayToast(mContext, R.string.call_failed_ho_not_feasible);
+             break;
+         case QtiCallConstants.CALL_FAIL_EXTRA_CODE_LOCAL_LOW_BATTERY:
+             QtiCallUtils.displayToast(mContext, R.string.call_failed_due_to_low_battery);
+             break;
+         case QtiCallConstants.CALL_FAIL_EXTRA_CODE_LOCAL_VALIDATE_NUMBER:
+             QtiCallUtils.displayToast(mContext, R.string.call_failed_due_to_validate_number);
+             break;
+         default:
+             break;
+       }
+    }
+
+    /*
+     * Handles any session modifictaion cause changes in the call.
+     *
+     * @param call The call for which orientation mode changed.
+     * @param sessionModificationCause The new session modifictaion cause
+     */
+    @Override
+    public void onSessionModificationCauseChanged(Call call, int sessionModificationCause) {
+        Log.d(this, "onSessionModificationCauseChanged: Call : " + call +
+                " Call modified due to "  + sessionModificationCause);
+
+        if (mContext == null || !mPrimaryCallTracker.isPrimaryCall(call)) {
+            Log.e(this,
+                    "onSessionModificationCauseChanged- Context is null/not primary call.");
+            return;
+        }
+
+        QtiCallUtils.displayToast(mContext,
+                getSessionModificationCauseResourceId(sessionModificationCause));
+    }
+
+    /**
+     * This method returns the string resource id (i.e. display string) that corresponds to the
+     * session modification cause code.
+     */
+    private static int getSessionModificationCauseResourceId(int cause) {
+        switch(cause) {
+            case QtiCallConstants.CAUSE_CODE_UNSPECIFIED:
+                return R.string.session_modify_cause_unspecified;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_UPGRADE_LOCAL_REQ:
+                return R.string.session_modify_cause_upgrade_local_request;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_UPGRADE_REMOTE_REQ:
+                return R.string.session_modify_cause_upgrade_remote_request;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_LOCAL_REQ:
+                return R.string.session_modify_cause_downgrade_local_request;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_REMOTE_REQ:
+                return R.string.session_modify_cause_downgrade_remote_request;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_RTP_TIMEOUT:
+                return R.string.session_modify_cause_downgrade_rtp_timeout;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_QOS:
+                return R.string.session_modify_cause_downgrade_qos;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_PACKET_LOSS:
+                return R.string.session_modify_cause_downgrade_packet_loss;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_LOW_THRPUT:
+                return R.string.session_modify_cause_downgrade_low_thrput;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_THERM_MITIGATION:
+                return R.string.session_modify_cause_downgrade_thermal_mitigation;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_LIPSYNC:
+                return R.string.session_modify_cause_downgrade_lipsync;
+            case QtiCallConstants.CAUSE_CODE_SESSION_MODIFY_DOWNGRADE_GENERIC_ERROR:
+            default:
+                return R.string.session_modify_cause_downgrade_generic_error;
+        }
+    }
+
+    @Override
+    public void onUpgradeToVideoRequest(Call call, int videoState) {
+        //no-op
+    }
+
+    @Override
+    public void onUpgradeToVideoFail(int error, Call call) {
+        Log.d(this, "onUpgradeToVideoFail: error = " + error);
+        showUpgradeFailInfo(error);
+    }
+
+    private void showUpgradeFailInfo(int errorCode) {
+        switch (errorCode) {
+            case QtiCallConstants.SESSION_MODIFY_REQUEST_FAILED_LOW_BATTERY:
+                QtiCallUtils.displayToast(mContext,
+                        R.string.modify_call_failed_due_to_low_battery);
+                break;
+            default:
+                break;
+        }
+    }
+
+     /**
+      * This method is used to ignore the repeatly calling onDetailsChanged
+      */
+     private boolean hasDataUsageChanged(long lteUsage, long wlanUsage) {
+         boolean hasChanged = false;
+         if (previousLteUsage != lteUsage) {
+             hasChanged = true;
+             previousLteUsage = lteUsage;
+         }
+         if (previousWlanUsage != wlanUsage) {
+             hasChanged = true;
+             previousWlanUsage = wlanUsage;
+         }
+         return hasChanged;
+     }
+
+     private void recordDataUsage(long lteUsage, long wlanUsage) {
+         String wifiIface;
+         String imsIface;
+         if(!hasDataUsageChanged(lteUsage, wlanUsage)) {
+             return;
+         }
+
+         if (wlanUsage != 0) {
+             wifiIface = getWifiIface();
+             if (wifiIface != null)
+                 recordUsage(wifiIface, ConnectivityManager.TYPE_WIFI, wlanUsage, 0);
+         }
+
+         if (lteUsage != 0 ) {
+             imsIface = getImsIface();
+             if (imsIface != null)
+                 recordUsage(imsIface, ConnectivityManager.TYPE_MOBILE, lteUsage, 0);
+         }
+     }
+
+     private void recordUsage(String ifaces, int ifaceType, long rx, long tx) {
+         if (ifaces == null) {
+             Log.d(this, "recordDataUseage ifaces is null");
+             return;
+         }
+         Log.d(this,"recordDataUseage ifaces ="+ ifaces + "   ifaceType=" + ifaceType +
+                 "rx = " + rx + " tx =" + tx);
+
+         try {
+             mStatsService.recordVideoCallData(ifaces, ifaceType, rx, tx);
+         } catch (RuntimeException e) {
+             Log.e(this, "recordDataUseage RuntimeException" + e);
+         } catch (RemoteException e) {
+             Log.e(this, "recordDataUseage RemoteException" + e);
+         }
+     }
+
+    private String getImsIface() {
+        final NetworkState[] states;
+        try {
+            states = mConnManager.getAllNetworkState();
+        } catch (RemoteException e) {
+            Log.e(this, "getVoiceCallIfaces RemoteException" + e);
+            return null;
+        }
+
+        if (states != null) {
+            for (NetworkState state : states) {
+                if (state.networkInfo.isConnected() && state.networkCapabilities.hasCapability(
+                        NetworkCapabilities.NET_CAPABILITY_IMS)) {
+                    final boolean isMobile = ConnectivityManager.isNetworkTypeMobile(
+                            state.networkInfo.getType());
+                    final String baseIface = state.linkProperties.getInterfaceName();
+                    if (isMobile)
+                        return baseIface;
+                }
+            }
+        }
+        return null;
+    }
+
+    private String getWifiIface() {
+        final LinkProperties wifiLinkProperties;
+        try {
+            wifiLinkProperties =
+                    mConnManager.getLinkPropertiesForType(ConnectivityManager.TYPE_WIFI);
+            if (wifiLinkProperties != null) {
+                return wifiLinkProperties.getInterfaceName();
+            }
+        } catch (RemoteException e) {
+            Log.e(this, "get wifi Iface RemoteException" + e);
+        }
+        return null;
+    }
+
+
+}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index c3ca6de..0acc35e 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -20,13 +20,16 @@
 
 import android.app.ActivityManager.TaskDescription;
 import android.app.FragmentManager;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
+
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Point;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.PowerManager;
 import android.provider.CallLog;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
@@ -123,6 +126,8 @@
     private InCallCameraManager mInCallCameraManager = null;
     private AnswerPresenter mAnswerPresenter = new AnswerPresenter();
     private FilteredNumberAsyncQueryHandler mFilteredQueryHandler;
+    private PowerManager mPowerManager;
+    private PowerManager.WakeLock mWakeLock = null;
 
     /**
      * Whether or not we are currently bound and waiting for Telecom to send us a new call.
@@ -331,8 +336,16 @@
         mProximitySensor = proximitySensor;
         addListener(mProximitySensor);
 
+        // dismiss any pending dialogues related to earlier call, which
+        // are no longer relevant now.
+        if (isActivityStarted()) {
+            mInCallActivity.dismissPendingDialogs();
+        }
         addIncomingCallListener(mAnswerPresenter);
         addInCallUiListener(mAnswerPresenter);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
+                PowerManager.ACQUIRE_CAUSES_WAKEUP, "InCallPresenter");
 
         mCallList = callList;
         mExternalCallList = externalCallList;
@@ -345,7 +358,10 @@
         // will kick off an update and the whole process can start.
         mCallList.addListener(this);
 
+        InCallCsRedialHandler.getInstance().setUp(mContext);
+        InCallUiStateNotifier.getInstance().setUp(mContext);
         VideoPauseController.getInstance().setUp(this);
+        InCallLowBatteryListener.getInstance().setUp(mContext);
         InCallVideoCallCallbackNotifier.getInstance().addSessionModificationListener(this);
 
         mFilteredQueryHandler = new FilteredNumberAsyncQueryHandler(context.getContentResolver());
@@ -353,6 +369,14 @@
         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
         mCallList.setFilteredNumberQueryHandler(mFilteredQueryHandler);
 
+        InCallMessageController.getInstance().setUp(mContext);
+        OrientationModeHandler.getInstance().setUp();
+        addDetailsListener(CallSubstateNotifier.getInstance());
+        addDetailsListener(SessionModificationCauseNotifier.getInstance());
+        CallList.getInstance().addListener(CallSubstateNotifier.getInstance());
+        CallList.getInstance().addListener(SessionModificationCauseNotifier.getInstance());
+
+        InCallZoomController.getInstance().setUp(mContext);
         Log.d(this, "Finished InCallPresenter.setUp");
     }
 
@@ -373,12 +397,30 @@
 
         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
         VideoPauseController.getInstance().tearDown();
+        InCallUiStateNotifier.getInstance().tearDown();
+        InCallLowBatteryListener.getInstance().tearDown();
         InCallVideoCallCallbackNotifier.getInstance().removeSessionModificationListener(this);
+        InCallMessageController.getInstance().tearDown();
+        OrientationModeHandler.getInstance().tearDown();
+        removeDetailsListener(CallSubstateNotifier.getInstance());
+
+        InCallZoomController.getInstance().tearDown();
+        removeDetailsListener(SessionModificationCauseNotifier.getInstance());
+        CallList.getInstance().removeListener(CallSubstateNotifier.getInstance());
+        CallList.getInstance().removeListener(SessionModificationCauseNotifier.getInstance());
     }
 
     private void attemptFinishActivity() {
         final boolean doFinish = (mInCallActivity != null && isActivityStarted());
         Log.i(this, "Hide in call UI: " + doFinish);
+
+        if ((mCallList != null)
+                && (CallList.getInstance().isDsdaEnabled())
+                && !(mCallList.hasAnyLiveCall(mCallList.getActiveSubId()))) {
+            Log.d(this, "Switch active sub");
+            if (mCallList.switchToOtherActiveSub()) return;
+        }
+
         if (doFinish) {
             mInCallActivity.setExcludeFromRecents(true);
             mInCallActivity.finish();
@@ -661,6 +703,9 @@
                     callList.getOutgoingCall() != null;
             mInCallActivity.dismissKeyguard(hasCall);
         }
+        if (CallList.getInstance().isDsdaEnabled() && (mInCallActivity != null)) {
+            mInCallActivity.updateDsdaTab();
+        }
     }
 
     /**
@@ -679,6 +724,11 @@
         for (IncomingCallListener listener : mIncomingCallListeners) {
             listener.onIncomingCall(oldState, mInCallState, call);
         }
+
+        if (CallList.getInstance().isDsdaEnabled() && (mInCallActivity != null)) {
+            mInCallActivity.updateDsdaTab();
+        }
+        wakeUpScreen();
     }
 
     @Override
@@ -703,6 +753,7 @@
         if (call.isEmergencyCall()) {
             FilteredNumbersUtil.recordLastEmergencyCallTime(mContext);
         }
+        wakeUpScreen();
     }
 
     @Override
@@ -713,9 +764,15 @@
             return;
         }
 
+        wakeUpScreen();
         call.setRequestedVideoState(videoState);
     }
 
+    @Override
+    public void onUpgradeToVideoFail(int error, Call call) {
+        //NO-OP
+    }
+
     /**
      * Given the call list, return the state in which the in-call screen should be.
      */
@@ -903,7 +960,7 @@
     /**
      * Answers any incoming call.
      */
-    public void answerIncomingCall(Context context, int videoState) {
+    public void answerIncomingCall(Context context) {
         // By the time we receive this intent, we could be shut down and call list
         // could be null.  Bail in those cases.
         if (mCallList == null) {
@@ -913,7 +970,29 @@
 
         Call call = mCallList.getIncomingCall();
         if (call != null) {
+            answerIncomingCall(context, call.getVideoState());
+        }
+    }
+
+    /**
+     * Answers any incoming call.
+     */
+    public void answerIncomingCall(Context context, int videoState) {
+        // By the time we receive this intent, we could be shut down and call list
+        // could be null.  Bail in those cases.
+        if (mCallList == null) {
+            StatusBarNotifier.clearAllCallNotifications(context);
+            return;
+        }
+
+        Call call = mCallList.getIncomingCall();
+        if (call != null && !InCallLowBatteryListener.getInstance().
+                onAnswerIncomingCall(call, videoState)) {
             TelecomAdapter.getInstance().answerCall(call.getId(), videoState);
+            InCallAudioManager.getInstance().onAnswerIncomingCall(call, videoState);
+        }
+
+        if (call != null) {
             showInCall(false, false/* newOutgoingCall */);
         }
     }
@@ -935,6 +1014,19 @@
         }
     }
 
+    public void acceptUpgradeRequest(Context context) {
+        if (mCallList == null) {
+            StatusBarNotifier.clearAllCallNotifications(context);
+            Log.e(this, " acceptUpgradeRequest mCallList is empty so returning");
+            return;
+        }
+
+        Call call = mCallList.getVideoUpgradeRequestCall();
+        if (call != null) {
+            acceptUpgradeRequest(call.getRequestedVideoState(), context);
+        }
+    }
+
     public void acceptUpgradeRequest(int videoState, Context context) {
         Log.d(this, " acceptUpgradeRequest videoState " + videoState);
         // Bail if we have been shut down and the call list is null.
@@ -949,6 +1041,7 @@
             VideoProfile videoProfile = new VideoProfile(videoState);
             call.getVideoCall().sendSessionModifyResponse(videoProfile);
             call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
+            InCallAudioManager.getInstance().onAcceptUpgradeRequest(call, videoState);
         }
     }
 
@@ -986,6 +1079,14 @@
     }
 
     /**
+     * Returns true if the manage conference ui is in foreground.
+     */
+    public boolean isShowingManageConferenceUi() {
+        return (isActivityStarted() && mInCallActivity.isVisible()
+                && mInCallActivity.isManageConferenceVisible());
+    }
+
+    /**
      * Returns true if the activity has been created and is running.
      * Returns true as long as activity is not destroyed or finishing.  This ensures that we return
      * true even if the activity is paused (not in foreground).
@@ -1072,20 +1173,26 @@
     /*package*/
     void onActivityStarted() {
         Log.d(this, "onActivityStarted");
-        notifyVideoPauseController(true);
+        notifyInCallUiStateNotifier(true);
+        if (mStatusBarNotifier != null) {
+            mStatusBarNotifier.updateCallStatusBar(mCallList);
+        }
     }
 
     /*package*/
     void onActivityStopped() {
         Log.d(this, "onActivityStopped");
-        notifyVideoPauseController(false);
+        notifyInCallUiStateNotifier(false);
+        if (mStatusBarNotifier != null ) {
+            mStatusBarNotifier.updateCallStatusBar(mCallList);
+        }
     }
 
-    private void notifyVideoPauseController(boolean showing) {
-        Log.d(this, "notifyVideoPauseController: mIsChangingConfigurations=" +
+    private void notifyInCallUiStateNotifier(boolean showing) {
+        Log.d(this, "notifyInCallUiStateNotifier: mIsChangingConfigurations=" +
                 mIsChangingConfigurations);
         if (!mIsChangingConfigurations) {
-            VideoPauseController.getInstance().onUiShowing(showing);
+            InCallUiStateNotifier.getInstance().onUiShowing(showing);
         }
     }
 
@@ -1270,6 +1377,18 @@
     }
 
     /**
+     * Update  color of sim card icon
+     */
+    public void updatePrimaryCallState() {
+        for (InCallEventListener listener : mInCallEventListeners) {
+            if (listener instanceof CallCardPresenter) {
+                listener.updatePrimaryCallState();
+                break;
+            }
+        }
+    }
+
+    /**
      * Called by the {@link CallCardPresenter} to inform of a change in visibility of the secondary
      * caller info bar.
      *
@@ -1283,6 +1402,15 @@
         }
     }
 
+    /**
+     * Called by the {@link VideoCallPresenter} to inform of a change in availability of
+     * incoming video stream.
+     */
+    public void notifyIncomingVideoAvailabilityChanged(boolean isAvailable) {
+        for (InCallEventListener listener : mInCallEventListeners) {
+            listener.onIncomingVideoAvailabilityChanged(isAvailable);
+        }
+    }
 
     /**
      * For some disconnected causes, we show a dialog.  This calls into the activity to show
@@ -1307,9 +1435,24 @@
 
         // TODO: Consider a proper state machine implementation
 
+        //If the call is auto answered bring up the InCallActivity
+        boolean isAutoAnswer = false;
+
+        if ((mCallList.getDisconnectedCall() == null) &&
+                (mCallList.getDisconnectingCall() == null)) {
+            isAutoAnswer = (mInCallState == InCallState.INCOMING) &&
+                               (newState == InCallState.INCALL) &&
+                               (mInCallActivity == null);
+        }
+
+        Log.d(this, "startOrFinishUi: " + isAutoAnswer);
+
+        boolean isAnyOtherSubActive = InCallState.INCOMING == newState &&
+                mCallList.isAnyOtherSubActive(mCallList.getActiveSubId());
+
         // If the state isn't changing we have already done any starting/stopping of activities in
         // a previous pass...so lets cut out early
-        if (newState == mInCallState) {
+        if ((newState == mInCallState) && !(mInCallActivity == null && isAnyOtherSubActive)) {
             return newState;
         }
 
@@ -1373,6 +1516,13 @@
         showCallUi |= InCallState.PENDING_OUTGOING == newState && mainUiNotVisible
                 && isCallWithNoValidAccounts(mCallList.getPendingOutgoingCall());
 
+        // Handle transition from InCallState.WAITING_FOR_ACCOUNT to InCallState.INCALL and
+        // and there is a call alive, this case can come for DSDA and hence we should show
+        // UI in such case.
+        showCallUi |= (newState == InCallState.INCALL) &&
+                (mInCallState == InCallState.WAITING_FOR_ACCOUNT) && (mCallList.hasLiveCall() ||
+                (mCallList.getBackgroundCall() != null));
+
         // The only time that we have an instance of mInCallActivity and it isn't started is
         // when it is being destroyed.  In that case, lets avoid bringing up another instance of
         // the activity.  When it is finally destroyed, we double check if we should bring it back
@@ -1383,7 +1533,7 @@
             return mInCallState;
         }
 
-        if (showCallUi || showAccountPicker) {
+        if (showCallUi || showAccountPicker || isAutoAnswer) {
             Log.i(this, "Start in call UI");
             showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);
         } else if (startIncomingCallSequence) {
@@ -1392,6 +1542,7 @@
             // We're about the bring up the in-call UI for an incoming call. If we still have
             // dialogs up, we need to clear them out before showing incoming screen.
             if (isActivityStarted()) {
+                mInCallActivity.showCallCardFragment(true);
                 mInCallActivity.dismissPendingDialogs();
             }
             if (!startUi(newState)) {
@@ -1467,6 +1618,7 @@
     }
 
     private boolean startUi(InCallState inCallState) {
+        final Call incomingCall = mCallList.getIncomingCall();
         boolean isCallWaiting = mCallList.getActiveCall() != null &&
                 mCallList.getIncomingCall() != null;
 
@@ -1477,7 +1629,13 @@
         // There should be no jank from this since the screen is already off and will remain so
         // until our new activity is up.
 
-        if (isCallWaiting) {
+        // In addition to call waiting scenario, we need to force finish() in case of DSDA when
+        // we get an incoming call on one sub and there is a live call in other sub and screen
+        // is off.
+        boolean anyOtherSubActive = (incomingCall != null &&
+                 mCallList.isAnyOtherSubActive(mCallList.getActiveSubId()));
+        Log.d(this, "Start UI " + " anyOtherSubActive:" + anyOtherSubActive);
+        if (isCallWaiting || anyOtherSubActive) {
             if (mProximitySensor.isScreenReallyOff() && isActivityStarted()) {
                 Log.i(this, "Restarting InCallActivity to turn screen on for call waiting");
                 mInCallActivity.finish();
@@ -1522,6 +1680,9 @@
             }
             mProximitySensor = null;
 
+            mWakeLock = null;
+            mPowerManager = null;
+
             mAudioModeProvider = null;
 
             if (mStatusBarNotifier != null) {
@@ -1532,6 +1693,8 @@
             }
             mStatusBarNotifier = null;
 
+            InCallCsRedialHandler.getInstance().tearDown();
+
             if (mCallList != null) {
                 mCallList.removeListener(this);
             }
@@ -1607,6 +1770,56 @@
         return intent;
     }
 
+    public void sendAddParticipantIntent() {
+        Intent intent = new Intent(Intent.ACTION_DIAL);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        // when we request the dialer come up, we also want to inform
+        // it that we're going through the "add participant" option from the
+        // InCallScreen.
+        intent.putExtra(TelecomAdapter.ADD_CALL_MODE_KEY, true);
+        intent.putExtra(TelecomAdapter.ADD_PARTICIPANT_KEY, true);
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            // This is rather rare but possible.
+            // Note: this method is used even when the phone is encrypted. At
+            // that moment
+            // the system may not find any Activity which can accept this Intent
+            Log.e(this, "Activity for adding calls isn't found.");
+        }
+    }
+
+    public void sendAddMultiParticipantsIntent() {
+        Intent intent = new Intent("android.intent.action.ADDPARTICIPANT");
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra("add_participant", true);
+
+        Call call = mCallList.getActiveOrBackgroundCall();
+        List<String> childCallIdList = call.getChildCallIds();
+        if (childCallIdList != null) {
+            StringBuffer sb = new StringBuffer();
+            for (int k=0; k<childCallIdList.size(); k++) {
+                String tmp = childCallIdList.get(k);
+                String number = CallList.getInstance()
+                        .getCallById(tmp).getNumber();
+                if (number.contains(";")) {
+                    String[] temp = number.split(";");
+                    number = temp[0];
+                }
+                sb.append(number).append(";");
+            }
+            Log.d(this, "sendAddMultiParticipantsIntent, numbers " + sb.toString());
+            intent.putExtra("current_participant_list", sb.toString());
+        } else {
+            Log.e(this, "sendAddMultiParticipantsIntent, childCallIdList null.");
+        }
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(this, "Activity for adding calls isn't found.");
+        }
+    }
+
     /**
      * Retrieves the current in-call camera manager instance, creating if necessary.
      *
@@ -1650,39 +1863,46 @@
      * Configures the in-call UI activity so it can change orientations or not. Enables the
      * orientation event listener if allowOrientationChange is true, disables it if false.
      *
-     * @param allowOrientationChange {@code True} if the in-call UI can change between portrait
-     *      and landscape.  {@Code False} if the in-call UI should be locked in portrait.
+     * @param orientation {@link ActivityInfo#screenOrientation} Actual orientation value to set
+     * @return returns whether the new orientation mode was set successfully or not.
      */
-    public void setInCallAllowsOrientationChange(boolean allowOrientationChange) {
+    public boolean setInCallAllowsOrientationChange(int orientation) {
         if (mInCallActivity == null) {
             Log.e(this, "InCallActivity is null. Can't set requested orientation.");
-            return;
+            return false;
         }
 
-        if (!allowOrientationChange) {
-            mInCallActivity.setRequestedOrientation(
-                    InCallOrientationEventListener.NO_SENSOR_SCREEN_ORIENTATION);
-        } else {
-            // Using SCREEN_ORIENTATION_FULL_SENSOR allows for reverse-portrait orientation, where
-            // SCREEN_ORIENTATION_SENSOR does not.
-            mInCallActivity.setRequestedOrientation(
-                    InCallOrientationEventListener.FULL_SENSOR_SCREEN_ORIENTATION);
-        }
-        mInCallActivity.enableInCallOrientationEventListener(allowOrientationChange);
+        mInCallActivity.setRequestedOrientation(orientation);
+        mInCallActivity.enableInCallOrientationEventListener(
+                orientation == InCallOrientationEventListener.FULL_SENSOR_SCREEN_ORIENTATION);
+        return true;
     }
 
-    public void enableScreenTimeout(boolean enable) {
-        Log.v(this, "enableScreenTimeout: value=" + enable);
-        if (mInCallActivity == null) {
-            Log.e(this, "enableScreenTimeout: InCallActivity is null.");
-            return;
-        }
+    /* returns TRUE if screen is turned ON else false */
+    private boolean isScreenInteractive() {
+        return mPowerManager.isInteractive();
+    }
 
-        final Window window = mInCallActivity.getWindow();
-        if (enable) {
-            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-        } else {
-            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+    private void wakeUpScreen() {
+        if (!isScreenInteractive()) {
+            acquireWakeLock();
+            releaseWakeLock();
+        }
+    }
+
+    private void acquireWakeLock() {
+        Log.v(this, "acquireWakeLock");
+
+        if (mWakeLock != null) {
+            mWakeLock.acquire();
+        }
+    }
+
+    private void releaseWakeLock() {
+        Log.v(this, "releaseWakeLock");
+
+        if (mWakeLock != null && mWakeLock.isHeld()) {
+            mWakeLock.release();
         }
     }
 
@@ -1907,6 +2127,8 @@
     public interface InCallEventListener {
         public void onFullscreenModeChanged(boolean isFullscreenMode);
         public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height);
+        public void updatePrimaryCallState();
+        public void onIncomingVideoAvailabilityChanged(boolean isAvailable);
     }
 
     public interface InCallUiListener {
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
index 1414bc5..8f011e3 100644
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
@@ -22,6 +22,7 @@
 import android.telecom.Call;
 import android.telecom.CallAudioState;
 import android.telecom.InCallService;
+import android.telephony.TelephonyManager;
 
 /**
  * Used to receive updates about calls from the Telecom component.  This service is bound to
@@ -31,6 +32,8 @@
  */
 public class InCallServiceImpl extends InCallService {
 
+    static int sPhoneCount = TelephonyManager.getDefault().getPhoneCount();
+
     @Override
     public void onCallAudioStateChanged(CallAudioState audioState) {
         AudioModeProvider.getInstance().onAudioStateChanged(audioState.isMuted(),
diff --git a/InCallUI/src/com/android/incallui/InCallSessionModificationCauseListener.java b/InCallUI/src/com/android/incallui/InCallSessionModificationCauseListener.java
new file mode 100644
index 0000000..e6ad761
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallSessionModificationCauseListener.java
@@ -0,0 +1,37 @@
+/* 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.
+ */
+
+package com.android.incallui;
+
+/**
+ * This interface will be implemented by classes that wish to listen to session modification cause
+ * updates.
+ */
+public interface InCallSessionModificationCauseListener {
+    public void onSessionModificationCauseChanged(Call call, int sessionModificationCause);
+}
diff --git a/InCallUI/src/com/android/incallui/InCallSubstateListener.java b/InCallUI/src/com/android/incallui/InCallSubstateListener.java
new file mode 100644
index 0000000..20449c4
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallSubstateListener.java
@@ -0,0 +1,36 @@
+/* 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.
+ */
+
+package com.android.incallui;
+
+/**
+ * This interface will be implemented by classes that wish to listen to call substate changes.
+ */
+public interface InCallSubstateListener {
+    public void onCallSubstateChanged(final Call call, final int callSubstate);
+}
diff --git a/InCallUI/src/com/android/incallui/InCallUiStateNotifier.java b/InCallUI/src/com/android/incallui/InCallUiStateNotifier.java
new file mode 100644
index 0000000..299c13b
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallUiStateNotifier.java
@@ -0,0 +1,229 @@
+/* Copyright (c) 2015, 2016, 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.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.view.Display;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.List;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * This class listens to below events and notifes whether InCallUI is visible to the user or not.
+ * a. InCallActivity's lifecycle events (onStop/onStart)
+ * b. Display state change events (DISPLAY_ON/DISPLAY_OFF)
+ */
+public class InCallUiStateNotifier implements DisplayManager.DisplayListener {
+
+    private List<InCallUiStateNotifierListener> mInCallUiStateNotifierListeners =
+            new CopyOnWriteArrayList<>();
+    private static InCallUiStateNotifier sInCallUiStateNotifier;
+    private DisplayManager mDisplayManager;
+    private Context mContext;
+
+    /**
+     * Tracks whether the application is in the background. {@code True} if the application is in
+     * the background, {@code false} otherwise.
+     */
+    private boolean mIsInBackground;
+
+    /**
+     * Tracks whether display is ON/OFF. {@code True} if display is ON, {@code false} otherwise.
+     */
+    private boolean mIsDisplayOn;
+
+    /**
+     * Handles set up of the {@class InCallUiStateNotifier}. Instantiates the context needed by
+     * the class and adds a listener to listen to display state changes.
+     */
+    public void setUp(Context context) {
+        mContext = context;
+        mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+        mDisplayManager.registerDisplayListener(this, null);
+        mIsDisplayOn = isDisplayOn(
+                mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState());
+        Log.d(this, "setUp mIsDisplayOn: " + mIsDisplayOn);
+    }
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private InCallUiStateNotifier() {
+    }
+
+    /**
+     * This method returns a singleton instance of {@class InCallUiStateNotifier}
+     */
+    public static synchronized InCallUiStateNotifier getInstance() {
+        if (sInCallUiStateNotifier == null) {
+            sInCallUiStateNotifier = new InCallUiStateNotifier();
+        }
+        return sInCallUiStateNotifier;
+    }
+
+   /**
+     * Adds a new {@link InCallUiStateNotifierListener}.
+     *
+     * @param listener The listener.
+     */
+    public void addListener(InCallUiStateNotifierListener listener) {
+        Preconditions.checkNotNull(listener);
+        mInCallUiStateNotifierListeners.add(listener);
+    }
+
+    /**
+     * Remove a {@link InCallUiStateNotifierListener}.
+     *
+     * @param listener The listener.
+     */
+    public void removeListener(InCallUiStateNotifierListener listener) {
+        if (listener != null) {
+            mInCallUiStateNotifierListeners.remove(listener);
+        } else {
+            Log.e(this, "Can't remove null listener");
+        }
+    }
+
+    /**
+     * Notfies when visibility of InCallUI is changed. For eg.
+     * when UE moves in/out of the foreground, display either turns ON/OFF
+     * @param showing true if InCallUI is visible, false  otherwise.
+     */
+    private void notifyOnUiShowing(boolean showing) {
+        Preconditions.checkNotNull(mInCallUiStateNotifierListeners);
+        for (InCallUiStateNotifierListener listener : mInCallUiStateNotifierListeners) {
+            listener.onUiShowing(showing);
+        }
+    }
+
+    /**
+     * Handles tear down of the {@class InCallUiStateNotifier}. Sets the context to null and
+     * unregisters it's display listener.
+     */
+    public void tearDown() {
+        mDisplayManager.unregisterDisplayListener(this);
+        mDisplayManager = null;
+        mContext = null;
+        mInCallUiStateNotifierListeners.clear();
+    }
+
+    /**
+      * checks to see whether InCallUI experience is visible to the user or not.
+      * returns true if InCallUI experience is visible to the user else false.
+      */
+    private boolean isUiShowing() {
+        /* Not in background and display is ON does mean that InCallUI is visible/showing.
+        Return true in such cases else false */
+        return  !mIsInBackground && mIsDisplayOn;
+    }
+
+    /**
+     * Checks whether the display is ON.
+     *
+     * @param displayState The display's current state.
+     */
+    public static boolean isDisplayOn(int displayState) {
+        return displayState == Display.STATE_ON ||
+                displayState == Display.STATE_DOZE ||
+                displayState == Display.STATE_DOZE_SUSPEND;
+    }
+
+    /**
+     * Called when UE goes in/out of the foreground.
+     * @param showing true if UE is in the foreground, false otherwise.
+     */
+    public void onUiShowing(boolean showing) {
+
+        //Check UI's old state before updating corresponding state variable(s)
+        final boolean wasShowing = isUiShowing();
+
+        mIsInBackground = !showing;
+
+        //Check UI's new state after updating corresponding state variable(s)
+        final boolean isShowing = isUiShowing();
+
+        Log.d(this, "onUiShowing wasShowing: " + wasShowing + " isShowing: " + isShowing);
+        //notify if there is a change in UI state
+        if (wasShowing != isShowing) {
+            notifyOnUiShowing(showing);
+        }
+    }
+
+    /**
+     * This method overrides onDisplayRemoved method of {@interface DisplayManager.DisplayListener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onDisplayRemoved(int displayId) {
+    }
+
+    /**
+     * This method overrides onDisplayAdded method of {@interface DisplayManager.DisplayListener}
+     * Added for completeness. No implementation yet.
+     */
+    @Override
+    public void onDisplayAdded(int displayId) {
+    }
+
+    /**
+     * This method overrides onDisplayAdded method of {@interface DisplayManager.DisplayListener}
+     * The method gets invoked whenever the properties of a logical display have changed.
+     */
+    @Override
+    public void onDisplayChanged(int displayId) {
+        /* Ignore display changed indications if they are received for displays
+         * other than default display
+         */
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            Log.w(this, "onDisplayChanged Ignoring...");
+            return;
+        }
+
+        final int displayState = mDisplayManager.getDisplay(displayId).getState();
+        Log.d(this, "onDisplayChanged displayState: " + displayState +
+                " displayId: " + displayId);
+
+        //Check UI's old state before updating corresponding state variable(s)
+        final boolean wasShowing = isUiShowing();
+
+        mIsDisplayOn = isDisplayOn(displayState);
+
+        //Check UI's new state after updating corresponding state variable(s)
+        final boolean isShowing = isUiShowing();
+
+        Log.d(this, "onDisplayChanged wasShowing: " + wasShowing + " isShowing: " + isShowing);
+        //notify if there is a change in UI state
+        if (wasShowing != isShowing) {
+            notifyOnUiShowing(mIsDisplayOn);
+        }
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallUiStateNotifierListener.java b/InCallUI/src/com/android/incallui/InCallUiStateNotifierListener.java
new file mode 100644
index 0000000..772f2da
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallUiStateNotifierListener.java
@@ -0,0 +1,38 @@
+/* Copyright (c) 2015, 2016, 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.
+ */
+
+package com.android.incallui;
+
+/**
+ * Listener interface for any class that wants to be notified when
+ * visibilitiy of InCallUI is changed. For eg. when UE moves in/out of the foreground,
+ * display either turns ON/OFF
+ */
+public interface InCallUiStateNotifierListener {
+    public void onUiShowing(boolean showing);
+}
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
index 99e6d51..e62e4d9 100644
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
@@ -55,10 +55,11 @@
         boolean wasVideoCall = VideoUtils.isVideoCall(previousVideoState);
         boolean isVideoCall = VideoUtils.isVideoCall(newVideoState);
 
-        // Check for upgrades to video.
-        if (!wasVideoCall && isVideoCall && previousVideoState != newVideoState) {
+        if (wasVideoCall && !isVideoCall) {
+            Log.v(this, " onSessionModifyRequestReceived Call downgraded to " + newVideoState);
+        } else if (previousVideoState != newVideoState) {
             InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoRequest(mCall,
-                newVideoState);
+                 newVideoState);
         }
     }
 
@@ -91,6 +92,7 @@
                             Call.SessionModificationState.REQUEST_FAILED);
                 }
             }
+            InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoFail(status, mCall);
         }
 
         // Finally clear the outstanding request.
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
index bb75292..1a24b16 100644
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallCallbackNotifier.java
@@ -46,6 +46,14 @@
     private final Set<SurfaceChangeListener> mSurfaceChangeListeners = Collections.newSetFromMap(
             new ConcurrentHashMap<SurfaceChangeListener, Boolean>(8, 0.9f, 1));
 
+    /* Invalid call session event */
+    public static final int CALL_SESSION_INVALID_EVENT = -1;
+
+    /** Cache the call session event for cases where the call is in background and listeners
+     * are unregistered.
+     */
+    private int mCallSessionEvent = CALL_SESSION_INVALID_EVENT;
+
     /**
      * Static singleton accessor method.
      */
@@ -91,6 +99,22 @@
     }
 
     /**
+     * Adds a new {@link VideoEventListener} and notifies the entity that registered
+     * if flag notify is true.
+     *
+     * @param listener The listener.
+     * @param notify true or false
+     */
+    public void addVideoEventListener(VideoEventListener listener, boolean notify) {
+        addVideoEventListener(listener);
+
+        // Notify registered listeners of cached call session event if it's a valid value
+        if (notify && mCallSessionEvent != CALL_SESSION_INVALID_EVENT) {
+            callSessionEvent(mCallSessionEvent);
+        }
+    }
+
+    /**
      * Remove a {@link VideoEventListener}.
      *
      * @param listener The listener.
@@ -135,13 +159,25 @@
     }
 
     /**
+     * Inform listeners of an unsuccessful response to a video request for a call.
+     *
+     * @param call The call.
+     */
+    public void upgradeToVideoFail(int status, Call call) {
+        for (SessionModificationListener listener : mSessionModificationListeners) {
+            listener.onUpgradeToVideoFail(status, call);
+        }
+    }
+
+    /**
      * Inform listeners of a call session event.
      *
      * @param event The call session event.
      */
     public void callSessionEvent(int event) {
+        mCallSessionEvent = event;
         for (VideoEventListener listener : mVideoEventListeners) {
-            listener.onCallSessionEvent(event);
+            listener.onCallSessionEvent(mCallSessionEvent);
         }
     }
 
@@ -217,6 +253,15 @@
          * @param videoState The requested video state.
          */
         public void onUpgradeToVideoRequest(Call call, int videoState);
+
+        /**
+         * Called when a request to a peer to upgrade an audio-only call to a video call is
+         * NOT successful. This can be if the peer chooses rejects the the video call, or if the
+         * peer does not support video calling, or if there is some error in sending the request.
+         *
+         * @param call The call the request was successful for.
+         */
+        public void onUpgradeToVideoFail(int status, Call call);
     }
 
     /**
diff --git a/InCallUI/src/com/android/incallui/InCallZoomController.java b/InCallUI/src/com/android/incallui/InCallZoomController.java
new file mode 100644
index 0000000..b44e99e
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallZoomController.java
@@ -0,0 +1,250 @@
+/* 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.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.view.View;
+import android.telecom.InCallService.VideoCall;
+import android.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.Window;
+import android.view.WindowManager;
+import org.codeaurora.ims.QtiCallConstants;
+import android.hardware.Camera;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraManager;
+import java.lang.Integer;
+import java.util.Objects;
+
+import com.android.incallui.ZoomControl.OnZoomChangedListener;
+
+
+/**
+ * This class implements the zoom listener for zoom control and shows the dialog and zoom controls
+ * on the InCall screen and maintains state info about the camera zoom index.
+ */
+public class InCallZoomController implements InCallPresenter.IncomingCallListener {
+
+    private static InCallZoomController sInCallZoomController;
+
+    private AlertDialog mAlertDialog;
+
+    private InCallPresenter mInCallPresenter;
+
+    private Context mContext;
+
+    private String mCameraId;
+
+    CameraManager mCameraManager;
+
+    /**
+     * This class implements the zoom listener for zoom control
+     */
+    private class ZoomChangeListener implements ZoomControl.OnZoomChangedListener {
+        private VideoCall mVideoCall;
+
+        public ZoomChangeListener(VideoCall videoCall) {
+            mVideoCall = videoCall;
+        }
+
+        @Override
+        public void onZoomValueChanged(int index) {
+            Log.v("this", "onZoomValueChanged:  index = " + index);
+            mZoomIndex = index;
+            mVideoCall.setZoom(mZoomIndex);
+        }
+    }
+
+    /**
+     * Default zoom value for camera
+     */
+    private static final int DEFAULT_CAMERA_ZOOM_VALUE = 0;
+
+    /**
+     * Transparency value for alert dialog
+     */
+    private static final float DIALOG_ALPHA_INDEX = 0.6f;
+
+    /**
+     * Static variable for storing zoom index value to maintain state
+     */
+    private int mZoomIndex = DEFAULT_CAMERA_ZOOM_VALUE;
+
+    /**
+     * This method returns a singleton instance of {@class InCallZoomController}
+     */
+    public static synchronized InCallZoomController getInstance() {
+        if (sInCallZoomController == null) {
+            sInCallZoomController = new InCallZoomController();
+        }
+        return sInCallZoomController;
+    }
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private InCallZoomController() {
+    }
+
+    /**
+     * Set up function called to add listener for camera selection changes
+     */
+    public void setUp(Context context) {
+        mContext = context;
+        mInCallPresenter = InCallPresenter.getInstance();
+        mInCallPresenter.addIncomingCallListener(this);
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+    }
+
+    /**
+     * Tear down function to reset all variables and remove camera selection listener
+     */
+    public void tearDown() {
+        mAlertDialog = null;
+        mContext = null;
+        mCameraId = null;
+        mZoomIndex = DEFAULT_CAMERA_ZOOM_VALUE;
+        mInCallPresenter.removeIncomingCallListener(this);
+        mInCallPresenter = null;
+        mCameraManager = null;
+    }
+
+    /**
+     * Sets the layout params for the alert dialog - transparency and clearing flag to dim
+     * background UI
+     */
+    private static void setLayoutParams(AlertDialog alertDialog) {
+        if (alertDialog == null) {
+            return;
+        }
+        final Window window = alertDialog.getWindow();
+        WindowManager.LayoutParams windowLayoutParams = window.getAttributes();
+        windowLayoutParams.alpha = DIALOG_ALPHA_INDEX;
+        window.setAttributes(windowLayoutParams);
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+    }
+
+    /**
+     * Called when preview surface is clicked on the InCallUI screen. Notification comes from
+     * {@class VideocallPresenter}. Create the alert dialog and the zoom control,
+     * set layout params attributes, set zoom params if zoom is supported and video call is valid
+     */
+    public void onPreviewSurfaceClicked(VideoCall videoCall) {
+        Log.d(this, "onPreviewSurfaceClicked: VideoCall - " + videoCall);
+
+        if(videoCall == null || !isCameraZoomSupported()) {
+            Log.e(this, "onPreviewSurfaceClicked: VideoCall is null or Zoom not supported ");
+            return;
+        }
+
+        try {
+            final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(
+                    mInCallPresenter.getActivity(), AlertDialog.THEME_HOLO_DARK);
+            final View zoomControlView = mInCallPresenter.getActivity().getLayoutInflater().
+                    inflate(R.layout.qti_video_call_zoom_control, null);
+            final ZoomControlBar zoomControl = (ZoomControlBar) zoomControlView.findViewById(
+                    R.id.zoom_control);
+            dialogBuilder.setView(zoomControlView);
+            mAlertDialog = dialogBuilder.create();
+            mAlertDialog.setCanceledOnTouchOutside(true);
+            setLayoutParams(mAlertDialog);
+            zoomControl.setOnZoomChangeListener(new ZoomChangeListener(videoCall));
+            initZoomControl(zoomControl, mZoomIndex);
+            mAlertDialog.show();
+        } catch (Exception e) {
+            Log.e(this, "onPreviewSurfaceClicked: Exception " + e);
+            return;
+        }
+    }
+
+    private static void initZoomControl(ZoomControlBar zoomControl, int zoomIndex) {
+        zoomControl.setZoomMax(QtiCallConstants.CAMERA_MAX_ZOOM);
+        zoomControl.setZoomIndex(zoomIndex);
+        zoomControl.setEnabled(true);
+    }
+
+    /**
+     * Queries the camera characteristics to figure out if zoom is supported or not
+     */
+    private boolean isCameraZoomSupported() {
+        try {
+            final InCallCameraManager inCallCameraManager = mInCallPresenter.
+                    getInCallCameraManager();
+            final float CAMERA_ZOOM_NOT_SUPPORTED = 1.0f;
+
+            CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(
+                inCallCameraManager.getActiveCameraId());
+            return (characteristics != null) && (characteristics.get(
+                    CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM)
+                    > CAMERA_ZOOM_NOT_SUPPORTED);
+        } catch (Exception e) {
+            Log.e(this, "isCameraZoomSupported: Failed to retrieve Max Zoom, " + e);
+            return false;
+        }
+    }
+
+    /**
+     * Called from the {@class VideoCallPresenter} when camera is enabled or disabled
+     * Reset the zoom index and dismiss the alert if camera id changes
+     */
+    public void onCameraEnabled(String cameraId) {
+        Log.d(this, "onCameraEnabled: - cameraId -" + cameraId);
+        if (!Objects.equals(mCameraId, cameraId)) {
+            mCameraId = cameraId;
+            mZoomIndex = DEFAULT_CAMERA_ZOOM_VALUE;
+            dismissAlertDialog();
+        }
+    }
+
+    private void dismissAlertDialog() {
+        try {
+            if (mAlertDialog != null) {
+                mAlertDialog.dismiss();
+                mAlertDialog = null;
+            }
+        } catch (Exception e) {
+            // Since exceptions caused in zoom control dialog should not crash the phone process,
+            // we intentionally capture the exception and ignore.
+            Log.e(this, "dismissAlertDialog: Exception: " + e);
+        }
+    }
+
+    /**
+     * Called when there is a new incoming call.
+     * Dismiss the alert.
+     */
+    @Override
+    public void onIncomingCall(InCallPresenter.InCallState oldState,
+            InCallPresenter.InCallState newState, Call call) {
+        Log.v(this, "onIncomingCall - Call " + call + "oldState " + oldState + "newState " +
+                newState);
+        dismissAlertDialog();
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java b/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
index 27f7115..94a852b 100644
--- a/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
+++ b/InCallUI/src/com/android/incallui/NotificationBroadcastReceiver.java
@@ -45,6 +45,8 @@
             "com.android.incallui.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST";
     public static final String ACTION_DECLINE_VIDEO_UPGRADE_REQUEST =
             "com.android.incallui.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST";
+    public static final String ADD_CALL_MODE_KEY = "add_call_mode";
+    public static final String ADD_PARTICIPANT_KEY = "add_participant";
     public static final String ACTION_PULL_EXTERNAL_CALL =
             "com.android.incallui.ACTION_PULL_EXTERNAL_CALL";
     public static final String EXTRA_NOTIFICATION_ID =
@@ -57,8 +59,7 @@
 
         // TODO: Commands of this nature should exist in the CallList.
         if (action.equals(ACTION_ANSWER_VIDEO_INCOMING_CALL)) {
-            InCallPresenter.getInstance().answerIncomingCall(
-                    context, VideoProfile.STATE_BIDIRECTIONAL);
+            InCallPresenter.getInstance().answerIncomingCall(context);
         } else if (action.equals(ACTION_ANSWER_VOICE_INCOMING_CALL)) {
             InCallPresenter.getInstance().answerIncomingCall(
                     context, VideoProfile.STATE_AUDIO_ONLY);
@@ -67,9 +68,7 @@
         } else if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
             InCallPresenter.getInstance().hangUpOngoingCall(context);
         } else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) {
-            //TODO: Change calltype after adding support for TX and RX
-            InCallPresenter.getInstance().acceptUpgradeRequest(
-                    VideoProfile.STATE_BIDIRECTIONAL, context);
+            InCallPresenter.getInstance().acceptUpgradeRequest(context);
         } else if (action.equals(ACTION_DECLINE_VIDEO_UPGRADE_REQUEST)) {
             InCallPresenter.getInstance().declineUpgradeRequest(context);
         } else if (action.equals(ACTION_PULL_EXTERNAL_CALL)) {
diff --git a/InCallUI/src/com/android/incallui/OrientationModeHandler.java b/InCallUI/src/com/android/incallui/OrientationModeHandler.java
new file mode 100644
index 0000000..edf05e3
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/OrientationModeHandler.java
@@ -0,0 +1,187 @@
+/* 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.
+ */
+
+package com.android.incallui;
+
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallUiListener;
+import org.codeaurora.ims.QtiCallConstants;
+
+/**
+ * This class listens to incoming events from the {@class InCallDetailsListener}.
+ * When call details change, this class is notified and we parse the extras from the details to
+ * figure out if orientation mode has changed and if changed, we call setRequestedOrientation
+ * on the activity to set the orientation mode for the device.
+ *
+ */
+public class OrientationModeHandler implements InCallDetailsListener, InCallUiListener {
+
+    private static OrientationModeHandler sOrientationModeHandler;
+
+    private PrimaryCallTracker mPrimaryCallTracker;
+
+    private int mOrientationMode = QtiCallConstants.ORIENTATION_MODE_UNSPECIFIED;
+
+    /**
+     * Returns a singleton instance of {@class OrientationModeHandler}
+     */
+    public static synchronized OrientationModeHandler getInstance() {
+        if (sOrientationModeHandler == null) {
+            sOrientationModeHandler = new OrientationModeHandler();
+        }
+        return sOrientationModeHandler;
+    }
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private OrientationModeHandler() {
+    }
+
+    /**
+     * Handles set up of the {@class OrientationModeHandler}. Registers primary call tracker to
+     * listen to call state changes and registers this class to listen to call details changes.
+     */
+    public void setUp() {
+        mPrimaryCallTracker = new PrimaryCallTracker();
+        InCallPresenter.getInstance().addListener(mPrimaryCallTracker);
+        InCallPresenter.getInstance().addDetailsListener(this);
+        InCallPresenter.getInstance().addInCallUiListener(this);
+    }
+
+    /**
+     * Handles tear down of the {@class OrientationModeHandler}. Unregisters primary call tracker
+     * from listening to call state changes and unregisters this class from listening to call
+     * details changes.
+     */
+    public void tearDown() {
+        InCallPresenter.getInstance().removeListener(mPrimaryCallTracker);
+        InCallPresenter.getInstance().removeDetailsListener(this);
+        InCallPresenter.getInstance().removeInCallUiListener(this);
+        mOrientationMode = QtiCallConstants.ORIENTATION_MODE_UNSPECIFIED;
+        mPrimaryCallTracker = null;
+    }
+
+    /**
+     * Overrides onDetailsChanged method of {@class InCallDetailsListener}. We are
+     * notified when call details change and extract the orientation mode from the
+     * extras, detect if the mode has changed and set the orientation mode for the device.
+     */
+    @Override
+    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+        Log.d(this, "onDetailsChanged: - call: " + call + "details: " + details);
+        mayBeUpdateOrientationMode(call, details);
+    }
+
+    /**
+     * This API conveys if incall experience is showing or not.
+     *
+     * @param showing TRUE if incall experience is showing else FALSE
+     */
+    @Override
+    public void onUiShowing(boolean showing) {
+        Call call = mPrimaryCallTracker.getPrimaryCall();
+        Log.d(this, "onUiShowing showing: " + showing + " call = " + call);
+
+        if (!showing || call == null) {
+            return;
+        }
+
+        mayBeUpdateOrientationMode(call, call.getTelecomCall().getDetails());
+    }
+
+    private void mayBeUpdateOrientationMode(Call call, android.telecom.Call.Details details) {
+        final Bundle extras =  (call != null && details != null) ? details.getExtras() : null;
+        final int orientationMode = (extras != null) ? extras.getInt(
+                QtiCallConstants.ORIENTATION_MODE_EXTRA_KEY,
+                QtiCallConstants.ORIENTATION_MODE_UNSPECIFIED) :
+                QtiCallConstants.ORIENTATION_MODE_UNSPECIFIED;
+
+        Log.d(this, "mayBeUpdateOrientationMode : orientationMode: " + orientationMode +
+                " mOrientationMode : " + mOrientationMode);
+        if (InCallPresenter.getInstance().getActivity() == null) {
+            Log.w(this, "mayBeUpdateOrientationMode : InCallActivity is null");
+            return;
+        }
+
+        if (orientationMode != mOrientationMode && orientationMode !=
+                QtiCallConstants.ORIENTATION_MODE_UNSPECIFIED) {
+            mOrientationMode = orientationMode;
+            onOrientationModeChanged(call, mOrientationMode);
+        }
+    }
+
+    /**
+     * Handles any orientation mode changes in the call.
+     *
+     * @param call The call for which orientation mode changed.
+     * @param orientationMode The new orientation mode of the device
+     */
+    private void onOrientationModeChanged(Call call, int orientationMode) {
+        Log.d(this, "onOrientationModeChanged: Call : " + call + " orientation mode = " +
+                orientationMode);
+
+        if (!mPrimaryCallTracker.isPrimaryCall(call)) {
+            Log.e(this, "Can't set requested orientation on a non-primary call");
+            return;
+        }
+
+        InCallPresenter.getInstance().setInCallAllowsOrientationChange(
+                QtiCallUtils.toUiOrientationMode(orientationMode));
+    }
+
+    /**
+     * Returns the current orientation mode based on the receipt of DISPLAY_MODE_EVT from lower
+     * layers and whether the call is a video call or not. If we have a video call and we
+     * did receive a valid orientation mode, return the corresponding
+     * {@link ActivityInfo#ScreenOrientation} else return
+     * ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR. If we are in a voice call, return
+     * ActivityInfo.SCREEN_ORIENTATION_NOSENSOR.
+     *
+     * @param call The current call.
+     */
+    public int getOrientation(Call call) {
+        if (VideoUtils.isVideoCall(call)) {
+            return (mOrientationMode == QtiCallConstants.ORIENTATION_MODE_UNSPECIFIED) ?
+                    ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR :
+                    QtiCallUtils.toUiOrientationMode(mOrientationMode);
+        } else {
+            return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+        }
+    }
+
+    /**
+     * Returns the current orientation mode.
+     * @see #getOrientation(Call)
+     */
+    public int getCurrentOrientation() {
+        return QtiCallUtils.toUiOrientationMode(mOrientationMode);
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/PictureModeHelper.java b/InCallUI/src/com/android/incallui/PictureModeHelper.java
new file mode 100644
index 0000000..d73ef3b
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/PictureModeHelper.java
@@ -0,0 +1,335 @@
+/**
+ * Copyright (c) 2016 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.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.CheckBox;
+import android.widget.ListView;
+
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+
+import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.List;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * This class displays the picture mode alert dialog and registers listener who wish to listen to
+ * user selection for the preview video and the incoming video.
+ */
+public class PictureModeHelper extends AlertDialog implements InCallDetailsListener,
+        InCallStateListener, CallList.Listener {
+
+    private AlertDialog mAlertDialog;
+
+    /**
+     * Indicates whether we should display camera preview video view
+     */
+    private static boolean mShowPreviewVideoView = true;
+
+    /**
+     * Indicates whether we should display incoming video view
+     */
+    private static boolean mShowIncomingVideoView = true;
+
+    public PictureModeHelper(Context context) {
+        super(context);
+    }
+
+    public void setUp(VideoCallPresenter videoCallPresenter) {
+        InCallPresenter.getInstance().addDetailsListener(this);
+        InCallPresenter.getInstance().addListener(this);
+        CallList.getInstance().addListener(this);
+        addListener(videoCallPresenter);
+    }
+
+    public void tearDown(VideoCallPresenter videoCallPresenter) {
+        InCallPresenter.getInstance().removeDetailsListener(this);
+        InCallPresenter.getInstance().removeListener(this);
+        CallList.getInstance().removeListener(this);
+        removeListener(videoCallPresenter);
+        mAlertDialog = null;
+    }
+
+    /**
+     * Displays the alert dialog
+     */
+    public void show() {
+        if (mAlertDialog != null) {
+            mAlertDialog.show();
+        }
+    }
+
+    /**
+     * Listener interface to implement if any class is interested in listening to preview
+     * video or incoming video selection changed
+     */
+    public interface Listener {
+        public void onPreviewVideoSelectionChanged();
+        public void onIncomingVideoSelectionChanged();
+    }
+
+    private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+
+    /**
+     * This method adds a new Listener. Users interested in listening to preview video selection
+     * and incoming video selection changes must register to this class
+     * @param Listener listener - the listener to be registered
+     */
+    public void addListener(Listener listener) {
+        Preconditions.checkNotNull(listener);
+        mListeners.add(listener);
+    }
+
+    /**
+     * This method unregisters the listener listening to preview video selection and incoming
+     * video selection
+     * @param Listener listener - the listener to be un-registered
+     */
+    public void removeListener(Listener listener) {
+        Preconditions.checkNotNull(listener);
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Creates and displays the picture mode alert dialog to enable the user to switch
+     * between picture modes - Picture in picture, Incoming mode or Camera preview mode
+     * Once users makes their choice, they can save or cancel. Saving will apply the
+     * new picture mode to the video call by notifying video call presenter of the change.
+     * Cancel will dismiss the alert dialog without making any changes. Alert dialog is
+     * cancelable so pressing home/back key will dismiss the dialog.
+     * @param Context context - The application context.
+     */
+    public void create(Context context) {
+        final ArrayList<CharSequence> items = new ArrayList<CharSequence>();
+        final Resources res = context.getResources();
+
+        final InCallActivity inCallActivity = InCallPresenter.getInstance().getActivity();
+        if (inCallActivity == null) {
+            return;
+        }
+
+        final View checkboxView = inCallActivity.getLayoutInflater().
+                inflate(R.layout.qti_video_call_picture_mode_menu, null);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        builder.setTitle(R.string.video_call_picture_mode_menu_title);
+        builder.setView(checkboxView);
+        builder.setCancelable(true);
+
+        CheckBox previewVideo = (CheckBox) checkboxView.findViewById(R.id.preview_video);
+        CheckBox incomingVideo = (CheckBox) checkboxView.findViewById(R.id.incoming_video);
+
+        if (previewVideo == null || incomingVideo == null) {
+            return;
+        }
+
+        // Intialize the checkboxes with the proper checked values
+        previewVideo.setChecked(mShowPreviewVideoView);
+        incomingVideo.setChecked(mShowIncomingVideoView);
+
+        // Ensure that at least one of the check boxes is enabled. Disable the other checkbox
+        // if checkbox is un-checked and vice versa. Say for e.g: if preview video was unchecked,
+        // disble incoming video and make it unclickable
+        enable(previewVideo, mShowIncomingVideoView);
+        enable(incomingVideo, mShowPreviewVideoView);
+
+        previewVideo.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                enable(incomingVideo, ((CheckBox) view).isChecked());
+            }
+        });
+
+        incomingVideo.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                enable(previewVideo, ((CheckBox) view).isChecked());
+            }
+        });
+
+        builder.setPositiveButton(res.getText(R.string.video_call_picture_mode_save_option),
+                new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int item) {
+                    mShowPreviewVideoView = previewVideo.isChecked();
+                    mShowIncomingVideoView = incomingVideo.isChecked();
+                    notifyOnSelectionChanged();
+                }
+        });
+
+        builder.setNegativeButton(res.getText(R.string.video_call_picture_mode_cancel_option),
+                new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int item) {
+                }
+        });
+
+        mAlertDialog = builder.create();
+        setDismissListener();
+    }
+
+    private void setDismissListener() {
+        mAlertDialog.setOnDismissListener(new OnDismissListener() {
+                @Override
+                public void onDismiss(DialogInterface dialog) {
+                    mAlertDialog = null;
+                }
+        });
+    }
+
+    /**
+     * This method enables or disables the checkbox passed in based on whether the flag enable
+     * is set to true or false. Also toggle the checkbox being clickable.
+     * @param CheckBox checkBox - the check Box to enable/disable
+     * @param boolean enable - Flag to enable/disable checkbox (true/false)
+     */
+    private void enable(CheckBox checkBox, boolean enable) {
+        checkBox.setEnabled(enable);
+        checkBox.setClickable(enable);
+    }
+
+    /**
+     * Determines if we can show the preview video view
+     */
+    public boolean canShowPreviewVideoView() {
+        return mShowPreviewVideoView;
+    }
+
+    /**
+     * Determines if we can show the incoming video view
+     */
+    public boolean canShowIncomingVideoView() {
+        return mShowIncomingVideoView;
+    }
+
+    /**
+     * Determines whether we are in Picture in Picture mode
+     */
+    public boolean isPipMode() {
+        return canShowPreviewVideoView() && canShowIncomingVideoView();
+    }
+
+    /**
+     * Notifies all registered classes of preview video or incoming video selection changed
+     */
+    public void notifyOnSelectionChanged() {
+        Preconditions.checkNotNull(mListeners);
+        for (Listener listener : mListeners) {
+            listener.onPreviewVideoSelectionChanged();
+            listener.onIncomingVideoSelectionChanged();
+        }
+    }
+
+    /**
+     * Listens to call details changed and dismisses the dialog if call has been downgraded to
+     * voice
+     * @param Call call - The call for which details changed
+     * @param android.telecom.Call.Details details - The changed details
+     */
+    @Override
+    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+        if (call == null) {
+            return;
+        }
+        if (!VideoUtils.isVideoCall(call) && mAlertDialog != null) {
+            mAlertDialog.dismiss();
+        }
+    }
+
+    /**
+     * Handles call state changes
+     *
+     * @param InCallPresenter.InCallState oldState - The old call state
+     * @param InCallPresenter.InCallState newState - The new call state
+     * @param CallList callList - The call list.
+     */
+    @Override
+    public void onStateChange(InCallPresenter.InCallState oldState,
+            InCallPresenter.InCallState newState, CallList callList) {
+        Log.d(this, "onStateChange oldState" + oldState + " newState=" + newState);
+
+        if (newState == InCallPresenter.InCallState.NO_CALLS) {
+            // Set both display preview video and incoming video to true as default display mode is
+            // to show picture in picture.
+            mShowPreviewVideoView = true;
+            mShowIncomingVideoView = true;
+        }
+    }
+
+    /**
+     * Overrides onIncomingCall method of {@interface CallList.Listener}
+     * @param Call call - The incoming call
+     */
+    @Override
+    public void onIncomingCall(Call call) {
+        if (mAlertDialog != null) {
+            mAlertDialog.dismiss();
+        }
+    }
+
+    /**
+     * Overrides onCallListChange method of {@interface CallList.Listener}
+     * Added for completeness
+     */
+    @Override
+    public void onCallListChange(CallList list) {
+        // no-op
+    }
+
+    /**
+     * Overrides onUpgradeToVideo method of {@interface CallList.Listener}
+     * @param Call call - The call to be upgraded
+     */
+    @Override
+    public void onUpgradeToVideo(Call call) {
+        if (mAlertDialog != null) {
+            mAlertDialog.dismiss();
+        }
+    }
+
+    /**
+     * Overrides onDisconnect method of {@interface CallList.Listener}
+     * @param Call call - The call to be disconnected
+     */
+    @Override
+    public void onDisconnect(Call call) {
+        // no-op
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/PrimaryCallTracker.java b/InCallUI/src/com/android/incallui/PrimaryCallTracker.java
new file mode 100644
index 0000000..e309ede
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/PrimaryCallTracker.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui;
+
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
+import java.util.Objects;
+
+/**
+ * Listens to call state changes from {@class InCallStateListener} and keeps track of the current
+ * primary call.
+ */
+public class PrimaryCallTracker implements InCallStateListener, IncomingCallListener {
+
+    private Call mPrimaryCall;
+
+    public PrimaryCallTracker() {
+    }
+
+    @Override
+    public void onIncomingCall(InCallPresenter.InCallState oldState,
+            InCallPresenter.InCallState newState, Call call) {
+        // same logic should happen as with onStateChange()
+        onStateChange(oldState, InCallPresenter.InCallState.INCOMING, CallList.getInstance());
+    }
+
+    /**
+     * Handles state changes (including incoming calls)
+     *
+     * @param newState The in call state.
+     * @param callList The call list.
+     */
+    @Override
+    public void onStateChange(InCallPresenter.InCallState oldState,
+            InCallPresenter.InCallState newState, CallList callList) {
+        Log.d(this, "onStateChange: oldState" + oldState + " newState=" + newState +
+                "callList =" + callList);
+
+        // Determine the primary active call.
+        Call primaryCall = null;
+
+        if (newState == InCallPresenter.InCallState.INCOMING) {
+            primaryCall = callList.getIncomingCall();
+        } else if (newState == InCallPresenter.InCallState.OUTGOING) {
+            primaryCall = callList.getOutgoingCall();
+        } else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) {
+            primaryCall = callList.getPendingOutgoingCall();
+        } else if (newState == InCallPresenter.InCallState.INCALL) {
+            primaryCall = callList.getActiveOrBackgroundCall();
+        }
+
+        if (!Objects.equals(mPrimaryCall, primaryCall)) {
+            mPrimaryCall = primaryCall;
+        }
+    }
+
+    /**
+     * Returns the current primary call.
+     */
+    public Call getPrimaryCall() {
+        return mPrimaryCall;
+    }
+
+    /**
+     * Checks if the current call passed in is a primary call. Returns true if it is, false
+     * otherwise
+     *
+     * @param Call The call to be compared with the primary call.
+     */
+    public boolean isPrimaryCall(final Call call) {
+        return Objects.equals(mPrimaryCall, call);
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/ProximitySensor.java b/InCallUI/src/com/android/incallui/ProximitySensor.java
index 3c9fd93..fbd24e5 100644
--- a/InCallUI/src/com/android/incallui/ProximitySensor.java
+++ b/InCallUI/src/com/android/incallui/ProximitySensor.java
@@ -25,6 +25,7 @@
 import android.os.PowerManager;
 import android.telecom.CallAudioState;
 import android.view.Display;
+import android.provider.Settings;
 
 import com.android.incallui.AudioModeProvider.AudioModeListener;
 import com.android.incallui.InCallPresenter.InCallState;
@@ -42,6 +43,7 @@
 public class ProximitySensor implements AccelerometerListener.OrientationListener,
         InCallStateListener, AudioModeListener {
     private static final String TAG = ProximitySensor.class.getSimpleName();
+    private static final String PROXIMITY_SENSOR = "proximity_sensor";
 
     private final PowerManager mPowerManager;
     private final PowerManager.WakeLock mProximityWakeLock;
@@ -52,6 +54,7 @@
     private boolean mUiShowing = false;
     private boolean mIsPhoneOffhook = false;
     private boolean mDialpadVisible;
+    private Context mContext;
 
     // True if the keyboard is currently *not* hidden
     // Gets updated whenever there is a Configuration change
@@ -59,6 +62,7 @@
 
     public ProximitySensor(Context context, AudioModeProvider audioModeProvider,
             AccelerometerListener accelerometerListener) {
+        mContext = context;
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         if (mPowerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
             mProximityWakeLock = mPowerManager.newWakeLock(
@@ -232,6 +236,8 @@
                     || CallAudioState.ROUTE_SPEAKER == audioMode
                     || CallAudioState.ROUTE_BLUETOOTH == audioMode
                     || mIsHardKeyboardOpen);
+            screenOnImmediately |= Settings.System.getInt(mContext.getContentResolver(),
+                    PROXIMITY_SENSOR, 1) == 0;
 
             // We do not keep the screen off when the user is outside in-call screen and we are
             // horizontal, but we do not force it on when we become horizontal until the
diff --git a/InCallUI/src/com/android/incallui/QtiCallUtils.java b/InCallUI/src/com/android/incallui/QtiCallUtils.java
new file mode 100644
index 0000000..6a4383a
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/QtiCallUtils.java
@@ -0,0 +1,545 @@
+/**
+ * Copyright (c) 2015, 2016 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.
+ */
+
+package com.android.incallui;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telecom.VideoProfile;
+import android.telecom.Connection.VideoProvider;
+import android.widget.Toast;
+import android.content.Context;
+import android.content.res.Resources;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.content.pm.ActivityInfo;
+import android.telecom.InCallService.VideoCall;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import java.lang.reflect.*;
+import java.util.ArrayList;
+
+import org.codeaurora.internal.IExtTelephony;
+import org.codeaurora.ims.QtiCallConstants;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
+/**
+ * This class contains Qti specific utiltity functions.
+ */
+public class QtiCallUtils {
+
+    private static String LOG_TAG = "QtiCallUtils";
+
+    /**
+     * Private constructor for QtiCallUtils as we don't want to instantiate this class
+     */
+    private QtiCallUtils() {
+    }
+
+    /**
+     * This utility method checks to see if bits in the mask are enabled in the value
+     * Returns true if all bits in {@code mask} are set in {@code value}
+     */
+    public static boolean isEnabled(final int mask, final int value) {
+        return (mask & value) == mask;
+    }
+
+    /**
+     * This utility method checks to see if none of the bits in the mask are enabled in the value
+     * Returns true if none of the bits in {@code mask} are set in {@code value}
+     */
+    public static boolean isNotEnabled(final int mask, final int value) {
+        return (mask & value) == 0;
+    }
+
+    /**
+     * Method to get the video quality display string resource id given the video quality
+     */
+    public static int getVideoQualityResourceId(int videoQuality) {
+        switch (videoQuality) {
+            case VideoProfile.QUALITY_HIGH:
+                return R.string.video_quality_high;
+            case VideoProfile.QUALITY_MEDIUM:
+                return R.string.video_quality_medium;
+            case VideoProfile.QUALITY_LOW:
+                return R.string.video_quality_low;
+            default:
+                return R.string.video_quality_unknown;
+        }
+    }
+
+    /**
+     * Returns the call session resource id given the call session event
+     */
+    public static int getCallSessionResourceId(int event) {
+        switch (event) {
+            case VideoProvider.SESSION_EVENT_RX_PAUSE:
+                return R.string.player_stopped;
+            case VideoProvider.SESSION_EVENT_RX_RESUME:
+                return R.string.player_started;
+            case VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
+                return R.string.camera_not_ready;
+            case VideoProvider.SESSION_EVENT_CAMERA_READY:
+                return R.string.camera_ready;
+            default:
+                return R.string.unknown_call_session_event;
+        }
+    }
+
+    /**
+     * Displays the message as a Toast on the UI
+     */
+    public static void displayToast(Context context, String msg) {
+        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    /**
+     * Displays the string corresponding to the resourceId as a Toast on the UI
+     */
+    public static void displayToast(Context context, int resourceId) {
+        displayToast(context, context.getResources().getString(resourceId));
+    }
+
+    /**
+     * The function is called when Modify Call button gets pressed. The function creates and
+     * displays modify call options.
+     */
+    public static void displayModifyCallOptions(final Call call, final Context context) {
+        if (call == null) {
+            Log.d(LOG_TAG, "Can't display modify call options. Call is null");
+            return;
+        }
+
+        if (isTtyEnabled(context)) {
+            Log.w(LOG_TAG, "Call session modification is allowed only when TTY is off.");
+            displayToast(context, R.string.video_call_not_allowed_if_tty_enabled);
+            return;
+        }
+
+        final ArrayList<CharSequence> items = new ArrayList<CharSequence>();
+        final ArrayList<Integer> itemToCallType = new ArrayList<Integer>();
+        final Resources res = context.getResources();
+
+        // Prepare the string array and mapping.
+        if (hasVoiceCapabilities(call)) {
+            items.add(res.getText(R.string.modify_call_option_voice));
+            itemToCallType.add(VideoProfile.STATE_AUDIO_ONLY);
+        }
+
+        if (hasReceiveVideoCapabilities(call)) {
+            items.add(res.getText(R.string.modify_call_option_vt_rx));
+            itemToCallType.add(VideoProfile.STATE_RX_ENABLED);
+        }
+
+        if (hasTransmitVideoCapabilities(call)) {
+            items.add(res.getText(R.string.modify_call_option_vt_tx));
+            itemToCallType.add(VideoProfile.STATE_TX_ENABLED);
+        }
+
+        if (hasReceiveVideoCapabilities(call) && hasTransmitVideoCapabilities(call)) {
+            items.add(res.getText(R.string.modify_call_option_vt));
+            itemToCallType.add(VideoProfile.STATE_BIDIRECTIONAL);
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        builder.setTitle(R.string.modify_call_option_title);
+        final AlertDialog alert;
+
+        DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                Toast.makeText(context, items.get(item), Toast.LENGTH_SHORT).show();
+                final int selCallType = itemToCallType.get(item);
+                Log.v(this, "Videocall: ModifyCall: upgrade/downgrade to "
+                        + callTypeToString(selCallType));
+                VideoProfile videoProfile = new VideoProfile(selCallType);
+                changeToVideoClicked(call, videoProfile, context);
+                dialog.dismiss();
+            }
+        };
+        final int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(call.getVideoState());
+        final int index = itemToCallType.indexOf(currUnpausedVideoState);
+        builder.setSingleChoiceItems(items.toArray(new CharSequence[0]), index, listener);
+        alert = builder.create();
+        alert.show();
+    }
+
+    public static void changeToVideoCall(Call call, VideoProfile videoProfile, Context context) {
+            changeToVideoClicked(call, videoProfile, context);
+    }
+    /**
+     * Converts the call type to string
+     */
+    public static String callTypeToString(int callType) {
+        switch (callType) {
+            case VideoProfile.STATE_BIDIRECTIONAL:
+                return "VT";
+            case VideoProfile.STATE_TX_ENABLED:
+                return "VT_TX";
+            case VideoProfile.STATE_RX_ENABLED:
+                return "VT_RX";
+        }
+        return "";
+    }
+
+    /**
+     * Sends a session modify request to the telephony framework
+     */
+    private static void changeToVideoClicked(Call call, VideoProfile videoProfile) {
+        changeToVideoClicked(call, videoProfile, null);
+    }
+
+    private static void changeToVideoClicked(Call call, VideoProfile videoProfile, Context ctx) {
+        VideoCall videoCall = call.getVideoCall();
+        if (videoCall == null) {
+            return;
+        }
+        videoCall.sendSessionModifyRequest(videoProfile);
+        if (shallShowPreviewWhileWaiting(ctx)) {
+            call.setRequestedVideoState(videoProfile.getVideoState());
+        }
+        call.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
+        InCallAudioManager.getInstance().onModifyCallClicked(call, videoProfile.getVideoState());
+    }
+
+    /**
+     * Checks the boolean flag in config file to figure out if we are going to use Qti extension or
+     * not
+     */
+    public static boolean useExt(Context context) {
+        if (context == null) {
+            Log.w(context, "Context is null...");
+        }
+        return context != null && QtiImsExtUtils.useExt(context);
+    }
+
+    /**
+     * Checks the boolean flag in config file to figure out if custom video ui is required or
+     * not
+     */
+    public static boolean useCustomVideoUi(Context context) {
+        if (context == null) {
+            Log.w(context, "Context is null...");
+        }
+        return context != null && QtiImsExtUtils.useCustomVideoUi(context);
+    }
+
+    /**
+     * Checks the boolean flag in config file to figure out if it support preview before the accept
+     * video call or not
+     */
+    public static boolean shallShowPreviewWhileWaiting(Context context) {
+        if (context == null) {
+            Log.w(context, "Context is null...");
+            return false;
+        }
+        return context.getResources().getBoolean(
+                R.bool.config_enable_modify_call_preview);
+    }
+
+    /**
+     * Returns user options for accepting an incoming video call based on Qti extension flag
+     */
+    public static int getIncomingCallAnswerOptions(Context context, int videoState,
+            boolean withSms) {
+        if (!useExt(context)) {
+            return withSms ? AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS :
+                    AnswerFragment.TARGET_SET_FOR_VIDEO_WITHOUT_SMS;
+        } else if (VideoProfile.isBidirectional(videoState)) {
+            return withSms ? AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_WITH_SMS :
+                    AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_WITHOUT_SMS;
+        } else if (VideoProfile.isTransmissionEnabled(videoState)) {
+            return withSms ?
+                    AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_WITH_SMS :
+                    AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_WITHOUT_SMS;
+        } else {
+            return withSms ?
+                    AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_WITH_SMS :
+                    AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_WITHOUT_SMS;
+        }
+    }
+
+    /**
+     * Returns the session modification user options based on session modify request video states
+     * (current video state and modify request video state)
+     */
+    public static int getSessionModificationOptions(Context context, int currentVideoState,
+            int modifyToVideoState) {
+        if (!useExt(context)) {
+            return AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST;
+        }
+
+        if (showVideoUpgradeOptions(currentVideoState, modifyToVideoState)) {
+            return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_ACCEPT_REJECT_REQUEST;
+        } else if (isEnabled(VideoProfile.STATE_BIDIRECTIONAL, modifyToVideoState)) {
+            return AnswerFragment.TARGET_SET_FOR_QTI_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST;
+        } else if (isEnabled(VideoProfile.STATE_TX_ENABLED, modifyToVideoState)) {
+            return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST;
+        } else if (isEnabled(VideoProfile.STATE_RX_ENABLED, modifyToVideoState)) {
+            return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST;
+        }
+        return AnswerFragment.TARGET_SET_FOR_QTI_VIDEO_ACCEPT_REJECT_REQUEST;
+    }
+
+    /**
+     * Returns true if we are upgrading from Voice to Bidirectional video, false otherwise
+     */
+    private static boolean showVideoUpgradeOptions(int currentVideoState, int modifyToVideoState) {
+        return currentVideoState == VideoProfile.STATE_AUDIO_ONLY &&
+                isEnabled(VideoProfile.STATE_BIDIRECTIONAL, modifyToVideoState);
+    }
+
+    /**
+     * Returns IExtTelephony handle
+     */
+    public static IExtTelephony getIExtTelephony() {
+        IExtTelephony mExtTelephony = null;
+        try {
+            Class c = Class.forName("android.os.ServiceManager");
+            Method m = c.getMethod("getService",new Class[]{String.class});
+
+            mExtTelephony =
+                IExtTelephony.Stub.asInterface((IBinder)m.invoke(null, "extphone"));
+        } catch (ClassNotFoundException e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        } catch (IllegalArgumentException e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        } catch (IllegalAccessException e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        } catch (InvocationTargetException e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        } catch (SecurityException e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        } catch (NoSuchMethodException e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        }
+        return mExtTelephony;
+    }
+
+    /**
+     * returns true if it is emrgency number else false
+     */
+    public static boolean isEmergencyNumber(String number) {
+        boolean isEmergencyNumber = false;
+
+        try {
+            isEmergencyNumber = getIExtTelephony().isEmergencyNumber(number);
+        } catch (RemoteException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        } catch (NullPointerException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        }
+        return isEmergencyNumber;
+    }
+
+    /**
+     * returns true if it is local emrgency number else false
+     */
+    public static boolean isLocalEmergencyNumber(String number) {
+        boolean isEmergencyNumber = false;
+
+        try {
+            isEmergencyNumber = getIExtTelephony().isLocalEmergencyNumber(number);
+        } catch (RemoteException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        } catch (NullPointerException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        }
+        return isEmergencyNumber;
+    }
+
+    /**
+     * Returns true if TTY mode is enabled, false otherwise
+     */
+    private static boolean isTtyEnabled(final Context context) {
+        if (context == null) {
+            Log.w(context, "Context is null...");
+            return false;
+        }
+
+        final int TTY_MODE_OFF = 0;
+        final String PREFERRED_TTY_MODE = "preferred_tty_mode";
+        return (android.provider.Settings.Secure.getInt(context.getContentResolver(),
+                PREFERRED_TTY_MODE, TTY_MODE_OFF) != TTY_MODE_OFF);
+    }
+
+    static int getPhoneId(int subId) {
+        try {
+            Class c = Class.forName("android.telephony.SubscriptionManager");
+            Method m = c.getMethod("getPhoneId",new Class[]{int.class});
+            int phoneId = (Integer)m.invoke(null, subId);
+            if (phoneId >= InCallServiceImpl.sPhoneCount || phoneId < 0) {
+                phoneId = 0;
+            }
+            Log.d (LOG_TAG, "phoneid:" + phoneId);
+            return phoneId;
+        } catch (Exception e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        }
+        return 0;
+    }
+
+    static int getSubId(int phoneId) {
+        try {
+            Class c = Class.forName("android.telephony.SubscriptionManager");
+            Method m = c.getMethod("getSubId",new Class[]{int.class});
+            int subId[] = (int[])m.invoke(null, phoneId);
+            Log.d (LOG_TAG, "getSubId:" + subId[0]);
+            if (subId != null && subId.length > 0) {
+                return subId[0];
+            } else {
+                Log.e(LOG_TAG, "subId not valid: " + subId);
+            }
+        } catch (Exception e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        }
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    static void switchToActiveSub(int subId) {
+        try {
+            IExtTelephony mExtTelephony = getIExtTelephony();
+            Log.d(LOG_TAG, "switchToActiveSub, mExtTelephony:" + mExtTelephony);
+            mExtTelephony.switchToActiveSub(subId);
+        } catch (RemoteException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        } catch (NullPointerException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        }
+    }
+
+    static int getPhoneCount(Context context) {
+        TelephonyManager tm = null;
+        try {
+            Class c = Class.forName("android.telephony.TelephonyManager");
+            Method m = c.getMethod("from",new Class[]{Context.class});
+            tm = (TelephonyManager)m.invoke(null, context);
+        } catch (Exception e) {
+            Log.e(LOG_TAG, " ex: " + e);
+        }
+        if (tm != null) {
+            return tm.getPhoneCount();
+        } else {
+            Log.e(LOG_TAG, "tm is null" );
+            return 1;
+        }
+    }
+
+    static Boolean dsdaEnabled = null;
+    static boolean isDsdaEnabled() {
+        try {
+            if (dsdaEnabled == null) {
+                IExtTelephony mExtTelephony = getIExtTelephony();
+                Log.d(LOG_TAG, "isDsdaEnabled, mExtTelephony:" + mExtTelephony);
+                dsdaEnabled = mExtTelephony.isDsdaEnabled();
+                return dsdaEnabled;
+            }
+        } catch (RemoteException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        } catch (NullPointerException ex) {
+            Log.e(LOG_TAG, "Exception : " + ex);
+        }
+        return (dsdaEnabled == null) ? false : dsdaEnabled;
+    }
+
+    public static void downgradeToVoiceCall(final Call call) {
+        final VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_AUDIO_ONLY);
+        changeToVideoClicked(call, videoProfile);
+    }
+
+    /**
+     * This method converts the QtiCallConstants' Orientation modes to the ActivityInfo
+     * screen orientation mode.
+     */
+    public static int toUiOrientationMode(int orientationMode) {
+        switch(orientationMode) {
+            case QtiCallConstants.ORIENTATION_MODE_LANDSCAPE:
+                return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+            case QtiCallConstants.ORIENTATION_MODE_PORTRAIT:
+                return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+            case QtiCallConstants.ORIENTATION_MODE_DYNAMIC:
+                return ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
+            default:
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+    }
+
+    public static int toVideoIcon(int videoState) {
+        if (VideoProfile.isBidirectional(videoState)) {
+            return R.drawable.ic_videocam;
+        } else if (VideoProfile.isTransmissionEnabled(videoState)) {
+            return R.drawable.ic_tx_videocam;
+        } else {
+            return R.drawable.ic_rx_videocam;
+        }
+    }
+
+    /**
+     * Returns true if the CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO is set to false.
+     * Note that - CAPABILITY_SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL and
+     * CAPABILITY_SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE maps to
+     * CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO
+     */
+    public static boolean hasVoiceCapabilities(Call call) {
+        return call != null &&
+                !call.can(android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
+    }
+
+    /**
+     * Returns true if local has the VT Transmit and if remote capability has VT Receive set i.e.
+     * Local can transmit and remote can receive
+     */
+    public static boolean hasTransmitVideoCapabilities(Call call) {
+        return call != null &&
+                call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
+                && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
+    }
+
+    /**
+     * Returns true if local has the VT Receive and if remote capability has VT Transmit set i.e.
+     * Remote can transmit and local can receive
+     */
+    public static boolean hasReceiveVideoCapabilities(Call call) {
+        return call != null &&
+                call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_RX)
+                && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_TX);
+    }
+
+    /**
+     * Returns true if both voice and video capabilities (see above) are set
+     */
+    public static boolean hasVoiceOrVideoCapabilities(Call call) {
+        return hasVoiceCapabilities(call) || hasTransmitVideoCapabilities(call)
+                || hasReceiveVideoCapabilities(call);
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/SessionModificationCauseNotifier.java b/InCallUI/src/com/android/incallui/SessionModificationCauseNotifier.java
new file mode 100644
index 0000000..670b307
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/SessionModificationCauseNotifier.java
@@ -0,0 +1,161 @@
+/* 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.
+ */
+
+package com.android.incallui;
+
+import android.os.Bundle;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
+import com.google.common.base.Preconditions;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.codeaurora.ims.QtiCallConstants;
+
+/**
+ * This class listens to incoming events from the {@class InCallDetailsListener}.
+ * When call details change, this class is notified and we parse the extras from the details to
+ * figure out if session modification cause has been sent when a call upgrades/downgrades and
+ * notify the {@class InCallMessageController} to display the indication on UI.
+ */
+public class SessionModificationCauseNotifier implements InCallDetailsListener, CallList.Listener {
+
+    private final List<InCallSessionModificationCauseListener> mSessionModificationCauseListeners
+            = new CopyOnWriteArrayList<>();
+
+    private static SessionModificationCauseNotifier sSessionModificationCauseNotifier;
+    private final HashMap<String, Integer> mSessionModificationCauseMap = new HashMap<>();
+
+    /**
+     * Returns a singleton instance of {@class SessionModificationCauseNotifier}
+     */
+    public static synchronized SessionModificationCauseNotifier getInstance() {
+        if (sSessionModificationCauseNotifier == null) {
+            sSessionModificationCauseNotifier = new SessionModificationCauseNotifier();
+        }
+        return sSessionModificationCauseNotifier;
+    }
+
+    /**
+     * Adds a new session modification cause listener. Users interested in this cause
+     * should add a listener of type {@class InCallSessionModificationCauseListener}
+     */
+    public void addListener(InCallSessionModificationCauseListener listener) {
+        Preconditions.checkNotNull(listener);
+        mSessionModificationCauseListeners.add(listener);
+    }
+
+    /**
+     * Removes an existing session modification cause listener. Users listening to any cause
+     * changes when not interested any more can de-register an existing listener of type
+     * {@class InCallSessionModificationCauseListener}
+     */
+    public void removeListener(InCallSessionModificationCauseListener listener) {
+        if (listener != null) {
+            mSessionModificationCauseListeners.remove(listener);
+        } else {
+            Log.e(this, "Can't remove null listener");
+        }
+    }
+
+    /**
+     * Private constructor. Must use getInstance() to get this singleton.
+     */
+    private SessionModificationCauseNotifier() {
+    }
+
+    private int getSessionModificationCause(Bundle callExtras) {
+        return callExtras.getInt(QtiCallConstants.SESSION_MODIFICATION_CAUSE_EXTRA_KEY,
+                QtiCallConstants.CAUSE_CODE_UNSPECIFIED);
+    }
+    /**
+     * Overrides onDetailsChanged method of {@class InCallDetailsListener}. We are
+     * notified when call details change and extract the session modification cause from the
+     * extras, detect if the cause has changed and notify all registered listeners.
+     */
+    @Override
+    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
+        Log.d(this, "onDetailsChanged: - call: " + call + "details: " + details);
+
+        if (call == null || details == null ||
+                !Call.State.isConnectingOrConnected(call.getState())) {
+            Log.d(this, "onDetailsChanged - Call/details is null/Call is not connected. Return");
+            return;
+        }
+
+        final Bundle callExtras = details.getExtras();
+
+        if (callExtras == null) {
+            return;
+        }
+
+        final String callId = call.getId();
+
+        final int oldSessionModificationCause = mSessionModificationCauseMap.containsKey(callId) ?
+            mSessionModificationCauseMap.get(callId) : QtiCallConstants.CAUSE_CODE_UNSPECIFIED;
+        final int newSessionModificationCause = getSessionModificationCause(callExtras);
+
+        if (oldSessionModificationCause == newSessionModificationCause) {
+            return;
+        }
+
+        mSessionModificationCauseMap.put(callId, newSessionModificationCause);
+        // Notify all listeners only when there is a valid value
+        if (newSessionModificationCause != QtiCallConstants.CAUSE_CODE_UNSPECIFIED) {
+            Preconditions.checkNotNull(mSessionModificationCauseListeners);
+            for (InCallSessionModificationCauseListener listener :
+                mSessionModificationCauseListeners) {
+                    listener.onSessionModificationCauseChanged(call, newSessionModificationCause);
+            }
+        }
+    }
+
+    /**
+     * This method overrides onDisconnect method of {@interface CallList.Listener}
+     */
+    @Override
+    public void onDisconnect(final Call call) {
+        Log.d(this, "onDisconnect: call: " + call);
+        mSessionModificationCauseMap.remove(call.getId());
+    }
+
+    @Override
+    public void onUpgradeToVideo(Call call) {
+        //NO-OP
+    }
+
+    @Override
+    public void onIncomingCall(Call call) {
+        //NO-OP
+    }
+
+    @Override
+    public void onCallListChange(CallList callList) {
+        //NO-OP
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 0662cca..1efa3cc 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -38,15 +38,21 @@
 import android.net.Uri;
 import android.provider.ContactsContract.Contacts;
 import android.support.annotation.Nullable;
+import android.os.SystemClock;
 import android.telecom.Call.Details;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.text.BidiFormatter;
 import android.text.TextDirectionHeuristics;
 import android.text.TextUtils;
 
 import com.android.contacts.common.ContactsUtils;
 import com.android.contacts.common.ContactsUtils.UserType;
+import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.preference.ContactsPreferences;
 import com.android.contacts.common.testing.NeededForTesting;
 import com.android.contacts.common.util.BitmapUtil;
@@ -61,6 +67,7 @@
 import com.android.incallui.ringtone.ToneGeneratorFactory;
 
 import java.util.Objects;
+import org.codeaurora.ims.QtiCallConstants;
 
 /**
  * This class adds Notifications to the status bar for the in-call experience.
@@ -82,9 +89,11 @@
     @Nullable private ContactsPreferences mContactsPreferences;
     private final ContactInfoCache mContactInfoCache;
     private final NotificationManager mNotificationManager;
+    private final TelephonyManager mTelephonyManager;
     private final DialerRingtoneManager mDialerRingtoneManager;
     private int mCurrentNotification = NOTIFICATION_NONE;
     private int mCallState = Call.State.INVALID;
+    private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
     private int mSavedIcon = 0;
     private String mSavedContent = null;
     private Bitmap mSavedLargeIcon;
@@ -92,12 +101,19 @@
     private String mCallId = null;
     private InCallState mInCallState;
     private Uri mRingtone;
+    private static final String EXTRA_KEY_SHOW = "showCallStatusBar";
+    private static final String EXTRA_KEY_CALL_STATE = "callState";
+    private static final String EXTRA_KEY_CHRONOMETER_TIME = "baseChronometerMillis";
+    private static final String EXTRA_KEY_AUDIO_MODE = "audioMode";
+    private static final String EXTRA_KEY_MUTE = "mute";
 
     public StatusBarNotifier(Context context, ContactInfoCache contactInfoCache) {
         Preconditions.checkNotNull(context);
         mContext = context;
         mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
         mContactInfoCache = contactInfoCache;
+        mTelephonyManager =
+                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         mNotificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mDialerRingtoneManager = new DialerRingtoneManager(
@@ -116,6 +132,34 @@
         updateNotification(newState, callList);
     }
 
+    private int getVoWiFiQualityIcon(int voWifiCallQuality) {
+        switch (voWifiCallQuality) {
+            case QtiCallConstants.VOWIFI_QUALITY_EXCELLENT:
+                return R.drawable.vowifi_in_call_good;
+
+            case QtiCallConstants.VOWIFI_QUALITY_FAIR:
+                return R.drawable.vowifi_in_call_fair;
+
+            case QtiCallConstants.VOWIFI_QUALITY_POOR:
+                return R.drawable.vowifi_in_call_poor;
+        }
+       return QtiCallConstants.VOWIFI_QUALITY_NONE;
+    }
+
+    private String getVoWiFiQualityText(int voWifiCallQuality) {
+        switch (voWifiCallQuality) {
+            case QtiCallConstants.VOWIFI_QUALITY_EXCELLENT:
+                return mContext.getResources().getString(R.string.vowifi_call_quality_good);
+
+            case QtiCallConstants.VOWIFI_QUALITY_FAIR:
+                return mContext.getResources().getString(R.string.vowifi_call_quality_fair);
+
+            case QtiCallConstants.VOWIFI_QUALITY_POOR:
+                return mContext.getResources().getString(R.string.vowifi_call_quality_poor);
+        }
+      return null;
+    }
+
     /**
      * Updates the phone app's status bar notification *and* launches the
      * incoming call UI in response to a new incoming call.
@@ -187,11 +231,48 @@
         } else {
             cancelNotification();
         }
+        if (!state.isIncoming()) {
+            updateCallStatusBar(call);
+        }
+    }
+
+    public void updateCallStatusBar() {
+        CallList callList = InCallPresenter.getInstance().getCallList();
+        updateCallStatusBar(callList);
+    }
+
+    public void updateCallStatusBar(CallList callList) {
+        updateCallStatusBar(getCallToShow(callList));
+    }
+
+    private void updateCallStatusBar(Call call) {
+        Intent intent = new Intent(
+                "com.android.incallui.UPDATE_CALL_STATUS_BAR");
+        boolean isShowingInCallUi = InCallPresenter.getInstance().isShowingInCallUi();
+        boolean show = (call != null) && !isShowingInCallUi;
+        intent.putExtra(EXTRA_KEY_SHOW, show);
+        if (call != null) {
+            int state = call.getState();
+            intent.putExtra(EXTRA_KEY_CALL_STATE, state);
+            if (state == Call.State.ACTIVE) {
+                long chronometerTime = call.getConnectTimeMillis() - System.currentTimeMillis()
+                        + SystemClock.elapsedRealtime();
+                intent.putExtra(EXTRA_KEY_CHRONOMETER_TIME, chronometerTime);
+            }
+            AudioModeProvider audioModeProvider = AudioModeProvider.getInstance();
+            intent.putExtra(EXTRA_KEY_AUDIO_MODE, audioModeProvider.getAudioMode());
+            intent.putExtra(EXTRA_KEY_MUTE, audioModeProvider.getMute());
+        }
+        mContext.sendBroadcast(intent);
     }
 
     private void showNotification(final Call call) {
-        final boolean isIncoming = (call.getState() == Call.State.INCOMING ||
-                call.getState() == Call.State.CALL_WAITING);
+        final boolean isGeocoderLocationNeeded = (call.getState() == Call.State.INCOMING ||
+                call.getState() == Call.State.CALL_WAITING ||
+                call.getState() == Call.State.DIALING ||
+                call.getState() == Call.State.CONNECTING ||
+                call.getState() == Call.State.SELECT_PHONE_ACCOUNT);
+        Log.d(this, "showNotification isGeocoderLocationNeeded = " + isGeocoderLocationNeeded);
         if (!TextUtils.isEmpty(mCallId)) {
             CallList.getInstance().removeCallUpdateListener(mCallId, this);
         }
@@ -203,7 +284,7 @@
         // This callback will always get called immediately and synchronously with whatever data
         // it has available, and may make a subsequent call later (same thread) if it had to
         // call into the contacts provider for more data.
-        mContactInfoCache.findInfo(call, isIncoming, new ContactInfoCacheCallback() {
+        mContactInfoCache.findInfo(call, isGeocoderLocationNeeded, new ContactInfoCacheCallback() {
             @Override
             public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
                 Call call = CallList.getInstance().getCallById(callId);
@@ -241,24 +322,34 @@
         final int callState = call.getState();
 
         // Check if data has changed; if nothing is different, don't issue another notification.
-        final int iconResId = getIconToDisplay(call);
+        final int iconResId;
         Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
-        final String content =
+        String content =
                 getContentString(call, contactInfo.userType);
+        int wifiQualityValue = call.getWifiQuality();
+        if (wifiQualityValue != QtiCallConstants.VOWIFI_QUALITY_NONE) {
+            iconResId = getVoWiFiQualityIcon(wifiQualityValue);
+            content += " " + getVoWiFiQualityText(wifiQualityValue);
+        } else {
+            iconResId = getIconToDisplay(call);
+        }
         final String contentTitle = getContentTitle(contactInfo, call);
 
         final boolean isVideoUpgradeRequest = call.getSessionModificationState()
                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
+        final Call pendingAccountSelectionCall = CallList.getInstance()
+                .getWaitingForAccountCall();
         final int notificationType;
-        if (callState == Call.State.INCOMING || callState == Call.State.CALL_WAITING
-                || isVideoUpgradeRequest) {
+        if ((callState == Call.State.INCOMING || callState == Call.State.CALL_WAITING
+                || isVideoUpgradeRequest) && (!InCallPresenter.getInstance().isShowingInCallUi()
+                || pendingAccountSelectionCall != null)) {
             notificationType = NOTIFICATION_INCOMING_CALL;
         } else {
             notificationType = NOTIFICATION_IN_CALL;
         }
 
         if (!checkForChangeAndSaveData(iconResId, content, largeIcon, contentTitle, callState,
-                notificationType, contactInfo.contactRingtoneUri)) {
+                call.getVideoState(), notificationType, contactInfo.contactRingtoneUri)) {
             return;
         }
 
@@ -266,6 +357,16 @@
             largeIcon = getRoundedIcon(largeIcon);
         }
 
+        //set the content
+        boolean isMultiSimDevice = mTelephonyManager.isMultiSimEnabled();
+        if (isMultiSimDevice) {
+            SubscriptionInfo info =
+                    SubscriptionManager.from(mContext).getActiveSubscriptionInfo(call.getSubId());
+            if (info != null) {
+                content += " (" + info.getDisplayName() + ")";
+            }
+        }
+
         /*
          * This builder is used for the notification shown when the device is locked and the user
          * has set their notification settings to 'hide sensitive content'
@@ -291,7 +392,8 @@
 
         // Set the intent as a full screen intent as well if a call is incoming
         if (notificationType == NOTIFICATION_INCOMING_CALL
-                && !InCallPresenter.getInstance().isShowingInCallUi()) {
+                && (!InCallPresenter.getInstance().isShowingInCallUi() ||
+                   (pendingAccountSelectionCall != null))) {
             configureFullScreenIntent(builder, inCallPendingIntent, call);
             // Set the notification category for incoming calls
             builder.setCategory(Notification.CATEGORY_CALL);
@@ -357,7 +459,7 @@
             addDismissAction(builder);
             if (call.isVideoCall(mContext)) {
                 addVoiceAction(builder);
-                addVideoCallAction(builder);
+                addVideoCallAction(builder, call.getVideoState());
             } else {
                 addAnswerAction(builder);
             }
@@ -384,7 +486,7 @@
      * we do not issue a new notification for the exact same data.
      */
     private boolean checkForChangeAndSaveData(int icon, String content, Bitmap largeIcon,
-            String contentTitle, int state, int notificationType, Uri ringtone) {
+            String contentTitle, int state, int videoState, int notificationType, Uri ringtone) {
 
         // The two are different:
         // if new title is not null, it should be different from saved version OR
@@ -395,8 +497,9 @@
 
         // any change means we are definitely updating
         boolean retval = (mSavedIcon != icon) || !Objects.equals(mSavedContent, content)
-                || (mCallState != state) || (mSavedLargeIcon != largeIcon)
-                || contentTitleChanged || !Objects.equals(mRingtone, ringtone);
+                || (mCallState != state) || (mVideoState != videoState)
+                || (mSavedLargeIcon != largeIcon) || contentTitleChanged
+                || !Objects.equals(mRingtone, ringtone);
 
         // If we aren't showing a notification right now or the notification type is changing,
         // definitely do an update.
@@ -410,6 +513,7 @@
         mSavedIcon = icon;
         mSavedContent = content;
         mCallState = state;
+        mVideoState = videoState;
         mSavedLargeIcon = largeIcon;
         mSavedContentTitle = contentTitle;
         mRingtone = ringtone;
@@ -426,17 +530,22 @@
      */
     @NeededForTesting
     String getContentTitle(ContactCacheEntry contactInfo, Call call) {
-        if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
+        if (call.isConferenceCall() || call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
             return mContext.getResources().getString(R.string.card_title_conf_call);
         }
-
-        String preferredName = ContactDisplayUtils.getPreferredDisplayName(contactInfo.namePrimary,
-                    contactInfo.nameAlternative, mContactsPreferences);
-        if (TextUtils.isEmpty(preferredName)) {
-            return TextUtils.isEmpty(contactInfo.number) ? null : BidiFormatter.getInstance()
-                    .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
+        if (TextUtils.isEmpty(contactInfo.namePrimary)) {
+            String contactNumberDisplayed = TextUtils.isEmpty(contactInfo.number) ?
+                    "" : contactInfo.number.toString();
+            String location_info = GeoUtil.getGeocodedLocationFor(mContext, contactNumberDisplayed);
+            if (!TextUtils.isEmpty(location_info)){
+                contactNumberDisplayed =  contactNumberDisplayed + " " + location_info;
+            }
+            return TextUtils.isEmpty(contactNumberDisplayed) ? null
+                    : BidiFormatter.getInstance().unicodeWrap(
+                    contactNumberDisplayed, TextDirectionHeuristics.LTR);
         }
-        return preferredName;
+
+        return contactInfo.namePrimary;
     }
 
     private void addPersonReference(Notification.Builder builder, ContactCacheEntry contactInfo,
@@ -490,13 +599,25 @@
         // different calls.  So if both lines are in use, display info
         // from the foreground call.  And if there's a ringing call,
         // display that regardless of the state of the other calls.
+        int resId;
+        boolean supportsVoicePrivacy = call.hasProperty(Details.PROPERTY_HAS_CDMA_VOICE_PRIVACY);
         if (call.getState() == Call.State.ONHOLD) {
-            return R.drawable.ic_phone_paused_white_24dp;
+            if (supportsVoicePrivacy) {
+                resId = R.drawable.stat_sys_vp_phone_call_on_hold;
+            } else {
+                resId =  R.drawable.ic_phone_paused_white_24dp;
+            }
         } else if (call.getSessionModificationState()
                 == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
-            return R.drawable.ic_videocam;
+            resId =  R.drawable.ic_videocam;
+        } else {
+            if (supportsVoicePrivacy) {
+                resId =  R.drawable.stat_sys_vp_phone_call;
+            } else {
+                resId =  R.drawable.ic_call_white_24dp;
+            }
         }
-        return R.drawable.ic_call_white_24dp;
+        return resId;
     }
 
     /**
@@ -522,7 +643,9 @@
         }
 
         if (isIncomingOrWaiting) {
-            if (call.hasProperty(Details.PROPERTY_WIFI)) {
+            if (call.isIncomingConfCall()) {
+                resId = R.string.notification_incoming_conf_call;
+            } else if (call.hasProperty(Details.PROPERTY_WIFI)) {
                 resId = R.string.notification_incoming_call_wifi;
             } else {
                 resId = R.string.notification_incoming_call;
@@ -609,12 +732,12 @@
                 hangupPendingIntent);
     }
 
-    private void addVideoCallAction(Notification.Builder builder) {
+    private void addVideoCallAction(Notification.Builder builder, int videoState) {
         Log.i(this, "Will show \"video\" action in the incoming call Notification");
 
         PendingIntent answerVideoPendingIntent = createNotificationPendingIntent(
                 mContext, ACTION_ANSWER_VIDEO_INCOMING_CALL);
-        builder.addAction(R.drawable.ic_videocam,
+        builder.addAction(QtiCallUtils.toVideoIcon(videoState),
                 mContext.getText(R.string.notification_action_answer_video),
                 answerVideoPendingIntent);
     }
@@ -684,12 +807,18 @@
         // If a call is onhold during an incoming call, the call actually comes in as
         // INCOMING.  For that case *and* traditional call-waiting, we want to
         // cancel the notification.
+
+        // For DSDA, we want to cancel the notification if we get an incoming call on
+        // one sub and there is a live call on another sub.
+        CallList callList = CallList.getInstance();
         boolean isCallWaiting = (call.getState() == Call.State.CALL_WAITING ||
                 (call.getState() == Call.State.INCOMING &&
-                        CallList.getInstance().getBackgroundCall() != null));
+                (callList.getBackgroundCall() != null ||
+                callList.isAnyOtherSubActive(callList.getActiveSubId()))));
 
         if (isCallWaiting) {
-            Log.i(this, "updateInCallNotification: call-waiting! force relaunch...");
+            Log.i(this, "configureFullScreenIntent: call-waiting or dsda incoming call!"
+                    + " force relaunch. Active sub:" + callList.getActiveSubId());
             // Cancel the IN_CALL_NOTIFICATION immediately before
             // (re)posting it; this seems to force the
             // NotificationManager to launch the fullScreenIntent.
@@ -746,7 +875,7 @@
      * @param sessionModificationState The new session modification state.
      */
     @Override
-    public void onSessionModificationStateChange(int sessionModificationState) {
+    public void onSessionModificationStateChange(Call call, int sessionModificationState) {
         if (sessionModificationState == Call.SessionModificationState.NO_REQUEST) {
             if (mCallId != null) {
                 CallList.getInstance().removeCallUpdateListener(mCallId, this);
diff --git a/InCallUI/src/com/android/incallui/TelecomAdapter.java b/InCallUI/src/com/android/incallui/TelecomAdapter.java
index f172270..4873f96 100644
--- a/InCallUI/src/com/android/incallui/TelecomAdapter.java
+++ b/InCallUI/src/com/android/incallui/TelecomAdapter.java
@@ -27,7 +27,8 @@
 import java.util.List;
 
 final class TelecomAdapter implements InCallServiceListener {
-    private static final String ADD_CALL_MODE_KEY = "add_call_mode";
+    public static final String ADD_CALL_MODE_KEY = "add_call_mode";
+    public static final String ADD_PARTICIPANT_KEY = "add_participant";
 
     private static TelecomAdapter sInstance;
     private InCallService mInCallService;
diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java
index 6a46a42..3a708ab 100644
--- a/InCallUI/src/com/android/incallui/VideoCallFragment.java
+++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java
@@ -16,11 +16,13 @@
 
 package com.android.incallui;
 
+import android.app.Activity;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.SurfaceTexture;
 import android.os.Bundle;
 import android.view.Display;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.Surface;
 import android.view.TextureView;
@@ -28,6 +30,8 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
@@ -115,7 +119,7 @@
      * changes.
      */
     private static class VideoCallSurface implements TextureView.SurfaceTextureListener,
-            View.OnClickListener, View.OnAttachStateChangeListener {
+            View.OnClickListener, View.OnAttachStateChangeListener, View.OnLongClickListener {
         private int mSurfaceId;
         private VideoCallPresenter mPresenter;
         private TextureView mTextureView;
@@ -174,6 +178,7 @@
             mTextureView = view;
             mTextureView.setSurfaceTextureListener(this);
             mTextureView.setOnClickListener(this);
+            mTextureView.setOnLongClickListener(this);
 
             final boolean areSameSurfaces =
                     Objects.equal(mSavedSurfaceTexture, mTextureView.getSurfaceTexture());
@@ -412,6 +417,18 @@
         }
 
         /**
+         * Handles a user long pressing on the surface, which is the trigger to show the
+         * picture mode pop up alert dialog
+         *
+         * @param View The view receiving the long press.
+         */
+        @Override
+        public boolean onLongClick(View v) {
+            Log.d(this, "onLongClick:");
+            return mPresenter.onLongClick();
+        }
+
+        /**
          * Returns the dimensions of the surface.
          *
          * @return The dimensions of the surface.
@@ -563,6 +580,8 @@
         if (mPreviewPhoto != null) {
             mPreviewPhoto.setVisibility(!previewPaused ? View.VISIBLE : View.INVISIBLE);
         }
+
+        enableScreenTimeout(false);
     }
 
     /**
@@ -570,6 +589,7 @@
      */
     @Override
     public void hideVideoUi() {
+        enableScreenTimeout(true);
         inflateVideoUi(false);
     }
 
@@ -693,9 +713,15 @@
             preview.setLayoutParams(params);
 
             if (mPreviewVideoContainer != null) {
-                ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams();
+                FrameLayout.LayoutParams containerParams = (FrameLayout.LayoutParams)
+                        mPreviewVideoContainer.getLayoutParams();
                 containerParams.width = width;
                 containerParams.height = height;
+                if (getPresenter().isCameraPreviewMode()) {
+                    containerParams.gravity = Gravity.CENTER;
+                } else {
+                    containerParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+                }
                 mPreviewVideoContainer.setLayoutParams(containerParams);
             }
 
@@ -809,6 +835,13 @@
         return sPreviewSurface.getSurfaceDimensions();
     }
 
+    @Override
+    public void showOutgoingVideoView(boolean show) {
+        if (mPreviewVideoContainer != null) {
+            mPreviewVideoContainer.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
     /**
      * Inflates the {@link ViewStub} containing the incoming and outgoing surfaces, if necessary,
      * and creates {@link VideoCallSurface} instances to track the surfaces.
@@ -828,7 +861,12 @@
 
             Log.d(this, "inflateVideoCallViews: sVideoSurfacesInUse=" + sVideoSurfacesInUse);
             //If peer adjusted screen size is not available, set screen size to default display size
-            Point screenSize = sDisplaySize == null ? getScreenSize() : sDisplaySize;
+            Point screenSize = getScreenSize();
+            if (sDisplaySize != null) {
+                screenSize = VideoCallPresenter.resizeForAspectRatio(screenSize,
+                        sDisplaySize.x, sDisplaySize.y);
+            }
+
             setSurfaceSizeAndTranslation(displaySurface, screenSize);
 
             if (!sVideoSurfacesInUse) {
@@ -898,4 +936,23 @@
             centerDisplayView(textureView);
         }
     }
+
+    private void enableScreenTimeout(boolean enable) {
+        Log.v(this, "enableScreenTimeout: value=" + enable);
+        final Activity activity = getActivity();
+        if (activity == null) {
+            Log.e(this, "enableScreenTimeout: Activity is null.");
+            return;
+        }
+        final Window window = activity.getWindow();
+        if (window == null) {
+            Log.e(this, "enableScreenTimeout: window is null");
+            return;
+        }
+        if (enable) {
+            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        } else {
+            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
 }
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index 06e3e44..58405a8 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -23,7 +23,9 @@
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.provider.ContactsContract;
+import android.content.pm.ActivityInfo;
 import android.telecom.Connection;
 import android.telecom.InCallService.VideoCall;
 import android.telecom.VideoProfile;
@@ -31,6 +33,8 @@
 import android.view.Surface;
 import android.widget.ImageView;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.compat.CompatUtils;
 import com.android.dialer.R;
@@ -68,7 +72,8 @@
 public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi> implements
         IncomingCallListener, InCallOrientationListener, InCallStateListener,
         InCallDetailsListener, SurfaceChangeListener, VideoEventListener,
-        InCallPresenter.InCallEventListener {
+        InCallPresenter.InCallEventListener, InCallUiStateNotifierListener,
+        CallList.CallUpdateListener, PictureModeHelper.Listener {
     public static final String TAG = "VideoCallPresenter";
 
     public static final boolean DEBUG = false;
@@ -193,11 +198,49 @@
     private int mAutoFullscreenTimeoutMillis = 0;
 
     /**
+     *Caches information about whether InCall UI is in the background or foreground
+     */
+    private boolean mIsInBackground;
+    /**
      * Determines if the countdown is currently running to automatically enter full screen video
      * mode.
      */
     private boolean mAutoFullScreenPending = false;
 
+    // Stores the current orientation mode from primary call
+    private int mActivityOrientationMode = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+    /**
+     * Determines if the incoming video is available. If the call session resume event has been
+     * received (i.e PLAYER_START has been received from lower layers), incoming video is
+     * available. If the call session pause event has been received (i.e PLAYER_STOP has been
+     * received from lower layers), incoming video is not available.
+     */
+    private static boolean mIsIncomingVideoAvailable = false;
+
+    /**
+     * Property when set will disable PIP mode.
+     * Default value is 0 (disable). To enable, set to 1 (enable)
+     */
+    private static final String PROP_DISABLE_VIDEOCALL_PIP_MODE =
+            "persist.disable.pip.mode";
+
+    /**
+     * Property set to specify the camera preview size when the picture mode is selected as
+     * camera preview mode only. Format is widthxheight (e.g 320x240)
+     */
+    private static final String PROP_CAMERA_PREVIEW_SIZE =
+            "persist.camera.preview.size";
+
+    private static final String CAMERA_PREVIEW_SIZE_DELIM = "x";
+
+    /**
+     * Cache the aspect ratio of the preview window.
+     */
+    private float mPreviewAspectRatio = 1.0f;
+
+    private PictureModeHelper mPictureModeHelper;
+
     /**
      * Initializes the presenter.
      *
@@ -205,6 +248,7 @@
      */
     public void init(Context context) {
         mContext = context;
+        mPictureModeHelper = new PictureModeHelper(mContext);
         mMinimumVideoDimension = mContext.getResources().getDimension(
                 R.dimen.video_preview_small_dimension);
         mHandler = new Handler(Looper.getMainLooper());
@@ -238,16 +282,19 @@
         // To get updates of video call details changes
         InCallPresenter.getInstance().addDetailsListener(this);
         InCallPresenter.getInstance().addInCallEventListener(this);
+        mPictureModeHelper.setUp(this);
 
         // Register for surface and video events from {@link InCallVideoCallListener}s.
         InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this);
-        InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this);
+        InCallUiStateNotifier.getInstance().addListener(this);
         mCurrentVideoState = VideoProfile.STATE_AUDIO_ONLY;
         mCurrentCallState = Call.State.INVALID;
 
         final InCallPresenter.InCallState inCallState =
              InCallPresenter.getInstance().getInCallState();
         onStateChange(inCallState, inCallState, CallList.getInstance());
+        InCallVideoCallCallbackNotifier.getInstance().addVideoEventListener(this,
+                VideoUtils.isVideoCall(mCurrentVideoState));
     }
 
     /**
@@ -274,6 +321,12 @@
 
         InCallVideoCallCallbackNotifier.getInstance().removeSurfaceChangeListener(this);
         InCallVideoCallCallbackNotifier.getInstance().removeVideoEventListener(this);
+        InCallUiStateNotifier.getInstance().removeListener(this);
+        if(mPrimaryCall != null) {
+            CallList.getInstance().removeCallUpdateListener(mPrimaryCall.getId(), this);
+        }
+        mPictureModeHelper.tearDown(this);
+        cancelAutoFullScreen();
     }
 
     /**
@@ -299,11 +352,17 @@
             if (mPreviewSurfaceState == PreviewSurfaceState.CAPABILITIES_RECEIVED) {
                 mPreviewSurfaceState = PreviewSurfaceState.SURFACE_SET;
                 mVideoCall.setPreviewSurface(ui.getPreviewVideoSurface());
-            } else if (mPreviewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()){
-                enableCamera(mVideoCall, true);
+            } else {
+                maybeEnableCamera();
             }
         } else if (surface == VideoCallFragment.SURFACE_DISPLAY) {
             mVideoCall.setDisplaySurface(ui.getDisplayVideoSurface());
+
+            // Show/hide the incoming video once surface is created based on
+            // whether PLAYER_START event has been received or not. Since we
+            // start with showing incoming video by default for surface creation,
+            // we need to make sure we hide it once surface is available.
+            showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall());
         }
     }
 
@@ -370,15 +429,35 @@
     }
 
     /**
-     * Handles clicks on the video surfaces by toggling full screen state.
-     * Informs the {@link InCallPresenter} of the change so that it can inform the
-     * {@link CallCardPresenter} of the change.
+     * Handles clicks on the video surfaces by toggling full screen state if surface is
+     * SURFACE_DISPLAY. Call onPreviewSurfaceClicked of InCallZoomController if preview surface
+     * is clicked. Informs the {@link InCallPresenter} of the change for Display surface so that
+     * it can inform the {@link CallCardPresenter} of the change.
      *
      * @param surfaceId The video surface receiving the click.
      */
     public void onSurfaceClick(int surfaceId) {
-        boolean isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode();
-        Log.v(this, "toggleFullScreen = " + isFullscreen);
+        if (!mIsVideoMode) {
+            Log.d(this, "onSurfaceClick: Not in video mode ignoring.");
+            return;
+        }
+        switch (surfaceId) {
+            case VideoCallFragment.SURFACE_DISPLAY:
+                boolean isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode();
+                Log.d(this, "toggleFullScreen = " + isFullscreen + "surfaceId =" + surfaceId);
+                break;
+            case VideoCallFragment.SURFACE_PREVIEW:
+                if (mPictureModeHelper.canShowPreviewVideoView() &&
+                        mPictureModeHelper.canShowIncomingVideoView()) {
+                    InCallZoomController.getInstance().onPreviewSurfaceClicked(mVideoCall);
+                } else {
+                    isFullscreen = InCallPresenter.getInstance().toggleFullscreenMode();
+                    Log.d(this, "toggleFullScreen = " + isFullscreen + "surfaceId =" + surfaceId);
+                }
+                break;
+            default:
+                break;
+        }
     }
 
     /**
@@ -411,7 +490,6 @@
             if (isVideoMode()) {
                 exitVideoMode();
             }
-
             cleanupSurfaces();
         }
 
@@ -471,6 +549,10 @@
         cancelAutoFullScreen();
     }
 
+    @Override
+    public void updatePrimaryCallState() {
+    }
+
     /**
      * Handles changes to the visibility of the secondary caller info bar.
      *
@@ -486,9 +568,14 @@
         getUi().adjustPreviewLocation(isVisible /* shiftUp */, height);
     }
 
+    @Override
+    public void onIncomingVideoAvailabilityChanged(boolean isAvailable) {
+        //NO OP
+    }
+
     private void checkForVideoStateChange(Call call) {
-        final boolean isVideoCall = VideoUtils.isVideoCall(call);
-        final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState();
+        boolean isVideoCall = VideoUtils.isVideoCall(call);
+        boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState();
 
         Log.d(this, "checkForVideoStateChange: isVideoCall= " + isVideoCall
                 + " hasVideoStateChanged=" + hasVideoStateChanged + " isVideoMode="
@@ -496,6 +583,11 @@
                 VideoProfile.videoStateToString(mCurrentVideoState) + " newVideoState: "
                 + VideoProfile.videoStateToString(call.getVideoState()));
 
+        if (isModifyCallPreview(mContext, call)) {
+            isVideoCall |= VideoUtils.isVideoCall(call.getRequestedVideoState());
+            hasVideoStateChanged |= mCurrentVideoState != call.getRequestedVideoState();
+         }
+
         if (!hasVideoStateChanged) {
             return;
         }
@@ -535,7 +627,7 @@
         }
 
         // Make sure we hide or show the video UI if needed.
-        showVideoUi(call.getVideoState(), call.getState());
+        showVideoUi(call.getVideoState(), call.getState(), call.isConferenceCall());
     }
 
     private void cleanupSurfaces() {
@@ -554,6 +646,7 @@
         Log.d(this, "onPrimaryCallChanged: isVideoCall=" + isVideoCall + " isVideoMode="
                 + isVideoMode);
 
+        listenToCallUpdates(newPrimaryCall);
         if (!isVideoCall && isVideoMode) {
             // Terminate video mode if new primary call is not a video call
             // and we are currently in video mode.
@@ -562,6 +655,7 @@
         } else if (isVideoCall) {
             Log.d(this, "onPrimaryCallChanged: Entering video mode...");
 
+            checkForOrientationAllowedChange(newPrimaryCall);
             updateCameraSelection(newPrimaryCall);
             adjustVideoMode(newPrimaryCall);
         }
@@ -619,8 +713,13 @@
     }
 
     private void checkForOrientationAllowedChange(Call call) {
-        InCallPresenter.getInstance().setInCallAllowsOrientationChange(
-                VideoUtils.isVideoCall(call));
+        final int newMode = OrientationModeHandler.getInstance().getOrientation(call);
+        if (newMode != mActivityOrientationMode && InCallPresenter.
+                getInstance().setInCallAllowsOrientationChange(newMode)) {
+            Log.d(this, "checkForOrientationAllowedChange: currMode = " +
+                    mActivityOrientationMode + " newMode = " + newMode);
+            mActivityOrientationMode = newMode;
+        }
     }
 
     /**
@@ -664,9 +763,9 @@
         }
     }
 
-    private static boolean isCameraRequired(int videoState) {
-        return VideoProfile.isBidirectional(videoState)
-                || VideoProfile.isTransmissionEnabled(videoState);
+    private boolean isCameraRequired(int videoState) {
+        return ((VideoProfile.isBidirectional(videoState) ||
+                VideoProfile.isTransmissionEnabled(videoState)) && !mIsInBackground);
     }
 
     private boolean isCameraRequired() {
@@ -689,8 +788,13 @@
             Log.e(this, "Error VideoCallUi is null so returning");
             return;
         }
+        if (isModifyCallPreview(mContext, call)) {
+           Log.d(this, "modifying video state = " + newVideoState +
+                " to video state: " + call.getRequestedVideoState());
+           newVideoState = call.getRequestedVideoState();
+        }
 
-        showVideoUi(newVideoState, call.getState());
+        showVideoUi(newVideoState, call.getState(), call.isConferenceCall());
 
         // Communicate the current camera to telephony and make a request for the camera
         // capabilities.
@@ -728,9 +832,11 @@
             mPreviewSurfaceState = PreviewSurfaceState.CAMERA_SET;
 
             videoCall.requestCameraCapabilities();
+            InCallZoomController.getInstance().onCameraEnabled(cameraManager.getActiveCameraId());
         } else {
             mPreviewSurfaceState = PreviewSurfaceState.NONE;
             videoCall.setCamera(null);
+            InCallZoomController.getInstance().onCameraEnabled(null);
         }
     }
 
@@ -740,7 +846,7 @@
     private void exitVideoMode() {
         Log.d(this, "exitVideoMode");
 
-        showVideoUi(VideoProfile.STATE_AUDIO_ONLY, Call.State.ACTIVE);
+        showVideoUi(VideoProfile.STATE_AUDIO_ONLY, Call.State.ACTIVE, false);
         enableCamera(mVideoCall, false);
         InCallPresenter.getInstance().setFullScreen(false);
 
@@ -750,39 +856,57 @@
     /**
      * Based on the current video state and call state, show or hide the incoming and
      * outgoing video surfaces.  The outgoing video surface is shown any time video is transmitting.
-     * The incoming video surface is shown whenever the video is un-paused and active.
+     * The incoming video surface is shown whenever the video is un-paused and active and incoming
+     * video is available. If display surface has not been created and video reception is enabled,
+     * we override the value returned by showIncomingVideo and show the incoming video so surface
+     * creation is enabled
      *
      * @param videoState The video state.
      * @param callState The call state.
      */
-    private void showVideoUi(int videoState, int callState) {
+    private void showVideoUi(int videoState, int callState, boolean isConf) {
         VideoCallUi ui = getUi();
         if (ui == null) {
             Log.e(this, "showVideoUi, VideoCallUi is null returning");
             return;
         }
-        boolean showIncomingVideo = showIncomingVideo(videoState, callState);
-        boolean showOutgoingVideo = showOutgoingVideo(videoState);
+
+        final boolean isDisplaySurfaceCreated = ui.isDisplayVideoSurfaceCreated();
+        final boolean isVideoReceptionEnabled = VideoProfile.isReceptionEnabled(videoState);
+        boolean showIncomingVideo = (showIncomingVideo(videoState, callState) &&
+                mPictureModeHelper.canShowIncomingVideoView()) ||
+                (!isDisplaySurfaceCreated && isVideoReceptionEnabled);
+        boolean showOutgoingVideo = showOutgoingVideo(videoState) &&
+                mPictureModeHelper.canShowPreviewVideoView();
+
         Log.v(this, "showVideoUi : showIncoming = " + showIncomingVideo + " showOutgoing = "
                 + showOutgoingVideo);
         if (showIncomingVideo || showOutgoingVideo) {
             ui.showVideoViews(showOutgoingVideo, showIncomingVideo);
 
-            if (VideoProfile.isReceptionEnabled(videoState)) {
+            boolean hidePreview = shallHidePreview(isConf, videoState);
+            Log.v(this, "showVideoUi, hidePreview = " + hidePreview);
+            if (hidePreview) {
+                ui.showOutgoingVideoView(!hidePreview);
+            }
+
+            if (showOutgoingVideo) {
+                setPreviewSize(mDeviceOrientation, mPreviewAspectRatio);
+            }
+
+            if (isVideoReceptionEnabled) {
                 loadProfilePhotoAsync();
             }
         } else {
             ui.hideVideoUi();
         }
-
-        InCallPresenter.getInstance().enableScreenTimeout(
-                VideoProfile.isAudioOnly(videoState));
     }
 
     /**
      * Determines if the incoming video surface should be shown based on the current videoState and
-     * callState.  The video surface is shown when incoming video is not paused, the call is active,
-     * and video reception is enabled.
+     * callState.  The video surface is shown when video reception is enabled AND either incoming
+     * video is not paused, the call is active or dialing, incoming video is available
+     * (i.e PLAYER_START event has been raised by lower layers)
      *
      * @param videoState The current video state.
      * @param callState The current call state.
@@ -795,8 +919,12 @@
 
         boolean isPaused = VideoProfile.isPaused(videoState);
         boolean isCallActive = callState == Call.State.ACTIVE;
+        //Show incoming Video for dialing calls to support early media
+        boolean isCallOutgoing = Call.State.isDialing(callState) ||
+                callState == Call.State.CONNECTING;
 
-        return !isPaused && isCallActive && VideoProfile.isReceptionEnabled(videoState);
+        return !isPaused && (isCallActive || isCallOutgoing) &&
+                VideoProfile.isReceptionEnabled(videoState) && mIsIncomingVideoAvailable;
     }
 
     /**
@@ -816,6 +944,40 @@
     }
 
     /**
+     * Opens camera if the camera has not yet been set on the {@link VideoCall}; negotiation has
+     * not yet started and if camera is required
+     */
+    private void maybeEnableCamera() {
+        if (mPreviewSurfaceState == PreviewSurfaceState.NONE && isCameraRequired()) {
+            enableCamera(mVideoCall, true);
+        }
+    }
+
+    /**
+     * This method gets invoked when visibility of InCallUI is changed. For eg.
+     * when UE moves in/out of the foreground, display either turns ON/OFF
+     * @param showing true if InCallUI is visible, false  otherwise.
+     */
+    @Override
+    public void onUiShowing(boolean showing) {
+        Log.d(this, "onUiShowing, showing = " + showing + " mPrimaryCall = " + mPrimaryCall +
+                " mPreviewSurfaceState = " + mPreviewSurfaceState);
+
+        mIsInBackground = !showing;
+
+        if (mPrimaryCall == null || !VideoUtils.isActiveVideoCall(mPrimaryCall)) {
+            Log.w(this, "onUiShowing, received for non-active video call");
+            return;
+        }
+
+        if (showing) {
+            maybeEnableCamera();
+        } else if (mPreviewSurfaceState != PreviewSurfaceState.NONE) {
+            enableCamera(mVideoCall, false);
+        }
+    }
+
+    /**
      * Handles peer video pause state changes.
      *
      * @param call The call which paused or un-pausedvideo transmission.
@@ -923,9 +1085,11 @@
             aspectRatio = (float) width / (float) height;
         }
 
+        mPreviewAspectRatio = aspectRatio;
+
         // Resize the textureview housing the preview video and rotate it appropriately based on
         // the device orientation
-        setPreviewSize(mDeviceOrientation, aspectRatio);
+        setPreviewSize(mDeviceOrientation, mPreviewAspectRatio);
     }
 
     /**
@@ -940,10 +1104,13 @@
 
         switch (event) {
             case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
-                sb.append("rx_pause");
-                break;
             case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
-                sb.append("rx_resume");
+                mIsIncomingVideoAvailable =
+                    event == Connection.VideoProvider.SESSION_EVENT_RX_RESUME;
+                showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall());
+                sb.append(mIsIncomingVideoAvailable ? "rx_resume" : "rx_pause");
+                InCallPresenter.getInstance().
+                        notifyIncomingVideoAvailabilityChanged(mIsIncomingVideoAvailable);
                 break;
             case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
                 sb.append("camera_failure");
@@ -996,6 +1163,9 @@
         changePreviewDimensions(previewDimensions.x, previewDimensions.y);
 
         ui.setPreviewRotation(mDeviceOrientation);
+        // Notify picture mode changed so that if camera preview is showing in non PIP
+        // mode, we can correctly resize the camera preview by swapping width and height.
+        showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall());
     }
 
     /**
@@ -1009,24 +1179,45 @@
      * @param aspectRatio The aspect ratio of the camera (width / height).
      */
     private void setPreviewSize(int orientation, float aspectRatio) {
+        Log.d(this, "setPreviewSize: orientation = " + orientation +
+                " aspectRatio = " + aspectRatio);
         VideoCallUi ui = getUi();
         if (ui == null) {
             return;
         }
 
-        int height;
-        int width;
+        float height = 0.0f;
+        float width = 0.0f;
+        final boolean isPipMode = mPictureModeHelper.isPipMode();
 
-        if (orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_90 ||
-                orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_270) {
-            width = (int) (mMinimumVideoDimension * aspectRatio);
-            height = (int) mMinimumVideoDimension;
+        if (isPipMode) {
+            width = mMinimumVideoDimension;
+            height = mMinimumVideoDimension;
         } else {
-            // Portrait or reverse portrait orientation.
-            width = (int) mMinimumVideoDimension;
-            height = (int) (mMinimumVideoDimension * aspectRatio);
+            Point size = getPreviewVideoSize();
+            // Swap width and height if landscape
+            final boolean isLayoutLandscape = mContext.getResources().getBoolean(
+                R.bool.is_layout_landscape);
+            width = isLayoutLandscape ? size.y : size.x;
+            height = isLayoutLandscape ? size.x : size.y;
         }
-        ui.setPreviewSize(width, height);
+
+        final boolean hasNoPreviewSizeInProp = ((SystemProperties.get(
+                PROP_CAMERA_PREVIEW_SIZE, "")).isEmpty());
+
+        // Do not apply aspect ratio if camera preview is set in the adb property -
+        // "persist.camera.preview.size". Aspect ratio is applied to full screen size for
+        // camera preview and for Pip mode
+        if (hasNoPreviewSizeInProp || isPipMode) {
+            if (orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_90 ||
+                    orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_270) {
+                width = (aspectRatio > 1.0) ? width * aspectRatio : width / aspectRatio;
+            } else {
+                // Portrait or reverse portrait orientation.
+                height = (aspectRatio > 1.0) ? height * aspectRatio : height / aspectRatio;
+            }
+        }
+        ui.setPreviewSize((int) width, (int) height);
     }
 
     /**
@@ -1046,6 +1237,12 @@
         Point size = ui.getScreenSize();
         Log.v(this, "setDisplayVideoSize: windowmgr width=" + size.x
                 + " windowmgr height=" + size.y);
+        size = resizeForAspectRatio(size, width, height);
+        ui.setDisplayVideoSize(size.x, size.y);
+    }
+
+    public static Point resizeForAspectRatio(Point inSize, int width, int height) {
+        Point size = new Point(inSize);
         if (size.y * width > size.x * height) {
             // current display height is too much. Correct it
             size.y = (int) (size.x * height / width);
@@ -1053,7 +1250,7 @@
             // current display width is too much. Correct it
             size.x = (int) (size.y * width / height);
         }
-        ui.setDisplayVideoSize(size.x, size.y);
+        return size;
     }
 
     /**
@@ -1117,9 +1314,16 @@
         }
         Log.v(this, "cancelAutoFullScreen : cancelling pending");
         mAutoFullScreenPending = false;
+        if (mHandler != null) {
+            mHandler.removeCallbacks(mAutoFullscreenRunnable);
+        }
     }
 
-    private static void updateCameraSelection(Call call) {
+    private static boolean isAudioRouteEnabled(int audioRoute, int audioRouteMask) {
+        return ((audioRoute & audioRouteMask) != 0);
+    }
+
+    private void updateCameraSelection(Call call) {
         Log.d(TAG, "updateCameraSelection: call=" + call);
         Log.d(TAG, "updateCameraSelection: call=" + toSimpleString(call));
 
@@ -1134,6 +1338,12 @@
                     + " Setting camera direction to default value (CAMERA_DIRECTION_UNKNOWN)");
         }
 
+        // for preview scenario if it is supported
+        else if(isModifyCallPreview(mContext, call)) {
+            cameraDir = toCameraDirection(call.getRequestedVideoState());
+            call.getVideoSettings().setCameraDir(cameraDir);
+        }
+
         // Clear camera direction if this is not a video call.
         else if (VideoUtils.isAudioCall(call)) {
             cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
@@ -1203,6 +1413,83 @@
     }
 
     /**
+     * The function is called to create and display picture mode alert dialog when user long
+     * presses on the video call screen
+     */
+     public boolean onLongClick() {
+        // Don't show the alert if either the adb property "persist.disable.pip.mode" is not set
+        // or if we are supposed to hide preview for conference calls
+        if ((SystemProperties.getInt(PROP_DISABLE_VIDEOCALL_PIP_MODE, 0) == 0) ||
+            shallHidePreview(isConfCall(), mCurrentVideoState)) {
+            return false;
+        }
+        mPictureModeHelper.create(mContext);
+        mPictureModeHelper.show();
+        return true;
+    }
+
+    /**
+     * Gets the preview video size either from the property - "persist.camera.preview.size" if it
+     * is set or return the full screen size
+     */
+    private Point getPreviewVideoSize() {
+        VideoCallUi ui = getUi();
+        if (ui == null) {
+            Log.e(this, "getPreviewVideoSize, VideoCallUi is null returning");
+            return null;
+        }
+
+        Point previewSize = getPreviewVideoSizeFromProp();
+
+        if (previewSize == null) {
+            previewSize = ui.getScreenSize();
+        }
+
+        return previewSize;
+    }
+
+    /**
+     * Gets the preview video size from the property - "persist.camera.preview.size"
+     * @return Point point - Size of the preview (width and height)
+     */
+    private static Point getPreviewVideoSizeFromProp() {
+        final String cameraPreviewSize = SystemProperties.get(
+                PROP_CAMERA_PREVIEW_SIZE, "");
+        if (!cameraPreviewSize.isEmpty()) {
+            final String[] sizeDimensions = cameraPreviewSize.split(CAMERA_PREVIEW_SIZE_DELIM);
+            final int width = Integer.parseInt(sizeDimensions[0]);
+            final int height = Integer.parseInt(sizeDimensions[1]);
+            return new Point(width, height);
+        }
+        return null;
+    }
+
+    /**
+     * Gets called when preview video selection changes
+     * @param boolean previewVideoSelection - New value for preview video selection
+     */
+    @Override
+    public void onPreviewVideoSelectionChanged() {
+        VideoCallUi ui = getUi();
+        if (ui == null) {
+            Log.e(this, "onPreviewVideoSelectionChanged, VideoCallUi is null returning");
+            return;
+        }
+
+        ui.showOutgoingVideoView(showOutgoingVideo(mCurrentVideoState) &&
+                mPictureModeHelper.canShowPreviewVideoView());
+    }
+
+    /**
+     * Gets called when incoming video selection changes
+     * @param boolean incomingVideoSelection - New value for incoming video selection
+     */
+    @Override
+    public void onIncomingVideoSelectionChanged() {
+        showVideoUi(mCurrentVideoState, mCurrentCallState, isConfCall());
+    }
+
+    /**
      * Starts an asynchronous load of the user's profile photo.
      */
     public void loadProfilePhotoAsync() {
@@ -1283,6 +1570,23 @@
     }
 
     /**
+     * Hide preview window if it is a VT conference call
+     */
+    private boolean shallHidePreview(boolean isConf, int videoState) {
+        return VideoProfile.isBidirectional(videoState) && isConf
+                && QtiImsExtUtils.shallHidePreviewInVtConference(mContext);
+    }
+
+    private boolean isConfCall() {
+        return mPrimaryCall != null ? mPrimaryCall.isConferenceCall() : false;
+    }
+
+    public boolean isCameraPreviewMode() {
+        return mPictureModeHelper.canShowPreviewVideoView() &&
+                !(mPictureModeHelper.canShowIncomingVideoView());
+    }
+
+    /**
      * Defines the VideoCallUI interactions.
      */
     public interface VideoCallUi extends Ui {
@@ -1302,5 +1606,71 @@
         ImageView getPreviewPhotoView();
         void adjustPreviewLocation(boolean shiftUp, int offset);
         void setPreviewRotation(int orientation);
+        void showOutgoingVideoView(boolean show);
+    }
+
+    /**
+     * Returns true if camera preview shall be shown till remote user react on the request.
+     */
+    private static boolean isModifyCallPreview(Context ctx, Call call) {
+        if (call == null || !QtiCallUtils.shallShowPreviewWhileWaiting(ctx)) {
+            return false;
+        }
+        return (call.getSessionModificationState() ==
+                Call.SessionModificationState.WAITING_FOR_RESPONSE) &&
+                VideoProfile.isTransmissionEnabled(call.getRequestedVideoState());
+    }
+
+    private void listenToCallUpdates(Call call) {
+        if (!QtiCallUtils.shallShowPreviewWhileWaiting(mContext)) {
+            return;
+        }
+
+        if (mPrimaryCall != null) {
+            CallList.getInstance().removeCallUpdateListener(mPrimaryCall.getId(), this);
+        }
+
+        if (call != null) {
+            CallList.getInstance().addCallUpdateListener(call.getId(), this);
+        }
+    }
+
+    @Override
+    public void onSessionModificationStateChange(Call call, int sessionModificationState) {
+        Log.d(this, "onSessionModificationStateChange : sessionModificationState = " +
+                sessionModificationState + " call:" + call);
+        if (call != mPrimaryCall ||
+                (sessionModificationState == Call.SessionModificationState.NO_REQUEST)) {
+            return;
+        }
+        if (!VideoProfile.isTransmissionEnabled(call.getRequestedVideoState())) {
+           call.setRequestedVideoState(VideoProfile.STATE_AUDIO_ONLY);
+           return;
+        }
+
+        if (sessionModificationState != Call.SessionModificationState.WAITING_FOR_RESPONSE) {
+            call.setRequestedVideoState(VideoProfile.STATE_AUDIO_ONLY);
+        }
+
+        checkForVideoStateChange(call);
+
+        if (sessionModificationState == Call.SessionModificationState.REQUEST_REJECTED
+                || sessionModificationState == Call.SessionModificationState.REQUEST_FAILED
+                || sessionModificationState ==
+                Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT) {
+             mCurrentVideoState = call.getVideoState();
+        }
+    }
+
+    @Override
+    public void onLastForwardedNumberChange() {
+    }
+
+    @Override
+    public void onCallChanged(Call call) {
+    }
+
+    @Override
+    public void onChildNumberChange() {
     }
 }
diff --git a/InCallUI/src/com/android/incallui/VideoPauseController.java b/InCallUI/src/com/android/incallui/VideoPauseController.java
index fb87350..a953f1d 100644
--- a/InCallUI/src/com/android/incallui/VideoPauseController.java
+++ b/InCallUI/src/com/android/incallui/VideoPauseController.java
@@ -29,8 +29,9 @@
  * This class is responsible for generating video pause/resume requests when the InCall UI is sent
  * to the background and subsequently brought back to the foreground.
  */
-class VideoPauseController implements InCallStateListener, IncomingCallListener {
-    private static final String TAG = "VideoPauseController";
+class VideoPauseController implements InCallStateListener, IncomingCallListener,
+        InCallUiStateNotifierListener {
+    private static final String TAG = "VideoPauseController:";
 
     /**
      * Keeps track of the current active/foreground call.
@@ -106,6 +107,7 @@
         mInCallPresenter = Preconditions.checkNotNull(inCallPresenter);
         mInCallPresenter.addListener(this);
         mInCallPresenter.addIncomingCallListener(this);
+        InCallUiStateNotifier.getInstance().addListener(this);
     }
 
     /**
@@ -114,6 +116,7 @@
      */
     public void tearDown() {
         log("tearDown...");
+        InCallUiStateNotifier.getInstance().removeListener(this);
         mInCallPresenter.removeListener(this);
         mInCallPresenter.removeIncomingCallListener(this);
         clear();
@@ -194,11 +197,10 @@
         Preconditions.checkState(!areSame(call, mPrimaryCallContext));
         final boolean canVideoPause = VideoUtils.canVideoPause(call);
 
-        if ((isIncomingCall(mPrimaryCallContext) || isDialing(mPrimaryCallContext) ||
-                (call != null && VideoProfile.isPaused(call.getVideoState())))
+        if ((isIncomingCall(mPrimaryCallContext) || isDialing(mPrimaryCallContext))
                 && canVideoPause && !mIsInBackground) {
             // Send resume request for the active call, if user rejects incoming call, ends dialing
-            // call, or the call was previously in a paused state and UI is in the foreground.
+            // call and UI is in the foreground.
             sendRequest(call, true);
         } else if (isIncomingCall(call) && canVideoPause(mPrimaryCallContext)) {
             // Send pause request if there is an active video call, and we just received a new
@@ -243,10 +245,13 @@
     }
 
     /**
-     * Called when UI goes in/out of the foreground.
-     * @param showing true if UI is in the foreground, false otherwise.
+     * This method gets invoked when visibility of InCallUI is changed. For eg.
+     * when UE moves in/out of the foreground, display either turns ON/OFF
+     * @param showing true if InCallUI is visible, false  otherwise.
      */
+    @Override
     public void onUiShowing(boolean showing) {
+        log("onUiShowing, showing = " + showing);
         // Only send pause/unpause requests if we are in the INCALL state.
         if (mInCallPresenter == null) {
             return;
@@ -267,6 +272,11 @@
     private void onResume(boolean isInCall) {
         log("onResume");
 
+        if (!mIsInBackground) {
+            log("onResume, Ignoring... already resumed");
+            return;
+        }
+
         mIsInBackground = false;
         if (canVideoPause(mPrimaryCallContext) && isInCall) {
             sendRequest(mPrimaryCallContext.getCall(), true);
@@ -283,6 +293,11 @@
     private void onPause(boolean isInCall) {
         log("onPause");
 
+        if (mIsInBackground) {
+            log("onPause, Ignoring... already paused");
+            return;
+        }
+
         mIsInBackground = true;
         if (canVideoPause(mPrimaryCallContext) && isInCall) {
             sendRequest(mPrimaryCallContext.getCall(), false);
@@ -366,7 +381,7 @@
      * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
      */
     private static boolean isIncomingCall(CallContext call) {
-        return call != null && isIncomingCall(call.getCall());
+        return call != null && isIncoming(call.getState());
     }
 
     /**
@@ -376,8 +391,17 @@
      * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
      */
     private static boolean isIncomingCall(Call call) {
-        return call != null && (call.getState() == Call.State.CALL_WAITING
-                || call.getState() == Call.State.INCOMING);
+        return call != null && isIncoming(call.getState());
+    }
+
+    /**
+     * Determines if a call state is incoming/waiting.
+     *
+     * @param state The call state
+     * @return {@code true} if the state is incoming or waiting, {@code false} otherwise.
+     */
+    private static boolean isIncoming(int state) {
+        return state == Call.State.CALL_WAITING || state == Call.State.INCOMING;
     }
 
     /**
diff --git a/InCallUI/src/com/android/incallui/ZoomControl.java b/InCallUI/src/com/android/incallui/ZoomControl.java
new file mode 100644
index 0000000..7e25b98
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/ZoomControl.java
@@ -0,0 +1,132 @@
+/* Copyright (c) 2012 - 2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+/**
+ * A view that contains camera zoom control which could adjust the zoom in/out
+ * if the camera supports zooming.
+ */
+public abstract class ZoomControl extends RelativeLayout{
+    protected ImageView mZoomIn;
+    protected ImageView mZoomOut;
+    protected ImageView mZoomSlider;
+    protected int mOrientation;
+
+    public interface OnZoomChangedListener {
+        void onZoomValueChanged(int index);  // only for immediate zoom
+    }
+
+    // The interface OnZoomIndexChangedListener is used to inform the
+    // ZoomIndexBar about the zoom index change. The index position is between
+    // 0 (the index is zero) and 1.0 (the index is mZoomMax).
+    public interface OnZoomIndexChangedListener {
+        void onZoomIndexChanged(double indexPosition);
+    }
+
+    protected int mZoomMax, mZoomIndex;
+    private OnZoomChangedListener mListener;
+
+    private int mStep;
+
+    public ZoomControl(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mZoomIn = addImageView(context, R.drawable.ic_zoom_in);
+        mZoomSlider = addImageView(context, R.drawable.ic_zoom_slider);
+        mZoomOut = addImageView(context, R.drawable.ic_zoom_out);
+    }
+
+    public void startZoomControl() {
+        mZoomSlider.setPressed(true);
+        setZoomIndex(mZoomIndex); // Update the zoom index bar.
+    }
+
+    protected ImageView addImageView(Context context, int iconResourceId) {
+        ImageView image = new ImageView(context);
+        image.setImageResource(iconResourceId);
+        addView(image);
+        return image;
+    }
+
+    public void closeZoomControl() {
+        mZoomSlider.setPressed(false);
+    }
+
+    public void setZoomMax(int zoomMax) {
+        mZoomMax = zoomMax;
+
+        // Layout should be requested as the maximum zoom level is the key to
+        // show the correct zoom slider position.
+        requestLayout();
+    }
+
+    public int getZoomMax() {
+        return mZoomMax;
+    }
+
+    public void setOnZoomChangeListener(OnZoomChangedListener listener) {
+        mListener = listener;
+    }
+
+    public void setZoomIndex(int index) {
+        if (index < 0 || index > mZoomMax) {
+            throw new IllegalArgumentException("Invalid zoom value:" + index);
+        }
+        mZoomIndex = index;
+        invalidate();
+    }
+
+    public int getZoomIndex() {
+        return mZoomIndex;
+    }
+
+    protected void setZoomStep(int step) {
+        mStep = step;
+    }
+
+    // Called from ZoomControlBar to change the zoom level.
+    protected void performZoom(double zoomPercentage) {
+        int index = (int) (mZoomMax * zoomPercentage);
+        if (mZoomIndex == index) return;
+        changeZoomIndex(index);
+   }
+
+    private boolean changeZoomIndex(int index) {
+        if (mListener != null) {
+            if (index > mZoomMax) index = mZoomMax;
+            if (index < 0) index = 0;
+            mListener.onZoomValueChanged(index);
+            mZoomIndex = index;
+        }
+        return true;
+    }
+
+    @Override
+    public void setActivated(boolean activated) {
+        super.setActivated(activated);
+        mZoomIn.setActivated(activated);
+        mZoomOut.setActivated(activated);
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/ZoomControlBar.java b/InCallUI/src/com/android/incallui/ZoomControlBar.java
new file mode 100644
index 0000000..8a89218
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/ZoomControlBar.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2012 - 2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * A view that contains camera zoom control and its layout.
+ */
+public class ZoomControlBar extends ZoomControl {
+    private static final int THRESHOLD_FIRST_MOVE = 10; // pixels
+    // Space between indicator icon and the zoom-in/out icon.
+    private static final int ICON_SPACING = 12;
+
+    private View mBar;
+    private boolean mStartChanging;
+    private static int mSliderPosition = 0;
+    private int mSliderLength;
+    private int mWidth;
+    private int mIconWidth;
+    private int mTotalIconWidth;
+
+    public ZoomControlBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mBar = new View(context);
+        mBar.setBackgroundResource(R.drawable.zoom_slider_bar);
+        addView(mBar);
+    }
+
+    @Override
+    public void setActivated(boolean activated) {
+        super.setActivated(activated);
+        mBar.setActivated(activated);
+    }
+
+    private int getSliderPosition(int x) {
+        // Calculate the absolute offset of the slider in the zoom control bar.
+        // For left-hand users, as the device is rotated for 180 degree for
+        // landscape mode, the zoom-in bottom should be on the top, so the
+        // position should be reversed.
+        int pos; // the relative position in the zoom slider bar
+        if (mOrientation == 90) {
+            pos = mWidth - mTotalIconWidth - x;
+        } else {
+            pos = x - mTotalIconWidth;
+        }
+        if (pos < 0) pos = 0;
+        if (pos > mSliderLength) pos = mSliderLength;
+        return pos;
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mWidth = w;
+        mIconWidth = mZoomIn.getMeasuredWidth();
+        mTotalIconWidth = mIconWidth + ICON_SPACING;
+        mSliderLength = mWidth  - (2 * mTotalIconWidth);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (!isEnabled() || (mWidth == 0)) return false;
+        int action = event.getAction();
+
+        switch (action) {
+            case MotionEvent.ACTION_OUTSIDE:
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                setActivated(false);
+                closeZoomControl();
+                break;
+
+            case MotionEvent.ACTION_DOWN:
+                setActivated(true);
+                mStartChanging = false;
+            case MotionEvent.ACTION_MOVE:
+                int pos = getSliderPosition((int) event.getX());
+                if (!mStartChanging) {
+                    // Make sure the movement is large enough before we start
+                    // changing the zoom.
+                    int delta = mSliderPosition - pos;
+                    if ((delta > THRESHOLD_FIRST_MOVE) ||
+                            (delta < -THRESHOLD_FIRST_MOVE)) {
+                        mStartChanging = true;
+                    }
+                }
+                if (mStartChanging) {
+                    performZoom(1.0d * pos / mSliderLength);
+                    mSliderPosition = pos;
+                }
+                requestLayout();
+        }
+        return true;
+    }
+
+    @Override
+    protected void onLayout(
+            boolean changed, int left, int top, int right, int bottom) {
+        if (mZoomMax == 0) return;
+        int height = bottom - top;
+        mBar.layout(mTotalIconWidth, 0, mWidth - mTotalIconWidth, height);
+        // For left-hand users, as the device is rotated for 180 degree,
+        // the zoom-in button should be on the top.
+        int pos; // slider position
+        int sliderPosition;
+        if (mSliderPosition != -1) { // -1 means invalid
+            sliderPosition = mSliderPosition;
+        } else {
+            sliderPosition = (int) ((double) mSliderLength * mZoomIndex / mZoomMax);
+        }
+        if (mOrientation == 90) {
+            mZoomIn.layout(0, 0, mIconWidth, height);
+            mZoomOut.layout(mWidth - mIconWidth, 0, mWidth, height);
+            pos = mBar.getRight() - sliderPosition;
+        } else {
+            mZoomOut.layout(0, 0, mIconWidth, height);
+            mZoomIn.layout(mWidth - mIconWidth, 0, mWidth, height);
+            pos = mBar.getLeft() + sliderPosition;
+        }
+        int sliderWidth = mZoomSlider.getMeasuredWidth();
+        mZoomSlider.layout((pos - sliderWidth / 2), 0,
+                (pos + sliderWidth / 2), height);
+    }
+
+    @Override
+    public void setZoomIndex(int index) {
+        super.setZoomIndex(index);
+        mSliderPosition = -1; // -1 means invalid
+        requestLayout();
+    }
+}
diff --git a/res/drawable-hdpi/fab_ic_wificall.png b/res/drawable-hdpi/fab_ic_wificall.png
new file mode 100755
index 0000000..1794845
--- /dev/null
+++ b/res/drawable-hdpi/fab_ic_wificall.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_add_group_holo_dark.png b/res/drawable-hdpi/ic_add_group_holo_dark.png
new file mode 100644
index 0000000..85924ab
--- /dev/null
+++ b/res/drawable-hdpi/ic_add_group_holo_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_hm_videocam.png b/res/drawable-hdpi/ic_hm_videocam.png
new file mode 100644
index 0000000..f80c2f3
--- /dev/null
+++ b/res/drawable-hdpi/ic_hm_videocam.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_m_videocam.png b/res/drawable-hdpi/ic_m_videocam.png
new file mode 100644
index 0000000..7724a8d
--- /dev/null
+++ b/res/drawable-hdpi/ic_m_videocam.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_videocam_off.png b/res/drawable-hdpi/ic_videocam_off.png
new file mode 100644
index 0000000..f59144c
--- /dev/null
+++ b/res/drawable-hdpi/ic_videocam_off.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_sys_vp_phone_call.png b/res/drawable-hdpi/stat_sys_vp_phone_call.png
new file mode 100644
index 0000000..69b9817
--- /dev/null
+++ b/res/drawable-hdpi/stat_sys_vp_phone_call.png
Binary files differ
diff --git a/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png b/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
new file mode 100644
index 0000000..f4074c1
--- /dev/null
+++ b/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
Binary files differ
diff --git a/res/drawable-hdpi/wifi_calling_on_notification.png b/res/drawable-hdpi/wifi_calling_on_notification.png
new file mode 100755
index 0000000..fc34ae3
--- /dev/null
+++ b/res/drawable-hdpi/wifi_calling_on_notification.png
Binary files differ
diff --git a/res/drawable-mdpi/fab_ic_wificall.png b/res/drawable-mdpi/fab_ic_wificall.png
new file mode 100755
index 0000000..611f0d2
--- /dev/null
+++ b/res/drawable-mdpi/fab_ic_wificall.png
Binary files differ
diff --git a/res/drawable-xhdpi/fab_ic_wificall.png b/res/drawable-xhdpi/fab_ic_wificall.png
new file mode 100755
index 0000000..f4d522e
--- /dev/null
+++ b/res/drawable-xhdpi/fab_ic_wificall.png
Binary files differ
diff --git a/res/drawable-xhdpi/wifi_calling_on_notification.png b/res/drawable-xhdpi/wifi_calling_on_notification.png
new file mode 100755
index 0000000..2c11f32
--- /dev/null
+++ b/res/drawable-xhdpi/wifi_calling_on_notification.png
Binary files differ
diff --git a/res/drawable-xxhdpi/fab_ic_wificall.png b/res/drawable-xxhdpi/fab_ic_wificall.png
new file mode 100755
index 0000000..ef9d17d
--- /dev/null
+++ b/res/drawable-xxhdpi/fab_ic_wificall.png
Binary files differ
diff --git a/res/drawable-xxhdpi/wifi_calling_on_notification.png b/res/drawable-xxhdpi/wifi_calling_on_notification.png
new file mode 100755
index 0000000..23bb6f9
--- /dev/null
+++ b/res/drawable-xxhdpi/wifi_calling_on_notification.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/fab_ic_wificall.png b/res/drawable-xxxhdpi/fab_ic_wificall.png
new file mode 100755
index 0000000..4d78f05
--- /dev/null
+++ b/res/drawable-xxxhdpi/fab_ic_wificall.png
Binary files differ
diff --git a/res/drawable/btn_addparticipant.xml b/res/drawable/btn_addparticipant.xml
new file mode 100644
index 0000000..85ab180
--- /dev/null
+++ b/res/drawable/btn_addparticipant.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2014, 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@drawable/btn_background" />
+
+    <item>
+        <bitmap android:src="@drawable/ic_add_group_holo_dark"
+            android:gravity="center"
+            android:tint="@color/selectable_icon_tint" />
+    </item>
+
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/btn_change_to_hm_video.xml b/res/drawable/btn_change_to_hm_video.xml
new file mode 100644
index 0000000..af23bc2
--- /dev/null
+++ b/res/drawable/btn_change_to_hm_video.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2016, 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/backgroundItem"
+          android:drawable="@drawable/btn_background" />
+
+    <item>
+        <bitmap android:src="@drawable/ic_hm_videocam"
+            android:gravity="center"
+            android:tint="@color/selectable_icon_tint"
+            android:autoMirrored="true" />
+    </item>
+
+</layer-list>
diff --git a/res/drawable/clear.xml b/res/drawable/clear.xml
new file mode 100644
index 0000000..882ca1e
--- /dev/null
+++ b/res/drawable/clear.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2016, 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.
+  ~
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+       android:height="24dp"
+       android:width="24dp"
+       android:viewportHeight="24"
+       android:viewportWidth="24">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z"/>
+</vector>
diff --git a/res/drawable/color_cursor.xml b/res/drawable/color_cursor.xml
new file mode 100644
index 0000000..be96253
--- /dev/null
+++ b/res/drawable/color_cursor.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (c) 2016, 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.
+  ~
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <size android:width="2dp"/>
+    <solid android:color="#FFFFFF"/>
+</shape>
diff --git a/res/drawable/ic_enhance_answer_rx_video.xml b/res/drawable/ic_enhance_answer_rx_video.xml
new file mode 100644
index 0000000..0dedbcb
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_rx_video.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_answer_rx_video_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_answer_rx_video_activated_layer" />
+    <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/ic_enhance_answer_rx_video_activated_layer" />
+</selector>
diff --git a/res/drawable/ic_enhance_answer_rx_video_activated_layer.xml b/res/drawable/ic_enhance_answer_rx_video_activated_layer.xml
new file mode 100644
index 0000000..e0e19d5
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_rx_video_activated_layer.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_blue" />
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_hm_videocam"
+            android:tint="@color/glowpad_widget_active_color"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_answer_rx_video_normal_layer.xml b/res/drawable/ic_enhance_answer_rx_video_normal_layer.xml
new file mode 100644
index 0000000..2d49b1d
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_rx_video_normal_layer.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="@dimen/incoming_call_widget_circle_size"
+                android:height="@dimen/incoming_call_widget_circle_size" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_hm_videocam"
+            android:tint="@color/glowpad_call_widget_normal_tint"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_answer_tx_video.xml b/res/drawable/ic_enhance_answer_tx_video.xml
new file mode 100644
index 0000000..33ba0e1
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_tx_video.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_answer_tx_video_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_answer_tx_video_activated_layer" />
+    <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/ic_enhance_answer_tx_video_activated_layer" />
+</selector>
diff --git a/res/drawable/ic_enhance_answer_tx_video_activated_layer.xml b/res/drawable/ic_enhance_answer_tx_video_activated_layer.xml
new file mode 100644
index 0000000..76e04e9
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_tx_video_activated_layer.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_blue" />
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_m_videocam"
+            android:tint="@color/glowpad_widget_active_color"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_answer_tx_video_normal_layer.xml b/res/drawable/ic_enhance_answer_tx_video_normal_layer.xml
new file mode 100644
index 0000000..95d6c81
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_tx_video_normal_layer.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="@dimen/incoming_call_widget_circle_size"
+                android:height="@dimen/incoming_call_widget_circle_size" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_m_videocam"
+            android:tint="@color/glowpad_call_widget_normal_tint"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_answer_video.xml b/res/drawable/ic_enhance_answer_video.xml
new file mode 100644
index 0000000..a79cd97
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_video.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_answer_video_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_answer_video_activated_layer" />
+   <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/ic_enhance_answer_video_activated_layer" />
+</selector>
diff --git a/res/drawable/ic_enhance_answer_video_activated_layer.xml b/res/drawable/ic_enhance_answer_video_activated_layer.xml
new file mode 100644
index 0000000..76e04e9
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_video_activated_layer.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_blue" />
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_m_videocam"
+            android:tint="@color/glowpad_widget_active_color"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_answer_video_normal_layer.xml b/res/drawable/ic_enhance_answer_video_normal_layer.xml
new file mode 100644
index 0000000..95d6c81
--- /dev/null
+++ b/res/drawable/ic_enhance_answer_video_normal_layer.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="@dimen/incoming_call_widget_circle_size"
+                android:height="@dimen/incoming_call_widget_circle_size" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_m_videocam"
+            android:tint="@color/glowpad_call_widget_normal_tint"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_decline_video.xml b/res/drawable/ic_enhance_decline_video.xml
new file mode 100644
index 0000000..854f7a2
--- /dev/null
+++ b/res/drawable/ic_enhance_decline_video.xml
@@ -0,0 +1,40 @@
+<?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.
+-->
+<!-- Used with incoming call wigdet. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_enabled="true" android:state_active="false" android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_decline_video_normal_layer"/>
+    <item
+        android:state_enabled="true" android:state_active="true"  android:state_focused="false"
+        android:drawable="@drawable/ic_enhance_decline_video_activated_layer" />
+   <item
+        android:state_enabled="true" android:state_active="false"  android:state_focused="true"
+        android:drawable="@drawable/ic_enhance_decline_video_activated_layer" />
+</selector>
diff --git a/res/drawable/ic_enhance_decline_video_activated_layer.xml b/res/drawable/ic_enhance_decline_video_activated_layer.xml
new file mode 100644
index 0000000..074fe95
--- /dev/null
+++ b/res/drawable/ic_enhance_decline_video_activated_layer.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/fab_red" />
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_videocam_off"
+            android:tint="#ffffff"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/ic_enhance_decline_video_normal_layer.xml b/res/drawable/ic_enhance_decline_video_normal_layer.xml
new file mode 100644
index 0000000..30c7d9c
--- /dev/null
+++ b/res/drawable/ic_enhance_decline_video_normal_layer.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- A fake circle to fix the size of this layer asset. -->
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+            <solid android:color="#00000000"/>
+            <size
+                android:width="56dp"
+                android:height="56dp" />
+        </shape>
+    </item>
+    <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@drawable/ic_videocam_off"
+            android:tint="#0288d1"
+            android:autoMirrored="true" />
+    </item>
+</layer-list>
diff --git a/res/drawable/volte_video.xml b/res/drawable/volte_video.xml
new file mode 100644
index 0000000..0008559
--- /dev/null
+++ b/res/drawable/volte_video.xml
@@ -0,0 +1,36 @@
+<!--Copyright (c) 2016, 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. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="13dp"
+    android:height="11dp"
+    android:autoMirrored="true"
+    android:viewportHeight="11.0"
+    android:viewportWidth="13.0">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M6.627,8.712c-0.404,0 -0.992,-0.063 -1.357,-0.185c-0.113,-0.039 -0.244,-0.01 -0.332,0.076L4.221,9.322C3.3,8.85 2.544,7.968 2.076,7.047l0.716,-0.718c0.091,-0.09 0.117,-0.218 0.081,-0.332c-0.12,-0.364 -0.186,-0.755 -0.186,-1.162c0,-0.181 -0.146,-0.326 -0.326,-0.326H1.223c-0.18,0 -0.326,0.146 -0.326,0.326c0,3.058 2.615,5.665 5.672,5.665c0.179,0 0.324,-0.146 0.324,-0.326V9.037C6.895,8.859 6.807,8.712 6.627,8.712zM10.857,0.5H4.212c-0.453,0 -0.683,0.379 -0.683,0.814v6.33c0,0.192 0.05,0.306 0.132,0.465C3.84,8.259 3.928,8.343 4.425,8.343l0.483,-0.483c0.188,-0.186 0.477,-0.254 0.725,-0.17C5.95,7.794 6.286,7.848 6.629,7.848c0.391,0 0.708,0.551 0.708,0.94v0.022h3.595c0.221,0 0.596,-0.19 0.596,-0.554V1.246C11.527,0.893 11.25,0.5 10.857,0.5zM4.561,7.166V1.514h5.972l0.001,5.652H4.561zM7.555,4.229c0.598,0 1.078,-0.48 1.078,-1.078c0,-0.599 -0.48,-1.137 -1.078,-1.137c-0.599,0 -1.08,0.538 -1.08,1.137C6.475,3.748 6.955,4.229 7.555,4.229zM8.178,4.877c-0.121,0 -1.02,-0.027 -1.186,-0.027c-0.506,0 -1.746,0.789 -1.746,0.899v0.76L9.87,6.492V5.746C9.869,5.624 8.504,4.877 8.178,4.877z" />
+</vector>
diff --git a/res/drawable/volte_voice.xml b/res/drawable/volte_voice.xml
new file mode 100644
index 0000000..1ce76fb
--- /dev/null
+++ b/res/drawable/volte_voice.xml
@@ -0,0 +1,36 @@
+<!--Copyright (c) 2016, 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. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="13dp"
+    android:height="11dp"
+    android:autoMirrored="true"
+    android:viewportHeight="11.0"
+    android:viewportWidth="13.0">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M8.336,7.479c-0.574,0 -1.091,-0.093 -1.609,-0.264C6.564,7.158 6.379,7.199 6.254,7.326l-1.02,1.021C3.922,7.674 2.848,6.604 2.178,5.291L3.198,4.27c0.129,-0.13 0.167,-0.311 0.116,-0.475C3.143,3.276 3.05,2.586 3.05,2.006c0,-0.256 -0.209,-0.465 -0.464,-0.465H0.964C0.709,1.541 0.5,1.75 0.5,2.006c0,4.353 3.482,8.017 7.836,8.017c0.255,0 0.464,-0.21 0.464,-0.465V7.942C8.8,7.687 8.591,7.479 8.336,7.479zM5.453,4.054l0.689,-2.511H5.604l-0.402,1.82l-0.4,-1.82H4.266L4.95,4.054H5.453zM6.426,3.854c0.063,0.077 0.139,0.135 0.227,0.174c0.09,0.039 0.188,0.06 0.3,0.06c0.108,0 0.208,-0.021 0.296,-0.06c0.089,-0.039 0.165,-0.098 0.227,-0.174c0.063,-0.076 0.11,-0.171 0.145,-0.284c0.033,-0.113 0.05,-0.242 0.05,-0.389V3.059c0,-0.146 -0.019,-0.275 -0.052,-0.389C7.584,2.558 7.536,2.463 7.473,2.386C7.41,2.31 7.336,2.25 7.246,2.211c-0.088,-0.041 -0.188,-0.06 -0.298,-0.06c-0.109,0 -0.208,0.021 -0.296,0.06S6.488,2.31 6.426,2.386C6.364,2.463 6.316,2.558 6.282,2.67C6.248,2.783 6.23,2.912 6.23,3.059v0.123c0,0.146 0.018,0.277 0.052,0.389C6.316,3.684 6.364,3.777 6.426,3.854zM6.697,3.06c0,-0.17 0.021,-0.297 0.066,-0.378C6.809,2.6 6.87,2.558 6.949,2.558c0.082,0 0.145,0.042 0.189,0.124S7.205,2.89 7.205,3.06v0.123c0,0.173 -0.021,0.3 -0.063,0.38c-0.043,0.08 -0.106,0.12 -0.191,0.12c-0.08,0 -0.143,-0.041 -0.188,-0.12c-0.046,-0.08 -0.068,-0.207 -0.068,-0.38V3.06H6.697zM8.479,1.541H7.997v2.512h1.329v-0.42H8.48L8.479,1.541L8.479,1.541zM9.178,1.965h0.58v2.089h0.484V1.965h0.59V1.543H9.177L9.178,1.965L9.178,1.965zM11.609,3.633V2.967h0.75V2.558h-0.75V1.965h0.886V1.543h-1.368v2.511h1.372V3.633H11.609L11.609,3.633z" />
+</vector>
diff --git a/res/drawable/wifi_calling.xml b/res/drawable/wifi_calling.xml
new file mode 100644
index 0000000..87849c5
--- /dev/null
+++ b/res/drawable/wifi_calling.xml
@@ -0,0 +1,36 @@
+<!--Copyright (c) 2016, 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. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="13dp"
+    android:height="11dp"
+    android:autoMirrored="true"
+    android:viewportHeight="11.0"
+    android:viewportWidth="13.75">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M8.608,7.242c-0.598,0 -1.223,-0.095 -1.762,-0.272C6.679,6.912 6.485,6.955 6.354,7.084L5.294,8.146C3.931,7.447 2.813,6.332 2.118,4.97l1.061,-1.062C3.313,3.774 3.352,3.586 3.3,3.416C3.122,2.877 3.024,2.299 3.024,1.697c0,-0.266 -0.217,-0.482 -0.48,-0.482H0.856c-0.266,0 -0.48,0.217 -0.48,0.482c0,4.525 3.707,8.193 8.233,8.193c0.265,0 0.481,-0.217 0.481,-0.482V7.727C9.089,7.459 8.874,7.242 8.608,7.242zM5.136,3.783l0.705,-2.574H5.29L4.878,3.076L4.466,1.209H3.919L4.62,3.783H5.136zM6.134,3.58c0.063,0.078 0.142,0.139 0.232,0.179c0.09,0.04 0.191,0.06 0.305,0.06s0.215,-0.02 0.305,-0.06c0.091,-0.04 0.168,-0.101 0.232,-0.179C7.271,3.5 7.319,3.404 7.354,3.289C7.389,3.174 7.405,3.041 7.405,2.89V2.765c0,-0.149 -0.018,-0.282 -0.052,-0.397C7.318,2.252 7.27,2.154 7.206,2.076c-0.063,-0.08 -0.143,-0.14 -0.232,-0.181C6.882,1.854 6.78,1.834 6.667,1.834c-0.111,0 -0.213,0.021 -0.304,0.062c-0.09,0.041 -0.167,0.101 -0.231,0.181C6.069,2.154 6.02,2.252 5.984,2.367C5.95,2.483 5.933,2.615 5.933,2.765V2.89c0,0.151 0.018,0.284 0.052,0.399C6.02,3.404 6.069,3.502 6.134,3.58zM6.411,2.766c0,-0.174 0.021,-0.305 0.067,-0.389s0.108,-0.125 0.188,-0.125c0.084,0 0.149,0.041 0.195,0.125c0.047,0.084 0.068,0.215 0.068,0.389v0.125c0,0.178 -0.021,0.309 -0.064,0.391C6.822,3.363 6.757,3.404 6.671,3.404c-0.082,0 -0.146,-0.041 -0.192,-0.123S6.409,3.068 6.409,2.891V2.766H6.411zM9.333,2.828L9.046,1.211H8.63L8.344,2.826L8.112,1.211H7.62l0.438,2.574h0.497l0.283,-1.518l0.285,1.518H9.62l0.437,-2.574h-0.49L9.333,2.828zM10.764,1.185c-0.022,-0.022 -0.05,-0.042 -0.081,-0.054C10.65,1.117 10.614,1.11 10.575,1.11s-0.074,0.007 -0.105,0.021c-0.031,0.013 -0.061,0.031 -0.081,0.054c-0.022,0.024 -0.041,0.052 -0.054,0.084s-0.019,0.067 -0.019,0.106s0.006,0.076 0.019,0.107c0.013,0.032 0.029,0.061 0.054,0.085c0.021,0.023 0.049,0.042 0.081,0.056c0.031,0.013 0.066,0.02 0.105,0.02s0.075,-0.006 0.107,-0.02c0.031,-0.014 0.059,-0.032 0.081,-0.056c0.022,-0.024 0.04,-0.053 0.052,-0.085c0.014,-0.033 0.02,-0.068 0.02,-0.107s-0.006,-0.074 -0.02,-0.106C10.804,1.236 10.786,1.209 10.764,1.185zM10.339,3.783h0.478V1.871h-0.478V3.783zM11.226,3.783h0.493v-1.05h0.769V2.303h-0.769v-0.66h0.862V1.209h-1.355V3.783zM13.356,1.269c-0.013,-0.033 -0.029,-0.062 -0.053,-0.084c-0.021,-0.022 -0.049,-0.042 -0.081,-0.054C13.19,1.117 13.155,1.11 13.116,1.11S13.04,1.117 13.01,1.131c-0.031,0.013 -0.06,0.031 -0.081,0.054c-0.021,0.024 -0.04,0.052 -0.053,0.084c-0.013,0.033 -0.019,0.067 -0.019,0.106s0.006,0.076 0.019,0.107c0.013,0.032 0.029,0.061 0.053,0.085c0.021,0.023 0.05,0.042 0.081,0.056c0.032,0.013 0.067,0.02 0.106,0.02s0.075,-0.006 0.106,-0.02c0.032,-0.014 0.06,-0.032 0.081,-0.056c0.021,-0.024 0.04,-0.053 0.053,-0.085c0.012,-0.033 0.018,-0.068 0.018,-0.107S13.368,1.302 13.356,1.269zM12.878,3.783h0.479V1.871h-0.479V3.783z" />
+</vector>
diff --git a/res/layout-land/dialpad_fragment.xml b/res/layout-land/dialpad_fragment.xml
index 70a38ae..0639899 100644
--- a/res/layout-land/dialpad_fragment.xml
+++ b/res/layout-land/dialpad_fragment.xml
@@ -81,6 +81,17 @@
 
             </FrameLayout>
 
+            <TextView
+                android:id="@+id/dialpad_floating_operator"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_above="@id/dialpad_floating_action_button_margin_bottom"
+                android:layout_toRightOf="@+id/dialpad_floating_action_button_container"
+                android:textColor="?attr/call_log_secondary_text_color"
+                android:textSize="12sp"
+                android:singleLine="true"
+                android:visibility="gone" />
+
         </RelativeLayout>
 
     </LinearLayout>
diff --git a/res/layout/add_speed_dial_dialog.xml b/res/layout/add_speed_dial_dialog.xml
new file mode 100644
index 0000000..7b8296f
--- /dev/null
+++ b/res/layout/add_speed_dial_dialog.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2014-2016, 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1.0"
+        android:gravity="center_vertical"
+        android:baselineAligned="false"
+        android:paddingStart="10dip"
+        android:paddingEnd="10dip">
+
+        <EditText
+            android:id="@+id/edit_container"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:orientation="vertical"
+            android:hint="@string/input_number"
+            android:inputType="phone"
+            android:singleLine="true" />
+
+        <ImageButton
+            android:id="@+id/select_contact"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="10dip"
+            android:src="@drawable/ic_person_24dp" />
+    </LinearLayout>
+
+    <View
+        android:background="?android:dividerHorizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="1dip" />
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content" >
+
+        <Button
+            android:id="@+id/btn_cancel"
+            android:focusable="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:text="@string/speed_dial_cancel"
+            style="?android:attr/buttonBarButtonStyle" />
+
+        <View
+            android:background="?android:dividerHorizontal"
+            android:layout_width="1dip"
+            android:layout_height="fill_parent" />
+
+        <Button
+            android:id="@+id/btn_complete"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:text="@string/speed_dial_ok"
+            style="?android:attr/buttonBarButtonStyle" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index 660bca3..c8a33c5 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -101,6 +101,14 @@
                             android:layout_marginEnd="@dimen/call_log_icon_margin"
                             android:layout_gravity="center_vertical" />
 
+                        <ImageView
+                            android:id="@+id/call_account_icon"
+                            android:layout_width="@dimen/call_provider_small_icon_size"
+                            android:layout_height="@dimen/call_provider_small_icon_size"
+                            android:layout_marginEnd="@dimen/call_log_icon_margin"
+                            android:layout_gravity="center_vertical"
+                            android:scaleType="centerInside" />
+
                         <ImageView android:id="@+id/work_profile_icon"
                             android:src="@drawable/ic_work_profile"
                             android:layout_width="wrap_content"
diff --git a/res/layout/call_log_list_item_actions.xml b/res/layout/call_log_list_item_actions.xml
index 78203b7..4cce073 100644
--- a/res/layout/call_log_list_item_actions.xml
+++ b/res/layout/call_log_list_item_actions.xml
@@ -64,6 +64,7 @@
         style="@style/CallLogActionStyle">
 
         <ImageView
+            android:id="@+id/videoCallIcon"
             style="@style/CallLogActionIconStyle"
             android:src="@drawable/ic_videocam_24dp" />
 
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index 21cb586..5748f5d 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -73,4 +73,14 @@
 
     </FrameLayout>
 
+    <TextView
+        android:id="@+id/dialpad_floating_operator"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/dialpad_floating_action_button_container"
+        android:layout_centerHorizontal="true"
+        android:textColor="?attr/call_log_secondary_text_color"
+        android:textSize="12sp"
+        android:singleLine="true"
+        android:visibility="gone" />
 </view>
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 782d4f3..70d0561 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -38,25 +38,35 @@
             android:clipChildren="false" />
     </FrameLayout>
 
-    <FrameLayout
+    <LinearLayout
         android:id="@+id/floating_action_button_container"
         android:background="@drawable/fab_blue"
-        android:layout_width="@dimen/floating_action_button_width"
+        android:layout_width="wrap_content"
         android:layout_height="@dimen/floating_action_button_height"
         android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
-        android:layout_gravity="center_horizontal|bottom"
-        app:layout_behavior="com.android.dialer.FloatingActionButtonBehavior">
+        android:layout_gravity="center_horizontal|bottom">
+        <!-- app:layout_behavior="com.android.dialer.FloatingActionButtonBehavior" -->
 
         <ImageButton
             android:id="@+id/floating_action_button"
-            android:background="@drawable/floating_action_button"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:layout_gravity="bottom|left"
+            android:layout_weight="1"
+            android:background="@drawable/floating_action_button"
             android:contentDescription="@string/action_menu_dialpad_button"
             android:src="@drawable/fab_ic_dial"/>
-
-    </FrameLayout>
-
+       <ImageButton
+            android:id="@+id/dialConferenceButton"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="bottom|right"
+            android:layout_weight="1"
+            android:visibility="gone"
+            android:background="@drawable/floating_action_button"
+            android:contentDescription="@string/action_menu_dialpad_button"
+            android:src="@drawable/ic_add_group_holo_dark"/>
+    </LinearLayout>
     <!-- Host container for the contact tile drag shadow -->
     <FrameLayout
         android:id="@+id/activity_overlay"
diff --git a/res/layout/empty_content_view.xml b/res/layout/empty_content_view.xml
index 97ac4c7..9436d67 100644
--- a/res/layout/empty_content_view.xml
+++ b/res/layout/empty_content_view.xml
@@ -26,8 +26,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal|top"
-        android:textSize="@dimen/empty_list_message_text_size"
-        android:textColor="@color/empty_list_text_color"
+        android:textSize="@dimen/empty_list_message_text_size_for_Marshmallow"
+        android:textColor="@color/no_call_log"
         android:paddingRight="16dp"
         android:paddingLeft="16dp"
         android:paddingTop="8dp"
diff --git a/res/layout/msim_call_log_activity.xml b/res/layout/msim_call_log_activity.xml
new file mode 100644
index 0000000..bca6bf4
--- /dev/null
+++ b/res/layout/msim_call_log_activity.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2013-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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/calllog_frame">
+    <android.support.v4.view.ViewPager
+        android:id="@+id/call_log_pager"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/res/layout/msim_call_log_fragment.xml b/res/layout/msim_call_log_fragment.xml
new file mode 100644
index 0000000..6ee584b
--- /dev/null
+++ b/res/layout/msim_call_log_fragment.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2013-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.
+-->
+
+<!-- Layout parameters are set programmatically. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@color/background_dialer_call_log">
+
+    <include layout="@layout/msim_call_log_spinner" />
+
+    <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/background_dialer_call_log"
+        android:paddingStart="@dimen/call_log_horizontal_margin"
+        android:paddingEnd="@dimen/call_log_horizontal_margin" />
+
+    <com.android.dialer.widget.EmptyContentView
+        android:id="@+id/empty_list_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:visibility="gone" />
+
+</LinearLayout>
diff --git a/res/layout/msim_call_log_spinner.xml b/res/layout/msim_call_log_spinner.xml
new file mode 100644
index 0000000..5310ace
--- /dev/null
+++ b/res/layout/msim_call_log_spinner.xml
@@ -0,0 +1,53 @@
+<?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, 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:paddingLeft="@dimen/call_log_outer_margin"
+    android:paddingRight="@dimen/call_log_outer_margin">
+    <Spinner
+        android:id="@+id/filter_sub_spinner"
+        android:layout_width="0dip"
+        android:textSize="@dimen/call_log_spinner_text_size"
+        android:textColor="@color/list_all_call"
+        android:layout_height="@dimen/list_section_divider_min_height"
+        android:layout_weight="1"
+        android:layout_marginTop="5dip" />
+    <Spinner
+        android:id="@+id/filter_status_spinner"
+        android:layout_width="0dip"
+        android:textSize="@dimen/call_log_spinner_text_size"
+        android:textColor="@color/list_all_call"
+        android:layout_height="@dimen/list_section_divider_min_height"
+        android:layout_weight="2"
+        android:layout_marginTop="5dip" />
+</LinearLayout>
diff --git a/res/layout/msim_call_log_spinner_item.xml b/res/layout/msim_call_log_spinner_item.xml
new file mode 100644
index 0000000..0b1bcf3
--- /dev/null
+++ b/res/layout/msim_call_log_spinner_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (c) 2013-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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/CallLogSpinnerStyle"
+    android:layout_width="match_parent"
+    android:paddingTop="5dp"
+    android:layout_height="50dip"
+    android:paddingLeft="16dip"
+    android:paddingRight="12dip"
+    android:paddingBottom="8dp" />
diff --git a/res/layout/search_action_bar.xml b/res/layout/search_action_bar.xml
new file mode 100644
index 0000000..2953d72
--- /dev/null
+++ b/res/layout/search_action_bar.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2016, 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.
+ -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dip"
+    android:orientation="horizontal"
+    android:layout_height="0dip" >
+
+   <EditText
+        android:layout_weight="1"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        android:layout_width="0dp"
+        android:singleLine="true"
+        android:id="@+id/search_view"
+        android:background="@null"
+        android:textCursorDrawable="@drawable/color_cursor"
+        android:layout_height="match_parent"
+        android:hint="@string/calllog_search_hint"
+        android:cursorVisible="true"
+        android:textColorHint="@color/searchview_edittext"
+        android:textColor="@color/actionbar_icon_color"
+        android:inputType="textFilter" />
+      <ImageView
+         android:id="@+id/search_close_button"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:src="@drawable/clear"
+         android:clickable="true"
+         android:contentDescription="@string/description_clear_search"
+         />
+</LinearLayout>
diff --git a/res/layout/speed_dial_item.xml b/res/layout/speed_dial_item.xml
new file mode 100644
index 0000000..948d2e7
--- /dev/null
+++ b/res/layout/speed_dial_item.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The CyanogenMod Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="16dip"
+    android:paddingEnd="16dip"
+    android:orientation="horizontal"
+    android:minHeight="?android:attr/listPreferredItemHeight">
+
+    <TextView
+        android:id="@+id/index"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginEnd="12dp"
+        android:textAppearance="?android:attr/textAppearanceLarge" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textColor="?attr/call_log_primary_text_color"
+            android:textSize="@dimen/call_log_primary_text_size" />
+
+        <TextView
+            android:id="@+id/number"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/call_log_name_margin_bottom"
+            android:textColor="?attr/call_log_secondary_text_color"
+            android:textSize="@dimen/call_log_secondary_text_size" />
+
+    </LinearLayout>
+
+    <QuickContactBadge
+        android:id="@+id/photo"
+        android:layout_width="@dimen/contact_photo_size"
+        android:layout_height="@dimen/contact_photo_size"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="8dp" />
+
+</LinearLayout>
+
diff --git a/res/layout/video_call_welcome.xml b/res/layout/video_call_welcome.xml
new file mode 100644
index 0000000..1a9af34
--- /dev/null
+++ b/res/layout/video_call_welcome.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2016, 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.
+ **/
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="16dip"
+    android:paddingEnd="16dip"
+    android:paddingTop="8dip"
+    android:paddingBottom="16dip"
+    android:orientation="vertical"
+    android:keepScreenOn="true">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/video_call_welcome_title"
+        android:paddingBottom="10dip"
+        style="?android:attr/textAppearanceMedium" />
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/video_call_welcome_message"
+            android:paddingBottom="10dip"
+            style="?android:attr/textAppearanceSmall" />
+    </ScrollView>
+    <CheckBox
+        android:id="@android:id/checkbox"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/video_call_welcome_message_repeat" />
+</LinearLayout>
diff --git a/res/menu/call_log_options.xml b/res/menu/call_log_options.xml
index da38d86..6570714 100644
--- a/res/menu/call_log_options.xml
+++ b/res/menu/call_log_options.xml
@@ -15,6 +15,12 @@
 -->
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
     <item
+        android:id="@+id/search_calllog"
+        android:title="@string/calllog_search_hint"
+        android:showAsAction="never"
+        android:orderInCategory="1"/>
+    <item
+        android:layout_height="58dp"
         android:id="@+id/delete_all"
         android:title="@string/call_log_delete_all"
         android:showAsAction="never"
diff --git a/res/menu/dialpad_options.xml b/res/menu/dialpad_options.xml
index 63fca07..00c0f1e 100644
--- a/res/menu/dialpad_options.xml
+++ b/res/menu/dialpad_options.xml
@@ -16,6 +16,10 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item
+        android:id="@+id/menu_add_to_4g_conference_call"
+        android:title="@string/menu_add_to_4g_conference_call"
+        android:showAsAction="withText" />
+    <item
         android:id="@+id/menu_2s_pause"
         android:title="@string/add_2sec_pause"
         android:showAsAction="withText" />
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
index 0f068f5..a5ca720 100644
--- a/res/menu/dialtacts_options.xml
+++ b/res/menu/dialtacts_options.xml
@@ -16,6 +16,9 @@
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item
+        android:id="@+id/menu_4g_conference_call"
+        android:title="@string/menu_4g_conference_call" />
+    <item
         android:id="@+id/menu_history"
         android:icon="@drawable/ic_menu_history_lt"
         android:title="@string/action_menu_call_history_description" />
diff --git a/res/values-zh-rCN/qtistrings.xml b/res/values-zh-rCN/qtistrings.xml
new file mode 100644
index 0000000..25f18c0
--- /dev/null
+++ b/res/values-zh-rCN/qtistrings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2015-2016 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="video_call_upgrade">不能升级!</string>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 200b6fe..ca28569 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -110,6 +110,9 @@
     <string name="type_incoming_video" msgid="82323391702796181">"视频通话来电"</string>
     <string name="type_outgoing_video" msgid="2858140021680755266">"拨出的视频通话"</string>
     <string name="type_missed_video" msgid="954396897034220545">"错过的视频通话"</string>
+    <string name="type_incoming_volte">4G语音通话来电</string>
+    <string name="type_outgoing_volte">外拨4G语音通话</string>
+    <string name="type_missed_volte">未接4G语音通话</string>
     <string name="type_voicemail" msgid="5153139450668549908">"语音信箱"</string>
     <string name="type_rejected" msgid="7783201828312472691">"拒接的来电"</string>
     <string name="type_blocked" msgid="3521686227115330015">"屏蔽的来电"</string>
@@ -130,6 +133,13 @@
     <string name="payphone" msgid="7726415831153618726">"公用电话"</string>
     <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
     <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> 分钟 <xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
+
+    <!-- Menu items for dialpad options as part of Pause and Wait ftr [CHAR LIMIT=30] -->
+    <string name="menu_add_to_4g_conference_call">"加入 4G 电话会议"</string>
+    <string name="menu_4g_conference_call">"4G 电话会议"</string>
+    <string name="add_2sec_pause">"延长暂停时间2秒"</string>
+    <string name="add_wait">"延长等待时间"</string>
+
     <!-- no translation found for voicemailCallLogToday (682363079840402849) -->
     <skip />
     <string name="voicemailCallLogDateTimeFormat" msgid="4388070029056487713">"<xliff:g id="DATE">%1$s</xliff:g><xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -271,4 +281,82 @@
     <string name="toast_cannot_write_system_settings" msgid="5614246168296606709">"电话应用不具备写入系统设置的权限。"</string>
     <string name="blocked_number_call_log_label" msgid="8912042441473014712">"已屏蔽"</string>
     <string name="accessibility_call_is_active" msgid="2297282583928508760">"正在与<xliff:g id="NAMEORNUMBER">^1</xliff:g>通话"</string>
+    <!--  for speed dial -->
+    <string name="speed_dial_settings">快速拨号设置</string>
+    <string name="speed_dial_not_set">(未设置)</string>
+    <string name="speed_dial_replace">替换</string>
+    <string name="speed_dial_delete">删除</string>
+    <string name="speed_dial_unassigned_dialog_title">按键未分配</string>
+    <string name="speed_dial_unassigned_dialog_message">没有设置快速拨号,现在设置?</string>
+    <string name="dialog_speed_dial_airplane_mode_message">关飞行模式后使用快速拨号</string>
+    <string name="yes">是</string>
+    <string name="no">否</string>
+    <string name="input_number">"输入号码"</string>
+    <string name="speed_dial_cancel">"取消"</string>
+    <string name="speed_dial_ok">"确定"</string>
+    <string name="call_log_show_all_slots">"所有 SIM 卡"</string>
+    <string name="call_log_all_calls_header">"所有通话"</string>
+    <string name="calllog_search_hint">"搜索通话记录"</string>
+    <string name="no_call_log">没有通话记录</string>
+    <string name="clear">清除</string>
+    <string name="description_clear_search">清除搜索记录</string>
+    <string name="recentCalls_empty">"您没有任何通话记录"</string>
+    <!-- The description text for the call log tab.
+    Note: AccessibilityServices use this attribute to announce what the view represents.
+    This is especially valuable for views without textual representation like ImageView.
+    [CHAR LIMIT=NONE] -->
+    <string name="recentCallsIconLabel">"通话记录"</string>
+    <!-- Menu item used to call a contact from the call log -->
+    <string name="recentCalls_callNumber">"呼叫<xliff:g id="NAME">%s</xliff:g>"</string>
+    <!-- Text for a menu item to report a call as having been incorrectly identified.
+         [CHAR LIMIT=30] -->
+    <string name="call_detail_menu_report">"报告错误的号码"</string>
+    <!-- Menu item used to copy a number from the call log to the dialer so it can be edited before calling it -->
+    <string name="recentCalls_editNumberBeforeCall">"呼叫之前编辑号码"</string>
+    <!-- Menu item used to add a number from the call log to contacts -->
+    <string name="recentCalls_addToContact">"添加到联系人"</string>
+    <!-- Menu item used to remove a single call from the call log -->
+    <string name="recentCalls_removeFromRecentList">"从通话记录中删除"</string>
+    <!-- Menu item used to remove all calls from the call log -->
+    <string name="recentCalls_deleteAll">"清除通话记录"</string>
+    <!-- Menu item used to delete a voicemail. [CHAR LIMIT=30] -->
+    <string name="recentCalls_trashVoicemail">"删除语音邮件"</string>
+    <!-- Menu item used to share a voicemail. [CHAR LIMIT=30] -->
+    <string name="recentCalls_shareVoicemail">"分享语音邮件"</string>
+    <!-- Label of the button displayed when the call log is empty. Allows the user to make a call. -->
+    <string name="recentCalls_empty_action">"拨打电话"</string>
+    <!-- String resource for the font-family to use for the call log activity's title
+         Do not translate. -->
+    <string name="call_log_activity_title_font_family">sans-serif-light</string>
+    <!-- String resource for the font-family to use for the full call history footer
+         Do not translate. -->
+    <string name="view_full_call_history_font_family">sans-serif</string>
+    <!-- Text displayed when the list of incoming calls is empty -->
+    <string name="recentIncoming_empty">"您没有任何来电。"</string>
+    <!-- Text displayed when the list of outgoing calls is empty -->
+    <string name="recentOutgoing_empty">"您没有任何外拨电话。"</string>
+    <!-- Text displayed when the list of missed calls is empty -->
+    <string name="recentMissed_empty">"您没有任何未接电话。"</string>
+    <!-- Text displayed when the list of voicemails is empty -->
+    <string name="recentVoicemails_empty">"您未收到任何语音邮件。"</string>
+
+    <string name="add_to_white_list">"加入白名单"</string>
+    <string name="add_to_black_list">"加入黑名单"</string>
+    <string name="remove_from_black_list">"从黑名单中移除"</string>
+    <string name="remove_from_white_list">"从白名单中移除"</string>
+    <string name="firewall_remove_success">"已成功移除"</string>
+    <string name="yes">"是"</string>
+    <string name="no">"否"</string>
+    <string name="input_number">"输入号码"</string>
+    <string name="speed_dial_cancel">"取消"</string>
+    <string name="speed_dial_ok">"确定"</string>
+    <string name="call_data_info_label">"通话/流量计时器"</string>
+    <string name="call_data_info_description">"语音通话时长和流量使用时间"</string>
+    <string name="alert_call_over_wifi">"将通过 Wi-Fi 拨打电话。"</string>
+    <string name="alert_call_no_cellular_coverage">"移动网络不可用。请连接至可用的 Wi-Fi 拨打电话。"</string>
+    <string name="alert_user_connect_to_wifi_for_call">"请连接至 Wi-Fi 拨打电话。"</string>
+    <string name="video_call">"视频通话"</string>
+    <string name="video_call_welcome_title"><b>"欢迎使用全新的视频通话拨号器"</b></string>
+    <string name="video_call_welcome_message">"我们都希望在电话的另一端传来熟悉的声音,但打电话显然比不上面对面交流!下面介绍了如何获取实时(或 FaceTime?)连接: 需要 4G LTE 或 Wi-Fi 连接;被叫方设备必须也支持视频通话;视频通话使用高速数据;您也可以将语音通话升级到视频通话;访问 "<a>"https://support.t-mobile.com/docs/DOC-23574"</a>" 了解详细信息。"</string>
+    <string name="video_call_welcome_message_repeat">"每次都显示这条信息"</string>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 38fd6b3..8b1fa55 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -139,4 +139,8 @@
     <color name="call_detail_footer_text_color">#616161</color>
     <color name="call_detail_footer_icon_tint">@color/call_detail_footer_text_color</color>
 
+    <color name="searchview_edittext">#59ffffff</color>
+    <color name="no_call_log">#42000000</color>
+    <color name="list_all_call">#d3000000</color>
+    <bool name="config_regional_presence_enable">false</bool>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..b6632c1
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ /* Copyright (c) 2016, 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.
+ *
+ */
+-->
+<resources>
+  <bool name="config_regional_video_call_welcome_dialog">false</bool>
+  <bool name="config_regional_pup_no_available_network">false</bool>
+  <bool name="config_regional_call_data_usage_enable">false</bool>
+  <!--not display SIP dial icon -->
+  <bool name="config_hide_SIP_dial_icon">false</bool>
+
+  <integer name="speed_dial_emergency_number_assigned_key">9</integer>
+
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 371a1c6..d9c425d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -117,6 +117,7 @@
     <dimen name="call_log_actions_top_padding">8dp</dimen>
     <dimen name="call_log_actions_bottom_padding">8dp</dimen>
     <dimen name="call_log_primary_text_size">16sp</dimen>
+    <dimen name="call_log_secondary_text_size">14sp</dimen>
     <dimen name="call_log_detail_text_size">12sp</dimen>
     <dimen name="call_log_day_group_heading_size">14sp</dimen>
     <dimen name="call_log_voicemail_transcription_text_size">14sp</dimen>
@@ -173,4 +174,8 @@
     <dimen name="call_type_icon_size">12dp</dimen>
 
     <dimen name="tab_unread_count_margin_left">0dp</dimen>
+
+    <dimen name="list_all_calls">200dp</dimen>
+    <dimen name="empty_list_message_text_size_for_Marshmallow">16dp</dimen>
+    <dimen name="call_log_spinner_text_size">16sp</dimen>
 </resources>
diff --git a/res/values/qtistrings.xml b/res/values/qtistrings.xml
new file mode 100644
index 0000000..a11e5e5
--- /dev/null
+++ b/res/values/qtistrings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2016, 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>
+    <!-- OEM Key strings -->
+    <string name="oem_key_code_action"></string>
+    <string name="oem_code"></string>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cb85684..ce088cd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -314,6 +314,8 @@
     <string name="menu_show_all_calls">Show all calls</string>
 
     <!-- Menu items for dialpad options as part of Pause and Wait ftr [CHAR LIMIT=30] -->
+    <string name="menu_add_to_4g_conference_call">Add to 4G conference call</string>
+    <string name="menu_4g_conference_call">4G conference call</string>
     <string name="add_2sec_pause">Add 2-sec pause</string>
     <string name="add_wait">Add wait</string>
 
@@ -365,6 +367,42 @@
     <!-- Title for missed video call in call details screen [CHAR LIMIT=60] -->
     <string name="type_missed_video">Missed video call</string>
 
+    <!-- Title for incoming 4G voice call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_incoming_volte">Incoming 4G voice call</string>
+
+     <!-- Title for incoming 4G video call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_incoming_video_lte">Incoming 4G video call</string>
+
+    <!-- Title for outgoing 4G voice call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_outgoing_volte">Outgoing 4G voice call</string>
+
+     <!-- Title for incoming 4G video call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_outgoing_video_lte">Outgoing 4G video call</string>
+
+    <!-- Title for missed 4G voice call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_missed_volte">Missed 4G voice call</string>
+
+     <!-- Title for missed 4G video call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_missed_video_lte">Missed 4G video call</string>
+
+    <!-- Title for incoming wifi voice call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_incoming_vowifi">Incoming WiFi voice call</string>
+
+     <!-- Title for incoming wifi video call in call details screen [CHAR LIMIT=60] -->
+     <string name="type_incoming_video_wifi">Incoming WiFi video call</string>
+
+    <!-- Title for outgoing wifi voice call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_outgoing_vowifi">Outgoing WiFi voice call</string>
+
+     <!-- Title for outgoing wifi video call in call details screen [CHAR LIMIT=60] -->
+     <string name="type_outgoing_video_wifi">Outgoing WiFi video call</string>
+
+    <!-- Title for missed wifi voice call in call details screen [CHAR LIMIT=60] -->
+    <string name="type_missed_vowifi">Missed WiFi voice call</string>
+
+     <!-- Title for missed wifi video call in call details screen [CHAR LIMIT=60] -->
+     <string name="type_missed_video_wifi">Missed WiFi video call</string>
+
     <!-- Title for voicemail details screen -->
     <string name="type_voicemail">Voicemail</string>
 
@@ -1063,4 +1101,70 @@
 
     <!-- Accessibility announcement to indicate which call is active -->
     <string name="accessibility_call_is_active"><xliff:g id="nameOrNumber">^1</xliff:g> is active</string>
+
+    <!--  for speed dial -->
+    <string name="speed_dial_settings">Speed dial settings</string>
+
+    <string name="speed_dial_not_set">(not set)</string>
+
+    <string name="speed_dial_replace">Replace</string>
+
+    <string name="speed_dial_delete">Delete</string>
+
+    <string name="speed_dial_unassigned_dialog_title">Key unassigned</string>
+
+    <string name="speed_dial_unassigned_dialog_message">No speed dial action is assigned to number k
+ey \'<xliff:g id="number">%s</xliff:g>\'. Do you want to assign an action now?</string>
+
+    <string name="dialog_speed_dial_airplane_mode_message">To use speed dial, first turn off Airplan
+e mode.</string>
+
+    <!-- Speed Dial can not be set for the key used for Emergency number-->
+    <string name="speed_dial_can_not_be_set">Speed Dial Can not be set for this Key</string>
+
+    <string name="yes">Yes</string>
+
+    <string name="no">No</string>
+
+    <string name="input_number">Input Number</string>
+
+    <string name="speed_dial_cancel">Cancel</string>
+
+    <string name="speed_dial_ok">OK</string>
+
+    <string name="call_log_show_all_slots">All SIMs</string>
+
+    <string name="call_log_all_calls_header">All calls</string>
+
+    <!-- Text displayed when the list of incoming calls is empty -->
+    <string name="recentIncoming_empty">You have no incoming calls.</string>
+
+    <!-- Text displayed when the list of outgoing calls is empty -->
+    <string name="recentOutgoing_empty">You have no outgoing calls.</string>
+
+    <!-- Text displayed when the call log is empty. -->
+    <string name="recentCalls_empty">Your call log is empty</string>
+    <string name="calllog_search_hint">"Search call log"</string>
+    <string name="no_call_log">No call log</string>
+    <string name="clear">Clear</string>
+    <string name="description_clear_search">Clear search</string>
+    <string name="video_call_welcome_title"><b>Welcome to the new Video Calling Dialer</b></string>
+    <string name="video_call_welcome_message">We all love to hear that familiar voice at the end of the line, but talking face-to-face is so much better! Here\'s what you should know to get your real-time (or FaceTime?) connections going: You\'ll need to be on 4G LTE or a Wi-Fi connection The device you\'re calling must also be video calling compatible Video calls use your high speed data You can also upgrade a voice call to a video call Check out <a>https://support.t-mobile.com/docs/DOC-23574</a> for more information.</string>
+    <string name="video_call_welcome_message_repeat">Show this message every time</string>
+
+    <string name="toast_change_video_call_failed">Can not change to video call, incorrect number format</string>
+    <string name="toast_make_video_call_failed">Can not make video call, incorrect number format</string>
+    <!-- Add for enhance videocall ui -->
+    <string name="overflowBothCallMenuItemText">Video Call, show me</string>
+    <string name="overflowRXCallMenuItemText">Video Call, hide me</string>
+    <string name="overflowVOCallMenuItemText">Voice Call</string>
+    <string name="video_call">Video Calling</string>
+    <string name="call_data_info_label">Call/data Usage Timers</string>
+    <string name="call_data_info_description">Voice call usage time and data usage time</string>
+    <string name="alert_call_over_wifi">Calls will be made over Wi-Fi.</string>
+    <string name="alert_call_no_cellular_coverage">No cellular network available. Connect to available Wi-Fi to make calls.</string>
+    <string name="alert_user_connect_to_wifi_for_call">Connect to Wi-Fi to make calls</string>
+    <string name="alert_user_connect_to_wifi_for_call_text">Tap here to view available networks</string>
+    <string name="missing_account_type">(No type)</string>
+    <string name="missing_account_name">(No name)</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6a40d09..dbe3244 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -343,4 +343,110 @@
         <item name="android:layout_height">1dp</item>
         <item name="android:background">?android:attr/listDivider</item>
     </style>
+
+    <style name="SpeedDialtactsTheme" parent="android:Theme.Material.Light">
+        <item name="android:textColorPrimary">@color/dialtacts_primary_text_color</item>
+        <item name="android:textColorSecondary">@color/dialtacts_secondary_text_color</item>
+
+        <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="windowActionBarOverlay">true</item>
+        <item name="android:windowActionModeOverlay">true</item>
+        <item name="windowActionModeOverlay">true</item>
+        <item name="android:actionBarStyle">@style/SpeedDialtactsActionBarStyle</item>
+        <item name="actionBarStyle">@style/SpeedDialtactsActionBarStyle</item>
+        <!-- Style for the overflow button in the actionbar. -->
+        <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+        <item name="actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+
+        <!--  Drawable for the back button -->
+        <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:listViewStyle">@style/ListViewStyle</item>
+        <item name="android:overlapAnchor">true</item>
+        <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
+        <item name="activated_background">@drawable/list_item_activated_background</item>
+        <item name="section_header_background">@drawable/list_title_holo</item>
+        <item name="list_section_header_height">32dip</item>
+        <item name="list_item_padding_top">12dp</item>
+        <item name="list_item_padding_right">24dp</item>
+        <item name="list_item_padding_bottom">12dp</item>
+        <item name="list_item_padding_left">16dp</item>
+        <item name="list_item_gap_between_image_and_text">
+            @dimen/contact_browser_list_item_gap_between_image_and_text
+        </item>
+        <item name="list_item_gap_between_label_and_data">8dip</item>
+        <item name="list_item_presence_icon_margin">4dip</item>
+        <item name="list_item_presence_icon_size">16dip</item>
+        <item name="list_item_photo_size">@dimen/contact_browser_list_item_photo_size</item>
+        <item name="list_item_profile_photo_size">70dip</item>
+        <item name="list_item_prefix_highlight_color">@color/people_app_theme_color</item>
+        <item name="list_item_background_color">@color/background_dialer_light</item>
+        <item name="list_item_header_text_indent">8dip</item>
+        <item name="list_item_header_text_color">@color/dialtacts_secondary_text_color</item>
+        <item name="list_item_header_text_size">14sp</item>
+        <item name="list_item_header_height">30dip</item>
+        <item name="list_item_data_width_weight">5</item>
+        <item name="list_item_label_width_weight">3</item>
+        <item name="contact_browser_list_padding_left">0dp</item>
+        <item name="contact_browser_list_padding_right">0dp</item>
+        <item name="contact_browser_background">@color/background_dialer_results</item>
+        <item name="list_item_name_text_color">@color/contact_list_name_text_color</item>
+        <item name="list_item_name_text_size">16sp</item>
+        <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
+        <item name="list_item_text_offset_top">-2dp</item>
+        <!-- CallLog -->
+        <item name="call_log_primary_text_color">@color/dialtacts_primary_text_color</item>
+        <item name="call_log_primary_background_color">#000000</item>
+        <item name="call_log_secondary_text_color">@color/dialtacts_secondary_text_color</item>
+        <item name="call_log_secondary_background_color">#333333</item>
+        <item name="call_log_header_color">#33b5e5</item>
+        <!-- VoicemailStatus -->
+        <item name="call_log_voicemail_status_height">48dip</item>
+        <item name="call_log_voicemail_status_background_color">#262626</item>
+        <item name="call_log_voicemail_status_text_color">#888888</item>
+        <item name="call_log_voicemail_status_action_text_color">#33b5e5</item>
+            <!-- Favorites -->
+        <item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
+        <item name="android:colorPrimary">@color/dialer_theme_color</item>
+        <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
+        <item name="dialpad_key_button_touch_tint">@color/dialer_dialpad_touch_tint</item>
+        <item name="android:colorControlActivated">@color/dialer_theme_color</item>
+        <item name="android:colorButtonNormal">@color/dialer_theme_color</item>
+        <item name="android:textAppearanceButton">@style/DialerButtonTextStyle</item>
+
+        <!-- Video call icon -->
+        <item name="list_item_video_call_icon_size">32dip</item>
+        <item name="list_item_video_call_icon_margin">8dip</item>
+    </style>
+
+    <style name="SpeedDialtactsActionBarStyle"
+           parent="android:Widget.Material.ActionBar">
+        <!-- Styles that require AppCompat compatibility, remember to update both sets -->
+         <item name="android:background">@color/actionbar_background_color</item>
+        <item name="background">@color/actionbar_background_color</item>
+        <item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
+        <item name="titleTextStyle">@style/DialtactsActionBarTitleText</item>
+        <item name="android:height">@dimen/action_bar_height</item>
+        <item name="height">@dimen/action_bar_height</item>
+        <item name="android:elevation">@dimen/action_bar_elevation</item>
+        <item name="elevation">@dimen/action_bar_elevation</item>
+        <!-- Empty icon -->
+        <item name="android:icon">@android:color/transparent</item>
+        <item name="icon">@android:color/transparent</item>
+        <!-- Shift the title text to the right -->
+        <item name="android:contentInsetStart">@dimen/actionbar_contentInsetStart</item>
+        <item name="contentInsetStart">@dimen/actionbar_contentInsetStart</item>
+    </style>
+
+    <style name="CallLogSpinnerStyle">
+        <item name="android:textSize">@dimen/call_log_spinner_text_size</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:ellipsize">marquee</item>
+        <item name="android:singleLine">true</item>
+    </style>
+    <style name="NOCallLOG">
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">#000000</item>
+    </style>
 </resources>
diff --git a/res/values/symbols.xml b/res/values/symbols.xml
new file mode 100644
index 0000000..2b1c756
--- /dev/null
+++ b/res/values/symbols.xml
@@ -0,0 +1,32 @@
+<?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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <java-symbol type="bool" name="config_regional_number_patterns_video_call" />
+</resources>
diff --git a/res/xml/video_calling_settings.xml b/res/xml/video_calling_settings.xml
new file mode 100644
index 0000000..65e1da6
--- /dev/null
+++ b/res/xml/video_calling_settings.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (c) 2015-2016, 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <SwitchPreference
+        android:key="video_calling_preference"
+        android:persistent="false"
+        android:title="@string/video_call" />
+
+</PreferenceScreen>
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index 94c2f00..db4abe7 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -283,10 +283,12 @@
                 if (TextUtils.isEmpty(mNumber)) {
                     return;
                 }
-                mContext.startActivity(
-                        new CallIntentBuilder(getDialableNumber())
-                                .setCallInitiationType(LogState.INITIATION_CALL_DETAILS)
-                                .build());
+                Intent dialIntent = new CallIntentBuilder(getDialableNumber())
+                        .setCallInitiationType(LogState.INITIATION_CALL_DETAILS).build();
+                if (DialerUtils.isConferenceURICallLog(mNumber, mPostDialDigits)) {
+                    dialIntent.putExtra("org.codeaurora.extra.DIAL_CONFERENCE_URI", true);
+                }
+                mContext.startActivity(dialIntent);
             }
         });
 
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index d063fef..2ae567f 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -25,6 +25,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.Manifest;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Trace;
@@ -32,6 +33,7 @@
 import android.speech.RecognizerIntent;
 import android.support.design.widget.CoordinatorLayout;
 import android.support.v4.view.ViewPager;
+import android.support.v4.app.ActivityCompat;
 import android.support.v7.app.ActionBar;
 import android.telecom.PhoneAccount;
 import android.text.Editable;
@@ -56,6 +58,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.dialog.ClearFrequentsDialog;
 import com.android.contacts.common.interactions.ImportExportDialogFragment;
 import com.android.contacts.common.interactions.TouchPointManager;
@@ -85,15 +88,19 @@
 import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.IntentUtil;
 import com.android.dialer.util.IntentUtil.CallIntentBuilder;
+import com.android.dialer.util.PresenceHelper;
 import com.android.dialer.util.TelecomUtil;
+import com.android.dialer.util.WifiCallUtils;
 import com.android.dialer.voicemail.VoicemailArchiveActivity;
 import com.android.dialer.widget.ActionBarController;
 import com.android.dialer.widget.SearchEditTextLayout;
 import com.android.dialerbind.DatabaseHelperManager;
 import com.android.dialerbind.ObjectFactory;
 import com.android.phone.common.animation.AnimUtils;
+import com.android.ims.ImsManager;
 import com.android.phone.common.animation.AnimationListenerAdapter;
 import com.google.common.annotations.VisibleForTesting;
+import com.android.contacts.common.CallUtil;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -132,6 +139,10 @@
     private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
     private static final String TAG_FAVORITES_FRAGMENT = "favorites";
 
+    /* Define for Activity permission request for reading phone state */
+    private static final int PERMISSION_REQUEST_CODE_PHONE_STATE_ENABLED = 0;
+    private static final int PERMISSION_REQUEST_CODE_PHONE_STATE_DISABLED = 1;
+    private static final int PERMISSION_REQUEST_CODE_LOCATION = 2;
     /**
      * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
      */
@@ -159,6 +170,8 @@
      */
     private SmartDialSearchFragment mSmartDialSearchFragment;
 
+    private boolean mDialConferenceButtonPressed = false;
+
     /**
      * Animation that slides in.
      */
@@ -202,6 +215,7 @@
     private boolean mClearSearchOnPause;
     private boolean mIsDialpadShown;
     private boolean mShowDialpadOnResume;
+    private WifiCallUtils mWifiCallUtils;
 
     /**
      * Whether or not the device is in landscape orientation.
@@ -227,6 +241,7 @@
     private PopupMenu mOverflowMenu;
     private EditText mSearchView;
     private View mVoiceSearchButton;
+    private View mDialCallButton;
 
     private String mSearchQuery;
     private String mDialpadQuery;
@@ -234,7 +249,9 @@
     private DialerDatabaseHelper mDialerDatabaseHelper;
     private DragDropController mDragDropController;
     private ActionBarController mActionBarController;
+    private ImageButton mFloatingActionButton;
 
+    private ImageButton mConferenceDialButton;
     private FloatingActionButtonController mFloatingActionButtonController;
 
     private int mActionBarHeight;
@@ -261,11 +278,15 @@
                     mListsFragment.getSpeedDialFragment() != null &&
                     mListsFragment.getSpeedDialFragment().hasFrequents() && hasContactsPermission);
 
-            menu.findItem(R.id.menu_import_export).setVisible(hasContactsPermission);
-            menu.findItem(R.id.menu_add_contact).setVisible(hasContactsPermission);
+            // Hide import/export function in Dialer, Contact supports it already
+            menu.findItem(R.id.menu_import_export).setVisible(false);
+            menu.findItem(R.id.menu_add_contact).setVisible(false);
 
             menu.findItem(R.id.menu_history).setVisible(
                     PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
+            final MenuItem ConferDialerOption = menu.findItem(R.id.menu_4g_conference_call);
+            ConferDialerOption.setVisible(
+                    IntentUtil.isConferDialerEnabled(getApplicationContext()));
             super.show();
         }
     }
@@ -425,10 +446,15 @@
         mPreviouslySelectedTabIndex = ListsFragment.TAB_INDEX_SPEED_DIAL;
         final View floatingActionButtonContainer = findViewById(
                 R.id.floating_action_button_container);
-        ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
-        floatingActionButton.setOnClickListener(this);
+        mFloatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
+        mDialCallButton =  findViewById(R.id.floating_action_button);
+        mFloatingActionButton.setOnClickListener(this);
+        if (!getResources().getBoolean(R.bool.config_hide_SIP_dial_icon)) {
+            mConferenceDialButton = (ImageButton) findViewById(R.id.dialConferenceButton);
+            mConferenceDialButton.setOnClickListener(this);
+        }
         mFloatingActionButtonController = new FloatingActionButtonController(this,
-                floatingActionButtonContainer, floatingActionButton);
+                floatingActionButtonContainer,mFloatingActionButton);
 
         ImageButton optionsMenuButton =
                 (ImageButton) searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
@@ -441,6 +467,7 @@
         if (savedInstanceState == null) {
             getFragmentManager().beginTransaction()
                     .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
+                    .add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT)
                     .commit();
         } else {
             mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
@@ -492,10 +519,38 @@
         Trace.beginSection(TAG + " initialize smart dialing");
         mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
         SmartDialPrefix.initializeNanpSettings(this);
-        Trace.endSection();
-        Trace.endSection();
-    }
 
+        boolean isPresenceEnabled = this.getResources().getBoolean(
+                R.bool.config_regional_presence_enable);
+        if (isPresenceEnabled && !PresenceHelper.isBound()) {
+            PresenceHelper.bindService((Context) DialtactsActivity.this);
+        }
+
+        mWifiCallUtils = new WifiCallUtils();
+        if (resources.getBoolean(R.bool.config_regional_pup_no_available_network)
+                && mFirstLaunch) {
+            mWifiCallUtils.addWifiCallReadyMarqueeMessage((Context) DialtactsActivity.this);
+            if (ActivityCompat.checkSelfPermission(DialtactsActivity.this,
+                        Manifest.permission.ACCESS_COARSE_LOCATION) !=
+                        PackageManager.PERMISSION_GRANTED) {
+                    requestPermissions(
+                            new String[] {Manifest.permission.ACCESS_COARSE_LOCATION},
+                            PERMISSION_REQUEST_CODE_LOCATION);
+            } else {
+                mWifiCallUtils.showWifiCallNotification((Context) DialtactsActivity.this);
+            }
+        }
+
+        Trace.endSection();
+        Trace.endSection();
+
+        if (getResources().getBoolean(
+                R.bool.config_regional_video_call_welcome_dialog)) {
+            if(CallUtil.isVideoEnabled(this)) {
+                showVideoCallWelcomeDialog();
+            }
+        }
+    }
     @Override
     protected void onResume() {
         Trace.beginSection(TAG + " onResume");
@@ -535,6 +590,8 @@
         prepareVoiceSearchButton();
         mDialerDatabaseHelper.startSmartDialUpdateThread();
         mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
+        setConferenceDialButtonImage(false);
+        setConferenceDialButtonVisibility(true);
 
         if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
             // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
@@ -582,6 +639,14 @@
     }
 
     @Override
+    protected void onStop() {
+        if (PresenceHelper.isBound()) {
+            PresenceHelper.unbindService((Context) DialtactsActivity.this);
+        }
+        super.onStop();
+    }
+
+    @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
         outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
@@ -626,6 +691,10 @@
     public void onClick(View view) {
         int resId = view.getId();
         if (resId == R.id.floating_action_button) {
+            mDialConferenceButtonPressed = false;
+            if (mDialpadFragment != null) {
+                mDialpadFragment.showDialConference(false);
+            }
             if (mListsFragment.getCurrentTabIndex()
                     == ListsFragment.TAB_INDEX_ALL_CONTACTS && !mInRegularSearch) {
                 DialerUtils.startActivityWithErrorToast(
@@ -635,6 +704,13 @@
             } else if (!mIsDialpadShown) {
                 mInCallDialpadUp = false;
                 showDialpadFragment(true);
+                mFloatingActionButton.setImageResource(R.drawable.fab_ic_call);
+                mFloatingActionButton.setVisibility(view.VISIBLE);
+                setConferenceDialButtonImage(false);
+                setConferenceDialButtonVisibility(true);
+            } else {
+                // Dial button was pressed; tell the Dialpad fragment
+                mDialpadFragment.dialButtonPressed();
             }
         } else if (resId == R.id.voice_search_button) {
             try {
@@ -644,6 +720,15 @@
                 Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
                         Toast.LENGTH_SHORT).show();
             }
+        } else if (resId == R.id.dialConferenceButton) {
+            mDialConferenceButtonPressed = true;
+            showDialpadFragment(true);
+            mIsDialpadShown = false;
+            mDialCallButton.setVisibility(view.VISIBLE);
+            mDialpadFragment.dialConferenceButtonPressed();
+            mFloatingActionButton.setImageResource(R.drawable.fab_ic_dial);
+            mFloatingActionButtonController.align(getFabAlignment(), true);
+            mFloatingActionButton.setVisibility(view.VISIBLE);
         } else if (resId == R.id.dialtacts_options_menu_button) {
             mOverflowMenu.show();
         } else {
@@ -692,6 +777,9 @@
             final Intent intent = new Intent(this, VoicemailArchiveActivity.class);
             startActivity(intent);
             return true;
+        } else if (resId == R.id.menu_4g_conference_call){
+            this.startActivity(IntentUtil.getConferenceDialerIntent(null));
+            return true;
         }
         return false;
     }
@@ -755,6 +843,7 @@
             maybeEnterSearchUi();
         }
         mActionBarController.onDialpadUp();
+        setConferenceDialButtonVisibility(animate);
 
         mListsFragment.getView().animate().alpha(0).withLayer();
 
@@ -767,6 +856,13 @@
      */
     public void onDialpadShown() {
         Assert.assertNotNull(mDialpadFragment);
+        if (mDialConferenceButtonPressed || !mIsDialpadShown) {
+            mFloatingActionButton.setImageResource(R.drawable.fab_ic_dial);
+            mDialConferenceButtonPressed = false;
+        } else {
+            mFloatingActionButton.setImageResource(R.drawable.fab_ic_call);
+        }
+        mFloatingActionButtonController.align(getFabAlignment(), mDialpadFragment.getAnimate());
         if (mDialpadFragment.getAnimate()) {
             mDialpadFragment.getView().startAnimation(mSlideIn);
         } else {
@@ -794,7 +890,8 @@
             mDialpadFragment.getDigitsWidget().setImportantForAccessibility(
                     View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
         }
-        if (!mIsDialpadShown) {
+        if (!mIsDialpadShown && !mDialpadFragment.isRecipientsShown()) {
+            mFloatingActionButtonController.align(getFabAlignment(), animate);
             return;
         }
         mIsDialpadShown = false;
@@ -803,6 +900,7 @@
         mListsFragment.sendScreenViewForCurrentPosition();
 
         updateSearchFragmentPosition();
+        mFloatingActionButton.setImageResource(R.drawable.fab_ic_dial);
 
         mFloatingActionButtonController.align(getFabAlignment(), animate);
         if (animate) {
@@ -825,7 +923,7 @@
     /**
      * Finishes hiding the dialpad fragment after any animations are completed.
      */
-    private void commitDialpadFragmentHide() {
+    public void commitDialpadFragmentHide() {
         if (!mStateSaved && mDialpadFragment != null && !mDialpadFragment.isHidden()) {
             final FragmentTransaction ft = getFragmentManager().beginTransaction();
             ft.hide(mDialpadFragment);
@@ -1122,7 +1220,10 @@
         if (mStateSaved) {
             return;
         }
-        if (mIsDialpadShown) {
+        setConferenceDialButtonImage(false);
+        setConferenceDialButtonVisibility(true);
+        boolean mIsRecipientsShown = mDialpadFragment.isRecipientsShown();
+        if (mIsDialpadShown || mIsRecipientsShown) {
             if (TextUtils.isEmpty(mSearchQuery) ||
                     (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()
                             && mSmartDialSearchFragment.getAdapter().getCount() == 0)) {
@@ -1224,6 +1325,42 @@
         // interactions with the ListsFragments.
     }
 
+    @Override
+    public void setConferenceDialButtonVisibility(boolean enabled) {
+        boolean imsUseEnabled = false;
+        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            requestPermissions(new String[] {Manifest.permission.READ_PHONE_STATE}
+                    , enabled ? PERMISSION_REQUEST_CODE_PHONE_STATE_ENABLED
+                    : PERMISSION_REQUEST_CODE_PHONE_STATE_DISABLED);
+        } else {
+            imsUseEnabled = ImsManager.isVolteEnabledByPlatform(this)
+                    && ImsManager.isEnhanced4gLteModeSettingEnabledByUser(this);
+        }
+        if(mConferenceDialButton != null) {
+            boolean isCurrentTabAllContacts = (mListsFragment != null) &&
+                    (mListsFragment.getCurrentTabIndex() == ListsFragment.TAB_INDEX_ALL_CONTACTS);
+            mConferenceDialButton.setVisibility((enabled && imsUseEnabled &&
+                    !isCurrentTabAllContacts) ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    @Override
+    public void setConferenceDialButtonImage(boolean setAddParticipantButton) {
+        if(mConferenceDialButton != null) {
+            /*
+             * If dial conference view is shown, button should show dialpad
+             * image. Pressing the button again will return to normal dialpad
+             * view. If normal dialpad view is shown, button should show dial
+             * conference image. Pressing the button again will show dial
+             * conference view
+             */
+            mConferenceDialButton
+                    .setImageResource(setAddParticipantButton ? R.drawable.fab_ic_call
+                            : R.drawable.ic_add_group_holo_dark);
+        }
+    }
+
     private boolean phoneIsInUse() {
         return TelecomUtil.isInCall(this);
     }
@@ -1300,6 +1437,12 @@
             // an error message.
             phoneNumber = "";
         }
+        if (getResources().getBoolean(R.bool.config_regional_number_patterns_video_call) &&
+                !CallUtil.isVideoCallNumValid(phoneNumber) &&
+                        isVideoCall && (CallUtil.isVideoEnabled(this))) {
+            Toast.makeText(this,R.string.toast_make_video_call_failed, Toast.LENGTH_LONG).show();
+            return;
+        }
 
         final Intent intent = new CallIntentBuilder(phoneNumber)
                 .setIsVideoCall(isVideoCall)
@@ -1343,10 +1486,12 @@
         int tabIndex = mListsFragment.getCurrentTabIndex();
         mPreviouslySelectedTabIndex = tabIndex;
         if (tabIndex == ListsFragment.TAB_INDEX_ALL_CONTACTS) {
+            setConferenceDialButtonVisibility(false);
             mFloatingActionButtonController.changeIcon(
                     getResources().getDrawable(R.drawable.ic_person_add_24dp),
                     getResources().getString(R.string.search_shortcut_create_new_contact));
         } else {
+            setConferenceDialButtonVisibility(true);
             mFloatingActionButtonController.changeIcon(
                     getResources().getDrawable(R.drawable.fab_ic_dial),
                     getResources().getString(R.string.action_menu_dialpad_button));
@@ -1403,9 +1548,47 @@
         return FloatingActionButtonController.ALIGN_END;
     }
 
+    private void showVideoCallWelcomeDialog() {
+        if (DialerUtils.canShowWelcomeScreen(this) || DialerUtils.isFirstLaunch(this)) {
+            final Intent intent = new Intent(this, VideoCallWelcomeActivity.class);
+            startActivity(intent);
+        }
+    }
+
     private void updateMissedCalls() {
         if (mPreviouslySelectedTabIndex == ListsFragment.TAB_INDEX_HISTORY) {
             mListsFragment.markMissedCallsAsReadAndRemoveNotifications();
         }
     }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        switch (requestCode) {
+            case PERMISSION_REQUEST_CODE_PHONE_STATE_ENABLED:
+            case PERMISSION_REQUEST_CODE_PHONE_STATE_DISABLED:
+                boolean imsUseEnabled = false;
+                boolean enabled = requestCode == PERMISSION_REQUEST_CODE_PHONE_STATE_ENABLED;
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    imsUseEnabled = ImsManager.isVolteEnabledByPlatform(this)
+                            && ImsManager.isEnhanced4gLteModeSettingEnabledByUser(this);
+                }
+                if(mConferenceDialButton != null) {
+                    boolean isCurrentTabAllContacts = (mListsFragment != null)
+                            && (mListsFragment.getCurrentTabIndex() ==
+                            ListsFragment.TAB_INDEX_ALL_CONTACTS);
+                    mConferenceDialButton.setVisibility((enabled && imsUseEnabled
+                            && !isCurrentTabAllContacts) ? View.VISIBLE : View.GONE);
+                }
+                break;
+            case PERMISSION_REQUEST_CODE_LOCATION:
+                if (grantResults.length > 0 && grantResults[0] ==
+                        PackageManager.PERMISSION_GRANTED) {
+                    WifiCallUtils.showWifiCallNotification((Context) DialtactsActivity.this);
+                }
+                break;
+            default:
+                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
 }
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index 17f1c2b..103a07d 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -23,6 +23,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.CallLog.Calls;
 import android.support.annotation.Nullable;
@@ -54,7 +55,10 @@
      * There might be multiple types if this represents a set of entries grouped together.
      */
     public int[] callTypes;
-
+    /**
+     * The icon for the account associated with the call.
+     */
+    public Drawable accountIcon;
     // The date of the call, in milliseconds since the epoch.
     public long date;
     // The duration of the call in milliseconds, or 0 for missed calls.
diff --git a/src/com/android/dialer/SpecialCharSequenceMgr.java b/src/com/android/dialer/SpecialCharSequenceMgr.java
index 4303f3e..fff7d19 100644
--- a/src/com/android/dialer/SpecialCharSequenceMgr.java
+++ b/src/com/android/dialer/SpecialCharSequenceMgr.java
@@ -32,7 +32,9 @@
 import android.provider.Settings;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -74,6 +76,9 @@
     private static final String MMI_IMEI_DISPLAY = "*#06#";
     private static final String MMI_REGULATORY_INFO_DISPLAY = "*#07#";
 
+    private static final String PRL_VERSION_DISPLAY = "*#0000#";
+
+    private static final int IMEI_14_DIGIT = 14;
     /**
      * Remembers the previous {@link QueryHandler} and cancel the operation when needed, to
      * prevent possible crash.
@@ -137,6 +142,7 @@
         String dialString = PhoneNumberUtils.stripSeparators(input);
 
         if (handleDeviceIdDisplay(context, dialString)
+                || handlePRLVersion(context, dialString)
                 || handleRegulatoryInfoDisplay(context, dialString)
                 || handlePinEntry(context, dialString)
                 || handleAdnEntry(context, dialString, textField)
@@ -147,6 +153,20 @@
         return false;
     }
 
+    static private boolean handlePRLVersion(Context context, String input) {
+        if (input.equals(PRL_VERSION_DISPLAY)) {
+            try {
+                Intent intent = new Intent("android.intent.action.ENGINEER_MODE_DEVICEINFO");
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                context.startActivity(intent);
+                return true;
+            } catch (ActivityNotFoundException e) {
+                Log.d(TAG, "no activity to handle showing device info");
+            }
+        }
+        return false;
+    }
+
     /**
      * Cleanup everything around this class. Must be run inside the main thread.
      *
@@ -182,7 +202,15 @@
             context.sendBroadcast(intent);
             return true;
         }
-
+        if (!TextUtils.isEmpty(context.getString(R.string.oem_key_code_action))) {
+            if (len > 10 && !input.startsWith("*#*#")
+                   && input.startsWith("*#") && input.endsWith("#")) {
+                Intent intent = new Intent(context.getString(R.string.oem_key_code_action));
+                intent.putExtra(context.getString(R.string.oem_code), input);
+                context.sendBroadcast(intent);
+                return true;
+            }
+        }
         return false;
     }
 
@@ -336,6 +364,26 @@
                             "getDeviceId", Integer.TYPE)) {
                 for (int slot = 0; slot < telephonyManager.getPhoneCount(); slot++) {
                     String deviceId = telephonyManager.getDeviceId(slot);
+                    boolean enable14DigitImei = false;
+                    try {
+                        CarrierConfigManager configManager =
+                                (CarrierConfigManager) context.getSystemService(
+                                 Context.CARRIER_CONFIG_SERVICE);
+                        int[] subIds = SubscriptionManager.getSubId(slot);
+                        if (configManager != null &&
+                                configManager.getConfigForSubId(subIds[0]) != null) {
+                            enable14DigitImei =
+                                    configManager.getConfigForSubId(subIds[0]).getBoolean(
+                                    "config_enable_display_14digit_imei");
+                        }
+                    } catch(RuntimeException ex) {
+                        //do Nothing
+                        Log.e(TAG, "Config for 14 digit IMEI not found: " + ex);
+                    }
+                    if (enable14DigitImei && !TextUtils.isEmpty(deviceId)
+                           && deviceId.length() > IMEI_14_DIGIT) {
+                        deviceId = deviceId.substring(0, IMEI_14_DIGIT);
+                    }
                     if (!TextUtils.isEmpty(deviceId)) {
                         deviceIds.add(deviceId);
                     }
diff --git a/src/com/android/dialer/SpeedDialListActivity.java b/src/com/android/dialer/SpeedDialListActivity.java
new file mode 100644
index 0000000..e979151
--- /dev/null
+++ b/src/com/android/dialer/SpeedDialListActivity.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2013-2016, 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.dialer;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.Settings;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.PopupMenu;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.internal.telephony.PhoneConstants;
+import com.google.common.base.FinalizablePhantomReference;
+
+import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
+import java.util.List;
+
+public class SpeedDialListActivity extends ListActivity implements
+        AdapterView.OnItemClickListener, PopupMenu.OnMenuItemClickListener {
+    private static final String TAG = "SpeedDial";
+    private static final String ACTION_ADD_VOICEMAIL =
+            "com.android.phone.CallFeaturesSetting.ADD_VOICEMAIL";
+    public static final String EXTRA_INITIAL_PICK_NUMBER = "initialPickNumber";
+
+    // Extra on intent containing the id of a subscription.
+    public static final String SUB_ID_EXTRA =
+            "com.android.phone.settings.SubscriptionInfoHelper.SubscriptionId";
+    // Extra on intent containing the label of a subscription.
+    private static final String SUB_LABEL_EXTRA =
+            "com.android.phone.settings.SubscriptionInfoHelper.SubscriptionLabel";
+
+    private static final String[] LOOKUP_PROJECTION = new String[] {
+        ContactsContract.Contacts._ID,
+        ContactsContract.Contacts.DISPLAY_NAME,
+        ContactsContract.Contacts.PHOTO_ID,
+        ContactsContract.PhoneLookup.NUMBER,
+        ContactsContract.PhoneLookup.NORMALIZED_NUMBER
+    };
+
+    private static final String[] PICK_PROJECTION = new String[] {
+        ContactsContract.Data.CONTACT_ID,
+        ContactsContract.Data.DISPLAY_NAME,
+        ContactsContract.Data.PHOTO_ID,
+        ContactsContract.CommonDataKinds.Phone.NUMBER,
+        ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER
+    };
+    private static final int COLUMN_ID = 0;
+    private static final int COLUMN_NAME = 1;
+    private static final int COLUMN_PHOTO = 2;
+    private static final int COLUMN_NUMBER = 3;
+    private static final int COLUMN_NORMALIZED = 4;
+    private static final int MENU_REPLACE = 1001;
+    private static final int MENU_DELETE = 1002;
+    private int mItemPosition;
+    private static String SPEAD_DIAL_NUMBER = "SpeedDialNumber";
+    private static String SAVE_CLICKED_POS = "Clicked_pos";
+    private String mInputNumber;
+    private boolean mConfigChanged;
+
+    private static final String PROPERTY_RADIO_ATEL_CARRIER = "persist.radio.atel.carrier";
+    private static final String CARRIER_ONE_DEFAULT_MCC_MNC = "405854";
+
+    private static class Record {
+        long contactId;
+        String name;
+        String number;
+        String normalizedNumber;
+        long photoId;
+        public Record(String number) {
+            this.number = number;
+            this.contactId = -1;
+        }
+    }
+
+    private SparseArray<Record> mRecords;
+
+    private int mPickNumber;
+    private int mInitialPickNumber;
+    private SpeedDialAdapter mAdapter;
+    private AlertDialog mAddSpeedDialDialog;
+    private EditText mEditNumber;
+    private Button mCompleteButton;
+
+    private static final int PICK_CONTACT_RESULT = 0;
+
+    private SubscriptionManager mSubscriptionManager;
+
+    private boolean mEmergencyCallSpeedDial = false;
+    private int mSpeedDialKeyforEmergncyCall = -1;
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mInitialPickNumber = getIntent().getIntExtra(EXTRA_INITIAL_PICK_NUMBER, -1);
+        mRecords = new SparseArray<Record>();
+
+        //the first item is the "1.voice mail", it never changes
+        mRecords.put(1, new Record(getString(R.string.voicemail)));
+
+        mSubscriptionManager = SubscriptionManager.from(this);
+
+        ListView listview = getListView();
+        listview.setOnItemClickListener(this);
+
+        // compensate for action bar overlay specified in theme
+        int actionBarHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
+        listview.setPaddingRelative(0, actionBarHeight, 0, 0);
+
+        final ActionBar actionBar = getActionBar();
+        actionBar.setDisplayShowHomeEnabled(true);
+        actionBar.setDisplayHomeAsUpEnabled(true);
+
+        mAdapter = new SpeedDialAdapter();
+        setListAdapter(mAdapter);
+
+        String property = SystemProperties.get(PROPERTY_RADIO_ATEL_CARRIER);
+        mEmergencyCallSpeedDial = CARRIER_ONE_DEFAULT_MCC_MNC.equals(property);
+        mSpeedDialKeyforEmergncyCall = getResources().getInteger(
+                R.integer.speed_dial_emergency_number_assigned_key);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mAddSpeedDialDialog == null || !mAddSpeedDialDialog.isShowing()) {
+            outState.clear();
+            return;
+        }
+        outState.putInt(SAVE_CLICKED_POS, mItemPosition);
+        outState.putString(SPEAD_DIAL_NUMBER, mEditNumber.getText().toString());
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+        if (state.isEmpty()) {
+            return;
+        }
+        mConfigChanged = true;
+        int number = state.getInt(SAVE_CLICKED_POS, mItemPosition);
+        mInputNumber = state.getString(SPEAD_DIAL_NUMBER, "");
+        showAddSpeedDialDialog(number);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // get number from shared preferences
+        for (int i = 2; i <= 9; i++) {
+            String phoneNumber = SpeedDialUtils.getNumber(this, i);
+            Record record = null;
+            if (phoneNumber != null) {
+                Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+                        Uri.encode(phoneNumber));
+                record = getRecordFromQuery(uri, LOOKUP_PROJECTION);
+                if (record == null) {
+                    record = new Record(phoneNumber);
+                }
+            }
+            mRecords.put(i, record);
+        }
+
+        mAdapter.notifyDataSetChanged();
+
+        if (mInitialPickNumber >= 2 && mInitialPickNumber <= 9) {
+            pickContact(mInitialPickNumber);
+            // we only want to trigger the picker once
+            mInitialPickNumber = -1;
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private Record getRecordFromQuery(Uri uri, String[] projection) {
+        Record record = null;
+        Cursor cursor = null;
+        try {
+            cursor = getContentResolver().query(uri, projection, null, null, null);
+            if (cursor != null && cursor.moveToFirst()) {
+                record = new Record(cursor.getString(COLUMN_NUMBER));
+                record.contactId = cursor.getLong(COLUMN_ID);
+                record.photoId = cursor.getLong(COLUMN_PHOTO);
+                record.name = cursor.getString(COLUMN_NAME);
+                record.normalizedNumber = cursor.getString(COLUMN_NORMALIZED);
+                if (record.normalizedNumber == null) {
+                    record.normalizedNumber = record.number;
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return record;
+    }
+
+    private void showAddSpeedDialDialog(final int number) {
+        mPickNumber = number;
+        mItemPosition = number;
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(R.string.speed_dial_settings);
+        View contentView = LayoutInflater.from(this).inflate(
+                R.layout.add_speed_dial_dialog, null);
+        builder.setView(contentView);
+        ImageButton pickContacts = (ImageButton) contentView
+                .findViewById(R.id.select_contact);
+        pickContacts.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                pickContact(number);
+                dismissDialog();
+            }
+        });
+        mEditNumber = (EditText) contentView.findViewById(R.id.edit_container);
+        if (null != mRecords.get(number)) {
+            mEditNumber.setText(SpeedDialUtils.getNumber(this, number));
+        }
+        if (mConfigChanged && !mInputNumber.isEmpty()) {
+            mEditNumber.setText(mInputNumber);
+            mConfigChanged = false;
+            mInputNumber = "";
+        }
+        Button cancelButton = (Button) contentView
+                .findViewById(R.id.btn_cancel);
+        cancelButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                dismissDialog();
+            }
+        });
+        mCompleteButton = (Button) contentView.findViewById(R.id.btn_complete);
+        mCompleteButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mEditNumber.getText().toString().isEmpty()) {
+                    dismissDialog();
+                    return;
+                }
+                saveSpeedDial();
+                dismissDialog();
+            }
+        });
+        mAddSpeedDialDialog = builder.create();
+        mAddSpeedDialDialog.show();
+    }
+
+    private void saveSpeedDial() {
+        String number = mEditNumber.getText().toString();
+        Record record = null;
+        if (number != null) {
+            Uri uri = Uri.withAppendedPath(
+                    ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+                    Uri.encode(number));
+            record = getRecordFromQuery(uri, LOOKUP_PROJECTION);
+            if (record == null) {
+                record = new Record(number);
+                record.normalizedNumber = number;
+            }
+        }
+        if (record != null) {
+            SpeedDialUtils.saveNumber(this, mPickNumber,
+                    record.normalizedNumber);
+            mRecords.put(mPickNumber, record);
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+    private void dismissDialog() {
+        if (null != mAddSpeedDialDialog && mAddSpeedDialDialog.isShowing()) {
+            mAddSpeedDialDialog.dismiss();
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        if (position == 0) {
+            Intent intent = new Intent(ACTION_ADD_VOICEMAIL);
+            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            if (TelephonyManager.getDefault().getPhoneCount() > 1) {
+                int sub = SubscriptionManager.getDefaultVoiceSubscriptionId();
+                SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(sub);
+                if (subInfo != null) {
+                    intent.putExtra(SUB_ID_EXTRA, subInfo.getSubscriptionId());
+                    intent.putExtra(SUB_LABEL_EXTRA, subInfo.getDisplayName().toString());
+                }
+            }
+            try {
+                startActivity(intent);
+            } catch(ActivityNotFoundException e) {
+                Log.w(TAG, "Could not find voice mail setup activity");
+            }
+        } else {
+            int number = position + 1;
+            if (mEmergencyCallSpeedDial && (number == mSpeedDialKeyforEmergncyCall)) {
+                Toast.makeText(SpeedDialListActivity.this, R.string.speed_dial_can_not_be_set,
+                Toast.LENGTH_SHORT).show();
+                return;
+            }
+            mItemPosition = number;
+            final Record record = mRecords.get(number);
+            if (record == null) {
+                showAddSpeedDialDialog(number);
+            } else {
+                PopupMenu pm = new PopupMenu(this, view, Gravity.START);
+                pm.getMenu().add(number, MENU_REPLACE, 0, R.string.speed_dial_replace);
+                pm.getMenu().add(number, MENU_DELETE, 0, R.string.speed_dial_delete);
+                pm.setOnMenuItemClickListener(this);
+                pm.show();
+            }
+        }
+    }
+
+    private boolean isMultiAccountAvailable() {
+        TelecomManager telecomManager = getTelecomManager(this);
+        return (telecomManager.getUserSelectedOutgoingPhoneAccount() == null)
+                && (telecomManager.getAllPhoneAccountsCount() > 1);
+    }
+
+    private void showSelectAccountDialog(Context context) {
+        TelecomManager telecomManager = getTelecomManager(context);
+        List<PhoneAccountHandle> accountsList = telecomManager
+                .getCallCapablePhoneAccounts();
+        final PhoneAccountHandle[] accounts = accountsList
+                .toArray(new PhoneAccountHandle[accountsList.size()]);
+        CharSequence[] accountEntries = new CharSequence[accounts.length];
+        for (int i = 0; i < accounts.length; i++) {
+            CharSequence label = telecomManager.getPhoneAccount(accounts[i])
+                    .getLabel();
+            accountEntries[i] = (label == null) ? null : label.toString();
+        }
+        AlertDialog dialog = new AlertDialog.Builder(context)
+                .setTitle(R.string.select_account_dialog_title)
+                .setItems(accountEntries, new DialogInterface.OnClickListener() {
+
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        Intent intent = new Intent(ACTION_ADD_VOICEMAIL);
+                        int sub = Integer.parseInt(accounts[which].getId());
+                        intent.setClassName("com.android.phone",
+                                "com.android.phone.MSimCallFeaturesSubSetting");
+                        intent.putExtra(SUBSCRIPTION_KEY, sub);
+                        try {
+                            startActivity(intent);
+                        } catch (ActivityNotFoundException e) {
+                            Log.w(TAG, "can not find activity deal with voice mail");
+                        }
+                    }
+                })
+                .create();
+        dialog.show();
+    }
+
+    private TelecomManager getTelecomManager(Context context) {
+        return TelecomManager.from(context);
+    }
+
+    /*
+     * goto contacts, used to set or replace speed number
+     */
+    private void pickContact(int number) {
+        mPickNumber = number;
+        Intent intent = new Intent(Intent.ACTION_PICK);
+        intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
+        startActivityForResult(intent, PICK_CONTACT_RESULT);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode != PICK_CONTACT_RESULT) {
+            super.onActivityResult(requestCode, resultCode, data);
+            return;
+        }
+
+        if (resultCode == RESULT_OK) {
+            Record record = getRecordFromQuery(data.getData(), PICK_PROJECTION);
+            if (record != null) {
+                SpeedDialUtils.saveNumber(this, mPickNumber, record.normalizedNumber);
+                mRecords.put(mPickNumber, record);
+                mAdapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    public boolean onMenuItemClick(MenuItem item) {
+        int number = item.getGroupId();
+
+        switch (item.getItemId()) {
+            case MENU_REPLACE:
+                showAddSpeedDialDialog(number);
+                return true;
+            case MENU_DELETE:
+                mRecords.put(number, null);
+                SpeedDialUtils.saveNumber(this, number, null);
+                mAdapter.notifyDataSetChanged();
+                return true;
+        }
+        return false;
+    }
+
+    private class SpeedDialAdapter extends BaseAdapter {
+        private LayoutInflater mInflater;
+        private ContactPhotoManager mPhotoManager;
+
+        public SpeedDialAdapter() {
+            mInflater = LayoutInflater.from(SpeedDialListActivity.this);
+            mPhotoManager = ContactPhotoManager.getInstance(SpeedDialListActivity.this);
+        }
+
+        @Override
+        public int getCount() {
+            return mRecords.size();
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position + 1;
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mRecords.get(position + 1);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = mInflater.inflate(R.layout.speed_dial_item, parent, false);
+            }
+
+            TextView index = (TextView) convertView.findViewById(R.id.index);
+            TextView name = (TextView) convertView.findViewById(R.id.name);
+            TextView number = (TextView) convertView.findViewById(R.id.number);
+            QuickContactBadge photo = (QuickContactBadge) convertView.findViewById(R.id.photo);
+            Record record = mRecords.get(position + 1);
+
+            index.setText(String.valueOf(position + 1));
+            if (record != null && record.name != null) {
+                name.setText(record.name);
+                number.setText(record.number);
+                number.setVisibility(View.VISIBLE);
+            } else {
+                name.setText(record != null ?
+                        record.number : getString(R.string.speed_dial_not_set));
+                number.setVisibility(View.GONE);
+            }
+
+            if (record != null && record.contactId != -1) {
+                DefaultImageRequest request = new DefaultImageRequest(record.name,
+                        record.normalizedNumber, true /* isCircular */);
+                mPhotoManager.removePhoto(photo);
+                mPhotoManager.loadThumbnail(photo, record.photoId,
+                        false /* darkTheme */, true /* isCircular */, request);
+                photo.assignContactUri(ContentUris.withAppendedId(
+                        ContactsContract.Contacts.CONTENT_URI, record.contactId));
+                photo.setVisibility(View.VISIBLE);
+            } else {
+                photo.setVisibility(View.GONE);
+            }
+            photo.setOverlay(null);
+
+            return convertView;
+        }
+    };
+}
diff --git a/src/com/android/dialer/SpeedDialUtils.java b/src/com/android/dialer/SpeedDialUtils.java
new file mode 100644
index 0000000..24f0e8c
--- /dev/null
+++ b/src/com/android/dialer/SpeedDialUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013-2016, 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.
+ */
+
+package com.android.dialer;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class SpeedDialUtils {
+    private static final String NUMBER_KEY_PREFIX = "number_";
+
+    public static void saveNumber(Context context, int position, String phoneNumber) {
+        if (position < 2 || position > 9) {
+            return;
+        }
+        SharedPreferences.Editor editor = getPrefs(context).edit();
+        String key = NUMBER_KEY_PREFIX + position;
+        if (phoneNumber == null) {
+            editor.remove(key);
+        } else {
+            editor.putString(key, phoneNumber);
+        }
+        editor.commit();
+    }
+
+    public static String getNumber(Context context, int position) {
+        if (position < 2 || position > 9) {
+            return null;
+        }
+        String key = NUMBER_KEY_PREFIX + position;
+        return getPrefs(context).getString(key, null);
+    }
+
+    private static SharedPreferences getPrefs(Context context) {
+        return context.getSharedPreferences("speeddial", context.MODE_PRIVATE);
+    }
+}
diff --git a/src/com/android/dialer/VideoCallWelcomeActivity.java b/src/com/android/dialer/VideoCallWelcomeActivity.java
new file mode 100644
index 0000000..4a72f13
--- /dev/null
+++ b/src/com/android/dialer/VideoCallWelcomeActivity.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2016, 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.
+ **/
+
+package com.android.dialer;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.widget.CheckBox;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.dialer.util.DialerUtils;
+
+public class VideoCallWelcomeActivity extends AlertActivity
+        implements DialogInterface.OnClickListener {
+
+    private CheckBox mConfirmRepeat;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mView = LayoutInflater.from(this).inflate(R.layout.video_call_welcome, null);
+        ap.mPositiveButtonText = getString(android.R.string.ok);
+        ap.mPositiveButtonListener = this;
+
+        mConfirmRepeat = (CheckBox) ap.mView.findViewById(android.R.id.checkbox);
+        mConfirmRepeat.setChecked(DialerUtils.canShowWelcomeScreen(this));
+
+        setupAlert();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            // Remember confirm state, and launch target
+            DialerUtils.setShowingState(this,
+                    mConfirmRepeat.isChecked());
+        }
+
+        finish();
+    }
+}
+
diff --git a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
index ac56332..bbd632c 100644
--- a/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
+++ b/src/com/android/dialer/calllog/CallDetailHistoryAdapter.java
@@ -20,6 +20,7 @@
 import android.provider.CallLog.Calls;
 import android.text.format.DateUtils;
 import android.text.format.Formatter;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,10 +31,14 @@
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.R;
 import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.AppCompatConstants;
+import com.android.dialer.util.PresenceHelper;
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 /**
  * Adapter for a ListView containing history items from the details of a call.
  */
@@ -105,12 +110,25 @@
         TextView durationView = (TextView) result.findViewById(R.id.duration);
 
         int callType = details.callTypes[0];
-        boolean isVideoCall = (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
-                && CallUtil.isVideoEnabled(mContext);
-
+        boolean isPresenceEnabled = mContext.getResources().getBoolean(
+                R.bool.config_regional_presence_enable);
+        boolean isVideoCall = (details.features & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO;
+        if (isPresenceEnabled) {
+            isVideoCall &= PresenceHelper.startAvailabilityFetch(details.number.toString());
+        }
+        Log.d("CallDetailHistoryAdapter", "isVideoCall = " + isVideoCall
+                    + ", callType = " + callType);
         callTypeIconView.clear();
         callTypeIconView.add(callType);
-        callTypeIconView.setShowVideo(isVideoCall);
+        /**
+         * Ims icon(VoLTE/VoWifi) or CarrierOne video icon will be shown if carrierOne is supported
+         * otherwise, default video icon will be shown if it is a video call.
+         */
+        if (QtiImsExtUtils.isCarrierOneSupported()) {
+             callTypeIconView.addImsOrVideoIcon(callType, isVideoCall);
+        } else {
+             callTypeIconView.setShowVideo(isVideoCall);
+        }
         callTypeTextView.setText(mCallTypeHelper.getCallTypeText(callType, isVideoCall));
         // Set the date.
         CharSequence dateValue = DateUtils.formatDateRange(mContext, details.date, details.date,
@@ -118,7 +136,10 @@
                 DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_SHOW_YEAR);
         dateView.setText(dateValue);
         // Set the duration
-        if (Calls.VOICEMAIL_TYPE == callType || CallTypeHelper.isMissedCallType(callType)) {
+        boolean callDurationEnabled = mContext.getResources()
+                .getBoolean(R.bool.call_duration_enabled);
+        if (Calls.VOICEMAIL_TYPE == callType || CallTypeHelper.isMissedCallType(callType) ||
+                !callDurationEnabled) {
             durationView.setVisibility(View.GONE);
         } else {
             durationView.setVisibility(View.VISIBLE);
diff --git a/src/com/android/dialer/calllog/CallLogActivity.java b/src/com/android/dialer/calllog/CallLogActivity.java
index 1823a5b..d661018 100644
--- a/src/com/android/dialer/calllog/CallLogActivity.java
+++ b/src/com/android/dialer/calllog/CallLogActivity.java
@@ -18,23 +18,40 @@
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
+import android.telephony.TelephonyManager;
 import android.support.v13.app.FragmentPagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBar.LayoutParams;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.inputmethod.InputMethodManager;
+import android.text.TextWatcher;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.MotionEvent;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.util.Log;
 
 import com.android.contacts.common.interactions.TouchPointManager;
 import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.contacts.common.SimContactsConstants;
 import com.android.contacts.common.util.PermissionsUtil;
 import com.android.contacts.commonbind.analytics.AnalyticsUtil;
 import com.android.dialer.DialtactsActivity;
@@ -44,15 +61,21 @@
 import com.android.dialer.logging.ScreenEvent;
 import com.android.dialer.util.DialerUtils;
 
-public class CallLogActivity extends TransactionSafeActivity implements ViewPager.OnPageChangeListener {
+public class CallLogActivity extends TransactionSafeActivity implements ViewPager.OnPageChangeListener,
+    CallLogFragment.HostInterface {
     private ViewPager mViewPager;
     private ViewPagerTabs mViewPagerTabs;
-    private ViewPagerAdapter mViewPagerAdapter;
+    private FragmentPagerAdapter mViewPagerAdapter;
     private CallLogFragment mAllCallsFragment;
     private CallLogFragment mMissedCallsFragment;
 
+    private MSimCallLogFragment mMSimCallsFragment;
+    private CallLogSearchFragment mSearchFragment;
+    private EditText mSearchView;
+    private ImageView mClearButtonView;
+    private boolean mInSearchUi;
     private String[] mTabTitles;
-
+    private String mSearchQuery;
     private static final int TAB_INDEX_ALL = 0;
     private static final int TAB_INDEX_MISSED = 1;
 
@@ -60,6 +83,9 @@
 
     private boolean mIsResumed;
 
+    private static final int TAB_INDEX_MSIM = 0;
+    private static final int TAB_INDEX_COUNT_MSIM = 1;
+
     public class ViewPagerAdapter extends FragmentPagerAdapter {
         public ViewPagerAdapter(FragmentManager fm) {
             super(fm);
@@ -108,6 +134,39 @@
         }
     }
 
+    public class MSimViewPagerAdapter extends FragmentPagerAdapter {
+        public MSimViewPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            switch (position) {
+                case TAB_INDEX_MSIM:
+                    mMSimCallsFragment = new MSimCallLogFragment();
+                    return mMSimCallsFragment;
+            }
+            throw new IllegalStateException("No fragment at position " + position);
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            final MSimCallLogFragment fragment =
+                    (MSimCallLogFragment) super.instantiateItem(container, position);
+            switch (position) {
+                case TAB_INDEX_MSIM:
+                    mMSimCallsFragment = fragment;
+                    break;
+            }
+            return fragment;
+        }
+
+        @Override
+        public int getCount() {
+            return TAB_INDEX_COUNT_MSIM;
+        }
+    }
+
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -120,15 +179,20 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.call_log_activity);
-        getWindow().setBackgroundDrawable(null);
-
         final ActionBar actionBar = getSupportActionBar();
         actionBar.setDisplayShowHomeEnabled(true);
         actionBar.setDisplayHomeAsUpEnabled(true);
         actionBar.setDisplayShowTitleEnabled(true);
         actionBar.setElevation(0);
 
+        if ( TelephonyManager.getDefault().isMultiSimEnabled()) {
+            initMSimCallLog();
+            return;
+        }
+
+        setContentView(R.layout.call_log_activity);
+        getWindow().setBackgroundDrawable(null);
+
         int startingTab = TAB_INDEX_ALL;
         final Intent intent = getIntent();
         if (intent != null) {
@@ -156,6 +220,16 @@
     }
 
     @Override
+    public void onAttachFragment(Fragment fragment) {
+        if (fragment instanceof CallLogSearchFragment) {
+            if (mViewPagerAdapter != null) {
+                mSearchFragment = (CallLogSearchFragment) fragment;
+                setupSearchUi();
+            }
+        }
+    }
+
+    @Override
     protected void onResume() {
         mIsResumed = true;
         super.onResume();
@@ -166,6 +240,25 @@
     protected void onPause() {
         mIsResumed = false;
         super.onPause();
+        if (mInSearchUi) {
+            exitSearchUi();
+        }
+    }
+
+    private void initMSimCallLog() {
+        setContentView(R.layout.msim_call_log_activity);
+        getWindow().setBackgroundDrawable(null);
+
+        final ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayShowHomeEnabled(true);
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        actionBar.setDisplayShowTitleEnabled(true);
+
+        mViewPager = (ViewPager) findViewById(R.id.call_log_pager);
+
+        mViewPagerAdapter = new MSimViewPagerAdapter(getFragmentManager());
+        mViewPager.setAdapter(mViewPagerAdapter);
+        mViewPager.setOffscreenPageLimit(1);
     }
 
     @Override
@@ -178,11 +271,32 @@
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         final MenuItem itemDeleteAll = menu.findItem(R.id.delete_all);
-        if (mAllCallsFragment != null && itemDeleteAll != null) {
-            // If onPrepareOptionsMenu is called before fragments are loaded, don't do anything.
-            final CallLogAdapter adapter = mAllCallsFragment.getAdapter();
+        final MenuItem itemSearchCallLog = menu.findItem(R.id.search_calllog);
+
+        if (mMSimCallsFragment != null && itemDeleteAll != null) {
+            final CallLogAdapter adapter = mMSimCallsFragment.getAdapter();
             itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
         }
+        if (mInSearchUi) {
+            if (itemDeleteAll != null) {
+                itemDeleteAll.setVisible(false);
+            }
+            if (itemSearchCallLog != null) {
+                itemSearchCallLog.setVisible(false);
+            }
+        } else {
+            if (mSearchFragment != null && itemSearchCallLog != null) {
+                final CallLogAdapter adapter = mSearchFragment.getAdapter();
+                itemSearchCallLog.setVisible(adapter != null
+                        && !adapter.isEmpty());
+            }
+            // If onPrepareOptionsMenu is called before fragments loaded. Don't do anything.
+            if (mAllCallsFragment != null && itemDeleteAll != null) {
+                // If onPrepareOptionsMenu is called before fragments are loaded, don't do anything.
+                final CallLogAdapter adapter = mAllCallsFragment.getAdapter();
+                itemDeleteAll.setVisible(adapter != null && !adapter.isEmpty());
+            }
+        }
         return true;
     }
 
@@ -198,12 +312,19 @@
             startActivity(intent);
             return true;
         } else if (item.getItemId() == R.id.delete_all) {
-            ClearCallLogDialog.show(getFragmentManager());
+            onDeleteCallLog();
+            return true;
+        } else if (item.getItemId() == R.id.search_calllog){
+            enterSearchUi();
             return true;
         }
         return super.onOptionsItemSelected(item);
     }
 
+    private void onDeleteCallLog() {
+        startActivity(new Intent(SimContactsConstants.ACTION_MULTI_PICK_CALL));
+    }
+
     @Override
     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
         mViewPagerTabs.onPageScrolled(position, positionOffset, positionOffsetPixels);
@@ -232,4 +353,205 @@
         }
         return position;
     }
+
+    private void enterSearchUi() {
+        mInSearchUi = true;
+        if (mSearchFragment == null) {
+            addSearchFragment();
+            return;
+        }
+        mSearchFragment.setUserVisibleHint(true);
+        final FragmentTransaction transaction = getFragmentManager()
+                .beginTransaction();
+        transaction.show(mSearchFragment);
+        transaction.commitAllowingStateLoss();
+        getFragmentManager().executePendingTransactions();
+        setupSearchUi();
+    }
+
+    private void setupSearchUi() {
+        if (mSearchView == null) {
+            prepareSearchView();
+        }
+        final ActionBar actionBar = getSupportActionBar();
+        actionBar.setDisplayShowCustomEnabled(true);
+        if (mMSimCallsFragment != null) {
+            updateMSimFragmentVisibility(false);
+        } else {
+            for (int i = 0; i < mViewPagerAdapter.getCount(); i++) {
+                updateFragmentVisibility(i, false /* not visible */);
+            }
+        }
+        mViewPager.setVisibility(View.GONE);
+        if (mViewPagerTabs != null) {
+            mViewPagerTabs.setVisibility(View.GONE);
+        }
+    }
+
+    private void updateFragmentVisibility(int position, boolean visibility) {
+        if (position >= TAB_INDEX_ALL) {
+            final Fragment fragment = getFragmentAt(position);
+            if (fragment != null) {
+                fragment.setMenuVisibility(visibility);
+                fragment.setUserVisibleHint(visibility);
+            }
+        }
+    }
+
+    private void updateMSimFragmentVisibility(boolean visibility) {
+        if (mMSimCallsFragment != null) {
+            mMSimCallsFragment.setMenuVisibility(visibility);
+            mMSimCallsFragment.setUserVisibleHint(visibility);
+        }
+    }
+
+    private Fragment getFragmentAt(int position) {
+        switch (position) {
+        case TAB_INDEX_ALL:
+            return mAllCallsFragment;
+        case TAB_INDEX_MISSED:
+            return mMissedCallsFragment;
+        default:
+            throw new IllegalStateException("Unknown fragment index: "
+                    + position);
+        }
+    }
+
+    private void addSearchFragment() {
+        if (mSearchFragment != null) {
+            return;
+        }
+        final FragmentTransaction ft = getFragmentManager().beginTransaction();
+        final Fragment searchFragment = new CallLogSearchFragment();
+        searchFragment.setUserVisibleHint(false);
+        ft.add(R.id.calllog_frame, searchFragment);
+        ft.commitAllowingStateLoss();
+    }
+
+    private void prepareSearchView() {
+        final View searchViewLayout = getLayoutInflater().inflate(
+                R.layout.search_action_bar, null);
+        mSearchView = (EditText) searchViewLayout
+                .findViewById(R.id.search_view);
+        mClearButtonView = (ImageView)searchViewLayout.findViewById(R.id.search_close_button);
+        mClearButtonView.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mSearchView.setText("");
+            }
+        });
+        mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+        mClearButtonView.setVisibility(View.GONE);
+        mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (hasFocus) {
+                    showInputMethod(v.findFocus());
+                } else {
+                    hideInputMethod(v);
+                }
+            }
+        });
+        getSupportActionBar().setCustomView(
+                searchViewLayout,
+                new LayoutParams(LayoutParams.MATCH_PARENT,
+                        LayoutParams.WRAP_CONTENT));
+    }
+
+    /**
+     * Implemented to satisfy {@link CallLogFragment.HostInterface}
+     */
+    @Override
+    public void showDialpad() {
+        finish();
+        if (mInSearchUi) {
+           exitSearchUi();
+        }
+        startActivity(new Intent(CallLogActivity.this, DialtactsActivity.class));
+    }
+
+    private void showInputMethod(View view) {
+        InputMethodManager imm = (InputMethodManager) getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
+    }
+
+    private void hideInputMethod(View view) {
+        InputMethodManager imm = (InputMethodManager) getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        if (imm != null && view != null) {
+            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+        }
+    }
+
+    /**
+     * Listener used to send search queries to the phone search fragment.
+     */
+    private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+        }
+
+        @Override
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            final String newText = s.toString();
+            if (newText.equals(mSearchQuery)) {
+                // If the query hasn't changed (perhaps due to activity being destroyed
+                // and restored, or user launching the same DIAL intent twice), then there is
+                // no need to do anything here.
+                return;
+            }
+            mSearchQuery = newText;
+            if (mSearchFragment != null) {
+                mClearButtonView.setVisibility(TextUtils.isEmpty(s) ? View.GONE : View.VISIBLE);
+                mSearchFragment.setQueryString(mSearchQuery);
+            }
+        }
+        @Override
+        public void afterTextChanged(Editable e) {
+        }
+    };
+
+    @Override
+    public void onBackPressed() {
+        if (mInSearchUi) {
+            // We should let the user go back to usual screens with tabs.
+            exitSearchUi();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    private void exitSearchUi() {
+        final ActionBar actionBar = getSupportActionBar();
+        if (mSearchFragment != null) {
+            mSearchFragment.setUserVisibleHint(false);
+
+            final FragmentTransaction transaction = getFragmentManager()
+                    .beginTransaction();
+            transaction.remove(mSearchFragment);
+            mSearchFragment = null;
+            transaction.commitAllowingStateLoss();
+        }
+
+        // We want to hide SearchView and show Tabs. Also focus on previously
+        // selected one.
+        actionBar.setDisplayShowCustomEnabled(false);
+        if (mMSimCallsFragment != null) {
+            updateMSimFragmentVisibility(true);
+        } else {
+            for (int i = 0; i < mViewPagerAdapter.getCount(); i++) {
+                updateFragmentVisibility(i, i == mViewPager.getCurrentItem());
+            }
+        }
+        mViewPager.setVisibility(View.VISIBLE);
+        if (mViewPagerTabs != null) {
+            mViewPagerTabs.setVisibility(View.VISIBLE);
+        }
+        hideInputMethod(getCurrentFocus());
+        invalidateOptionsMenu();
+        mSearchView.clearFocus();
+        mInSearchUi = false;
+    }
+
 }
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 3958611..6a447a6 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -23,6 +23,7 @@
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.Cursor;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Trace;
@@ -59,11 +60,13 @@
 import com.android.dialer.logging.InteractionEvent;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.service.ExtendedBlockingButtonRenderer;
+import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.PhoneNumberUtil;
 import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 /**
  * Adapter class to fill in data for the Call Log.
@@ -107,6 +110,7 @@
     private final Map<String, Boolean> mBlockedNumberCache = new ArrayMap<>();
 
     protected ContactInfoCache mContactInfoCache;
+    private String mFilterString;
 
     private final int mActivityType;
 
@@ -179,7 +183,9 @@
                 if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
                     CallLogAsyncTaskUtil.markCallAsRead(mContext, viewHolder.callIds);
                     if (mActivityType == ACTIVITY_TYPE_DIALTACTS) {
-                        ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
+                        if (v.getContext() instanceof DialtactsActivity) {
+                            ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
+                        }
                     }
                 }
                 expandViewHolderActions(viewHolder);
@@ -355,6 +361,12 @@
         mContactInfoCache.invalidate();
     }
 
+    public void startCache() {
+        if (PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
+            mContactInfoCache.start();
+        }
+    }
+
     public void onResume() {
         if (PermissionsUtil.hasPermission(mContext, android.Manifest.permission.READ_CONTACTS)) {
             mContactInfoCache.start();
@@ -370,6 +382,13 @@
         }
     }
 
+    public void onStop () {
+        pauseCache();
+        if (mHiddenItemUri != null) {
+            CallLogAsyncTaskUtil.deleteVoicemail(mContext, mHiddenItemUri, null);
+        }
+    }
+
     @VisibleForTesting
     /* package */ void pauseCache() {
         mContactInfoCache.stop();
@@ -488,12 +507,15 @@
         }
 
         int count = getGroupSize(position);
-
-        final String number = c.getString(CallLogQuery.NUMBER);
+        final String phoneNumber = c.getString(CallLogQuery.NUMBER);
+        Pattern pattern = Pattern.compile("[,;]");
+        String[] num = pattern.split(phoneNumber);
         final String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
         final String postDialDigits = CompatUtils.isNCompatible()
                 && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
                 c.getString(CallLogQuery.POST_DIAL_DIGITS) : "";
+        final String number = DialerUtils.isConferenceURICallLog(phoneNumber, postDialDigits) ?
+                phoneNumber : num.length > 0 ? num[0] : "";
         final String viaNumber = CompatUtils.isNCompatible()
                 && mActivityType != ACTIVITY_TYPE_ARCHIVE ?
                 c.getString(CallLogQuery.VIA_NUMBER) : "";
@@ -501,6 +523,7 @@
         final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
                 c.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME),
                 c.getString(CallLogQuery.ACCOUNT_ID));
+        final Drawable accountIcon = mCallLogCache.getAccountIcon(accountHandle);
         final ContactInfo cachedContactInfo = ContactInfoHelper.getContactInfo(c);
         final boolean isVoicemailNumber =
                 mCallLogCache.isVoicemailNumber(accountHandle, number);
@@ -511,8 +534,11 @@
         ContactInfo info = ContactInfo.EMPTY;
         if (PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemailNumber) {
             // Lookup contacts with this number
-            info = mContactInfoCache.getValue(number + postDialDigits,
-                    countryIso, cachedContactInfo);
+            boolean isConfCallLog = num != null && num.length > 1
+                    && DialerUtils.isConferenceURICallLog(phoneNumber, postDialDigits);
+            String queryNumber = isConfCallLog ? phoneNumber : number;
+            info = mContactInfoCache.getValue(queryNumber, postDialDigits,
+                    countryIso, cachedContactInfo, isConfCallLog);
         }
         CharSequence formattedNumber = info.formattedNumber == null
                 ? null : PhoneNumberUtilsCompat.createTtsSpannable(info.formattedNumber);
@@ -522,6 +548,7 @@
                 postDialDigits, isVoicemailNumber);
         details.viaNumber = viaNumber;
         details.accountHandle = accountHandle;
+        details.accountIcon = accountIcon;
         details.countryIso = countryIso;
         details.date = c.getLong(CallLogQuery.DATE);
         details.duration = c.getLong(CallLogQuery.DURATION);
@@ -592,7 +619,7 @@
             views.voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
         }
 
-        mCallLogListItemHelper.setPhoneCallDetails(views, details);
+        mCallLogListItemHelper.setPhoneCallDetails(views, details, mFilterString);
 
         if (mCurrentlyExpandedRowId == views.rowId) {
             // In case ViewHolders were added/removed, update the expanded position if the rowIds
@@ -604,7 +631,7 @@
         }
         views.updatePhoto();
 
-        mCallLogListItemHelper.setPhoneCallDetails(views, details);
+        mCallLogListItemHelper.setPhoneCallDetails(views, details, mFilterString);
     }
 
     private String getPreferredDisplayName(ContactInfo contactInfo) {
@@ -915,4 +942,8 @@
         PromoCardViewHolder viewHolder = PromoCardViewHolder.create(view);
         return viewHolder;
     }
+
+    public void setQueryString(String filter) {
+        mFilterString = filter;
+    }
 }
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index 34b2f0e..667d6b9 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -41,11 +41,13 @@
 import com.android.dialer.util.AsyncTaskExecutor;
 import com.android.dialer.util.AsyncTaskExecutors;
 import com.android.dialer.util.PhoneNumberUtil;
+import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.TelecomUtil;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
+import java.util.regex.Pattern;
 
 public class CallLogAsyncTaskUtil {
     private static String TAG = CallLogAsyncTaskUtil.class.getSimpleName();
@@ -208,13 +210,20 @@
                     PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
             ContactInfo info = ContactInfo.EMPTY;
 
+            Pattern pattern = Pattern.compile("[,;]");
+            String[] num = pattern.split(number);
+            boolean isConf = num != null && num.length > 1
+                    && DialerUtils.isConferenceURICallLog(number, postDialDigits);
+            String phoneNumber = num != null && num.length > 0 ? num[0] : "";
+            String queryNumber = isConf ? number : phoneNumber;
             if (shouldLookupNumber) {
-                ContactInfo lookupInfo = contactInfoHelper.lookupNumber(number, countryIso);
+                ContactInfo lookupInfo = contactInfoHelper.lookupNumber(queryNumber, postDialDigits,
+                        countryIso, isConf);
                 info = lookupInfo != null ? lookupInfo : ContactInfo.EMPTY;
             }
 
             PhoneCallDetails details = new PhoneCallDetails(
-                    context, number, numberPresentation, info.formattedNumber,
+                    context, queryNumber, numberPresentation, info.formattedNumber,
                     postDialDigits, isVoicemail);
 
             details.viaNumber = viaNumber;
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 67b72a5..96551a4 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -82,8 +82,8 @@
 
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLayoutManager;
-    private CallLogAdapter mAdapter;
-    private CallLogQueryHandler mCallLogQueryHandler;
+    protected CallLogAdapter mAdapter;
+    protected CallLogQueryHandler mCallLogQueryHandler;
     private boolean mScrollToTop;
 
 
@@ -354,14 +354,13 @@
     @Override
     public void onPause() {
         cancelDisplayUpdate();
-        mAdapter.onPause();
         super.onPause();
     }
 
     @Override
     public void onStop() {
         updateOnTransition();
-
+        mAdapter.onStop();
         super.onStop();
     }
 
@@ -414,7 +413,11 @@
                 messageId = R.string.call_log_voicemail_empty;
                 break;
             case CallLogQueryHandler.CALL_TYPE_ALL:
-                messageId = R.string.call_log_all_empty;
+                if (mIsCallLogActivity) {
+                    messageId = R.string.no_call_log;
+                } else {
+                    messageId = R.string.recentCalls_empty;
+                }
                 break;
             default:
                 throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: "
@@ -424,6 +427,7 @@
         if (mIsCallLogActivity) {
             mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
         } else if (filterType == CallLogQueryHandler.CALL_TYPE_ALL) {
+            mEmptyListView.setImage(R.drawable.empty_call_log);
             mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
         }
     }
diff --git a/src/com/android/dialer/calllog/CallLogGroupBuilder.java b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
index aa45029..ab9d535 100644
--- a/src/com/android/dialer/calllog/CallLogGroupBuilder.java
+++ b/src/com/android/dialer/calllog/CallLogGroupBuilder.java
@@ -27,6 +27,9 @@
 import com.android.contacts.common.util.DateUtils;
 import com.android.contacts.common.util.PhoneNumberHelper;
 import com.android.dialer.util.AppCompatConstants;
+import com.android.dialer.util.DialerUtils;
+
+import java.util.regex.Pattern;
 
 /**
  * Groups together calls in the call log.  The primary grouping attempts to group together calls
@@ -130,6 +133,8 @@
         int groupCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
         String groupAccountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
         String groupAccountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+        boolean isGroupConfCallLog = DialerUtils.isConferenceURICallLog(groupNumber,
+                groupPostDialDigits);
         int groupSize = 1;
 
         String number;
@@ -138,6 +143,7 @@
         int callType;
         String accountComponentName;
         String accountId;
+        boolean isNumberConfCallLog = false;
 
         while (cursor.moveToNext()) {
             // Obtain the values for the current call to group.
@@ -149,8 +155,10 @@
             callType = cursor.getInt(CallLogQuery.CALL_TYPE);
             accountComponentName = cursor.getString(CallLogQuery.ACCOUNT_COMPONENT_NAME);
             accountId = cursor.getString(CallLogQuery.ACCOUNT_ID);
+            isNumberConfCallLog = DialerUtils.isConferenceURICallLog(number, numberPostDialDigits);
 
-            final boolean isSameNumber = equalNumbers(groupNumber, number);
+            final boolean isSameNumber = equalNumbers(groupNumber, isGroupConfCallLog,
+                    number, isNumberConfCallLog);
             final boolean isSamePostDialDigits = groupPostDialDigits.equals(numberPostDialDigits);
             final boolean isSameViaNumbers = groupViaNumbers.equals(numberViaNumbers);
             final boolean isSameAccount = isSameAccount(
@@ -184,6 +192,8 @@
                 groupCallType = callType;
                 groupAccountComponentName = accountComponentName;
                 groupAccountId = accountId;
+                isGroupConfCallLog = DialerUtils.isConferenceURICallLog(groupNumber,
+                        groupPostDialDigits);
             }
 
             // Save the day group associated with the current call.
@@ -224,8 +234,27 @@
 
     @VisibleForTesting
     boolean equalNumbers(String number1, String number2) {
+        return equalNumbers(number1, false, number2, false);
+    }
+
+    boolean equalNumbers(String number1, boolean isConf1, String number2, boolean isConf2) {
         if (PhoneNumberHelper.isUriNumber(number1) || PhoneNumberHelper.isUriNumber(number2)) {
             return compareSipAddresses(number1, number2);
+        } else if (isConf1 && isConf2) {
+            Pattern pattern = Pattern.compile("[,;]");
+            String[] num1 = pattern.split(number1);
+            String[] num2 = pattern.split(number2);
+            if (num1 == null || num2 == null || num1.length != num2.length) {
+                return false;
+            }
+            for (int i = 0; i < num1.length; i++) {
+                if (!PhoneNumberUtils.compare(num1[i], num2[i])) {
+                    return false;
+                }
+            }
+            return true;
+        } else if (isConf1 != isConf2) {
+            return false;
         } else {
             return PhoneNumberUtils.compare(number1, number2);
         }
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 07e2bb4..0aa9fdc 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -63,8 +63,8 @@
      */
     public void setPhoneCallDetails(
             CallLogListItemViewHolder views,
-            PhoneCallDetails details) {
-        mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details);
+            PhoneCallDetails details, String filter) {
+        mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details, filter);
 
         // Set the accessibility text for the contact badge
         views.quickContactView.setContentDescription(getContactBadgeDescription(details));
diff --git a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
index baf2e1a..d63531b 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViewHolder.java
@@ -20,6 +20,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
@@ -60,6 +62,7 @@
 import com.android.dialer.service.ExtendedBlockingButtonRenderer;
 import com.android.dialer.util.DialerUtils;
 import com.android.dialer.util.PhoneNumberUtil;
+import com.android.dialer.util.PresenceHelper;
 import com.android.dialer.voicemail.VoicemailPlaybackLayout;
 import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
 import com.android.dialerbind.ObjectFactory;
@@ -67,6 +70,9 @@
 
 import java.util.List;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+import org.codeaurora.presenceserv.IPresenceService;
+
 /**
  * This is an object containing references to views contained by the call log list item. This
  * improves performance by reducing the frequency with which we need to find views by IDs.
@@ -104,6 +110,7 @@
     public View detailsButtonView;
     public View callWithNoteButtonView;
     public ImageView workIconView;
+    public ImageView videoCallIconView;
 
     /**
      * The row Id for the first call associated with the call log entry.  Used as a key for the
@@ -418,6 +425,8 @@
             videoCallButtonView = actionsView.findViewById(R.id.video_call_action);
             videoCallButtonView.setOnClickListener(this);
 
+            videoCallIconView = (ImageView) actionsView.findViewById(R.id.videoCallIcon);
+
             createNewContactButtonView = actionsView.findViewById(R.id.create_new_contact_action);
             createNewContactButtonView.setOnClickListener(this);
 
@@ -504,12 +513,22 @@
         } else {
             callButtonView.setVisibility(View.GONE);
         }
-
-        // If one of the calls had video capabilities, show the video call button.
-        if (mCallLogCache.isVideoEnabled() && canPlaceCallToNumber &&
-                phoneCallDetailsViews.callTypeIcons.isVideoShown()) {
+        boolean isPresenceEnabled = mContext.getResources().getBoolean(
+                R.bool.config_regional_presence_enable);
+        boolean showVideoCallBtn = isPresenceEnabled ? PresenceHelper.startAvailabilityFetch(number)
+                : phoneCallDetailsViews.callTypeIcons.isVideoShown();
+        //If presence is enabled,only both of sides had video capabilities,
+        //show the video call button,If not, one of the calls had video capabilities,
+        //the video call button will be shown.
+        if (mCallLogCache.isVideoEnabled() && canPlaceCallToNumber && showVideoCallBtn) {
             videoCallButtonView.setTag(IntentProvider.getReturnVideoCallIntentProvider(number));
             videoCallButtonView.setVisibility(View.VISIBLE);
+            if (QtiImsExtUtils.isCarrierOneSupported()) {
+                Drawable drawable =  mContext.getResources().getDrawable(R.drawable.volte_video);
+                drawable.setColorFilter(mContext.getResources().getColor(
+                    R.color.dialtacts_secondary_text_color), PorterDuff.Mode.MULTIPLY);
+                videoCallIconView.setImageDrawable(drawable);
+            }
         } else {
             videoCallButtonView.setVisibility(View.GONE);
         }
@@ -691,6 +710,9 @@
             if (intentProvider != null) {
                 final Intent intent = intentProvider.getIntent(mContext);
                 // See IntentProvider.getCallDetailIntentProvider() for why this may be null.
+                if (DialerUtils.isConferenceURICallLog(number, postDialDigits)) {
+                    intent.putExtra("org.codeaurora.extra.DIAL_CONFERENCE_URI", true);
+                }
                 if (intent != null) {
                     DialerUtils.startActivityWithErrorToast(mContext, intent);
                 }
@@ -729,4 +751,4 @@
         viewHolder.workIconView = new ImageButton(context);
         return viewHolder;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index cf86bad..116d9ef 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -50,6 +50,8 @@
 
 /** Handles asynchronous queries to the call log. */
 public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
     private static final String TAG = "CallLogQueryHandler";
     private static final int NUM_LOGS_TO_DISPLAY = 1000;
 
@@ -76,6 +78,11 @@
      */
     public static final int CALL_TYPE_ALL = -1;
 
+    /**
+     * To specify all slots.
+     */
+    public static final int CALL_SUB_ALL = -1;
+
     private final WeakReference<Listener> mListener;
 
     private final Context mContext;
@@ -155,10 +162,33 @@
         }
     }
 
+    public void fetchCalls(int callType, long newerThan, int sub) {
+        cancelFetch();
+        if (PermissionsUtil.hasPhonePermissions(mContext)) {
+            fetchCalls(QUERY_CALLLOG_TOKEN, callType, false /* newOnly */, newerThan, sub);
+        } else {
+            updateAdapterData(null);
+        }
+    }
+
     public void fetchCalls(int callType) {
         fetchCalls(callType, 0);
     }
 
+    public void fetchCalls(String filter) {
+        cancelFetch();
+        fetchCalls(QUERY_CALLLOG_TOKEN, filter);
+    }
+
+    public void fetchCalls(int token, String filter) {
+        String selection = "(" + Calls.NUMBER + " like '%" + filter
+                + "%'  or  " + Calls.CACHED_NAME + " like '%" + filter + "%' )";
+
+        startQuery(token, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
+                CallLogQuery._PROJECTION, selection, null,
+                Calls.DEFAULT_SORT_ORDER);
+    }
+
     public void fetchVoicemailStatus() {
         if (TelecomUtil.hasReadWriteVoicemailPermissions(mContext)) {
             startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null, Status.CONTENT_URI,
@@ -195,13 +225,35 @@
         }
 
         if (callType > CALL_TYPE_ALL) {
-            where.append(" AND (").append(Calls.TYPE).append(" = ?)");
+            if (where.length() > 0) {
+                where.append(" AND ");
+            }
+
+            if ((callType == Calls.INCOMING_TYPE) || (callType == Calls.OUTGOING_TYPE)
+                    || (callType == Calls.MISSED_TYPE)) {
+                where.append(String.format("(%s = ? OR %s = ? OR %s = ?)",
+                        Calls.TYPE, Calls.TYPE, Calls.TYPE));
+            } else {
+                where.append(String.format("(%s = ?)", Calls.TYPE));
+            }
             selectionArgs.add(Integer.toString(callType));
+            if (callType == Calls.INCOMING_TYPE) {
+                selectionArgs.add(Integer.toString(AppCompatConstants.INCOMING_IMS_TYPE));
+                selectionArgs.add(Integer.toString(AppCompatConstants.INCOMING_WIFI_TYPE));
+            } else if (callType == Calls.OUTGOING_TYPE) {
+                selectionArgs.add(Integer.toString(AppCompatConstants.OUTGOING_IMS_TYPE));
+                selectionArgs.add(Integer.toString(AppCompatConstants.OUTGOING_WIFI_TYPE));
+            } else if (callType == Calls.MISSED_TYPE) {
+                selectionArgs.add(Integer.toString(AppCompatConstants.MISSED_IMS_TYPE));
+                selectionArgs.add(Integer.toString(AppCompatConstants.MISSED_WIFI_TYPE));
+            }
         } else {
+            // Add a clause to fetch only items of type voicemail.
             where.append(" AND NOT ");
             where.append("(" + Calls.TYPE + " = " + AppCompatConstants.CALLS_VOICEMAIL_TYPE + ")");
         }
 
+        // Add a clause to fetch only items newer than the requested date
         if (newerThan > 0) {
             where.append(" AND (").append(Calls.DATE).append(" > ?)");
             selectionArgs.add(Long.toString(newerThan));
@@ -216,6 +268,74 @@
                 new String[selectionArgs.size()]), Calls.DEFAULT_SORT_ORDER);
     }
 
+    private void fetchCalls(int token, int callType, boolean newOnly,
+            long newerThan, int sub) {
+        // We need to check for NULL explicitly otherwise entries with where READ is NULL
+        // may not match either the query or its negation.
+        // We consider the calls that are not yet consumed (i.e. IS_READ = 0) as "new".
+        StringBuilder where = new StringBuilder();
+        List<String> selectionArgs = Lists.newArrayList();
+
+        // Ignore voicemails marked as deleted
+        where.append(Voicemails.DELETED);
+        where.append(" = 0");
+
+        if (newOnly) {
+            where.append(" AND ");
+            where.append(Calls.NEW);
+            where.append(" = 1");
+        }
+
+        if (callType > CALL_TYPE_ALL) {
+            where.append(" AND ");
+            if ((callType == Calls.INCOMING_TYPE) || (callType == Calls.OUTGOING_TYPE)
+                    || (callType == Calls.MISSED_TYPE)) {
+                where.append(String.format("(%s = ? OR %s = ? OR %s = ?)",
+                        Calls.TYPE, Calls.TYPE, Calls.TYPE));
+            } else {
+                // Add a clause to fetch only items of type voicemail.
+                where.append(String.format("(%s = ?)", Calls.TYPE));
+            }
+            // Add a clause to fetch only items newer than the requested date
+            selectionArgs.add(Integer.toString(callType));
+            if (callType == Calls.INCOMING_TYPE) {
+                selectionArgs.add(Integer.toString(AppCompatConstants.INCOMING_IMS_TYPE));
+                selectionArgs.add(Integer.toString(AppCompatConstants.INCOMING_WIFI_TYPE));
+            } else if (callType == Calls.OUTGOING_TYPE) {
+                selectionArgs.add(Integer.toString(AppCompatConstants.OUTGOING_IMS_TYPE));
+                selectionArgs.add(Integer.toString(AppCompatConstants.OUTGOING_WIFI_TYPE));
+            } else if (callType == Calls.MISSED_TYPE) {
+                selectionArgs.add(Integer.toString(AppCompatConstants.MISSED_IMS_TYPE));
+                selectionArgs.add(Integer.toString(AppCompatConstants.MISSED_WIFI_TYPE));
+            }
+        } else {
+            where.append(" AND NOT ");
+            where.append("(" + Calls.TYPE + " = " + Calls.VOICEMAIL_TYPE + ")");
+        }
+
+        if (sub > CALL_SUB_ALL) {
+            where.append(" AND ");
+            where.append(String.format("(%s = ?)", Calls.PHONE_ACCOUNT_ID));
+            selectionArgs.add(Integer.toString(sub));
+        }
+
+        if (newerThan > 0) {
+            where.append(" AND ");
+            where.append(String.format("(%s > ?)", Calls.DATE));
+            selectionArgs.add(Long.toString(newerThan));
+        }
+
+        final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
+        final String selection = where.length() > 0 ? where.toString() : null;
+        Uri uri = TelecomUtil.getCallLogUri(mContext).buildUpon()
+                .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit))
+                .build();
+        startQuery(token, null, uri,
+                CallLogQuery._PROJECTION, selection, selectionArgs.toArray(EMPTY_STRING_ARRAY),
+                Calls.DEFAULT_SORT_ORDER);
+    }
+
+
     /** Cancel any pending fetch request. */
     private void cancelFetch() {
         cancelOperation(QUERY_CALLLOG_TOKEN);
diff --git a/src/com/android/dialer/calllog/CallLogSearchFragment.java b/src/com/android/dialer/calllog/CallLogSearchFragment.java
new file mode 100644
index 0000000..4c8db97
--- /dev/null
+++ b/src/com/android/dialer/calllog/CallLogSearchFragment.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016, 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.dialer.calllog;
+
+import android.app.ListFragment;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.CallLog.Calls;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.dialer.R;
+import com.android.dialerbind.ObjectFactory;
+
+
+public class CallLogSearchFragment extends CallLogFragment {
+
+    private String mQueryString;
+
+    private void updateCallList(int filterType) {
+        mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+    }
+
+    public void fetchCalls() {
+        if (TextUtils.isEmpty(mQueryString)) {
+            mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+        } else {
+            mCallLogQueryHandler.fetchCalls(mQueryString);
+        }
+    }
+
+    public void startCallsQuery() {
+        mAdapter.setLoading(true);
+        if (TextUtils.isEmpty(mQueryString)) {
+            mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+        } else {
+            mCallLogQueryHandler.fetchCalls(mQueryString);
+        }
+    }
+
+    public void setQueryString(String queryString) {
+        if (!TextUtils.equals(mQueryString, queryString)) {
+            mQueryString = queryString;
+            if (mAdapter != null) {
+                mAdapter.setLoading(true);
+                mAdapter.setQueryString(mQueryString);
+                if (TextUtils.isEmpty(queryString)) {
+                    mCallLogQueryHandler
+                            .fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+                } else {
+                    mCallLogQueryHandler.fetchCalls(queryString);
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/com/android/dialer/calllog/CallTypeHelper.java b/src/com/android/dialer/calllog/CallTypeHelper.java
index acc114c..045b604 100644
--- a/src/com/android/dialer/calllog/CallTypeHelper.java
+++ b/src/com/android/dialer/calllog/CallTypeHelper.java
@@ -37,6 +37,30 @@
     private final CharSequence mOutgoingVideoName;
     /** Name used to identify missed video calls. */
     private final CharSequence mMissedVideoName;
+    /** Name used to identify incoming video calls. */
+    private final CharSequence mIncomingVoLTEName;
+    /** Name used to identify outgoing video calls. */
+    private final CharSequence mOutgoingVoLTEName;
+    /** Name used to identify missed video calls. */
+    private final CharSequence mMissedVoLTEName;
+    /** Name used to identify incoming videoLTE calls. */
+    private final CharSequence mIncomingVideoLTEName;
+    /** Name used to identify outgoing videoLTE calls. */
+    private final CharSequence mOutgoingVideoLTEName;
+    /** Name used to identify missed videoLTE calls. */
+    private final CharSequence mMissedVideoLTEName;
+    /** Name used to identify incoming VoWifi calls. */
+    private final CharSequence mIncomingVoWifiName;
+    /** Name used to identify outgoing VoWifi calls. */
+    private final CharSequence mOutgoingVoWifiName;
+    /** Name used to identify missed VoWifi calls. */
+    private final CharSequence mMissedVoWifiName;
+    /** Name used to identify incoming video wifi calls. */
+    private final CharSequence mIncomingVideoWifiName;
+    /** Name used to identify outgoing video wifi calls. */
+    private final CharSequence mOutgoingVideoWifiName;
+    /** Name used to identify missed video wifi calls. */
+    private final CharSequence mMissedVideoWifiName;
     /** Name used to identify voicemail calls. */
     private final CharSequence mVoicemailName;
     /** Name used to identify rejected calls. */
@@ -56,6 +80,18 @@
         mIncomingVideoName = resources.getString(R.string.type_incoming_video);
         mOutgoingVideoName = resources.getString(R.string.type_outgoing_video);
         mMissedVideoName = resources.getString(R.string.type_missed_video);
+        mIncomingVoLTEName = resources.getString(R.string.type_incoming_volte);
+        mOutgoingVoLTEName = resources.getString(R.string.type_outgoing_volte);
+        mMissedVoLTEName = resources.getString(R.string.type_missed_volte);
+        mIncomingVideoLTEName = resources.getString(R.string.type_incoming_video_lte);
+        mOutgoingVideoLTEName = resources.getString(R.string.type_outgoing_video_lte);
+        mMissedVideoLTEName = resources.getString(R.string.type_missed_video_lte);
+        mIncomingVoWifiName = resources.getString(R.string.type_incoming_vowifi);
+        mOutgoingVoWifiName = resources.getString(R.string.type_outgoing_vowifi);
+        mMissedVoWifiName = resources.getString(R.string.type_missed_vowifi);
+        mIncomingVideoWifiName = resources.getString(R.string.type_incoming_video_wifi);
+        mOutgoingVideoWifiName = resources.getString(R.string.type_outgoing_video_wifi);
+        mMissedVideoWifiName = resources.getString(R.string.type_missed_video_wifi);
         mVoicemailName = resources.getString(R.string.type_voicemail);
         mRejectedName = resources.getString(R.string.type_rejected);
         mBlockedName = resources.getString(R.string.type_blocked);
@@ -73,6 +109,20 @@
                     return mIncomingName;
                 }
 
+            case AppCompatConstants.INCOMING_IMS_TYPE:
+                if (isVideoCall) {
+                    return mIncomingVideoLTEName;
+                } else {
+                    return mIncomingVoLTEName;
+                }
+
+            case AppCompatConstants.INCOMING_WIFI_TYPE:
+                if (isVideoCall) {
+                    return mIncomingVideoWifiName;
+                } else {
+                    return mIncomingVoWifiName;
+                }
+
             case AppCompatConstants.CALLS_OUTGOING_TYPE:
                 if (isVideoCall) {
                     return mOutgoingVideoName;
@@ -80,6 +130,20 @@
                     return mOutgoingName;
                 }
 
+            case AppCompatConstants.OUTGOING_IMS_TYPE:
+                if (isVideoCall) {
+                    return mOutgoingVideoLTEName;
+                } else {
+                    return mOutgoingVoLTEName;
+                }
+
+            case AppCompatConstants.OUTGOING_WIFI_TYPE:
+                if (isVideoCall) {
+                    return mOutgoingVideoWifiName;
+                } else {
+                    return mOutgoingVoWifiName;
+                }
+
             case AppCompatConstants.CALLS_MISSED_TYPE:
                 if (isVideoCall) {
                     return mMissedVideoName;
@@ -87,6 +151,20 @@
                     return mMissedName;
                 }
 
+            case AppCompatConstants.MISSED_IMS_TYPE:
+                if (isVideoCall) {
+                    return mMissedVideoLTEName;
+                } else {
+                    return mMissedVoLTEName;
+                }
+
+            case AppCompatConstants.MISSED_WIFI_TYPE:
+                if (isVideoCall) {
+                    return mMissedVideoWifiName;
+                } else {
+                    return mMissedVoWifiName;
+                }
+
             case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
                 return mVoicemailName;
 
@@ -105,14 +183,20 @@
     public Integer getHighlightedColor(int callType) {
         switch (callType) {
             case AppCompatConstants.CALLS_INCOMING_TYPE:
+            case AppCompatConstants.INCOMING_IMS_TYPE:
+            case AppCompatConstants.INCOMING_WIFI_TYPE:
                 // New incoming calls are not highlighted.
                 return null;
 
             case AppCompatConstants.CALLS_OUTGOING_TYPE:
+            case AppCompatConstants.OUTGOING_IMS_TYPE:
+            case AppCompatConstants.OUTGOING_WIFI_TYPE:
                 // New outgoing calls are not highlighted.
                 return null;
 
             case AppCompatConstants.CALLS_MISSED_TYPE:
+            case AppCompatConstants.MISSED_IMS_TYPE:
+            case AppCompatConstants.MISSED_WIFI_TYPE:
                 return mNewMissedColor;
 
             case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
@@ -129,6 +213,10 @@
     public static boolean isMissedCallType(int callType) {
         return (callType != AppCompatConstants.CALLS_INCOMING_TYPE
                 && callType != AppCompatConstants.CALLS_OUTGOING_TYPE
+                && callType != AppCompatConstants.INCOMING_IMS_TYPE
+                && callType != AppCompatConstants.OUTGOING_IMS_TYPE
+                && callType != AppCompatConstants.INCOMING_WIFI_TYPE
+                && callType != AppCompatConstants.OUTGOING_WIFI_TYPE
                 && callType != AppCompatConstants.CALLS_VOICEMAIL_TYPE);
     }
 }
diff --git a/src/com/android/dialer/calllog/CallTypeIconsView.java b/src/com/android/dialer/calllog/CallTypeIconsView.java
index 1474843..56bd605 100644
--- a/src/com/android/dialer/calllog/CallTypeIconsView.java
+++ b/src/com/android/dialer/calllog/CallTypeIconsView.java
@@ -34,6 +34,8 @@
 
 import java.util.List;
 
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 /**
  * View that draws one or more symbols for different types of calls (missed calls, outgoing etc).
  * The symbols are set up horizontally. As this view doesn't create subviews, it is better suited
@@ -47,12 +49,15 @@
 
     private static Resources sResources;
 
+    private static boolean mIsCarrierOneSupported = false;
+
     public CallTypeIconsView(Context context) {
         this(context, null);
     }
 
     public CallTypeIconsView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mIsCarrierOneSupported = QtiImsExtUtils.isCarrierOneSupported();
         if (sResources == null) {
           sResources = new Resources(context);
         }
@@ -74,6 +79,40 @@
         invalidate();
     }
 
+    public void addImsOrVideoIcon(int callType, boolean showVideo) {
+        mShowVideo = showVideo;
+        if (showVideo) {
+            mWidth += sResources.videoCall.getIntrinsicWidth();
+            mHeight = Math.max(mHeight, sResources.videoCall.getIntrinsicHeight());
+            invalidate();
+        } else {
+            final Drawable drawable = getImsOrWifiDrawable(callType);
+            if (drawable != null) {
+                // calculating drawable's width and adding it to total width for correct position
+                // of icon.
+                // calculating height by max of drawable height and other icons' height.
+                mWidth += drawable.getIntrinsicWidth();
+                mHeight = Math.max(mHeight, drawable.getIntrinsicHeight());
+                invalidate();
+            }
+        }
+    }
+
+    private Drawable getImsOrWifiDrawable(int callType) {
+       switch(callType) {
+         case AppCompatConstants.INCOMING_IMS_TYPE:
+         case AppCompatConstants.OUTGOING_IMS_TYPE:
+         case AppCompatConstants.MISSED_IMS_TYPE:
+              return sResources.imsCall;
+         case AppCompatConstants.INCOMING_WIFI_TYPE:
+         case AppCompatConstants.OUTGOING_WIFI_TYPE:
+         case AppCompatConstants.MISSED_WIFI_TYPE:
+              return sResources.wifiCall;
+         default:
+              return null;
+       }
+    }
+
     /**
      * Determines whether the video call icon will be shown.
      *
@@ -81,6 +120,12 @@
      */
     public void setShowVideo(boolean showVideo) {
         mShowVideo = showVideo;
+        if (mIsCarrierOneSupported) {
+            //  Don't show video icon in call log item. For CarrierOne, show more precise icon
+            //  based on call type in call detail history.
+            return;
+        }
+
         if (showVideo) {
             mWidth += sResources.videoCall.getIntrinsicWidth();
             mHeight = Math.max(mHeight, sResources.videoCall.getIntrinsicHeight());
@@ -110,10 +155,16 @@
     private Drawable getCallTypeDrawable(int callType) {
         switch (callType) {
             case AppCompatConstants.CALLS_INCOMING_TYPE:
+            case AppCompatConstants.INCOMING_IMS_TYPE:
+            case AppCompatConstants.INCOMING_WIFI_TYPE:
                 return sResources.incoming;
             case AppCompatConstants.CALLS_OUTGOING_TYPE:
+            case AppCompatConstants.OUTGOING_IMS_TYPE:
+            case AppCompatConstants.OUTGOING_WIFI_TYPE:
                 return sResources.outgoing;
             case AppCompatConstants.CALLS_MISSED_TYPE:
+            case AppCompatConstants.MISSED_IMS_TYPE:
+            case AppCompatConstants.MISSED_WIFI_TYPE:
                 return sResources.missed;
             case AppCompatConstants.CALLS_VOICEMAIL_TYPE:
                 return sResources.voicemail;
@@ -147,9 +198,19 @@
         // If showing the video call icon, draw it scaled appropriately.
         if (mShowVideo) {
             final Drawable drawable = sResources.videoCall;
-            final int right = left + sResources.videoCall.getIntrinsicWidth();
-            drawable.setBounds(left, 0, right, sResources.videoCall.getIntrinsicHeight());
+            final int right = left + drawable.getIntrinsicWidth();
+            drawable.setBounds(left, 0, right, drawable.getIntrinsicHeight());
             drawable.draw(canvas);
+            left = right + sResources.iconMargin;
+        }
+
+        for (Integer callType : mCallTypes) {
+            final Drawable drawableIms = getImsOrWifiDrawable(callType);
+            if (drawableIms != null) {
+                final int right = left + drawableIms.getIntrinsicWidth();
+                drawableIms.setBounds(left, 0, right, drawableIms.getIntrinsicHeight());
+                drawableIms.draw(canvas);
+            }
         }
     }
 
@@ -179,6 +240,15 @@
         public final int iconMargin;
 
         /**
+         * Drawable repesenting a wifi call.
+         */
+        public final Drawable wifiCall;
+
+        /**
+         * Drawable repesenting a IMS call.
+         */
+        public final Drawable imsCall;
+        /**
          * Configures the call icon drawables.
          * A single white call arrow which points down and left is used as a basis for all of the
          * call arrow icons, applying rotation and colors as needed.
@@ -205,11 +275,26 @@
             blocked = getScaledBitmap(context, R.drawable.ic_block_24dp);
             blocked.setColorFilter(r.getColor(R.color.blocked_call), PorterDuff.Mode.MULTIPLY);
 
-            videoCall = getScaledBitmap(context, R.drawable.ic_videocam_24dp);
+            if (mIsCarrierOneSupported) {
+                videoCall = r.getDrawable(R.drawable.volte_video).mutate();
+            } else {
+            // Get the video call icon, scaled to match the height of the call arrows.
+            // We want the video call icon to be the same height as the call arrows, while keeping
+            // the same width aspect ratio.
+                videoCall = getScaledBitmap(context, R.drawable.ic_videocam_24dp);
+            }
             videoCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
                     PorterDuff.Mode.MULTIPLY);
 
             iconMargin = r.getDimensionPixelSize(R.dimen.call_log_icon_margin);
+
+            wifiCall = r.getDrawable(R.drawable.wifi_calling).mutate();
+            wifiCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
+                    PorterDuff.Mode.MULTIPLY);
+
+            imsCall = r.getDrawable(R.drawable.volte_voice).mutate();
+            imsCall.setColorFilter(r.getColor(R.color.dialtacts_secondary_text_color),
+                    PorterDuff.Mode.MULTIPLY);
         }
 
         // Gets the icon, scaled to the height of the call type icons. This helps display all the
diff --git a/src/com/android/dialer/calllog/ClearCallLogDialog.java b/src/com/android/dialer/calllog/ClearCallLogDialog.java
index bef5010..457f1df 100644
--- a/src/com/android/dialer/calllog/ClearCallLogDialog.java
+++ b/src/com/android/dialer/calllog/ClearCallLogDialog.java
@@ -88,10 +88,9 @@
         };
         return new AlertDialog.Builder(getActivity())
             .setTitle(R.string.clearCallLogConfirmation_title)
-            .setIconAttribute(android.R.attr.alertDialogIcon)
             .setMessage(R.string.clearCallLogConfirmation)
             .setNegativeButton(android.R.string.cancel, null)
-            .setPositiveButton(android.R.string.ok, okListener)
+            .setPositiveButton(R.string.clear, okListener)
             .setCancelable(true)
             .create();
     }
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index b0ef0ab..ea39d03 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -43,6 +43,8 @@
 import com.android.dialer.util.TelecomUtil;
 import com.android.dialerbind.ObjectFactory;
 
+import java.util.regex.Pattern;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -76,6 +78,26 @@
      */
     @Nullable
     public ContactInfo lookupNumber(String number, String countryIso) {
+        return lookupNumber(number, null, countryIso, false);
+    }
+
+    /**
+     * Returns the contact information for the given number.
+     * <p>
+     * If the number does not match any contact, returns a contact info containing only the number
+     * and the formatted number.
+     * <p>
+     * If an error occurs during the lookup, it returns null.
+     *
+     * @param number the number to look up
+     * @param postDialString append into number if required
+     * @param countryIso the country associated with this number
+     * @param isConfUrlLog whether call log is for Conference URL call
+     */
+    @Nullable
+    public ContactInfo lookupNumber(String number, String postDialString, String countryIso,
+            boolean isConfUrlCallLog) {
+
         if (TextUtils.isEmpty(number)) {
             return null;
         }
@@ -89,12 +111,14 @@
                 // If lookup failed, check if the "username" of the SIP address is a phone number.
                 String username = PhoneNumberHelper.getUsernameFromUriNumber(number);
                 if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
-                    info = queryContactInfoForPhoneNumber(username, countryIso, true);
+                    info = queryContactInfoForPhoneNumber(username, null, countryIso, true,
+                            isConfUrlCallLog);
                 }
             }
         } else {
             // Look for a contact that has the given phone number.
-            info = queryContactInfoForPhoneNumber(number, countryIso, false);
+            info = queryContactInfoForPhoneNumber(number, postDialString, countryIso,
+                    false, isConfUrlCallLog);
         }
 
         final ContactInfo updatedInfo;
@@ -106,6 +130,9 @@
             if (info == ContactInfo.EMPTY) {
                 // Did not find a matching contact.
                 updatedInfo = new ContactInfo();
+                if (!isConfUrlCallLog && !TextUtils.isEmpty(postDialString)) {
+                    number += postDialString;
+                }
                 updatedInfo.number = number;
                 updatedInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
                 updatedInfo.normalizedNumber = PhoneNumberUtils.formatNumberToE164(
@@ -244,14 +271,47 @@
      * <p>
      * If the lookup fails for some other reason, it returns null.
      */
-    private ContactInfo queryContactInfoForPhoneNumber(String number, String countryIso,
-                                                       boolean isSip) {
+    private ContactInfo queryContactInfoForPhoneNumber(String number, String postDialString,
+            String countryIso, boolean isSip, boolean isConfUrlLog) {
         if (TextUtils.isEmpty(number)) {
             return null;
         }
 
         ContactInfo info = lookupContactFromUri(getContactInfoLookupUri(number), isSip);
+        if (isConfUrlLog) {
+            Pattern pattern = Pattern.compile("[,;]");
+            String[] nums = pattern.split(number);
+            if (nums != null && nums.length > 1) {
+                if (info == null || info == ContactInfo.EMPTY) {
+                    info = new ContactInfo();
+                    info.number = number;
+                    info.formattedNumber = formatPhoneNumber(number, null, countryIso);
+                    info.lookupUri = createTemporaryContactUri(info.formattedNumber);
+                    info.normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
+                            countryIso);
+                }
+                String combName = "";
+                for (String num : nums) {
+                    ContactInfo singleCi = lookupContactFromUri(getContactInfoLookupUri(num),
+                            isSip);
+                    // If contact does not exist, need to avoid changing static empty-contact.
+                    if (singleCi == ContactInfo.EMPTY) {
+                        singleCi = new ContactInfo();
+                    }
+                    if (TextUtils.isEmpty(singleCi.name)) {
+                        singleCi.name = formatPhoneNumber(num, null, countryIso);
+                    }
+                    combName += singleCi.name + ";";
+                }
+                if (!TextUtils.isEmpty(combName) && combName.length() > 1) {
+                    info.name = combName.substring(0, combName.length() - 1);
+                }
+            }
+        }
         if (info != null && info != ContactInfo.EMPTY) {
+            if (!isConfUrlLog && TextUtils.isEmpty(postDialString)) {
+                number += postDialString;
+            }
             info.formattedNumber = formatPhoneNumber(number, null, countryIso);
         } else if (mCachedNumberLookupService != null) {
             CachedContactInfo cacheInfo =
diff --git a/src/com/android/dialer/calllog/MSimCallLogFragment.java b/src/com/android/dialer/calllog/MSimCallLogFragment.java
new file mode 100644
index 0000000..1eefe53
--- /dev/null
+++ b/src/com/android/dialer/calllog/MSimCallLogFragment.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved
+ * Not a Contribution.
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.calllog;
+
+import static android.Manifest.permission.READ_CALL_LOG;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.KeyguardManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.provider.VoicemailContract.Status;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.util.PermissionsUtil;
+import com.android.contacts.common.util.ViewUtil;
+import com.android.dialer.R;
+import com.android.dialer.list.ListsFragment.HostInterface;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.EmptyLoader;
+import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
+import com.android.dialer.voicemail.VoicemailStatusHelper;
+import com.android.dialer.voicemail.VoicemailStatusHelper.StatusMessage;
+import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
+import com.android.dialer.widget.EmptyContentView;
+import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener;
+import com.android.dialerbind.ObjectFactory;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Spinner;
+import android.util.Log;
+import android.preference.PreferenceManager;
+import android.telephony.SubscriptionManager;
+
+import java.util.List;
+
+/**
+ * Displays a list of call log entries. To filter for a particular kind of call
+ * (all, missed or voicemails), specify it in the constructor.
+ */
+public class MSimCallLogFragment extends Fragment implements CallLogQueryHandler.Listener,
+        CallLogAdapter.CallFetcher, OnEmptyViewActionButtonClickedListener {
+    private static final String TAG = "CallLogFragment";
+
+    /**
+     * ID of the empty loader to defer other fragments.
+     */
+    private static final int EMPTY_LOADER_ID = 0;
+
+    private static final String KEY_FILTER_TYPE = "filter_type";
+    private static final String KEY_LOG_LIMIT = "log_limit";
+    private static final String KEY_DATE_LIMIT = "date_limit";
+
+    // No limit specified for the number of logs to show; use the CallLogQueryHandler's default.
+    private static final int NO_LOG_LIMIT = -1;
+    // No date-based filtering.
+    private static final int NO_DATE_LIMIT = 0;
+
+    private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
+
+    private RecyclerView mRecyclerView;
+    private LinearLayoutManager mLayoutManager;
+    private CallLogAdapter mAdapter;
+    protected CallLogQueryHandler mCallLogQueryHandler;
+    private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+    private boolean mScrollToTop;
+
+    /** Whether there is at least one voicemail source installed. */
+    private boolean mVoicemailSourcesAvailable = false;
+
+    private EmptyContentView mEmptyListView;
+    private KeyguardManager mKeyguardManager;
+
+    private boolean mEmptyLoaderRunning;
+    private boolean mCallLogFetched;
+    private boolean mVoicemailStatusFetched;
+
+    private final Handler mHandler = new Handler();
+
+    // The Spinners to filter call log.
+    private Spinner mFilterSubSpinnerView;
+    private Spinner mFilterStatusSpinnerView;
+    // Default to all slots.
+    private int mCallSubFilter = CallLogQueryHandler.CALL_SUB_ALL;
+    /**
+     * Key for the call log sub saved in the default preference.
+     */
+    private static final String PREFERENCE_KEY_CALLLOG_SUB = "call_log_sub";
+
+    private class CustomContentObserver extends ContentObserver {
+        public CustomContentObserver() {
+            super(mHandler);
+        }
+        @Override
+        public void onChange(boolean selfChange) {
+            mRefreshDataRequired = true;
+        }
+    }
+
+    // See issue 6363009
+    private final ContentObserver mCallLogObserver = new CustomContentObserver();
+    private final ContentObserver mContactsObserver = new CustomContentObserver();
+    private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver();
+    private boolean mRefreshDataRequired = true;
+
+    private boolean mHasReadCallLogPermission = false;
+
+    // Exactly same variable is in Fragment as a package private.
+    private boolean mMenuVisible = true;
+
+    // Default to all calls.
+    protected int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
+
+    // Log limit - if no limit is specified, then the default in {@link CallLogQueryHandler}
+    // will be used.
+    private int mLogLimit = NO_LOG_LIMIT;
+
+    // Date limit (in millis since epoch) - when non-zero, only calls which occurred on or after
+    // the date filter are included.  If zero, no date-based filtering occurs.
+    private long mDateLimit = NO_DATE_LIMIT;
+
+    /*
+     * True if this instance of the CallLogFragment is the Recents screen shown in
+     * DialtactsActivity.
+     */
+    private boolean mIsRecentsFragment;
+
+    public interface HostInterface {
+        public void showDialpad();
+    }
+
+    public MSimCallLogFragment() {
+        this(CallLogQueryHandler.CALL_TYPE_ALL, NO_LOG_LIMIT);
+    }
+
+    public MSimCallLogFragment(int filterType) {
+        this(filterType, NO_LOG_LIMIT);
+    }
+
+    public MSimCallLogFragment(int filterType, int logLimit) {
+        this(filterType, logLimit, NO_DATE_LIMIT);
+    }
+
+    /**
+     * Creates a call log fragment, filtering to include only calls of the desired type, occurring
+     * after the specified date.
+     * @param filterType type of calls to include.
+     * @param dateLimit limits results to calls occurring on or after the specified date.
+     */
+    public MSimCallLogFragment(int filterType, long dateLimit) {
+        this(filterType, NO_LOG_LIMIT, dateLimit);
+    }
+
+    /**
+     * Creates a call log fragment, filtering to include only calls of the desired type, occurring
+     * after the specified date.  Also provides a means to limit the number of results returned.
+     * @param filterType type of calls to include.
+     * @param logLimit limits the number of results to return.
+     * @param dateLimit limits results to calls occurring on or after the specified date.
+     */
+    public MSimCallLogFragment(int filterType, int logLimit, long dateLimit) {
+        mCallTypeFilter = filterType;
+        mLogLimit = logLimit;
+        mDateLimit = dateLimit;
+    }
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        if (state != null) {
+            mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
+            mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit);
+            mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
+        }
+
+        mIsRecentsFragment = mLogLimit != NO_LOG_LIMIT;
+
+        final Activity activity = getActivity();
+        final ContentResolver resolver = activity.getContentResolver();
+        String currentCountryIso = GeoUtil.getCurrentCountryIso(activity);
+        mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);
+        mKeyguardManager =
+                (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
+        resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);
+        resolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true,
+                mContactsObserver);
+        resolver.registerContentObserver(Status.CONTENT_URI, true, mVoicemailStatusObserver);
+        setHasOptionsMenu(true);
+
+        if (mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
+            mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter
+                    .getInstance(activity, state);
+        }
+    }
+
+    /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
+    @Override
+    public boolean onCallsFetched(Cursor cursor) {
+        if (getActivity() == null || getActivity().isFinishing()) {
+            // Return false; we did not take ownership of the cursor
+            return false;
+        }
+
+        mAdapter.setLoading(false);
+        mAdapter.changeCursor(cursor);
+        // This will update the state of the "Clear call log" menu item.
+        getActivity().invalidateOptionsMenu();
+
+        boolean showListView = cursor != null && cursor.getCount() > 0;
+        mRecyclerView.setVisibility(showListView ? View.VISIBLE : View.GONE);
+        mEmptyListView.setVisibility(!showListView ? View.VISIBLE : View.GONE);
+
+        if (mScrollToTop) {
+            // The smooth-scroll animation happens over a fixed time period.
+            // As a result, if it scrolls through a large portion of the list,
+            // each frame will jump so far from the previous one that the user
+            // will not experience the illusion of downward motion.  Instead,
+            // if we're not already near the top of the list, we instantly jump
+            // near the top, and animate from there.
+            if (mLayoutManager.findFirstVisibleItemPosition() > 5) {
+                // TODO: Jump to near the top, then begin smooth scroll.
+                mRecyclerView.smoothScrollToPosition(0);
+            }
+            // Workaround for framework issue: the smooth-scroll doesn't
+            // occur if setSelection() is called immediately before.
+            mHandler.post(new Runnable() {
+               @Override
+               public void run() {
+                   if (getActivity() == null || getActivity().isFinishing()) {
+                       return;
+                   }
+                   mRecyclerView.smoothScrollToPosition(0);
+               }
+            });
+
+            mScrollToTop = false;
+        }
+        mCallLogFetched = true;
+        destroyEmptyLoaderIfAllDataFetched();
+        return true;
+    }
+
+    /**
+     * Called by {@link CallLogQueryHandler} after a successful query to voicemail status provider.
+     */
+    @Override
+    public void onVoicemailStatusFetched(Cursor statusCursor) {
+        Activity activity = getActivity();
+        if (activity == null || activity.isFinishing()) {
+            return;
+        }
+
+        mVoicemailStatusFetched = true;
+        destroyEmptyLoaderIfAllDataFetched();
+    }
+
+    @Override
+    public void onVoicemailUnreadCountFetched(Cursor cursor){
+        //to do somthing
+    }
+
+    @Override
+    public void onMissedCallsUnreadCountFetched(Cursor cursor){
+        //to do somthing
+    }
+
+    private void destroyEmptyLoaderIfAllDataFetched() {
+        if (mCallLogFetched && mVoicemailStatusFetched && mEmptyLoaderRunning) {
+            mEmptyLoaderRunning = false;
+            getLoaderManager().destroyLoader(EMPTY_LOADER_ID);
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+        View view = inflater.inflate(R.layout.msim_call_log_fragment, container, false);
+
+        mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
+        mRecyclerView.setHasFixedSize(true);
+        mLayoutManager = new LinearLayoutManager(getActivity());
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        mEmptyListView = (EmptyContentView) view.findViewById(R.id.empty_list_view);
+        mEmptyListView.setImage(R.drawable.empty_call_log);
+        mEmptyListView.setActionClickedListener(this);
+        mFilterSubSpinnerView = (Spinner) view.findViewById(R.id.filter_sub_spinner);
+        mFilterStatusSpinnerView = (Spinner) view.findViewById(R.id.filter_status_spinner);
+        // Update the filter views.
+        updateFilterSpinnerViews();
+
+        String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
+        mAdapter = ObjectFactory.newCallLogAdapter(
+                getActivity(),
+                this,
+                new ContactInfoHelper(getActivity(), currentCountryIso),
+                mVoicemailPlaybackPresenter,
+                CallLogAdapter.ACTIVITY_TYPE_CALL_LOG);
+        mRecyclerView.setAdapter(mAdapter);
+
+        fetchCalls();
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        updateEmptyMessage(mCallTypeFilter);
+        mAdapter.onRestoreInstanceState(savedInstanceState);
+    }
+
+    @Override
+    public void onStart() {
+        // Start the empty loader now to defer other fragments.  We destroy it when both calllog
+        // and the voicemail status are fetched.
+        getLoaderManager().initLoader(EMPTY_LOADER_ID, null,
+                new EmptyLoader.Callback(getActivity()));
+        mEmptyLoaderRunning = true;
+        super.onStart();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        final boolean hasReadCallLogPermission =
+                PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG);
+        if (!mHasReadCallLogPermission && hasReadCallLogPermission) {
+            // We didn't have the permission before, and now we do. Force a refresh of the call log.
+            // Note that this code path always happens on a fresh start, but mRefreshDataRequired
+            // is already true in that case anyway.
+            mRefreshDataRequired = true;
+            updateEmptyMessage(mCallTypeFilter);
+        }
+        mHasReadCallLogPermission = hasReadCallLogPermission;
+        refreshData();
+        mAdapter.startCache();
+    }
+
+    @Override
+    public void onPause() {
+        if (mVoicemailPlaybackPresenter != null) {
+            mVoicemailPlaybackPresenter.onPause();
+        }
+        mAdapter.pauseCache();
+        super.onPause();
+    }
+
+    @Override
+    public void onStop() {
+        updateOnTransition(false /* onEntry */);
+
+        super.onStop();
+    }
+
+    @Override
+    public void onDestroy() {
+        mAdapter.pauseCache();
+        mAdapter.changeCursor(null);
+
+        if (mVoicemailPlaybackPresenter != null) {
+            mVoicemailPlaybackPresenter.onDestroy();
+        }
+
+        getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
+        getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
+        getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
+        outState.putInt(KEY_LOG_LIMIT, mLogLimit);
+        outState.putLong(KEY_DATE_LIMIT, mDateLimit);
+
+        mAdapter.onSaveInstanceState(outState);
+
+        if (mVoicemailPlaybackPresenter != null) {
+            mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
+        }
+    }
+
+    @Override
+    public void fetchCalls() {
+        if (mFilterSubSpinnerView.isEnabled()) {
+            int[] subId = SubscriptionManager.getSubId(mCallSubFilter);
+            if (subId != null) {
+                Log.d(TAG, "fetchCalls subId = " + subId[0]);
+                mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit, subId[0]);
+            } else {
+                mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
+            }
+        } else {
+            mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
+        }
+        updateEmptyMessage(mCallTypeFilter);
+    }
+
+    private void updateEmptyMessage(int filterType) {
+        final Context context = getActivity();
+        if (context == null) {
+            return;
+        }
+
+        if (!PermissionsUtil.hasPermission(context, READ_CALL_LOG)) {
+            mEmptyListView.setDescription(R.string.permission_no_calllog);
+            mEmptyListView.setActionLabel(R.string.permission_single_turn_on);
+            return;
+        }
+
+        final int messageId;
+        switch (filterType) {
+            case Calls.INCOMING_TYPE:
+                messageId = R.string.recentIncoming_empty;
+                break;
+            case Calls.OUTGOING_TYPE:
+                messageId = R.string.recentOutgoing_empty;
+                break;
+            case Calls.MISSED_TYPE:
+                messageId = R.string.call_log_missed_empty;
+                break;
+            case Calls.VOICEMAIL_TYPE:
+                messageId = R.string.call_log_voicemail_empty;
+                break;
+            case CallLogQueryHandler.CALL_TYPE_ALL:
+                messageId = R.string.recentCalls_empty;
+                break;
+            default:
+                throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: "
+                        + filterType);
+        }
+        mEmptyListView.setDescription(messageId);
+        if (mIsRecentsFragment) {
+            mEmptyListView.setActionLabel(R.string.call_log_all_empty_action);
+        } else {
+            mEmptyListView.setActionLabel(EmptyContentView.NO_LABEL);
+        }
+    }
+
+    CallLogAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    @Override
+    public void setMenuVisibility(boolean menuVisible) {
+        super.setMenuVisibility(menuVisible);
+        if (mMenuVisible != menuVisible) {
+            mMenuVisible = menuVisible;
+            if (!menuVisible) {
+                updateOnTransition(false /* onEntry */);
+            } else if (isResumed()) {
+                refreshData();
+            }
+        }
+    }
+
+    /** Requests updates to the data to be shown. */
+    protected void refreshData() {
+        // Prevent unnecessary refresh.
+        if (mRefreshDataRequired) {
+            // Mark all entries in the contact info cache as out of date, so they will be looked up
+            // again once being shown.
+            mAdapter.invalidateCache();
+            mAdapter.setLoading(true);
+
+            fetchCalls();
+            mCallLogQueryHandler.fetchVoicemailStatus();
+
+            updateOnTransition(true /* onEntry */);
+            mRefreshDataRequired = false;
+        } else {
+            // Refresh the display of the existing data to update the timestamp text descriptions.
+            mAdapter.notifyDataSetChanged();
+        }
+    }
+
+    /**
+     * Updates the call data and notification state on entering or leaving the call log tab.
+     *
+     * If we are leaving the call log tab, mark all the missed calls as read.
+     *
+     * TODO: Move to CallLogActivity
+     */
+    private void updateOnTransition(boolean onEntry) {
+        // We don't want to update any call data when keyguard is on because the user has likely not
+        // seen the new calls yet.
+        // This might be called before onCreate() and thus we need to check null explicitly.
+        if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()) {
+            // On either of the transitions we update the missed call and voicemail notifications.
+            // While exiting we additionally consume all missed calls (by marking them as read).
+            mCallLogQueryHandler.markNewCallsAsOld();
+            if (!onEntry) {
+                mCallLogQueryHandler.markMissedCallsAsRead();
+            }
+            CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
+            CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
+        }
+    }
+
+    @Override
+    public void onEmptyViewActionButtonClicked() {
+        final Activity activity = getActivity();
+        if (activity == null) {
+            return;
+        }
+
+        if (!PermissionsUtil.hasPermission(activity, READ_CALL_LOG)) {
+            requestPermissions(new String[] {READ_CALL_LOG}, READ_CALL_LOG_PERMISSION_REQUEST_CODE);
+        } else if (mIsRecentsFragment) {
+            // Show dialpad if we are the recents fragment.
+            ((HostInterface) activity).showDialpad();
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        if (requestCode == READ_CALL_LOG_PERMISSION_REQUEST_CODE) {
+            if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) {
+                // Force a refresh of the data since we were missing the permission before this.
+                mRefreshDataRequired = true;
+            }
+        }
+    }
+
+    private OnItemSelectedListener mSubSelectedListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            Log.i(TAG, "Sub selected, position: " + position);
+            int sub = position - 1;
+            if (sub != mCallSubFilter) {
+                mCallSubFilter = sub;
+                setSelectedSub(sub);
+                fetchCalls();
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Do nothing.
+        }
+
+    };
+
+    private OnItemSelectedListener mStatusSelectedListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            Log.i(TAG, "Status selected, position: " + position);
+            int type = ((SpinnerContent)parent.getItemAtPosition(position)).value;
+            if (type != mCallTypeFilter) {
+                mCallTypeFilter = type;
+                fetchCalls();
+            }
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Do nothing.
+        }
+
+    };
+
+    /**
+     * Initialize the filter views content.
+     */
+    private void updateFilterSpinnerViews() {
+        if (mFilterSubSpinnerView == null
+                || mFilterStatusSpinnerView == null) {
+            Log.w(TAG, "The filter spinner view is null!");
+            return;
+        }
+
+        // Update the sub filter's content.
+        final SubscriptionManager subscriptionManager = SubscriptionManager.from(getActivity());
+        if (subscriptionManager.getActiveSubscriptionInfoCount() < 2) {
+            mFilterSubSpinnerView.setVisibility(View.GONE);
+        }else{
+            ArrayAdapter<SpinnerContent> filterSubAdapter = new ArrayAdapter<SpinnerContent>(
+                    getActivity(), R.layout.msim_call_log_spinner_item,
+                    SpinnerContent.setupSubFilterContent(getActivity()));
+            if (filterSubAdapter.getCount() <= 1) {
+                mFilterSubSpinnerView.setVisibility(View.GONE);
+            }else{
+                mCallSubFilter = getSelectedSub();
+                mFilterSubSpinnerView.setAdapter(filterSubAdapter);
+                mFilterSubSpinnerView.setOnItemSelectedListener(mSubSelectedListener);
+                SpinnerContent.setSpinnerContentValue(mFilterSubSpinnerView, mCallSubFilter);
+            }
+        }
+        // Update the status filter's content.
+        ArrayAdapter<SpinnerContent> filterStatusAdapter = new ArrayAdapter<SpinnerContent>(
+                getActivity(), R.layout.msim_call_log_spinner_item,
+                SpinnerContent.setupStatusFilterContent(getActivity()));
+        mFilterStatusSpinnerView.setAdapter(filterStatusAdapter);
+        mFilterStatusSpinnerView.setOnItemSelectedListener(mStatusSelectedListener);
+        SpinnerContent.setSpinnerContentValue(mFilterStatusSpinnerView, mCallTypeFilter);
+    }
+
+    /**
+     * @return the saved selected subscription.
+     */
+    private int getSelectedSub() {
+        // Get the saved selected sub, and the default value is display all.
+        int sub = PreferenceManager.getDefaultSharedPreferences(this.getActivity()).getInt(
+                PREFERENCE_KEY_CALLLOG_SUB, CallLogQueryHandler.CALL_SUB_ALL);
+        return sub;
+    }
+
+    /**
+     * Save the selected subscription to preference.
+     */
+    private void setSelectedSub(int sub) {
+        // Save the selected sub to the default preference.
+        PreferenceManager.getDefaultSharedPreferences(this.getActivity()).edit()
+                .putInt(PREFERENCE_KEY_CALLLOG_SUB, sub).commit();
+    }
+
+}
diff --git a/src/com/android/dialer/calllog/PhoneAccountUtils.java b/src/com/android/dialer/calllog/PhoneAccountUtils.java
index b3ce18b..2346ca7 100644
--- a/src/com/android/dialer/calllog/PhoneAccountUtils.java
+++ b/src/com/android/dialer/calllog/PhoneAccountUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -66,6 +67,17 @@
     }
 
     /**
+      *Extract account Icon from PhoneAccount object.
+      */
+    public static Drawable getAccountIcon(Context context, PhoneAccountHandle phoneAccount) {
+        final PhoneAccount account = getAccountOrNull(context, phoneAccount);
+        if (account == null) {
+            return null;
+        }
+        return account.getIcon().loadDrawable(context);
+    }
+
+    /**
      * Extract account label from PhoneAccount object.
      */
     @Nullable
@@ -107,9 +119,9 @@
      * single registered and enabled account, return null.
      */
     @Nullable
-    private static PhoneAccount getAccountOrNull(Context context,
+    public static PhoneAccount getAccountOrNull(Context context,
             @Nullable PhoneAccountHandle accountHandle) {
-        if (TelecomUtil.getCallCapablePhoneAccounts(context).size() <= 1) {
+        if (TelecomUtil.getCallCapablePhoneAccounts(context).size() < 1) {
             return null;
         }
         return TelecomUtil.getPhoneAccount(context, accountHandle);
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index 4f1c455..79e1ebb 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -21,13 +21,17 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.graphics.Typeface;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.support.v4.content.ContextCompat;
 import android.telecom.PhoneAccount;
+import android.text.SpannableString;
+import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
+import android.text.style.StyleSpan;
 import android.view.View;
 import android.widget.TextView;
 
@@ -86,6 +90,10 @@
 
     /** Fills the call details views with content. */
     public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details) {
+        setPhoneCallDetails(views, details, null);
+    }
+    public void setPhoneCallDetails(PhoneCallDetailsViews views,
+            PhoneCallDetails details, String filter) {
         // Display up to a given number of icons.
         views.callTypeIcons.clear();
         int count = details.callTypes.length;
@@ -114,6 +122,15 @@
         // Set the call count, location, date and if voicemail, set the duration.
         setDetailText(views, callCount, details);
 
+        //set the account icon if it exists.
+        Drawable icon = details.accountIcon;
+        if (icon != null) {
+            views.callAccountIcon.setVisibility(View.VISIBLE);
+            views.callAccountIcon.setImageDrawable(icon);
+        } else {
+            views.callAccountIcon.setVisibility(View.GONE);
+        }
+
         // Set the account label if it exists.
         String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
         if (!TextUtils.isEmpty(details.viaNumber)) {
@@ -139,14 +156,34 @@
             views.callAccountLabel.setVisibility(View.GONE);
         }
 
-        final CharSequence nameText;
-        final CharSequence displayNumber = details.displayNumber;
+        CharSequence nameText;
+        CharSequence displayNumber = details.displayNumber;
+        String phoneNum = (String) details.number;
+        if (!TextUtils.isEmpty(filter) && phoneNum.contains(filter)) {
+            int start, end;
+            start = phoneNum.indexOf(filter);
+            end = start + filter.length();
+            SpannableString result = new SpannableString(phoneNum);
+            result.setSpan(new StyleSpan(Typeface.BOLD), start, end,
+                    Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            displayNumber = result;
+        }
         if (TextUtils.isEmpty(details.getPreferredName())) {
             nameText = displayNumber;
             // We have a real phone number as "nameView" so make it always LTR
             views.nameView.setTextDirection(View.TEXT_DIRECTION_LTR);
         } else {
             nameText = details.getPreferredName();
+            if (!TextUtils.isEmpty(filter) && nameText.toString().toUpperCase()
+                    .contains(filter.toUpperCase())) {
+                int start,end;
+                start = nameText.toString().toUpperCase().indexOf(filter.toUpperCase());
+                end = start + filter.length();
+                SpannableString style = new SpannableString(nameText);
+                style.setSpan(new StyleSpan(Typeface.BOLD), start, end,
+                        Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+                nameText = style;
+            }
         }
 
         views.nameView.setText(nameText);
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsViews.java b/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
index 94f4411..4c0d45f 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsViews.java
@@ -32,16 +32,19 @@
     public final CallTypeIconsView callTypeIcons;
     public final TextView callLocationAndDate;
     public final TextView voicemailTranscriptionView;
+    public final ImageView callAccountIcon;
     public final TextView callAccountLabel;
 
     private PhoneCallDetailsViews(TextView nameView, View callTypeView,
             CallTypeIconsView callTypeIcons, TextView callLocationAndDate,
-            TextView voicemailTranscriptionView, TextView callAccountLabel) {
+            TextView voicemailTranscriptionView,  ImageView callAccountIcon,
+            TextView callAccountLabel) {
         this.nameView = nameView;
         this.callTypeView = callTypeView;
         this.callTypeIcons = callTypeIcons;
         this.callLocationAndDate = callLocationAndDate;
         this.voicemailTranscriptionView = voicemailTranscriptionView;
+        this.callAccountIcon = callAccountIcon;
         this.callAccountLabel = callAccountLabel;
     }
 
@@ -58,6 +61,7 @@
                 (CallTypeIconsView) view.findViewById(R.id.call_type_icons),
                 (TextView) view.findViewById(R.id.call_location_and_date),
                 (TextView) view.findViewById(R.id.voicemail_transcription),
+                (ImageView) view.findViewById(R.id.call_account_icon),
                 (TextView) view.findViewById(R.id.call_account_label));
     }
 
@@ -68,6 +72,7 @@
                 new CallTypeIconsView(context),
                 new TextView(context),
                 new TextView(context),
+                new ImageView(context),
                 new TextView(context));
     }
 }
diff --git a/src/com/android/dialer/calllog/SpinnerContent.java b/src/com/android/dialer/calllog/SpinnerContent.java
new file mode 100644
index 0000000..43bdbbb
--- /dev/null
+++ b/src/com/android/dialer/calllog/SpinnerContent.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved
+ * Not a Contribution.
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.dialer.calllog;
+
+import android.content.Context;
+import android.provider.CallLog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Spinner;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.contacts.common.MoreContactUtils;
+import com.android.dialer.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * To save the spinner content.
+ */
+public class SpinnerContent {
+    private static String TAG = "SpinnerContent";
+
+    public final int value;
+    private final String label;
+    // The index for call type spinner.
+    private static final int INDEX_CALL_TYPE_ALL = 0;
+    private static final int INDEX_CALL_TYPE_INCOMING = 1;
+    private static final int INDEX_CALL_TYPE_OUTGOING = 2;
+    private static final int INDEX_CALL_TYPE_MISSED = 3;
+
+    public static void setSpinnerContentValue(Spinner spinner, int value) {
+        for (int i = 0, count = spinner.getCount(); i < count; i++) {
+            SpinnerContent sc = (SpinnerContent) spinner.getItemAtPosition(i);
+            if (sc.value == value) {
+                spinner.setSelection(i, true);
+                Log.i(TAG, "Set selection for spinner(" + sc + ") with the value: " + value);
+                return;
+            }
+        }
+    }
+
+    public SpinnerContent(int value, String label) {
+        this.value = value;
+        this.label = label;
+    }
+
+    @Override
+    public String toString() {
+        return label;
+    }
+
+    /**
+     * @return the spinner contents for the different sims (all, sim0, sim1 etc)
+     */
+    public static List<SpinnerContent> setupSubFilterContent(Context context) {
+        TelephonyManager telephonyManager =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        int count = telephonyManager.getPhoneCount();
+        // Update the filter sub content.
+        ArrayList<SpinnerContent> values = new ArrayList<SpinnerContent>(count + 1);
+        values.add(new SpinnerContent(CallLogQueryHandler.CALL_SUB_ALL,
+                context.getString(R.string.call_log_show_all_slots)));
+
+        List<PhoneAccountHandle>  mPhoneAccountHandle =
+                PhoneAccountUtils.getSubscriptionPhoneAccounts(context);
+        for (int i = 0; i < mPhoneAccountHandle.size(); i++) {
+            String subDisplayName = PhoneAccountUtils.getAccountLabel(context,
+                    mPhoneAccountHandle.get(i));
+            if (!TextUtils.isEmpty(subDisplayName) && subDisplayName.indexOf("Unknown") == -1) {
+                values.add(new SpinnerContent(i, subDisplayName));
+            }
+        }
+        return values;
+    }
+
+    /**
+     * @return the spinner contents for the different call types (incoming, outgoing etc)
+     */
+    public static List<SpinnerContent> setupStatusFilterContent(Context context) {
+        int statusCount = 4;
+        ArrayList<SpinnerContent> values = new ArrayList<SpinnerContent>(statusCount);
+        for (int i = 0; i < statusCount; i++) {
+            int value = CallLogQueryHandler.CALL_TYPE_ALL;
+            String label = null;
+            switch (i) {
+                case INDEX_CALL_TYPE_ALL:
+                    value = CallLogQueryHandler.CALL_TYPE_ALL;
+                    label = context.getString(R.string.call_log_all_calls_header);
+                    break;
+                case INDEX_CALL_TYPE_INCOMING:
+                    value = CallLog.Calls.INCOMING_TYPE;
+                    label = context.getString(R.string.call_log_incoming_header);
+                    break;
+                case INDEX_CALL_TYPE_OUTGOING:
+                    value = CallLog.Calls.OUTGOING_TYPE;
+                    label = context.getString(R.string.call_log_outgoing_header);
+                    break;
+                case INDEX_CALL_TYPE_MISSED:
+                    value = CallLog.Calls.MISSED_TYPE;
+                    label = context.getString(R.string.call_log_missed_header);
+                    break;
+            }
+            values.add(new SpinnerContent(value, label));
+        }
+        return values;
+    }
+}
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCache.java b/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
index dc1217c..7dda5a1 100644
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
+++ b/src/com/android/dialer/calllog/calllogcache/CallLogCache.java
@@ -17,6 +17,7 @@
 package com.android.dialer.calllog.calllogcache;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.telecom.PhoneAccountHandle;
 
 import com.android.contacts.common.CallUtil;
@@ -93,4 +94,9 @@
      * @return {@code true} if calling with a note is supported, {@code false} otherwise.
      */
     public abstract boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle);
+
+    /**
+     * Returns the account icon if present, else null.
+     */
+    public abstract Drawable getAccountIcon(PhoneAccountHandle phoneAccount);
 }
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
index 770cc9d..040855d 100644
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
+++ b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipop.java
@@ -17,6 +17,7 @@
 package com.android.dialer.calllog.calllogcache;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.PhoneNumberUtils;
@@ -70,4 +71,9 @@
     public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
         return false;
     }
+
+    @Override
+    public Drawable getAccountIcon(PhoneAccountHandle accountHandle) {
+        return null;
+    }
 }
diff --git a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
index d1e3f7b..ccf11da 100644
--- a/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
+++ b/src/com/android/dialer/calllog/calllogcache/CallLogCacheLollipopMr1.java
@@ -17,7 +17,12 @@
 package com.android.dialer.calllog.calllogcache;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Pair;
 
@@ -37,22 +42,27 @@
 class CallLogCacheLollipopMr1 extends CallLogCache {
     // Maps from a phone-account/number pair to a boolean because multiple numbers could return true
     // for the voicemail number if those numbers are not pre-normalized.
-    private final Map<Pair<PhoneAccountHandle, CharSequence>, Boolean> mVoicemailQueryCache =
-            new HashMap<>();
     private final Map<PhoneAccountHandle, String> mPhoneAccountLabelCache = new HashMap<>();
     private final Map<PhoneAccountHandle, Integer> mPhoneAccountColorCache = new HashMap<>();
     private final Map<PhoneAccountHandle, Boolean> mPhoneAccountCallWithNoteCache = new HashMap<>();
+    private final Map<PhoneAccountHandle, Drawable> mPhoneAccountCallWithDrawableCache
+            = new HashMap<>();
+    private final Map<PhoneAccountHandle, String> mPhoneAccountCallWithVoiceMailNumberCache
+            = new HashMap<>();
+    private TelephonyManager mTelephonyManager;
 
     /* package */ CallLogCacheLollipopMr1(Context context) {
         super(context);
+        mTelephonyManager = TelephonyManager.from(context);
     }
 
     @Override
     public void reset() {
-        mVoicemailQueryCache.clear();
         mPhoneAccountLabelCache.clear();
         mPhoneAccountColorCache.clear();
         mPhoneAccountCallWithNoteCache.clear();
+        mPhoneAccountCallWithDrawableCache.clear();
+        mPhoneAccountCallWithVoiceMailNumberCache.clear();
 
         super.reset();
     }
@@ -63,15 +73,28 @@
             return false;
         }
 
-        Pair<PhoneAccountHandle, CharSequence> key = new Pair<>(accountHandle, number);
-        if (mVoicemailQueryCache.containsKey(key)) {
-            return mVoicemailQueryCache.get(key);
+        String curNumber = PhoneNumberUtils.extractNetworkPortionAlt(number.toString());
+        if (mPhoneAccountCallWithVoiceMailNumberCache.containsKey(accountHandle)) {
+            String vmNumber = mPhoneAccountCallWithVoiceMailNumberCache.get(accountHandle);
+            return !TextUtils.isEmpty(curNumber) && PhoneNumberUtils.compare(curNumber, vmNumber);
         } else {
-            Boolean isVoicemail =
-                    PhoneNumberUtil.isVoicemailNumber(mContext, accountHandle, number.toString());
-            mVoicemailQueryCache.put(key, isVoicemail);
-            return isVoicemail;
+            PhoneAccount account = PhoneAccountUtils.getAccountOrNull(mContext, accountHandle);
+            if (account != null
+                    && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+                try {
+                    int subId = Integer.parseInt(accountHandle.getId());
+                    String vmNumber =  mTelephonyManager.getVoiceMailNumber(subId);
+                    mPhoneAccountCallWithVoiceMailNumberCache.put(accountHandle, vmNumber);
+                    return !TextUtils.isEmpty(curNumber)
+                            && PhoneNumberUtils.compare(curNumber, vmNumber);
+                } catch (NumberFormatException e) {
+                    mPhoneAccountCallWithVoiceMailNumberCache.put(accountHandle, null);
+                }
+            } else {
+                mPhoneAccountCallWithVoiceMailNumberCache.put(accountHandle, null);
+            }
         }
+        return false;
     }
 
     @Override
@@ -107,4 +130,20 @@
             return supportsCallWithNote;
         }
     }
+
+    @Override
+    public Drawable getAccountIcon(PhoneAccountHandle accountHandle) {
+        if (mPhoneAccountCallWithDrawableCache.containsKey(accountHandle)) {
+            return mPhoneAccountCallWithDrawableCache.get(accountHandle);
+        } else {
+            PhoneAccount account = PhoneAccountUtils.getAccountOrNull(mContext, accountHandle);
+            if (account == null) {
+                mPhoneAccountCallWithDrawableCache.put(accountHandle, null);
+                return null;
+            }
+            Drawable drawable = account.getIcon().loadDrawable(mContext);
+            mPhoneAccountCallWithDrawableCache.put(accountHandle, drawable);
+            return drawable;
+        }
+    }
 }
diff --git a/src/com/android/dialer/contactinfo/ContactInfoCache.java b/src/com/android/dialer/contactinfo/ContactInfoCache.java
index 1e24579..4fd3013 100644
--- a/src/com/android/dialer/contactinfo/ContactInfoCache.java
+++ b/src/com/android/dialer/contactinfo/ContactInfoCache.java
@@ -76,7 +76,8 @@
 
                 if (req != null) {
                     // Process the request. If the lookup succeeds, schedule a redraw.
-                    needRedraw |= queryContactInfo(req.number, req.countryIso, req.callLogInfo);
+                    needRedraw |= queryContactInfo(req.number, req.postDialString, req.countryIso,
+                            req.callLogInfo, req.isConf);
                 } else {
                     // Throttle redraw rate by only sending them when there are
                     // more requests.
@@ -130,6 +131,7 @@
     private final LinkedList<ContactInfoRequest> mRequests;
 
     private ExpirableCache<NumberWithCountryIso, ContactInfo> mCache;
+    private ExpirableCache<NumberWithCountryIso, ContactInfo> mCacheFor4gConfCall;
 
     private ContactInfoHelper mContactInfoHelper;
     private QueryThread mContactInfoQueryThread;
@@ -142,32 +144,52 @@
 
         mRequests = new LinkedList<ContactInfoRequest>();
         mCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
+        mCacheFor4gConfCall = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
     }
 
     public ContactInfo getValue(String number, String countryIso, ContactInfo cachedContactInfo) {
-        NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
-        ExpirableCache.CachedValue<ContactInfo> cachedInfo =
-                mCache.getCachedValue(numberCountryIso);
+        return getValue(number, null, countryIso, cachedContactInfo, false);
+    }
+
+    public ContactInfo getValue(String number, String postDialString, String countryIso,
+                ContactInfo cachedContactInfo, boolean isConf) {
+        String phoneNumber = number;
+        if (!isConf && !TextUtils.isEmpty(postDialString)) {
+            phoneNumber += postDialString;
+        }
+        NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(phoneNumber, countryIso);
+        ExpirableCache.CachedValue<ContactInfo> cachedInfo = null;
+        if (isConf) {
+            cachedInfo = mCacheFor4gConfCall.getCachedValue(numberCountryIso);
+        } else {
+            cachedInfo = mCache.getCachedValue(numberCountryIso);
+        }
         ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
         if (cachedInfo == null) {
-            mCache.put(numberCountryIso, ContactInfo.EMPTY);
+            if (isConf) {
+                mCacheFor4gConfCall.put(numberCountryIso, ContactInfo.EMPTY);
+            } else {
+                mCache.put(numberCountryIso, ContactInfo.EMPTY);
+            }
             // Use the cached contact info from the call log.
             info = cachedContactInfo;
             // The db request should happen on a non-UI thread.
             // Request the contact details immediately since they are currently missing.
-            enqueueRequest(number, countryIso, cachedContactInfo, true);
+            enqueueRequest(number, postDialString, countryIso, cachedContactInfo, true, isConf);
             // We will format the phone number when we make the background request.
         } else {
             if (cachedInfo.isExpired()) {
                 // The contact info is no longer up to date, we should request it. However, we
                 // do not need to request them immediately.
-                enqueueRequest(number, countryIso, cachedContactInfo, false);
+                enqueueRequest(number, postDialString, countryIso,
+                        cachedContactInfo, false, isConf);
             } else if (!callLogInfoMatches(cachedContactInfo, info)) {
                 // The call log information does not match the one we have, look it up again.
                 // We could simply update the call log directly, but that needs to be done in a
                 // background thread, so it is easier to simply request a new lookup, which will, as
                 // a side-effect, update the call log.
-                enqueueRequest(number, countryIso, cachedContactInfo, false);
+                enqueueRequest(number, postDialString, countryIso,
+                        cachedContactInfo, false, isConf);
             }
 
             if (info == ContactInfo.EMPTY) {
@@ -186,11 +208,15 @@
      *
      * The number might be either a SIP address or a phone number.
      *
+     * @param postDialString if required, append into number
+     * @param isConf determine whether it is a 4g conf call log
      * It returns true if it updated the content of the cache and we should therefore tell the
      * view to update its content.
      */
-    private boolean queryContactInfo(String number, String countryIso, ContactInfo callLogInfo) {
-        final ContactInfo info = mContactInfoHelper.lookupNumber(number, countryIso);
+    private boolean queryContactInfo(String number, String postDialString, String countryIso,
+            ContactInfo callLogInfo, boolean isConf) {
+        final ContactInfo info = mContactInfoHelper.lookupNumber(number, postDialString,
+                countryIso, isConf);
 
         if (info == null) {
             // The lookup failed, just return without requesting to update the view.
@@ -199,8 +225,17 @@
 
         // Check the existing entry in the cache: only if it has changed we should update the
         // view.
-        NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(number, countryIso);
-        ContactInfo existingInfo = mCache.getPossiblyExpired(numberCountryIso);
+        String phoneNumber = number;
+        if (!isConf && !TextUtils.isEmpty(postDialString)) {
+            phoneNumber += postDialString;
+        }
+        NumberWithCountryIso numberCountryIso = new NumberWithCountryIso(phoneNumber, countryIso);
+        ContactInfo existingInfo = null;
+        if (isConf) {
+            existingInfo = mCacheFor4gConfCall.getPossiblyExpired(numberCountryIso);
+        } else {
+            existingInfo = mCache.getPossiblyExpired(numberCountryIso);
+        }
 
         final boolean isRemoteSource = info.sourceType != 0;
 
@@ -215,11 +250,21 @@
 
         // Store the data in the cache so that the UI thread can use to display it. Store it
         // even if it has not changed so that it is marked as not expired.
-        mCache.put(numberCountryIso, info);
+        if (isConf) {
+            mCacheFor4gConfCall.put(numberCountryIso, info);
+        } else {
+            mCache.put(numberCountryIso, info);
+        }
 
         // Update the call log even if the cache it is up-to-date: it is possible that the cache
         // contains the value from a different call log entry.
-        mContactInfoHelper.updateCallLogContactInfo(number, countryIso, info, callLogInfo);
+        if (isConf) {
+            mContactInfoHelper.updateCallLogContactInfo(number, countryIso,
+                    info, callLogInfo);
+        } else {
+            mContactInfoHelper.updateCallLogContactInfo(phoneNumber, countryIso,
+                    info, callLogInfo);
+        }
         return updated;
     }
 
@@ -264,6 +309,7 @@
 
     public void invalidate() {
         mCache.expireAll();
+        mCacheFor4gConfCall.expireAll();
         stopRequestProcessing();
     }
 
@@ -293,7 +339,24 @@
      */
     protected void enqueueRequest(String number, String countryIso, ContactInfo callLogInfo,
             boolean immediate) {
-        ContactInfoRequest request = new ContactInfoRequest(number, countryIso, callLogInfo);
+        enqueueRequest(number, null, countryIso, callLogInfo, immediate, false);
+    }
+
+    /**
+     * Enqueues a request to look up the contact details for the given phone number.
+     * <p>
+     * It also provides the current contact info stored in the call log for this number.
+     * <p>
+     * If the {@code immediate} parameter is true, it will start immediately the thread that looks
+     * up the contact information (if it has not been already started). Otherwise, it will be
+     * started with a delay. See {@link #START_PROCESSING_REQUESTS_DELAY_MILLIS}.
+     * @param postDialString if required, append into number
+     * @param isConf indicate whether call log is for Conference Url call
+     */
+    protected void enqueueRequest(String number, String postDialString, String countryIso,
+            ContactInfo callLogInfo, boolean immediate, boolean isConf) {
+        ContactInfoRequest request = new ContactInfoRequest(number, postDialString, countryIso,
+                callLogInfo, isConf);
         synchronized (mRequests) {
             if (!mRequests.contains(request)) {
                 mRequests.add(request);
diff --git a/src/com/android/dialer/contactinfo/ContactInfoRequest.java b/src/com/android/dialer/contactinfo/ContactInfoRequest.java
index ec5c119..98208bc 100644
--- a/src/com/android/dialer/contactinfo/ContactInfoRequest.java
+++ b/src/com/android/dialer/contactinfo/ContactInfoRequest.java
@@ -31,11 +31,20 @@
     public final String countryIso;
     /** The cached contact information stored in the call log. */
     public final ContactInfo callLogInfo;
+    public final boolean isConf;
+    public final String postDialString;
 
     public ContactInfoRequest(String number, String countryIso, ContactInfo callLogInfo) {
+        this(number, null, countryIso, callLogInfo, false);
+    }
+
+    public ContactInfoRequest(String number, String postDialString, String countryIso,
+            ContactInfo callLogInfo, boolean isConf) {
         this.number = number;
+        this.postDialString = postDialString;
         this.countryIso = countryIso;
         this.callLogInfo = callLogInfo;
+        this.isConf = isConf;
     }
 
     @Override
@@ -47,6 +56,7 @@
         ContactInfoRequest other = (ContactInfoRequest) obj;
 
         if (!TextUtils.equals(number, other.number)) return false;
+        if (!TextUtils.equals(postDialString, other.postDialString)) return false;
         if (!TextUtils.equals(countryIso, other.countryIso)) return false;
         if (!Objects.equal(callLogInfo, other.callLogInfo)) return false;
 
@@ -60,6 +70,7 @@
         result = prime * result + ((callLogInfo == null) ? 0 : callLogInfo.hashCode());
         result = prime * result + ((countryIso == null) ? 0 : countryIso.hashCode());
         result = prime * result + ((number == null) ? 0 : number.hashCode());
+        result = prime * result + ((postDialString == null) ? 0 : postDialString.hashCode());
         return result;
     }
 }
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
index 5edfb27..2d9b8aa 100644
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/src/com/android/dialer/database/DialerDatabaseHelper.java
@@ -19,6 +19,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
@@ -33,6 +34,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -49,6 +51,8 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 
+import java.io.File;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
@@ -70,6 +74,11 @@
     private static final AtomicBoolean sInUpdate = new AtomicBoolean(false);
     private final Context mContext;
 
+    private Class mMultiMatchClass;
+    private Object mMultiMatchObject;
+    private Method mMultiMatchMethod;
+    private Method mMultiGetNameNumberMethod;
+
     /**
      * SmartDial DB version ranges:
      * <pre>
@@ -77,6 +86,8 @@
      * </pre>
      */
     public static final int DATABASE_VERSION = 9;
+    public static final int DATABASE_SHAREPREF_VERSION = 1;
+    public static final String DATABASE_SHAREPREF_KEY = "database_sharepref_key";
     public static final String DATABASE_NAME = "dialer.db";
 
     /**
@@ -86,7 +97,7 @@
     private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
     private static final String DATABASE_VERSION_PROPERTY = "database_version";
 
-    private static final int MAX_ENTRIES = 20;
+    private static final int MAX_ENTRIES = 40;
 
     public interface Tables {
         /** Saves a list of numbers to be blocked.*/
@@ -120,6 +131,8 @@
         static final String IS_PRIMARY = "is_primary";
         static final String CARRIER_PRESENCE = "carrier_presence";
         static final String LAST_SMARTDIAL_UPDATE_TIME = "last_smartdial_update_time";
+        static final String ACCOUNT_TYPE = "account_type";
+        static final String ACCOUNT_NAME = "account_name";
     }
 
     public static interface PrefixColumns extends BaseColumns {
@@ -156,6 +169,8 @@
             Contacts.IN_VISIBLE_GROUP,          // 12
             Data.IS_PRIMARY,                    // 13
             Data.CARRIER_PRESENCE,              // 14
+            RawContacts.ACCOUNT_TYPE,           // 15
+            RawContacts.ACCOUNT_NAME,           // 16
         };
 
         static final int PHONE_ID = 0;
@@ -173,6 +188,8 @@
         static final int PHONE_IN_VISIBLE_GROUP = 12;
         static final int PHONE_IS_PRIMARY = 13;
         static final int PHONE_CARRIER_PRESENCE = 14;
+        static final int PHONE_ACCOUNT_TYPE = 15;
+        static final int PHONE_ACCOUNT_NAME = 16;
 
         /** Selects only rows that have been updated after a certain time stamp.*/
         static final String SELECT_UPDATED_CLAUSE =
@@ -275,6 +292,8 @@
         public final String lookupKey;
         public final long photoId;
         public final int carrierPresence;
+        public final String accountType;
+        public final String accountName;
 
         public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
                 String lookupKey, long photoId, int carrierPresence) {
@@ -285,6 +304,22 @@
             this.lookupKey = lookupKey;
             this.photoId = photoId;
             this.carrierPresence = carrierPresence;
+            this.accountType = null;
+            this.accountName = null;
+        }
+
+        public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
+                String lookupKey, long photoId, int carrierPresence,
+                        String accountType, String accountName) {
+            this.dataId = dataID;
+            this.id = id;
+            this.displayName = displayName;
+            this.phoneNumber = phoneNumber;
+            this.lookupKey = lookupKey;
+            this.photoId = photoId;
+            this.carrierPresence = carrierPresence;
+            this.accountType = accountType;
+            this.accountName = accountName;
         }
 
         @Override
@@ -306,7 +341,10 @@
                         && Objects.equal(this.phoneNumber, that.phoneNumber)
                         && Objects.equal(this.lookupKey, that.lookupKey)
                         && Objects.equal(this.photoId, that.photoId)
-                        && Objects.equal(this.carrierPresence, that.carrierPresence);
+                        && Objects.equal(this.carrierPresence, that.carrierPresence)
+                        && Objects.equal(this.accountType, that.accountType)
+                        && Objects.equal(this.accountName, that.accountName);
+
             }
             return false;
         }
@@ -382,6 +420,39 @@
         mContext = Preconditions.checkNotNull(context, "Context must not be null");
     }
 
+    private void initMultiLanguageSearch() {
+        try {
+            if (mMultiMatchClass == null) {
+                mMultiMatchClass = Class.forName("com.qualcomm.qti.smartsearch.SmartMatch");
+                Log.d(TAG, "create multi match success");
+            }
+            if (mMultiMatchClass != null) {
+                if (mMultiMatchObject == null) {
+                    mMultiMatchObject = mMultiMatchClass.newInstance();
+                }
+                if (mMultiMatchMethod == null) {
+                    mMultiMatchMethod = mMultiMatchClass.getDeclaredMethod(
+                            "getMatchStringIndex", String.class, String.class,
+                            int.class);
+                }
+                if (mMultiGetNameNumberMethod == null) {
+                    mMultiGetNameNumberMethod = mMultiMatchClass.getDeclaredMethod(
+                            "getNameNumber", String.class, int.class);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public Object getMultiMatchObject() {
+        return mMultiMatchObject;
+    }
+
+    public Method getMultiMatchMethod() {
+        return mMultiMatchMethod;
+    }
+
     /**
      * Creates tables in the database when database is created for the first time.
      *
@@ -392,6 +463,11 @@
         setupTables(db);
     }
 
+    @Override
+    public void onOpen(SQLiteDatabase db) {
+        upgradeSmartSearchDatabase(db);
+    }
+
     private void setupTables(SQLiteDatabase db) {
         dropTables(db);
         db.execSQL("CREATE TABLE " + Tables.SMARTDIAL_TABLE + " ("
@@ -409,7 +485,9 @@
                 + SmartDialDbColumns.IS_SUPER_PRIMARY + " INTEGER, "
                 + SmartDialDbColumns.IN_VISIBLE_GROUP + " INTEGER, "
                 + SmartDialDbColumns.IS_PRIMARY + " INTEGER, "
-                + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0"
+                + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0,"
+                + SmartDialDbColumns.ACCOUNT_TYPE + " TEXT, "
+                + SmartDialDbColumns.ACCOUNT_NAME + " TEXT "
                 + ");");
 
         db.execSQL("CREATE TABLE " + Tables.PREFIX_TABLE + " ("
@@ -445,6 +523,49 @@
         }
     }
 
+    private boolean isNeedUpgradeForSmartSearch() {
+        String FILENAME = "upgradeSmartSearchTable";
+
+        Log.d(TAG, "Shared Preference Created with name:  " + FILENAME);
+        SharedPreferences pref = mContext.getSharedPreferences(FILENAME,
+                mContext.MODE_PRIVATE);
+        if (pref != null) {
+            int mSharePrefVersion = pref.getInt(DATABASE_SHAREPREF_KEY,0);
+            if(mSharePrefVersion < DATABASE_SHAREPREF_VERSION) {
+                Editor editor;
+                editor = pref.edit();
+                editor.putInt(DATABASE_SHAREPREF_KEY, DATABASE_SHAREPREF_VERSION);
+                editor.commit();
+                return true;
+            }
+            return false;
+        } else {
+            Log.d(TAG, "fail to get SharedPreferences !");
+            return false;
+        }
+    }
+
+    private void upgradeSmartSearchDatabase(SQLiteDatabase db) {
+        if (isNeedUpgradeForSmartSearch()) {
+            db.beginTransaction();
+            try {
+                upgradeDatabaseSmartSearch(db);
+                db.setTransactionSuccessful();
+            } catch (Throwable ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+            } finally {
+                db.endTransaction();
+            }
+        }
+    }
+
+    private void upgradeDatabaseSmartSearch(SQLiteDatabase db) {
+        db.execSQL("ALTER TABLE " +  Tables.SMARTDIAL_TABLE + " ADD COLUMN " +
+                SmartDialDbColumns.ACCOUNT_TYPE + " TEXT;");
+        db.execSQL("ALTER TABLE " +  Tables.SMARTDIAL_TABLE + " ADD COLUMN " +
+                SmartDialDbColumns.ACCOUNT_NAME + " TEXT;");
+    }
+
     public void dropTables(SQLiteDatabase db) {
         db.execSQL("DROP TABLE IF EXISTS " + Tables.PREFIX_TABLE);
         db.execSQL("DROP TABLE IF EXISTS " + Tables.SMARTDIAL_TABLE);
@@ -770,8 +891,10 @@
                     SmartDialDbColumns.IN_VISIBLE_GROUP+ ", " +
                     SmartDialDbColumns.IS_PRIMARY + ", " +
                     SmartDialDbColumns.CARRIER_PRESENCE + ", " +
-                    SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ") " +
-                    " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+                    SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ", " +
+                    SmartDialDbColumns.ACCOUNT_TYPE + ", " +
+                    SmartDialDbColumns.ACCOUNT_NAME + ") " +
+                    " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
             final SQLiteStatement insert = db.compileStatement(sqlInsert);
 
             final String numberSqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
@@ -820,6 +943,24 @@
                 insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
                 insert.bindLong(13, updatedContactCursor.getInt(PhoneQuery.PHONE_CARRIER_PRESENCE));
                 insert.bindLong(14, currentMillis);
+
+                final String accountType = updatedContactCursor.getString(
+                        PhoneQuery.PHONE_ACCOUNT_TYPE);
+                if (accountType == null) {
+                    insert.bindString(15, mContext.getResources().getString(
+                            R.string.missing_account_type));
+                } else {
+                    insert.bindString(15, accountType);
+                }
+
+                final String accountName = updatedContactCursor.getString(
+                        PhoneQuery.PHONE_ACCOUNT_NAME);
+                if (accountName == null) {
+                    insert.bindString(16, mContext.getResources().getString(
+                            R.string.missing_account_name));
+                } else {
+                    insert.bindString(16, accountName);
+                }
                 insert.executeInsert();
                 final String contactPhoneNumber =
                         updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
@@ -863,14 +1004,27 @@
 
             while (nameCursor.moveToNext()) {
                 /** Computes a list of prefixes of a given contact name. */
-                final ArrayList<String> namePrefixes =
-                        SmartDialPrefix.generateNamePrefixes(nameCursor.getString(columnIndexName));
-
-                for (String namePrefix : namePrefixes) {
-                    insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
-                    insert.bindString(2, namePrefix);
-                    insert.executeInsert();
-                    insert.clearBindings();
+                if (mMultiGetNameNumberMethod != null) {
+                    try {
+                        String nameNumber = (String) mMultiGetNameNumberMethod.invoke(
+                                mMultiMatchObject,nameCursor.getString(columnIndexName), 0);
+                        nameNumber = nameNumber.replaceAll("[\\[\\.\\]]", "");
+                        insert.bindLong(1,nameCursor.getLong(columnIndexContactId));
+                        insert.bindString(2, nameNumber);
+                        insert.executeInsert();
+                        insert.clearBindings();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                } else {
+                    final ArrayList<String> namePrefixes = SmartDialPrefix
+                            .generateNamePrefixes(nameCursor.getString(columnIndexName));
+                    for (String namePrefix : namePrefixes) {
+                        insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
+                        insert.bindString(2, namePrefix);
+                        insert.executeInsert();
+                        insert.clearBindings();
+                    }
                 }
             }
 
@@ -888,6 +1042,8 @@
      * update.
      */
     public void updateSmartDialDatabase() {
+        initMultiLanguageSearch();
+
         final SQLiteDatabase db = getWritableDatabase();
 
         synchronized(mLock) {
@@ -1064,14 +1220,19 @@
     public ArrayList<ContactNumber>  getLooseMatches(String query,
             SmartDialNameMatcher nameMatcher) {
         final boolean inUpdate = sInUpdate.get();
-        if (inUpdate) {
+        if (inUpdate || query.length() == 0) {
             return Lists.newArrayList();
         }
 
         final SQLiteDatabase db = getReadableDatabase();
 
         /** Uses SQL query wildcard '%' to represent prefix matching.*/
-        final String looseQuery = query + "%";
+        StringBuilder looseQuery = new StringBuilder(query);
+        for (int i = 0; i < looseQuery.toString().length();) {
+            looseQuery.insert(i, "%");
+            i = i + 2;
+        }
+        looseQuery.append("%");
 
         final ArrayList<ContactNumber> result = Lists.newArrayList();
 
@@ -1087,10 +1248,12 @@
                 SmartDialDbColumns.NUMBER + ", " +
                 SmartDialDbColumns.CONTACT_ID + ", " +
                 SmartDialDbColumns.LOOKUP_KEY + ", " +
-                SmartDialDbColumns.CARRIER_PRESENCE +
-                " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
-                SmartDialDbColumns.CONTACT_ID + " IN " +
-                    " (SELECT " + PrefixColumns.CONTACT_ID +
+                SmartDialDbColumns.CARRIER_PRESENCE + ", " +
+                SmartDialDbColumns.ACCOUNT_TYPE + ", " +
+                SmartDialDbColumns.ACCOUNT_NAME +
+                " FROM " + Tables.SMARTDIAL_TABLE +
+                " WHERE " + SmartDialDbColumns.CONTACT_ID + " IN " +
+                " (SELECT " + PrefixColumns.CONTACT_ID +
                     " FROM " + Tables.PREFIX_TABLE +
                     " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
                     " LIKE '" + looseQuery + "')" +
@@ -1112,6 +1275,8 @@
             final int columnId = 4;
             final int columnLookupKey = 5;
             final int columnCarrierPresence = 6;
+            final int columnAccountType = 7;
+            final int columnAccountName = 8;
             if (DEBUG) {
                 stopWatch.lap("Found column IDs");
             }
@@ -1130,6 +1295,8 @@
                 final long photoId = cursor.getLong(columnPhotoId);
                 final String lookupKey = cursor.getString(columnLookupKey);
                 final int carrierPresence = cursor.getInt(columnCarrierPresence);
+                final String accountType = cursor.getString(columnAccountType);
+                final String accountName = cursor.getString(columnAccountName);
 
                 /** If a contact already exists and another phone number of the contact is being
                  * processed, skip the second instance.
@@ -1150,7 +1317,7 @@
                     /** If a contact has not been added, add it to the result and the hash set.*/
                     duplicates.add(contactMatch);
                     result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
-                            photoId, carrierPresence));
+                            photoId, carrierPresence, accountType, accountName));
                     counter++;
                     if (DEBUG) {
                         stopWatch.lap("Added one result: Name: " + displayName);
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
index 68a2e85..8d8f874 100644
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
@@ -130,7 +130,13 @@
                 new Listener() {
                     @Override
                     protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-                        listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0);
+                        try {
+                            listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0);
+                        } finally {
+                            if (cursor != null) {
+                                cursor.close();
+                            }
+                        }
                     }
                 },
                 FilteredNumberCompat.getContentUri(null),
@@ -164,20 +170,26 @@
                          * example, both '16502530000' and '6502530000' can exist at the same time
                          * and will be returned by this query.
                          */
-                        if (cursor == null || cursor.getCount() == 0) {
-                            listener.onCheckComplete(null);
-                            return;
+                        try {
+                            if (cursor == null || cursor.getCount() == 0) {
+                                listener.onCheckComplete(null);
+                                return;
+                            }
+                            cursor.moveToFirst();
+                            // New filtering doesn't have a concept of type
+                            if (!FilteredNumberCompat.useNewFiltering()
+                                    && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
+                                    != FilteredNumberTypes.BLOCKED_NUMBER) {
+                                listener.onCheckComplete(null);
+                                return;
+                            }
+                            listener.onCheckComplete(
+                                    cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)));
+                        } finally {
+                            if (cursor != null) {
+                                cursor.close();
+                            }
                         }
-                        cursor.moveToFirst();
-                        // New filtering doesn't have a concept of type
-                        if (!FilteredNumberCompat.useNewFiltering()
-                                && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
-                                != FilteredNumberTypes.BLOCKED_NUMBER) {
-                            listener.onCheckComplete(null);
-                            return;
-                        }
-                        listener.onCheckComplete(
-                                cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)));
                     }
                 },
                 FilteredNumberCompat.getContentUri(null),
@@ -248,25 +260,31 @@
         startQuery(NO_TOKEN, new Listener() {
             @Override
             public void onQueryComplete(int token, Object cookie, Cursor cursor) {
-                int rowsReturned = cursor == null ? 0 : cursor.getCount();
-                if (rowsReturned != 1) {
-                    throw new SQLiteDatabaseCorruptException
-                            ("Returned " + rowsReturned + " rows for uri "
-                                    + uri + "where 1 expected.");
-                }
-                cursor.moveToFirst();
-                final ContentValues values = new ContentValues();
-                DatabaseUtils.cursorRowToContentValues(cursor, values);
-                values.remove(FilteredNumberCompat.getIdColumnName());
-
-                startDelete(NO_TOKEN, new Listener() {
-                    @Override
-                    public void onDeleteComplete(int token, Object cookie, int result) {
-                        if (listener != null) {
-                            listener.onUnblockComplete(result, values);
-                        }
+                try {
+                    int rowsReturned = cursor == null ? 0 : cursor.getCount();
+                    if (rowsReturned != 1) {
+                        throw new SQLiteDatabaseCorruptException
+                                ("Returned " + rowsReturned + " rows for uri "
+                                        + uri + "where 1 expected.");
                     }
-                }, uri, null, null);
+                    cursor.moveToFirst();
+                    final ContentValues values = new ContentValues();
+                    DatabaseUtils.cursorRowToContentValues(cursor, values);
+                    values.remove(FilteredNumberCompat.getIdColumnName());
+
+                    startDelete(NO_TOKEN, new Listener() {
+                        @Override
+                        public void onDeleteComplete(int token, Object cookie, int result) {
+                            if (listener != null) {
+                                listener.onUnblockComplete(result, values);
+                            }
+                        }
+                    }, uri, null, null);
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
             }
         }, uri, null, null, null, null);
     }
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 55d5346..b4070cc 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -29,21 +29,27 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.media.AudioManager;
 import android.media.ToneGenerator;
+import android.Manifest;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Trace;
+import android.os.SystemProperties;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
 import android.provider.Contacts.PhonesColumns;
 import android.provider.Settings;
+import android.support.v4.app.ActivityCompat;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -71,6 +77,7 @@
 import com.android.contacts.common.CallUtil;
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.dialog.CallSubjectDialog;
+import com.android.contacts.common.MoreContactUtils;
 import com.android.contacts.common.util.PermissionsUtil;
 import com.android.contacts.common.util.PhoneNumberFormatter;
 import com.android.contacts.common.util.StopWatch;
@@ -79,16 +86,23 @@
 import com.android.dialer.NeededForReflection;
 import com.android.dialer.R;
 import com.android.dialer.SpecialCharSequenceMgr;
+import com.android.dialer.SpeedDialListActivity;
+import com.android.dialer.SpeedDialUtils;
 import com.android.dialer.calllog.PhoneAccountUtils;
 import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
 import com.android.dialer.util.IntentUtil.CallIntentBuilder;
 import com.android.dialer.util.TelecomUtil;
 import com.android.incallui.Call.LogState;
+import com.android.dialer.util.WifiCallUtils;
 import com.android.phone.common.CallLogAsync;
 import com.android.phone.common.animation.AnimUtils;
 import com.android.phone.common.dialpad.DialpadKeyButton;
 import com.android.phone.common.dialpad.DialpadView;
 
+import static android.Manifest.permission.READ_CALL_LOG;
+import static android.Manifest.permission.WRITE_CALL_LOG;
+
 import java.util.HashSet;
 import java.util.List;
 
@@ -103,6 +117,9 @@
         DialpadKeyButton.OnPressedListener {
     private static final String TAG = "DialpadFragment";
 
+    /* define for Activity permission request for Android >= 6.0 */
+    private static final int PERMISSION_REQUEST_CODE_LOCATION = 2;
+    private static final int READ_WRITE_CALL_LOG_PERMISSION_REQUEST_CODE = 3;
     /**
      * LinearLayout with getter and setter methods for the translationY property using floats,
      * for animation purposes.
@@ -145,6 +162,20 @@
          * be dismissed, unless there happens to be content showing.
          */
         boolean onDialpadSpacerTouchWithEmptyQuery();
+
+        /**
+         * This interface allows the DialpadFragment to tell its hosting Activity when and when not
+         * to display the "dial" button. While this is logically part of the DialpadFragment, the
+         * need to have a particular kind of slick animation puts the "dial" button in the parent.
+         *
+         * The parent calls dialButtonPressed() and optionsMenuInvoked() on the dialpad fragment
+         * when appropriate.
+         *
+         * TODO: Refactor the app so this interchange is a bit cleaner.
+         */
+        void setConferenceDialButtonVisibility(boolean enabled);
+        void setConferenceDialButtonImage(boolean setAddParticipantButton);
+
     }
 
     private static final boolean DEBUG = DialtactsActivity.DEBUG;
@@ -166,11 +197,16 @@
     /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
     private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
 
+    private static final String PROPERTY_RADIO_ATEL_CARRIER = "persist.radio.atel.carrier";
+    private static final String CARRIER_ONE_DEFAULT_MCC_MNC = "405854";
 
     private OnDialpadQueryChangedListener mDialpadQueryListener;
 
     private DialpadView mDialpadView;
     private EditText mDigits;
+    private EditText mRecipients;
+    private View mDigitsContainer;
+    private View mDialpad;
     private int mDialpadSlideInDuration;
 
     /** Remembers if we need to clear digits field when the screen is completely gone. */
@@ -212,6 +248,13 @@
     /** Identifier for the "Add Call" intent extra. */
     private static final String ADD_CALL_MODE_KEY = "add_call_mode";
 
+    /** Identifier for the "Add Participant" intent extra. */
+    private static final String ADD_PARTICIPANT_KEY = "add_participant";
+
+    private static final String EXTRA_DIAL_CONFERENCE_URI = "org.codeaurora.extra.DIAL_CONFERENCE_URI";
+
+    private boolean mAddParticipant = false;
+
     /**
      * Identifier for intent extra for sending an empty Flash message for
      * CDMA networks. This message is used by the network to simulate a
@@ -229,6 +272,8 @@
 
     private CallStateReceiver mCallStateReceiver;
 
+    private boolean mHasReadAndWriteCallLogPermission = false;
+
     private class CallStateReceiver extends BroadcastReceiver {
         /**
          * Receive call state changes so that we can take down the
@@ -251,6 +296,12 @@
                 // onscreen, but useless...)
                 showDialpadChooser(false);
             }
+            if (state == TelephonyManager.EXTRA_STATE_IDLE) {
+                final Activity activity = getActivity();
+                if (activity != null) {
+                    ((HostInterface) activity).setConferenceDialButtonVisibility(true);
+                }
+            }
         }
     }
 
@@ -266,9 +317,17 @@
     private boolean mStartedFromNewIntent = false;
     private boolean mFirstLaunch = false;
     private boolean mAnimate = false;
+    private TextView mOperator;
 
     private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent";
 
+    private static final String ACTION_WIFI_CALL_READY_STATUS_CHANGE
+        = "com.android.wificall.READY";
+    private static final String ACTION_WIFI_CALL_READY_EXTRA
+        = "com.android.wificall.ready.extra";
+    private BroadcastReceiver mWifiCallReadyReceiver;
+    private boolean isConfigAvailableNetwork = false;
+
     private TelephonyManager getTelephonyManager() {
         return (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
     }
@@ -320,6 +379,20 @@
         updateDeleteButtonEnabledState();
     }
 
+    private void changeDialpadButton(boolean ready) {
+        Log.d(TAG, "changeDialpadButton, ready = " + ready);
+        ImageView floatingActionButton =
+                (ImageButton) getView().findViewById(R.id.dialpad_floating_action_button);
+        if (floatingActionButton == null) {
+            return;
+        }
+        if (ready) {
+            floatingActionButton.setImageResource(R.drawable.fab_ic_wificall);
+        } else {
+            floatingActionButton.setImageResource(R.drawable.fab_ic_call);
+        }
+    }
+
     @Override
     public void onCreate(Bundle state) {
         Trace.beginSection(TAG + " onCreate");
@@ -344,6 +417,8 @@
             mCallStateReceiver = new CallStateReceiver();
             ((Context) getActivity()).registerReceiver(mCallStateReceiver, callStateIntentFilter);
         }
+        isConfigAvailableNetwork = getActivity().getResources().getBoolean(
+                R.bool.config_regional_pup_no_available_network);
         Trace.endSection();
     }
 
@@ -363,6 +438,13 @@
         mDialpadView = (DialpadView) fragmentView.findViewById(R.id.dialpad_view);
         mDialpadView.setCanDigitsBeEdited(true);
         mDigits = mDialpadView.getDigits();
+        mRecipients = (EditText) fragmentView.findViewById(R.id.recipients);
+        mDigitsContainer = fragmentView.findViewById(R.id.digits_container);
+        mDialpad = fragmentView.findViewById(R.id.dialpad);
+        if (mRecipients != null) {
+            mRecipients.setVisibility(View.GONE);
+            mRecipients.addTextChangedListener(this);
+        }
         mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
         mDigits.setOnClickListener(this);
         mDigits.setOnKeyListener(this);
@@ -410,6 +492,7 @@
         floatingActionButton.setOnClickListener(this);
         mFloatingActionButtonController = new FloatingActionButtonController(getActivity(),
                 floatingActionButtonContainer, floatingActionButton);
+        mOperator = (TextView)fragmentView.findViewById(R.id.dialpad_floating_operator);
         Trace.endSection();
         Trace.endSection();
         return fragmentView;
@@ -545,6 +628,9 @@
                 }
 
             }
+        } else {
+            mAddParticipant = intent.getBooleanExtra(ADD_PARTICIPANT_KEY, false);
+            ((HostInterface) getActivity()).setConferenceDialButtonVisibility(true);
         }
         showDialpadChooser(needToShowDialpadChooser);
         setStartedFromNewIntent(false);
@@ -568,11 +654,16 @@
     private void setFormattedDigits(String data, String normalizedNumber) {
         final String formatted = getFormattedDigits(data, normalizedNumber, mCurrentCountryIso);
         if (!TextUtils.isEmpty(formatted)) {
-            Editable digits = mDigits.getText();
-            digits.replace(0, digits.length(), formatted);
-            // for some reason this isn't getting called in the digits.replace call above..
-            // but in any case, this will make sure the background drawable looks right
-            afterTextChanged(digits);
+            if (isRecipientsShown()) {
+                mRecipients.setText(formatted);
+                afterTextChanged(mRecipients.getText());
+            } else {
+                Editable digits = mDigits.getText();
+                digits.replace(0, digits.length(), formatted);
+                // for some reason this isn't getting called in the digits.replace call above..
+                // but in any case, this will make sure the background drawable looks right
+                afterTextChanged(digits);
+            }
         }
     }
 
@@ -617,6 +708,10 @@
         for (int i = 0; i < buttonIds.length; i++) {
             dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
             dialpadKey.setOnPressedListener(this);
+            // Long-pressing button from two to nine will set up speed key dial.
+            if (i > 0 && i < buttonIds.length - 3) {
+                dialpadKey.setOnLongClickListener(this);
+            }
         }
 
         // Long-pressing one button will initiate Voicemail.
@@ -626,6 +721,14 @@
         // Long-pressing zero button will enter '+' instead.
         final DialpadKeyButton zero = (DialpadKeyButton) fragmentView.findViewById(R.id.zero);
         zero.setOnLongClickListener(this);
+
+        // Long-pressing star button will enter ','(pause) instead.
+        final DialpadKeyButton star = (DialpadKeyButton) fragmentView.findViewById(R.id.star);
+        star.setOnLongClickListener(this);
+
+        // Long-pressing pound button will enter ';'(wait) instead.
+        final DialpadKeyButton pound = (DialpadKeyButton) fragmentView.findViewById(R.id.pound);
+        pound.setOnLongClickListener(this);
     }
 
     @Override
@@ -663,8 +766,16 @@
         final StopWatch stopWatch = StopWatch.start("Dialpad.onResume");
 
         // Query the last dialed number. Do it first because hitting
+        mHasReadAndWriteCallLogPermission =
+                PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG) &&
+                PermissionsUtil.hasPermission(getActivity(), WRITE_CALL_LOG);
         // the DB is 'slow'. This call is asynchronous.
-        queryLastOutgoingCall();
+        if (mHasReadAndWriteCallLogPermission){
+            queryLastOutgoingCall();
+        } else {
+            ActivityCompat.requestPermissions(getActivity(), new String[] {READ_CALL_LOG,
+                    WRITE_CALL_LOG}, READ_WRITE_CALL_LOG_PERMISSION_REQUEST_CODE);
+        }
 
         stopWatch.lap("qloc");
 
@@ -713,6 +824,28 @@
         }
 
         mFirstLaunch = false;
+
+        if (MoreContactUtils.shouldShowOperator(getContext())) {
+                mOperator.setVisibility(View.VISIBLE);
+                mOperator.setText(MoreContactUtils.getNetworkSpnName(getContext(),
+                    SubscriptionManager.getDefaultVoiceSubscriptionId()));
+        } else {
+            mOperator.setVisibility(View.GONE);
+        }
+
+        if (isConfigAvailableNetwork) {
+            Context context = getActivity();
+            mWifiCallReadyReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    changeDialpadButton(
+                            intent.getBooleanExtra(ACTION_WIFI_CALL_READY_EXTRA, false));
+                }
+            };
+            IntentFilter filter = new IntentFilter(ACTION_WIFI_CALL_READY_STATUS_CHANGE);
+            context.registerReceiver(mWifiCallReadyReceiver, filter);
+            changeDialpadButton(WifiCallUtils.isWifiCallReadyEnabled(context));
+         }
         Trace.endSection();
     }
 
@@ -729,6 +862,10 @@
         mLastNumberDialed = EMPTY_NUMBER;  // Since we are going to query again, free stale number.
 
         SpecialCharSequenceMgr.cleanup();
+
+        if (isConfigAvailableNetwork) {
+            (getActivity()).unregisterReceiver(mWifiCallReadyReceiver);
+        }
     }
 
     @Override
@@ -891,6 +1028,10 @@
             public void show() {
                 final Menu menu = getMenu();
 
+                final MenuItem conferDialerOption
+                        = menu.findItem(R.id.menu_add_to_4g_conference_call);
+                conferDialerOption.setVisible(IntentUtil.isConferDialerEnabled(getActivity()));
+
                 boolean enable = !isDigitsEmpty();
                 for (int i = 0; i < menu.size(); i++) {
                     MenuItem item = menu.getItem(i);
@@ -907,12 +1048,88 @@
         return popupMenu;
     }
 
+    /**
+     * Called by the containing Activity to tell this Fragment that the dial button has been
+     * pressed.
+     */
+    public void dialButtonPressed() {
+        handleDialButtonPressed();
+    }
+
+    public void dialConferenceButtonPressed() {
+        // show dial conference screen if it is not shown
+        // If it is already shown, show normal dial screen
+        boolean show = (mRecipients != null) && !mRecipients.isShown();
+        Log.d(TAG, "dialConferenceButtonPressed show " + show);
+        if (show) {
+            showDialConference(show);
+        } else {
+            handleDialButtonPressed();
+            showDialConference(!show);
+        }
+    }
+
+    public void showDialConference(boolean enabled) {
+        // Check if onCreateView() is already called by checking one of View
+        // objects.
+        if (!isLayoutReady()) {
+            return;
+        }
+        Log.d(TAG, "showDialConference " + enabled);
+        /*
+         * if enabled is true then pick child views that should be
+         * visible/invisible when dialpad is choosen from conference dial button
+         * if enabled is false then pick child views that should be
+         * visible/invisible when dialpad is choosen from other buttons
+         */
+
+        // viewable when choosen through conference button
+        int conferenceButtonVisibility = (enabled ? View.VISIBLE : View.GONE);
+        // not viewable when choosen through conference button
+        int nonConferenceButtonVisibility = (enabled ? View.GONE : View.VISIBLE);
+
+        // change the image visibility of the button
+        if (mRecipients != null)
+            mRecipients.setVisibility(conferenceButtonVisibility);
+        if (mDigits != null)
+            mDigits.setVisibility(nonConferenceButtonVisibility);
+        if (mDelete != null)
+            mDelete.setVisibility(nonConferenceButtonVisibility);
+        if (mDialpad != null)
+            mDialpad.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
+
+        if (enabled && (HostInterface)getActivity() != null) {
+            ((HostInterface)getActivity()).setConferenceDialButtonImage(enabled);
+        }
+    }
+
+    public void hideAndClearDialConference() {
+        // hide the image visibility of the button
+        if (mRecipients != null)
+            mRecipients.setVisibility(View.GONE);
+        if (mDigits != null)
+            mDigits.setVisibility(View.GONE);
+        if (mDelete != null)
+            mDelete.setVisibility(View.GONE);
+        if (mDialpad != null)
+            mDialpad.setVisibility(View.GONE);
+        ((DialtactsActivity) getActivity()).commitDialpadFragmentHide();
+    }
+
+    public boolean isRecipientsShown() {
+        return mRecipients != null && mRecipients.isShown();
+    }
+
     @Override
     public void onClick(View view) {
         int resId = view.getId();
         if (resId == R.id.dialpad_floating_action_button) {
-            view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-            handleDialButtonPressed();
+            if (isConfigAvailableNetwork) {
+                dialAfterNetworkCheck();
+            } else {
+                view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                handleDialButtonPressed();
+            }
         } else if (resId == R.id.deleteButton) {
             keyPressed(KeyEvent.KEYCODE_DEL);
         } else if (resId == R.id.digits) {
@@ -927,65 +1144,152 @@
         }
     }
 
+    private int getNumberfromId(int id) {
+        int number = -1;
+        switch(id) {
+            case R.id.zero: number = 0; break;
+            case R.id.one: number = 1; break;
+            case R.id.two: number = 2; break;
+            case R.id.three: number = 3; break;
+            case R.id.four: number = 4; break;
+            case R.id.five: number = 5; break;
+            case R.id.six: number = 6; break;
+            case R.id.seven: number = 7; break;
+            case R.id.eight: number = 8; break;
+            case R.id.nine: number = 9; break;
+        }
+        return number;
+    }
+
+    private void placeEmergencyCall() {
+        Resources resources = getContext().getResources();
+        String emergencyNumber = resources.getString(
+                    com.android.internal.R.string.power_key_emergency_number);
+        Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY);
+        intent.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, emergencyNumber, null));
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
     @Override
     public boolean onLongClick(View view) {
         final Editable digits = mDigits.getText();
         final int id = view.getId();
-        if (id == R.id.deleteButton) {
-            digits.clear();
-            return true;
-        } else if (id == R.id.one) {
-            if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
-                // We'll try to initiate voicemail and thus we want to remove irrelevant string.
-                removePreviousDigitIfPossible('1');
+        switch (id) {
+            case R.id.deleteButton: {
+                digits.clear();
+                return true;
+            }
+            case R.id.one: {
+                if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
+                    // We'll try to initiate voicemail and thus we want to remove irrelevant string.
+                    removePreviousDigitIfPossible('1');
 
-                List<PhoneAccountHandle> subscriptionAccountHandles =
-                        PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
-                boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
-                        TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(),
-                                PhoneAccount.SCHEME_VOICEMAIL));
-                boolean needsAccountDisambiguation = subscriptionAccountHandles.size() > 1
-                        && !hasUserSelectedDefault;
+                    List<PhoneAccountHandle> subscriptionAccountHandles =
+                            PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
+                    boolean hasUserSelectedDefault = subscriptionAccountHandles.contains(
+                            TelecomUtil.getDefaultOutgoingPhoneAccount(getActivity(),
+                                    PhoneAccount.SCHEME_VOICEMAIL));
+                    boolean needsAccountDisambiguation = subscriptionAccountHandles.size() > 1
+                            && !hasUserSelectedDefault;
 
-                if (needsAccountDisambiguation || isVoicemailAvailable()) {
-                    // On a multi-SIM phone, if the user has not selected a default
-                    // subscription, initiate a call to voicemail so they can select an account
-                    // from the "Call with" dialog.
-                    callVoicemail();
-                } else if (getActivity() != null) {
-                    // Voicemail is unavailable maybe because Airplane mode is turned on.
-                    // Check the current status and show the most appropriate error message.
+                    if (needsAccountDisambiguation || isVoicemailAvailable()) {
+                        // On a multi-SIM phone, if the user has not selected a default
+                        // subscription, initiate a call to voicemail so they can select an account
+                        // from the "Call with" dialog.
+                        callVoicemail();
+                    } else if (getActivity() != null) {
+                        // Voicemail is unavailable maybe because Airplane mode is turned on.
+                        // Check the current status and show the most appropriate error message.
+                        final boolean isAirplaneModeOn =
+                                Settings.System.getInt(getActivity().getContentResolver(),
+                                        Settings.System.AIRPLANE_MODE_ON, 0) != 0;
+                        if (isAirplaneModeOn) {
+                            DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
+                                    R.string.dialog_voicemail_airplane_mode_message);
+                            dialogFragment.show(getFragmentManager(),
+                                "voicemail_request_during_airplane_mode");
+                        } else {
+                            DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
+                                    R.string.dialog_voicemail_not_ready_message);
+                            dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
+                        }
+                    }
+                    return true;
+                }
+                return false;
+            }
+            case R.id.zero: {
+                if (mPressedDialpadKeys.contains(view)) {
+                    // If the zero key is currently pressed, then the long press occurred by touch
+                    // (and not via other means like certain accessibility input methods).
+                    // Remove the '0' that was input when the key was first pressed.
+                    removePreviousDigitIfPossible('0');
+                }
+                keyPressed(KeyEvent.KEYCODE_PLUS);
+                stopTone();
+                mPressedDialpadKeys.remove(view);
+                return true;
+            }
+            case R.id.digits: {
+                mDigits.setCursorVisible(true);
+                return false;
+            }
+            case R.id.star: {
+                if (mDigits.length() > 1) {
+                    // Remove tentative input ('*') done by onTouch().
+                    removePreviousDigitIfPossible('*');
+                    keyPressed(KeyEvent.KEYCODE_COMMA);
+                    stopTone();
+                    mPressedDialpadKeys.remove(view);
+                    return true;
+                }
+                return false;
+            }
+            case R.id.pound: {
+                if (mDigits.length() > 1) {
+                    // Remove tentative input ('#') done by onTouch().
+                    removePreviousDigitIfPossible('#');
+                    keyPressed(KeyEvent.KEYCODE_SEMICOLON);
+                    stopTone();
+                    mPressedDialpadKeys.remove(view);
+                    return true;
+                }
+                return false;
+            }
+            case R.id.two:
+            case R.id.three:
+            case R.id.four:
+            case R.id.five:
+            case R.id.six:
+            case R.id.seven:
+            case R.id.eight:
+            case R.id.nine: {
+                if (mDigits.length() == 1) {
+                    //removePreviousDigitIfPossible();
+                    String property = SystemProperties.get(PROPERTY_RADIO_ATEL_CARRIER);
+                    boolean isCarrierOneSupported = CARRIER_ONE_DEFAULT_MCC_MNC.equals(property);
+                    if (isCarrierOneSupported &&
+                           (getNumberfromId(id) == getContext().getResources().getInteger(
+                            R.integer.speed_dial_emergency_number_assigned_key))) {
+                        placeEmergencyCall();
+                        return true;
+                    }
                     final boolean isAirplaneModeOn =
                             Settings.System.getInt(getActivity().getContentResolver(),
                                     Settings.System.AIRPLANE_MODE_ON, 0) != 0;
                     if (isAirplaneModeOn) {
                         DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
-                                R.string.dialog_voicemail_airplane_mode_message);
+                                R.string.dialog_speed_dial_airplane_mode_message);
                         dialogFragment.show(getFragmentManager(),
-                                "voicemail_request_during_airplane_mode");
+                                "speed_dial_request_during_airplane_mode");
                     } else {
-                        DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
-                                R.string.dialog_voicemail_not_ready_message);
-                        dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
+                        callSpeedNumber(id);
                     }
+                    return true;
                 }
-                return true;
+                return false;
             }
-            return false;
-        } else if (id == R.id.zero) {
-            if (mPressedDialpadKeys.contains(view)) {
-                // If the zero key is currently pressed, then the long press occurred by touch
-                // (and not via other means like certain accessibility input methods).
-                // Remove the '0' that was input when the key was first pressed.
-                removePreviousDigitIfPossible('0');
-            }
-            keyPressed(KeyEvent.KEYCODE_PLUS);
-            stopTone();
-            mPressedDialpadKeys.remove(view);
-            return true;
-        } else if (id == R.id.digits) {
-            mDigits.setCursorVisible(true);
-            return false;
         }
         return false;
     }
@@ -1085,32 +1389,50 @@
      * case described above).
      */
     private void handleDialButtonPressed() {
-        if (isDigitsEmpty()) { // No number entered.
+        if (isDigitsEmpty() && (mRecipients == null || !mRecipients.isShown())) {
+            // No number entered.
             handleDialButtonClickWithEmptyDigits();
         } else {
-            final String number = mDigits.getText().toString();
-
-            // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
-            // test equipment.
-            // TODO: clean it up.
-            if (number != null
-                    && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
-                    && number.matches(mProhibitedPhoneNumberRegexp)) {
-                Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
-                if (getActivity() != null) {
-                    DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
-                            R.string.dialog_phone_call_prohibited_message);
-                    dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
-                }
-
-                // Clear the digits just in case.
-                clearDialpad();
+            boolean isDigitsShown = mDigits.isShown();
+            final String number = isDigitsShown ? mDigits.getText().toString() :
+                    mRecipients.getText().toString().trim();
+            if (isDigitsShown && isDigitsEmpty()) {
+                handleDialButtonClickWithEmptyDigits();
+            } else if (mAddParticipant && isPhoneInUse() && isDigitsEmpty()
+                    && mRecipients.isShown() && isRecipientEmpty()) {
+                // mRecipients must be empty
+                // TODO add support for conference URI in last number dialed
+                // use ErrorDialogFragment instead? also see
+                // android.app.AlertDialog
+                android.widget.Toast.makeText(getActivity(),
+                        "Error: Cannot dial.  Please provide conference recipients.",
+                        android.widget.Toast.LENGTH_SHORT).show();
             } else {
-                final Intent intent = new CallIntentBuilder(number).
-                        setCallInitiationType(LogState.INITIATION_DIALPAD)
-                        .build();
-                DialerUtils.startActivityWithErrorToast(getActivity(), intent);
-                hideAndClearDialpad(false);
+                // "persist.radio.otaspdial" is a temporary hack needed for one carrier's automated
+                // test equipment.
+                // TODO: clean it up.
+                if (number != null
+                        && !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
+                        && number.matches(mProhibitedPhoneNumberRegexp)) {
+                    Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
+                    if (getActivity() != null) {
+                        DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
+                                R.string.dialog_phone_call_prohibited_message);
+                        dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
+                    }
+
+                    // Clear the digits just in case.
+                    clearDialpad();
+                } else {
+                    final Intent intent = CallUtil.getCallIntent(number);
+                    if (!isDigitsShown) {
+                        // must be dial conference add extra
+                        intent.putExtra(EXTRA_DIAL_CONFERENCE_URI, true);
+                    }
+                    intent.putExtra(ADD_PARTICIPANT_KEY, mAddParticipant && isPhoneInUse());
+                    DialerUtils.startActivityWithErrorToast(getActivity(), intent);
+                    hideAndClearDialpad(false);
+                }
             }
         }
     }
@@ -1457,6 +1779,10 @@
             CallSubjectDialog.start(getActivity(), mDigits.getText().toString());
             hideAndClearDialpad(false);
             return true;
+        } else if (resId == R.id.menu_add_to_4g_conference_call){
+            getActivity().startActivity(IntentUtil.getConferenceDialerIntent(
+                        mDigits.getText().toString()));
+            return true;
         } else {
             return false;
         }
@@ -1594,6 +1920,13 @@
     }
 
     /**
+     * @return true if the widget with the mRecipients is empty.
+     */
+    private boolean isRecipientEmpty() {
+        return  (mRecipients == null) || (mRecipients.length() == 0);
+    }
+
+    /**
      * Starts the asyn query to get the last dialed/outgoing
      * number. When the background query finishes, mLastNumberDialed
      * is set to the last dialed number or an empty string if none
@@ -1692,4 +2025,95 @@
         }
     }
 
+    private void callSpeedNumber(int id) {
+        int number;
+
+        switch(id) {
+            case R.id.two: number = 2; break;
+            case R.id.three: number = 3; break;
+            case R.id.four: number = 4; break;
+            case R.id.five: number = 5; break;
+            case R.id.six: number = 6; break;
+            case R.id.seven: number = 7; break;
+            case R.id.eight: number = 8; break;
+            case R.id.nine: number = 9; break;
+            default: return;
+        }
+
+        String phoneNumber = SpeedDialUtils.getNumber(getActivity(), number);
+        if (phoneNumber == null) {
+            showNoSpeedNumberDialog(number);
+        } else {
+            final DialtactsActivity activity = getActivity() instanceof DialtactsActivity
+                    ? (DialtactsActivity) getActivity() : null;
+            final Intent intent = CallUtil.getCallIntent(phoneNumber);
+            DialerUtils.startActivityWithErrorToast(getActivity(), intent);
+            hideAndClearDialpad(false);
+        }
+    }
+
+    private void showNoSpeedNumberDialog(final int number) {
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.speed_dial_unassigned_dialog_title)
+                .setMessage(getString(R.string.speed_dial_unassigned_dialog_message, number))
+                .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // go to speed dial setting screen to set speed dial number.
+                        Intent intent = new Intent(getActivity(), SpeedDialListActivity.class);
+                        startActivity(intent);
+                    }
+                })
+                .setNegativeButton(R.string.no, null)
+                .show();
+    }
+
+    private void dialAfterNetworkCheck() {
+        if (ActivityCompat.checkSelfPermission(getContext(),
+                Manifest.permission.ACCESS_COARSE_LOCATION) !=
+            PackageManager.PERMISSION_GRANTED) {
+            requestPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION},
+                    PERMISSION_REQUEST_CODE_LOCATION);
+        } else {
+            if(WifiCallUtils.shallShowWifiCallDialog(getActivity())) {
+                 WifiCallUtils.showWifiCallDialog(getActivity());
+                 WifiCallUtils.showWifiCallNotification(getActivity());
+             } else {
+                 getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                 handleDialButtonPressed();
+             }
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        switch (requestCode) {
+            case PERMISSION_REQUEST_CODE_LOCATION:
+                if ( grantResults.length > 0 && grantResults[0] ==
+                        PackageManager.PERMISSION_GRANTED) {
+                    if(WifiCallUtils.shallShowWifiCallDialog(getActivity())) {
+                        WifiCallUtils.showWifiCallDialog(getActivity());
+                    } else {
+                        getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                        handleDialButtonPressed();
+                    }
+                } else {
+                    getView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+                    handleDialButtonPressed();
+                }
+                break;
+            case READ_WRITE_CALL_LOG_PERMISSION_REQUEST_CODE:
+                for (int i = 0; i < grantResults.length; i++) {
+                    mHasReadAndWriteCallLogPermission &=
+                            PackageManager.PERMISSION_GRANTED == grantResults[i];
+                }
+                if (mHasReadAndWriteCallLogPermission) {
+                    queryLastOutgoingCall();
+                }
+                break;
+            default:
+                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
 }
diff --git a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
index 93b649b..16776d4 100644
--- a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
+++ b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
@@ -65,7 +65,7 @@
         mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
 
         /** Constructs a name matcher object for matching names. */
-        mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
+        mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap(), mContext);
     }
 
     /**
@@ -103,6 +103,8 @@
             row[PhoneQuery.PHOTO_ID] = contact.photoId;
             row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
             row[PhoneQuery.CARRIER_PRESENCE] = contact.carrierPresence;
+            row[PhoneQuery.PHONE_ACCOUNT_TYPE] = contact.accountType;
+            row[PhoneQuery.PHONE_ACCOUNT_NAME] = contact.accountName;
             cursor.addRow(row);
         }
         return cursor;
diff --git a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
index a54fe16..f82dbf8 100644
--- a/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
+++ b/src/com/android/dialer/dialpad/SmartDialNameMatcher.java
@@ -17,13 +17,17 @@
 package com.android.dialer.dialpad;
 
 import android.support.annotation.Nullable;
+import android.content.Context;
 import android.text.TextUtils;
+import android.util.Log;
 
+import com.android.dialer.database.DialerDatabaseHelper;
 import com.android.dialer.dialpad.SmartDialPrefix.PhoneNumberTokens;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 
 /**
@@ -35,6 +39,8 @@
  */
 public class SmartDialNameMatcher {
 
+    private final static String TAG = "SmartDialNameMatcher";
+
     private String mQuery;
 
     // Whether or not we allow matches like 57 - (J)ohn (S)mith
@@ -53,14 +59,24 @@
     private String mNameMatchMask = "";
     private String mPhoneNumberMatchMask = "";
 
+    private Context mContext;
+    private String mSchar = "+*#-.(,)/ ";
+    private Object mMultiMatchObject;
+    private Method mMultiMatchMethod;
+
     @VisibleForTesting
-    public SmartDialNameMatcher(String query) {
-        this(query, LATIN_SMART_DIAL_MAP);
+    public SmartDialNameMatcher(String query, Context context) {
+        this(query, LATIN_SMART_DIAL_MAP, context);
     }
 
-    public SmartDialNameMatcher(String query, SmartDialMap map) {
+    public SmartDialNameMatcher(String query, SmartDialMap map, Context context) {
         mQuery = query;
         mMap = map;
+        mContext = context;
+        mMultiMatchObject = DialerDatabaseHelper.getInstance(mContext)
+                .getMultiMatchObject();
+        mMultiMatchMethod = DialerDatabaseHelper.getInstance(mContext)
+                .getMultiMatchMethod();
     }
 
     /**
@@ -135,22 +151,6 @@
 
         // Try matching the number as is
         SmartDialMatchPosition matchPos = matchesNumberWithOffset(phoneNumber, query, 0);
-        if (matchPos == null) {
-            final PhoneNumberTokens phoneNumberTokens =
-                    SmartDialPrefix.parsePhoneNumber(phoneNumber);
-
-            if (phoneNumberTokens == null) {
-                return matchPos;
-            }
-            if (phoneNumberTokens.countryCodeOffset != 0) {
-                matchPos = matchesNumberWithOffset(phoneNumber, query,
-                        phoneNumberTokens.countryCodeOffset);
-            }
-            if (matchPos == null && phoneNumberTokens.nanpCodeOffset != 0 && useNanp) {
-                matchPos = matchesNumberWithOffset(phoneNumber, query,
-                        phoneNumberTokens.nanpCodeOffset);
-            }
-        }
         if (matchPos != null) {
             replaceBitInMask(builder, matchPos);
             mPhoneNumberMatchMask = builder.toString();
@@ -195,40 +195,47 @@
      */
     private SmartDialMatchPosition matchesNumberWithOffset(String phoneNumber, String query,
             int offset) {
-        if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)) {
+        if (TextUtils.isEmpty(phoneNumber) || TextUtils.isEmpty(query)
+                || query.length() > phoneNumber.length()) {
             return null;
         }
-        int queryAt = 0;
-        int numberAt = offset;
-        for (int i = offset; i < phoneNumber.length(); i++) {
-            if (queryAt == query.length()) {
-                break;
-            }
-            char ch = phoneNumber.charAt(i);
-            if (mMap.isValidDialpadNumericChar(ch)) {
-                if (ch != query.charAt(queryAt)) {
-                    return null;
+
+        String phoneNum = phoneNumber.replaceAll("[\\+\\*\\#\\-\\.\\(\\,\\)\\/ ]", "");
+        if (!TextUtils.isEmpty(phoneNum) && phoneNum.contains(query)) {
+            // firstly, find the start position in original phone number.
+            int start = phoneNum.indexOf(query);
+            int length = phoneNumber.length();
+            for (int i = start; i < length; i++) {
+                char ch = phoneNumber.charAt(i);
+                if (ch != phoneNum.charAt(start)) {
+                    continue;
                 }
-                queryAt++;
-            } else {
-                if (queryAt == 0) {
-                    // Found a separator before any part of the query was matched, so advance the
-                    // offset to avoid prematurely highlighting separators before the rest of the
-                    // query.
-                    // E.g. don't highlight the first '-' if we're matching 1-510-111-1111 with
-                    // '510'.
-                    // However, if the current offset is 0, just include the beginning separators
-                    // anyway, otherwise the highlighting ends up looking weird.
-                    // E.g. if we're matching (510)-111-1111 with '510', we should include the
-                    // first '('.
-                    if (offset != 0) {
-                        offset++;
-                    }
+                if (phoneNumber.substring(i).replaceAll("[\\+\\*\\#\\-\\.\\(\\,\\)\\/ ]", "")
+                        .indexOf(query) == 0) {
+                    start = i;
+                    break;
                 }
             }
-            numberAt++;
+            // secondly, find the end position in original phone number.
+            int specialCount = 0;
+            int queryLength = query.length();
+            int end = start + queryLength;
+            for (int i = start; i < length; i++) {
+                char ch = phoneNumber.charAt(i);
+                if (mSchar.indexOf(ch) != -1) {
+                    specialCount++;
+                    continue;
+                }
+
+                if (i - start + 1 - specialCount == queryLength) {
+                    end = i + 1;
+                    break;
+                }
+            }
+            return new SmartDialMatchPosition(start, end);
+        } else {
+            return null;
         }
-        return new SmartDialMatchPosition(0 + offset, numberAt);
     }
 
     /**
@@ -412,7 +419,11 @@
 
     public boolean matches(String displayName) {
         mMatchPositions.clear();
-        return matchesCombination(displayName, mQuery, mMatchPositions);
+        if (mMultiMatchObject != null && mMultiMatchMethod != null) {
+            return matchesMultiLanguage(displayName, mQuery, mMatchPositions);
+        } else {
+            return matchesCombination(displayName, mQuery, mMatchPositions);
+        }
     }
 
     public ArrayList<SmartDialMatchPosition> getMatchPositions() {
@@ -436,4 +447,47 @@
     public String getQuery() {
         return mQuery;
     }
+
+    boolean matchesMultiLanguage(String displayName, String query,
+            ArrayList<SmartDialMatchPosition> matchList) {
+        StringBuilder builder = new StringBuilder();
+        constructEmptyMask(builder, displayName.length());
+        mNameMatchMask = builder.toString();
+        final int nameLength = displayName.length();
+        final int queryLength = query.length();
+
+        if (queryLength == 0) {
+            return false;
+        }
+        // contains the start, not the end poing
+        int[] indexs = null;
+        try {
+            indexs = (int[]) mMultiMatchMethod.invoke(mMultiMatchObject,
+                    query, displayName, 0);
+            // mMultimatch.getMatchStringIndex(query, displayName, 0);
+            if (indexs == null) {
+                return false;
+            }
+        } catch (Exception e) {
+            Log.d(TAG, "Exception:" + e);
+            return false;
+        }
+
+        for (int i = 0; i < indexs.length; i = i + 2) {
+            int start = indexs[i];
+            int end = indexs[i + 1];
+            if (start >= 0 && end >= 0) {
+                matchList.add(new SmartDialMatchPosition(start, end + 1));
+            } else {
+                Log.d(TAG, "Invalid index, start is:" + start + " end is:"
+                        + end + " for name:" + displayName);
+            }
+        }
+
+        for (SmartDialMatchPosition match : matchList) {
+            replaceBitInMask(builder, match);
+        }
+        mNameMatchMask = builder.toString();
+        return true;
+    }
 }
diff --git a/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java b/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
index 3c60a96..6255dec 100644
--- a/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
+++ b/src/com/android/dialer/filterednumber/BlockNumberDialogFragment.java
@@ -238,7 +238,9 @@
         final OnUnblockNumberListener onUndoListener = new OnUnblockNumberListener() {
             @Override
             public void onUnblockComplete(int rows, ContentValues values) {
-                Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
+                if (mParentView != null) {
+                    Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
+                }
                 if (callback != null) {
                     callback.onChangeFilteredNumberUndo();
                 }
@@ -256,12 +258,12 @@
                         mHandler.unblock(onUndoListener, uri);
                     }
                 };
-
-                Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.block_number_undo, undoListener)
-                        .setActionTextColor(actionTextColor)
-                        .show();
-
+                if (mParentView != null) {
+                    Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
+                            .setAction(R.string.block_number_undo, undoListener)
+                            .setActionTextColor(actionTextColor)
+                            .show();
+                }
                 if (callback != null) {
                     callback.onFilterNumberSuccess();
                 }
@@ -287,7 +289,9 @@
         final OnBlockNumberListener onUndoListener = new OnBlockNumberListener() {
             @Override
             public void onBlockComplete(final Uri uri) {
-                Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
+                if (mParentView != null) {
+                    Snackbar.make(mParentView, undoMessage, Snackbar.LENGTH_LONG).show();
+                }
                 if (callback != null) {
                     callback.onChangeFilteredNumberUndo();
                 }
@@ -305,12 +309,12 @@
                         mHandler.blockNumber(onUndoListener, values);
                     }
                 };
-
-                Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
-                        .setAction(R.string.block_number_undo, undoListener)
-                        .setActionTextColor(actionTextColor)
-                        .show();
-
+                if (mParentView != null) {
+                    Snackbar.make(mParentView, message, Snackbar.LENGTH_LONG)
+                            .setAction(R.string.block_number_undo, undoListener)
+                            .setActionTextColor(actionTextColor)
+                            .show();
+                }
                 if (callback != null) {
                     callback.onUnfilterNumberSuccess();
                 }
diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
index fe27a25..ed1f825 100644
--- a/src/com/android/dialer/list/SmartDialNumberListAdapter.java
+++ b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
@@ -45,7 +45,7 @@
 
     public SmartDialNumberListAdapter(Context context) {
         super(context);
-        mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap());
+        mNameMatcher = new SmartDialNameMatcher("", SmartDialPrefix.getMap(), context);
         setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);
 
         if (DEBUG) {
diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java
index dc1e214..303c73a 100644
--- a/src/com/android/dialer/settings/DialerSettingsActivity.java
+++ b/src/com/android/dialer/settings/DialerSettingsActivity.java
@@ -30,6 +30,8 @@
 
 import com.android.contacts.common.compat.CompatUtils;
 import com.android.contacts.common.compat.TelephonyManagerCompat;
+import com.android.dialer.SpeedDialListActivity;
+import com.android.contacts.common.CallUtil;
 import com.android.dialer.R;
 import com.android.dialer.compat.FilteredNumberCompat;
 import com.android.dialer.compat.SettingsCompat;
@@ -83,6 +85,14 @@
             target.add(quickResponseSettingsHeader);
         }
 
+        Header speedDialSettingsHeader = new Header();
+        Intent speedDialSettingsIntent = new Intent(this, SpeedDialListActivity.class);
+        speedDialSettingsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        speedDialSettingsHeader.titleRes = R.string.speed_dial_settings;
+        speedDialSettingsHeader.intent = speedDialSettingsIntent;
+        target.add(speedDialSettingsHeader);
+
         TelephonyManager telephonyManager =
                 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
 
@@ -127,6 +137,27 @@
             accessibilitySettingsHeader.intent = accessibilitySettingsIntent;
             target.add(accessibilitySettingsHeader);
         }
+        //video calling
+        boolean enablePresence = this.getResources().getBoolean(
+                R.bool.config_regional_presence_enable);
+        if(enablePresence){
+            Header videocallingHeader = new Header();
+            videocallingHeader.titleRes = R.string.video_call;
+            videocallingHeader.fragment = VideoCallingSettingsFragment.class.getName();
+            target.add(videocallingHeader);
+        }
+
+        boolean usageEnable = getResources().getBoolean(
+                R.bool.config_regional_call_data_usage_enable);
+        if (usageEnable) {
+            final Header historyInfoHeader = new Header();
+            historyInfoHeader.titleRes = R.string.call_data_info_label;
+            historyInfoHeader.summaryRes = R.string.call_data_info_description;
+            historyInfoHeader.intent = new Intent(Intent.ACTION_MAIN);
+            historyInfoHeader.intent
+                    .setAction("android.intent.action.SHOW_TIMERINFO");
+            target.add(historyInfoHeader);
+        }
     }
 
     /**
diff --git a/src/com/android/dialer/settings/VideoCallingSettingsFragment.java b/src/com/android/dialer/settings/VideoCallingSettingsFragment.java
new file mode 100644
index 0000000..7a25901
--- /dev/null
+++ b/src/com/android/dialer/settings/VideoCallingSettingsFragment.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2015-2016, 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.
+ **/
+
+package com.android.dialer.settings;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.SwitchPreference;
+import android.util.Log;
+
+import java.lang.Object;
+import java.lang.Override;
+import java.lang.String;
+
+import com.android.contacts.common.CallUtil;
+import com.android.dialer.R;
+
+public class VideoCallingSettingsFragment extends PreferenceFragment implements
+        Preference.OnPreferenceChangeListener {
+
+    private final static String KEY_VIDEO_CALL = "video_calling_preference";
+    private SwitchPreference mVideoCallingPreference;
+    private Context mContext;
+    private static final String TAG = "VideoCallingSettingsFragment";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.video_calling_settings);
+
+        mContext = getActivity();
+        mVideoCallingPreference = (SwitchPreference)findPreference(KEY_VIDEO_CALL);
+        mVideoCallingPreference.setOnPreferenceChangeListener(this);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        int enable = Settings.System.getInt(mContext.getContentResolver(),
+                CallUtil.DIALOG_VIDEO_CALLING,CallUtil.DISABLE_VIDEO_CALLING);
+        if(mVideoCallingPreference != null)
+            mVideoCallingPreference.setChecked(enable == CallUtil.ENABLE_VIDEO_CALLING);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object objValue) {
+        if (preference == mVideoCallingPreference) {
+            boolean isCheck = mVideoCallingPreference.isChecked();
+            CallUtil.createVideoCallingDialog(isCheck , mContext);
+            boolean isSaved = CallUtil.saveVideoCallConfig(mContext,isCheck);
+            Log.d(TAG, "onPreferenceChange isSaved = " + isSaved);
+        }
+        return true;
+    }
+
+}
diff --git a/src/com/android/dialer/util/AppCompatConstants.java b/src/com/android/dialer/util/AppCompatConstants.java
index 1d52eee..dd6c024 100644
--- a/src/com/android/dialer/util/AppCompatConstants.java
+++ b/src/com/android/dialer/util/AppCompatConstants.java
@@ -27,4 +27,10 @@
     public static final int CALLS_REJECTED_TYPE = 5;
     // Added to android.provider.CallLog.Calls in N+.
     public static final int CALLS_BLOCKED_TYPE = 6;
+    public static final int INCOMING_IMS_TYPE = 8;
+    public static final int OUTGOING_IMS_TYPE = 9;
+    public static final int MISSED_IMS_TYPE = 10;
+    public static final int INCOMING_WIFI_TYPE = Calls.INCOMING_WIFI_TYPE;
+    public static final int OUTGOING_WIFI_TYPE = Calls.OUTGOING_WIFI_TYPE;
+    public static final int MISSED_WIFI_TYPE = Calls.MISSED_WIFI_TYPE;
 }
diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java
index 95d6a81..f2b04f9 100644
--- a/src/com/android/dialer/util/DialerUtils.java
+++ b/src/com/android/dialer/util/DialerUtils.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.content.SharedPreferences;
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Bundle;
@@ -48,6 +49,9 @@
  */
 public class DialerUtils {
 
+    private static final String PREFS_MESSAGE = "video_call_welcome";
+    private static final String KEY_STATE = "message-repeat";
+    private static final String KEY_FIRST_LAUNCH = "first-launch";
     /**
      * Attempts to start an activity and displays a toast with the default error message if the
      * activity is not found, instead of throwing an exception.
@@ -192,4 +196,49 @@
             imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
         }
     }
+
+
+    /**
+     * @return true if it is the first launch.
+     */
+    public static boolean isFirstLaunch(Context context) {
+        final SharedPreferences prefs = context.getSharedPreferences(
+                PREFS_MESSAGE, Context.MODE_PRIVATE);
+        boolean isFirstLaunch = prefs.getBoolean(KEY_FIRST_LAUNCH, true);
+        if (isFirstLaunch) {
+            prefs.edit().putBoolean(KEY_FIRST_LAUNCH, false).apply();
+        }
+        return isFirstLaunch;
+    }
+
+    /**
+     * @return true if the Welcome Screen shall be presented to the user, false otherwise.
+     */
+    public static boolean canShowWelcomeScreen(Context context) {
+        final SharedPreferences prefs = context.getSharedPreferences(
+                PREFS_MESSAGE, Context.MODE_PRIVATE);
+        return prefs.getBoolean(KEY_STATE, false);
+    }
+
+
+    /**
+     * Save the state of Welcome Screen.
+     *
+     *@param context
+     *@param show if the Welcome Screen should be presented
+     */
+    public static void setShowingState(Context context, boolean show) {
+        final SharedPreferences prefs = context.getSharedPreferences(
+                PREFS_MESSAGE, Context.MODE_PRIVATE);
+        prefs.edit().putBoolean(KEY_STATE, show).apply();
+    }
+
+    /**
+     * @return true if calllog inserted earlier when dial a ConfURI call.
+     */
+    public static boolean isConferenceURICallLog(String number, String postDialDigits) {
+        return (number == null || number.contains(";") || number.contains(",")) &&
+                (postDialDigits == null || postDialDigits.equals(""));
+    }
+
 }
diff --git a/src/com/android/dialer/util/IntentUtil.java b/src/com/android/dialer/util/IntentUtil.java
index 5a4a80b..bb87a49 100644
--- a/src/com/android/dialer/util/IntentUtil.java
+++ b/src/com/android/dialer/util/IntentUtil.java
@@ -17,14 +17,21 @@
 package com.android.dialer.util;
 
 import android.content.Intent;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.content.Context;
+import com.android.dialer.R;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
 
 import com.android.contacts.common.CallUtil;
+import java.util.List;
 
 /**
  * Utilities for creation of intents in Dialer, such as {@link Intent#ACTION_CALL}.
@@ -155,4 +162,51 @@
             intent.putExtra(ContactsContract.Intents.Insert.PHONE_TYPE, phoneNumberType);
         }
     }
+    /**
+     * if true, conference dialer  is enabled.
+     */
+    public static boolean isConferDialerEnabled(Context context) {
+        boolean isEnabled = false;
+        List<SubscriptionInfo> subInfos = SubscriptionManager.from(context)
+                .getActiveSubscriptionInfoList();
+        if (subInfos != null) {
+            for (SubscriptionInfo subInfo : subInfos ) {
+                if (SubscriptionManager.isValidSubscriptionId(subInfo.getSubscriptionId())) {
+                    Resources subRes = SubscriptionManager.getResourcesForSubId(context,
+                            subInfo.getSubscriptionId());
+                    if (subRes.getBoolean(R.bool.config_enable_conference_dialer)) {
+                        TelephonyManager telephonyMgr = (TelephonyManager) context.
+                                getSystemService(Context.TELEPHONY_SERVICE);
+                        isEnabled = telephonyMgr.isImsRegisteredForSubscriber(subInfo
+                                .getSubscriptionId());
+                        if (isEnabled) {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return isEnabled;
+    }
+
+    /**
+     * get intent to start conference dialer
+     * with this intent, we can originate an conference call
+     */
+    public static Intent getConferenceDialerIntent(String number) {
+        Intent intent = new Intent("android.intent.action.ADDPARTICIPANT");
+        intent.putExtra("confernece_number_key", number);
+        return intent;
+    }
+
+    /**
+     * used to get intent to start conference dialer
+     * with this intent, we can add participants to an existing conference call
+     */
+    public static Intent getAddParticipantsIntent(String number) {
+        Intent intent = new Intent("android.intent.action.ADDPARTICIPANT");
+        intent.putExtra("add_participant", true);
+        intent.putExtra("current_participant_list", number);
+        return intent;
+    }
 }
diff --git a/src/com/android/dialer/util/PresenceHelper.java b/src/com/android/dialer/util/PresenceHelper.java
new file mode 100644
index 0000000..cf94df0
--- /dev/null
+++ b/src/com/android/dialer/util/PresenceHelper.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2015-2016, 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.
+ **/
+package com.android.dialer.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.incallui.Log;
+
+import org.codeaurora.presenceserv.IPresenceService;
+import org.codeaurora.presenceserv.IPresenceServiceCB;
+
+/**
+ * General presnece service utility methods for the Dialer.
+ */
+public class PresenceHelper {
+
+    private static final String TAG = "PresenceHelper";
+    private static volatile IPresenceService mService;
+    private static boolean mIsBound;
+
+    private static ServiceConnection mConnection = new ServiceConnection() {
+
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            Log.d(TAG, "PresenceService connected");
+            mService = IPresenceService.Stub.asInterface(service);
+            try {
+                mService.registerCallback(mCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "PresenceService registerCallback error " + e);
+            }
+        }
+        public void onServiceDisconnected(ComponentName className) {
+            Log.d(TAG, "PresenceService disconnected");
+            mService = null;
+        }
+    };
+
+    private static IPresenceServiceCB mCallback = new IPresenceServiceCB.Stub() {
+
+        public void setIMSEnabledCB() {
+            Log.d(TAG, "PresenceService setIMSEnabled callback");
+        }
+
+    };
+
+    public static void bindService(Context context) {
+        Log.d(TAG, "PresenceService BindService ");
+        Intent intent = new Intent(IPresenceService.class.getName());
+        intent.setClassName("com.qualcomm.qti.presenceserv",
+                "com.qualcomm.qti.presenceserv.PresenceService");
+        mIsBound = context.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    public static void unbindService(Context context) {
+        Log.d(TAG, "PresenceService unbindService");
+        if (mService == null) {
+            Log.d(TAG, "PresenceService unbindService: mService is null");
+            return;
+        }
+        try {
+            mService.unregisterCallback(mCallback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "PresenceService unregister error " + e);
+        }
+        if (mIsBound) {
+            Log.d(TAG, "PresenceService unbind");
+            context.unbindService(mConnection);
+            mIsBound = false;
+        }
+    }
+
+    public static boolean isBound() {
+        return mIsBound;
+    }
+
+    public static boolean startAvailabilityFetch(String number){
+        Log.d(TAG, "startAvailabilityFetch   number " + number);
+        if (mService == null) {
+             Log.d(TAG, "startAvailabilityFetch mService is null");
+             return false;
+        }
+        try {
+            return mService.invokeAvailabilityFetch(number);
+        } catch (Exception e) {
+            Log.d(TAG, "getVTCapOfContact ERROR " + e);
+        }
+        return false;
+    }
+
+    public static boolean getVTCapability(String number) {
+        Log.d(TAG, "getVTCapability   number " + number);
+        if (null == mService) {
+            Log.d(TAG, "getVTCapability mService is null");
+            return false;
+        }
+        try {
+            return mService.hasVTCapability(number);
+        } catch (Exception e) {
+            Log.d(TAG, "getVTCapability ERROR " + e);
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/dialer/util/WifiCallUtils.java b/src/com/android/dialer/util/WifiCallUtils.java
new file mode 100644
index 0000000..5e789ed
--- /dev/null
+++ b/src/com/android/dialer/util/WifiCallUtils.java
@@ -0,0 +1,225 @@
+/*
+* 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.
+*/
+
+package com.android.dialer.util;
+
+import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.provider.Settings;
+import android.telephony.CellInfo;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.dialer.R;
+
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class WifiCallUtils {
+
+    private static final String TAG = WifiCallUtils.class.getSimpleName();
+    private static final int DELAYED_TIME = 5 * 1000;
+    private static final int NOTIFICATION_WIFI_CALL_ID = 1;
+    private WindowManager mWindowManager;
+    private TextView mTextView;
+    private boolean mViewRemoved = true;
+
+    private static final String WIFI_CALL_READY = "wifi_call_ready";
+    private static final String WIFI_CALL_TURNON = "wifi_call_turnon";
+    private static final int WIFI_CALLING_DISABLED = 0;
+    private static final int WIFI_CALLING_ENABLED = 1;
+
+    public void addWifiCallReadyMarqueeMessage(Context context) {
+        if (mViewRemoved && isWifiCallReadyEnabled(context)) {
+            if (mWindowManager == null) mWindowManager =
+                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+            if(mTextView == null){
+                mTextView = new TextView(context);
+                Log.d(TAG, "mTextView is null, new mTextView = " + mTextView);
+                mTextView.setText(
+                        com.android.dialer.R.string.alert_call_over_wifi);
+                mTextView.setSingleLine(true);
+                mTextView.setEllipsize(android.text.TextUtils.TruncateAt.MARQUEE);
+                mTextView.setMarqueeRepeatLimit(-1);
+                mTextView.setFocusableInTouchMode(true);
+            } else {
+                Log.d(TAG, "mTextView is not null, mTextView = " + mTextView);
+            }
+
+            WindowManager.LayoutParams windowParam = new WindowManager.LayoutParams();
+            windowParam.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+            windowParam.format= 1;
+            windowParam.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+            windowParam.alpha = 1.0f;
+            windowParam.x = 0;
+            windowParam.y = -500;
+            windowParam.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            windowParam.width = (mWindowManager.getDefaultDisplay().getWidth()
+                    < mWindowManager.getDefaultDisplay().getHeight()
+                    ? mWindowManager.getDefaultDisplay().getWidth()
+                    : mWindowManager.getDefaultDisplay().getHeight()) - 64;
+            mWindowManager.addView(mTextView, windowParam);
+            mViewRemoved = false;
+            Log.d(TAG, "addWifiCallReadyMarqueeMessage, mWindowManager:" + mWindowManager
+                    + " addView, mTextView:" + mTextView
+                    + " addWifiCallReadyMarqueeMessage, mViewRemoved = " + mViewRemoved);
+
+            scheduleRemoveWifiCallReadyMarqueeMessageTimer();
+        }
+    }
+
+    public void removeWifiCallReadyMarqueeMessage() {
+        if (!mViewRemoved) {
+            mWindowManager.removeView(mTextView);
+            mViewRemoved = true;
+            Log.d(TAG, "removeWifiCallReadyMarqueeMessage, mWindowManager:" + mWindowManager
+                    + " removeView, mTextView:" + mTextView
+                    + " removeWifiCallReadyMarqueeMessage, mViewRemoved = " + mViewRemoved);
+        }
+    }
+
+    private void scheduleRemoveWifiCallReadyMarqueeMessageTimer() {
+        // Schedule a timer, 5s later, remove the message
+        new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Log.d(TAG, "Handler is running");
+                    removeWifiCallReadyMarqueeMessage();
+                }
+        }, DELAYED_TIME);
+        Log.d(TAG, "schedule timerTask");
+    }
+
+    private static boolean isCellularNetworkAvailable(Context context) {
+        boolean available = false;
+
+        TelephonyManager tm = (TelephonyManager) context.
+                getSystemService(Context.TELEPHONY_SERVICE);
+        List<CellInfo> cellInfoList = tm.getAllCellInfo();
+
+        if (cellInfoList != null) {
+            for (CellInfo cellinfo : cellInfoList) {
+                if (cellinfo.isRegistered()) {
+                    available = true;
+                }
+            }
+        }
+
+        return available;
+    }
+
+    public static void showWifiCallDialog(final Context context) {
+        String promptMessage = context.getString(com.android.dialer.R.string
+                .alert_call_no_cellular_coverage);
+        AlertDialog.Builder diaBuilder = new AlertDialog.Builder(context);
+        diaBuilder.setMessage(promptMessage);
+        diaBuilder.setPositiveButton(com.android.internal.R.string.ok, new OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
+                context.startActivity(intent);
+            }
+        });
+        diaBuilder.setOnCancelListener(new OnCancelListener() {
+            @Override
+            public void onCancel(DialogInterface dialog) {
+            }
+        });
+        diaBuilder.create().show();
+    }
+
+    public static boolean isWifiCallReadyEnabled(final Context context) {
+        return (Settings.Global.getInt(context.getContentResolver(),
+                WIFI_CALL_READY, WIFI_CALLING_DISABLED) == WIFI_CALLING_ENABLED);
+    }
+
+    public static boolean isWifiCallTurnOnEnabled(final Context context){
+        return (Settings.Global.getInt(context.getContentResolver(),
+                WIFI_CALL_TURNON, WIFI_CALLING_DISABLED) == WIFI_CALLING_ENABLED);
+    }
+
+    public static boolean shallShowWifiCallDialog(final Context context) {
+        boolean wifiCallTurnOn = isWifiCallTurnOnEnabled(context);
+
+        ConnectivityManager conManager = (ConnectivityManager) context
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkInfo wifiNetworkInfo = conManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        boolean wifiAvailableNotConnected =
+                wifiNetworkInfo.isAvailable() && !wifiNetworkInfo.isConnected();
+
+        return wifiCallTurnOn && wifiAvailableNotConnected && !isCellularNetworkAvailable(context);
+    }
+
+    public static void showWifiCallNotification(final Context context) {
+        if (shallShowWifiCallDialog(context)) {
+            final NotificationManager notiManager =
+                    (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+            Intent intent = new Intent();
+            intent.setAction(android.provider.Settings.ACTION_WIFI_SETTINGS);
+            PendingIntent pendingIntent =
+                    PendingIntent.getActivity(
+                            context, NOTIFICATION_WIFI_CALL_ID,
+                            intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+            Notification.Builder builder = new Notification.Builder(context);
+            builder.setOngoing(false);
+            builder.setWhen(0);
+            builder.setContentIntent(pendingIntent);
+            builder.setAutoCancel(true);
+            builder.setSmallIcon(R.drawable.wifi_calling_on_notification);
+            builder.setContentTitle(
+                    context.getResources().getString(
+                        R.string.alert_user_connect_to_wifi_for_call));
+            builder.setContentText(
+                    context.getResources().getString(
+                        R.string.alert_user_connect_to_wifi_for_call_text));
+            notiManager.notify(NOTIFICATION_WIFI_CALL_ID, builder.build());
+            new Handler().postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        notiManager.cancel(NOTIFICATION_WIFI_CALL_ID);
+                    }
+           }, 5000);
+        }
+    }
+}
diff --git a/src/com/android/dialer/widget/EmptyContentView.java b/src/com/android/dialer/widget/EmptyContentView.java
index 719fd3f..70f0e5a 100644
--- a/src/com/android/dialer/widget/EmptyContentView.java
+++ b/src/com/android/dialer/widget/EmptyContentView.java
@@ -74,6 +74,11 @@
             mDescriptionView.setText(null);
             mDescriptionView.setVisibility(View.GONE);
         } else {
+            if (resourceId == R.string.no_call_log) {
+                mDescriptionView.setText(resourceId);
+                mDescriptionView.setVisibility(View.VISIBLE);
+                mDescriptionView.setPadding(0, 0, 0, 700);
+            }
             mDescriptionView.setText(resourceId);
             mDescriptionView.setVisibility(View.VISIBLE);
         }
diff --git a/src/com/android/dialer/widget/SearchEditTextLayout.java b/src/com/android/dialer/widget/SearchEditTextLayout.java
index 4f100dc..abc5c04 100644
--- a/src/com/android/dialer/widget/SearchEditTextLayout.java
+++ b/src/com/android/dialer/widget/SearchEditTextLayout.java
@@ -27,6 +27,11 @@
 import android.view.View;
 import android.widget.EditText;
 import android.widget.FrameLayout;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.speech.RecognizerIntent;
+import java.util.List;
 
 import com.android.dialer.R;
 import com.android.dialer.util.DialerUtils;
@@ -267,7 +272,11 @@
 
         mSearchIcon.setVisibility(collapsedViewVisibility);
         mCollapsedSearchBox.setVisibility(collapsedViewVisibility);
-        mVoiceSearchButtonView.setVisibility(collapsedViewVisibility);
+        if (!isExpand && canIntentBeHandled()) {
+            mVoiceSearchButtonView.setVisibility(collapsedViewVisibility);
+        } else {
+            mVoiceSearchButtonView.setVisibility(View.GONE);
+        }
         mOverflowButtonView.setVisibility(collapsedViewVisibility);
         mBackButtonView.setVisibility(expandedViewVisibility);
         // TODO: Prevents keyboard from jumping up in landscape mode after exiting the
@@ -318,4 +327,12 @@
         params.rightMargin = (int) (mRightMargin * fraction);
         requestLayout();
     }
+
+    private boolean canIntentBeHandled() {
+        final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+        final PackageManager packageManager = getContext().getPackageManager();
+        final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(voiceIntent,
+                PackageManager.MATCH_DEFAULT_ONLY);
+        return resolveInfo != null && resolveInfo.size() > 0;
+    }
 }
diff --git a/src/org/codeaurora/presenceserv/IPresenceService.aidl b/src/org/codeaurora/presenceserv/IPresenceService.aidl
new file mode 100644
index 0000000..edd0ac1
--- /dev/null
+++ b/src/org/codeaurora/presenceserv/IPresenceService.aidl
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2015-2016, 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.
+ **/
+
+package org.codeaurora.presenceserv;
+
+import org.codeaurora.presenceserv.IPresenceServiceCB;
+
+/**
+ * Presence service interface.
+ */
+interface IPresenceService {
+
+    String getImsEnablerState();
+    boolean hasVTCapability(String number);
+    void invokPublish();
+    boolean invokeAvailabilityFetch(String number);
+    void invokeCapabilityPolling(String number);
+    void invokeListAvailabilityFetch();
+    void invokeListCapabilityPolling();
+    void registerCallback(IPresenceServiceCB cb);
+    void unregisterCallback(IPresenceServiceCB cb);
+
+}
+
diff --git a/src/org/codeaurora/presenceserv/IPresenceServiceCB.aidl b/src/org/codeaurora/presenceserv/IPresenceServiceCB.aidl
new file mode 100644
index 0000000..05ad7d7
--- /dev/null
+++ b/src/org/codeaurora/presenceserv/IPresenceServiceCB.aidl
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2015-2016, 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.
+ **/
+package org.codeaurora.presenceserv;
+
+/**
+ * Presence service callback interface.
+ */
+interface IPresenceServiceCB {
+
+    void setIMSEnabledCB();
+}
+
diff --git a/tests/Android.mk b/tests/Android.mk
index 07f4f00..1b39526 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,7 +5,8 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_CERTIFICATE := shared
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test \
+                               ims-ext-common
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/src/com/android/dialer/SpecialCharSequenceMgrTest.java b/tests/src/com/android/dialer/SpecialCharSequenceMgrTest.java
new file mode 100644
index 0000000..79179ae
--- /dev/null
+++ b/tests/src/com/android/dialer/SpecialCharSequenceMgrTest.java
@@ -0,0 +1,64 @@
+/* Copyright (c) 2016, 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.
+ *
+ */
+package com.android.dialer;
+
+import android.content.Context;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.AndroidTestCase;
+import java.lang.reflect.*;
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import com.android.dialer.SpecialCharSequenceMgr;
+
+@SmallTest
+public class SpecialCharSequenceMgrTest extends AndroidTestCase {
+
+    Context context;
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        context = getContext();
+    }
+
+    public void testHandlePRLVersion(){
+        Class<SpecialCharSequenceMgr> scMgrClass = SpecialCharSequenceMgr.class;
+
+        try {
+            Method handlePRLVersion = scMgrClass.getDeclaredMethod("handlePRLVersion",
+                    Context.class, String.class);
+            handlePRLVersion.setAccessible(true);
+            boolean result1 = (boolean)handlePRLVersion.invoke(context, "*#0000#");
+            boolean result2 = (boolean)handlePRLVersion.invoke(context, "*#BAD#");
+            assertTrue(result1);
+            assertFalse(result2);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
index daba428..7108841 100644
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
@@ -109,14 +109,14 @@
     public void testSetPhoneCallDetails_ReadVoicemail() {
         PhoneCallDetails details =
                 getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
-        mHelper.setPhoneCallDetails(mViewHolder, details);
+        mHelper.setPhoneCallDetails(mViewHolder, details, null);
         assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
     }
 
     public void testSetPhoneCallDetails_UnreadVoicemail() {
         PhoneCallDetails details =
                 getPhoneCallDetailsWithTypes(AppCompatConstants.CALLS_VOICEMAIL_TYPE);
-        mHelper.setPhoneCallDetails(mViewHolder, details);
+        mHelper.setPhoneCallDetails(mViewHolder, details, null);
         assertEquals(View.VISIBLE, mViewHolder.voicemailPlaybackView.getVisibility());
     }
 
@@ -264,7 +264,7 @@
         PhoneCallDetails details = getPhoneCallDetails(
                 number, postDialDigits, presentation, formattedNumber);
         details.callTypes = new int[] {callType};
-        mHelper.setPhoneCallDetails(mViewHolder, details);
+        mHelper.setPhoneCallDetails(mViewHolder, details, null);
     }
 
     private PhoneCallDetails getPhoneCallDetails(
diff --git a/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java b/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java
index 270019a..a7f7988 100644
--- a/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java
+++ b/tests/src/com/android/dialer/calllog/calllogcache/TestTelecomCallLogCache.java
@@ -17,6 +17,7 @@
 package com.android.dialer.calllog.calllogcache;
 
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 
@@ -62,4 +63,9 @@
     public boolean doesAccountSupportCallSubject(PhoneAccountHandle accountHandle) {
         return false;
     }
+
+    @Override
+    public Drawable getAccountIcon(PhoneAccountHandle accountHandle) {
+        return null;
+    }
 }
diff --git a/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java b/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java
index a95a79e..14a0bfd 100644
--- a/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java
+++ b/tests/src/com/android/dialer/database/DialerDatabaseHelperTest.java
@@ -148,7 +148,7 @@
 
     private ArrayList<ContactNumber> getMatchesFromDb(String query) {
         final SmartDialNameMatcher nameMatcher = new SmartDialNameMatcher(query,
-                SmartDialPrefix.getMap());
+                SmartDialPrefix.getMap(), getContext());
         return mTestHelper.getLooseMatches(query, nameMatcher);
     }
 }
diff --git a/tests/src/com/android/dialer/database/SmartDialPrefixTest.java b/tests/src/com/android/dialer/database/SmartDialPrefixTest.java
index 78962e3..7fde707 100644
--- a/tests/src/com/android/dialer/database/SmartDialPrefixTest.java
+++ b/tests/src/com/android/dialer/database/SmartDialPrefixTest.java
@@ -89,7 +89,7 @@
 
     private ArrayList<ContactNumber> getLooseMatchesFromDb(String query) {
         final SmartDialNameMatcher nameMatcher = new SmartDialNameMatcher(query,
-                SmartDialPrefix.getMap());
+                SmartDialPrefix.getMap(), getContext());
         return mTestHelper.getLooseMatches(query, nameMatcher);
     }
 
diff --git a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
index c1365f5..b0dc91d 100644
--- a/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
+++ b/tests/src/com/android/dialer/dialpad/SmartDialNameMatcherTest.java
@@ -30,7 +30,7 @@
 import junit.framework.TestCase;
 
 @SmallTest
-public class SmartDialNameMatcherTest extends TestCase {
+public class SmartDialNameMatcherTest extends AndroidTestCase {
     private static final String TAG = "SmartDialNameMatcherTest";
 
     public void testMatches() {
@@ -239,7 +239,7 @@
 
     private void checkMatchesNumber(String number, String query, boolean expectedMatches,
             boolean matchNanp, int matchStart, int matchEnd) {
-        final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query);
+        final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query, getContext());
         final SmartDialMatchPosition pos = matcher.matchesNumber(number, query, matchNanp);
         assertEquals(expectedMatches, pos != null);
         if (expectedMatches) {
@@ -250,7 +250,7 @@
 
     private void checkMatches(String displayName, String query, boolean expectedMatches,
             int... expectedMatchPositions) {
-        final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query);
+        final SmartDialNameMatcher matcher = new SmartDialNameMatcher(query, getContext());
         final ArrayList<SmartDialMatchPosition> matchPositions =
                 new ArrayList<SmartDialMatchPosition>();
         final boolean matches = matcher.matchesCombination(