Merge TP1A.211214.001

Change-Id: I0940d70749f98497a4c8179a9b2e3a8a2f789853
diff --git a/Android.bp b/Android.bp
index 0649958..3ce5ac3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,8 +42,12 @@
         "libprotobuf-java-lite",
         "app-compat-annotations",
         "unsupportedappusage",
+        "telephony-ext",
+         "extphonelib",
+        "ims-ext-common",
         "org.apache.http.legacy",
     ],
+    enforce_uses_libs: false,
 
     static_libs: [
         "androidx.appcompat_appcompat",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a2ee607..45eab98 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -90,6 +90,17 @@
     <protected-broadcast android:name= "android.telephony.action.SIM_CARD_STATE_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.PRIMARY_CARD_CHANGED_IN_SERVICE" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.ACTION_SET_PRIMARY_CARD_DONE" />
+    <protected-broadcast android:name= "codeaurora.intent.action.ACTION_LTE_CONFIGURE" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.ACTION_UICC_MANUAL_PROVISION_STATUS_CHANGED" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.ACTION_RADIO_CAPABILITY_UPDATED" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.ACTION_NETWORK_SPECIFIER_SET" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.ACTION_DDS_SWITCH_DONE" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.RADIO_POWER_STATE" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.SMS_CALLBACK_MODE_CHANGED" />
+    <protected-broadcast android:name= "org.codeaurora.intent.action.SHOW_NOTICE_SCM_BLOCK_OTHERS" />
     <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.TOGGLE_PROVISION" />
@@ -104,13 +115,17 @@
 
     <protected-broadcast android:name= "com.android.phone.settings.CARRIER_PROVISIONING" />
     <protected-broadcast android:name= "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING" />
+    <protected-broadcast android:name= "org.codeaurora.telephony.VOWIFI_ENABLED" />
 
     <!-- For Vendor Debugging in Telephony -->
     <protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
 
     <protected-broadcast android:name= "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED" />
-    <protected-broadcast android:name= "android.intent.action.ACTION_MANAGED_ROAMING_IND" />
     <protected-broadcast android:name= "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE" />
+    <!-- Intent sent to notify clients that the voice capability has changed and the property
+         has been modified -->
+    <protected-broadcast android:name=
+            "org.codeaurora.intent.action.MSIM_VOICE_CAPABILITY_CHANGED" />
 
     <!-- Allows granting runtime permissions to telephony related components. -->
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
@@ -245,6 +260,20 @@
                 android:label="Access last known cell identity."
                 android:protectionLevel="signature"/>
 
+    <uses-permission android:name="com.qti.permission.BIND_QTI_IMS_SERVICE" />
+    <uses-permission android:name="com.qualcomm.qti.permission.USE_EXT_TELEPHONY_SERVICE" />
+    <uses-permission android:name="com.qti.permission.RECEIVE_SMS_CALLBACK_MODE" />
+    <!-- Allows us to know if concurrent calls on both subs are possible by handling broadcast
+         intent "org.codeaurora.intent.action.MSIM_VOICE_CAPABILITY".-->
+    <uses-permission android:name="com.qti.permission.RECEIVE_MSIM_VOICE_CAPABILITY" />
+    <!-- Permission needed to handle receiving
+         intent "org.codeaurora.intent.action.MSIM_VOICE_CAPABILITY_CHANGED".-->
+    <uses-permission android:name="com.qti.permission.RECEIVE_MSIM_VOICE_CAPABILITY_CHANGED" />
+
+    <permission android:name="com.qti.permission.RECEIVE_MSIM_VOICE_CAPABILITY_CHANGED"
+        android:label="Broadcast multi_sim_voice_capability property has changed"
+        android:protectionLevel="signature|system"/>
+
     <application android:name="PhoneApp"
             android:persistent="true"
             android:label="@string/phoneAppLabel"
@@ -389,6 +418,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="CallForwardType"
+                android:exported="true"
+                android:label="@string/call_forward_option"
+                android:configChanges="orientation|screenSize|keyboardHidden"
+                android:theme="@style/DialerSettingsLight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
         <!-- fdn setting -->
         <activity android:name="com.android.phone.settings.fdn.FdnSetting"
                 android:label="@string/fdn"
@@ -482,6 +521,9 @@
         <service android:name="EmergencyCallbackModeService">
         </service>
 
+        <service android:name="SmsCallbackModeService">
+        </service>
+
         <!-- service to dump telephony information -->
         <service android:name="com.android.phone.TelephonyDebugService"
                 android:exported="true"
@@ -574,6 +616,30 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="SmsCallbackModeExitDialog"
+            android:excludeFromRecents="true"
+            android:label="@string/scm_exit_dialog"
+            android:launchMode="singleTop"
+            android:exported="true"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter>
+                <action android:name="org.codeaurora.intent.action.ACTION_SHOW_SCM_EXIT_DIALOG" />
+                <action android:name="org.codeaurora.intent.action.SHOW_NOTICE_SCM_BLOCK_OTHERS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="LimitedServiceActivity"
+            android:exported="true"
+            android:excludeFromRecents="true"
+            android:launchMode="singleTask"
+            android:taskAffinity=""
+            android:theme="@android:style/Theme.Translucent.NoTitleBar">
+            <intent-filter android:priority="1000">
+                <action android:name = "org.codeaurora.telephony.VOWIFI_ENABLED" />
+            </intent-filter>
+        </activity>
+
         <!-- Start SIP -->
         <service android:name="com.android.services.telephony.sip.SipConnectionService"
                  android:label="@string/sip_connection_service_label"
@@ -745,5 +811,8 @@
             android:multiprocess="false"
             android:singleUser="true"
             android:writePermission="android.permission.MODIFY_PHONE_STATE"/>
+
+        <uses-library android:name="ims-ext-common"/>
+        <uses-library android:name="com.qti.extphone.extphonelib"/>
     </application>
 </manifest>
diff --git a/res/layout/frag_limited_service_alert_dialog.xml b/res/layout/frag_limited_service_alert_dialog.xml
new file mode 100644
index 0000000..81132aa
--- /dev/null
+++ b/res/layout/frag_limited_service_alert_dialog.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2020, 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:paddingTop="24dp"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp"
+    android:paddingBottom="4dp"
+    android:orientation="vertical">
+
+  <TextView
+      android:id="@+id/message"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginBottom="10dp"
+      android:text="@string/limited_service_alert_dialog_description"
+      android:textColor="?android:attr/textColorPrimary"
+      android:textSize="16sp"/>
+
+  <CheckBox
+      android:id="@+id/do_not_show"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:buttonTint="?android:attr/textColorPrimary"
+      android:focusable="true"
+      android:clickable="true"
+      android:text="@string/do_not_show_again"
+      android:textColor="?android:attr/textColorPrimary"
+      android:textSize="14sp"/>
+</LinearLayout>
diff --git a/res/layout/pref_dialog_editphonenumber.xml b/res/layout/pref_dialog_editphonenumber.xml
index 3bd52c0..0543288 100644
--- a/res/layout/pref_dialog_editphonenumber.xml
+++ b/res/layout/pref_dialog_editphonenumber.xml
@@ -14,43 +14,170 @@
      limitations under the License.
 -->
 
-<!-- Layout used as the dialog's content View for EditPhoneNumberPreference. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:padding="?android:attr/dialogPreferredPadding">
-
-    <TextView android:id="@+id/message"
-        style="?android:attr/textAppearanceMedium"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:scrollbars="vertical">
+    <!-- Layout used as the dialog's content View for EditPhoneNumberPreference. -->
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:textColor="?android:attr/textColorPrimary" />
-
-    <LinearLayout
-        android:id="@+id/number_field"
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1.0"
-        android:addStatesFromChildren="true"
-        android:gravity="center_vertical"
-        android:baselineAligned="false">
-
-        <!-- The EditText field in the dialog is now created programmatically.
-             We're replacing the field in this layout with a container to
-             attach the EditText field. -->
-        <LinearLayout android:id="@+id/edit_container"
-            android:layout_width="0dip"
-            android:layout_weight="1"
-            android:layout_height="wrap_content"/>
-
-        <ImageButton android:id="@+id/select_contact"
-            android:layout_width="wrap_content"
+        android:orientation="vertical"
+        android:padding="5dip">
+        <TextView android:id="@+id/message"
+            style="?android:attr/textAppearanceMedium"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="10dip"
-            android:src="@drawable/ic_see_contacts_holo_dark"
-            android:contentDescription="@string/selectContact" />
+            android:textColor="?android:attr/textColorPrimary" />
 
+        <LinearLayout
+            android:id="@+id/number_field"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:addStatesFromChildren="true"
+            android:gravity="center_vertical"
+            android:baselineAligned="false">
+
+            <!-- The EditText field in the dialog is now created programmatically.
+                 We're replacing the field in this layout with a container to
+                 attach the EditText field. -->
+            <LinearLayout android:id="@+id/edit_container"
+                android:layout_width="0dip"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"/>
+
+            <ImageButton android:id="@+id/select_contact"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:padding="10dip"
+                android:src="@drawable/ic_see_contacts_holo_dark"
+                android:contentDescription="@string/selectContact" />
+
+        </LinearLayout>
+        <!-- Start Time -->
+        <TextView android:id="@+id/time_start"
+            style="?android:attr/textAppearanceSmall"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/time_start"
+            android:textColor="?android:attr/textColorPrimary"
+            android:paddingStart="5dip"/>
+
+        <LinearLayout
+            android:id="@+id/start_time_setting"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:addStatesFromChildren="true"
+            android:gravity="center_vertical"
+            android:baselineAligned="false"
+            android:paddingStart="5dip">
+            <Spinner
+                style="@style/cfut_value"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_start_hour_24"
+                android:prompt="@string/time_start_hour"
+                android:entries="@array/hour_24_items"/>
+            <Spinner
+                style="@style/cfut_value"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_start_hour_12"
+                android:prompt="@string/time_start_hour"
+                android:entries="@array/hour_12_items"/>
+            <Spinner
+                style="@style/cfut_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_start_minute"
+                android:prompt="@string/time_start_minute"
+                android:entries="@array/minute_items"/>
+            <Spinner
+                style="@style/cfut_value"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_start_formate"
+                android:prompt="@string/time_start_minute"
+                android:entries="@array/time_formate_items"/>
+        </LinearLayout>
+
+        <!-- End Time -->
+        <TextView android:id="@+id/time_end"
+            style="?android:attr/textAppearanceSmall"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/time_end"
+            android:textColor="?android:attr/textColorPrimary"
+            android:paddingStart="5dip"/>
+
+        <LinearLayout
+            android:id="@+id/end_time_setting"
+            android:visibility="gone"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:addStatesFromChildren="true"
+            android:gravity="center_vertical"
+            android:baselineAligned="false"
+            android:paddingStart="5dip">
+            <Spinner
+                style="@style/cfut_value"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_end_hour_24"
+                android:prompt="@string/time_end_hour"
+                android:entries="@array/hour_24_items"/>
+            <Spinner
+                style="@style/cfut_value"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_end_hour_12"
+                android:prompt="@string/time_end_hour"
+                android:entries="@array/hour_12_items"/>
+            <Spinner
+                style="@style/cfut_value"
+                android:id="@+id/time_end_minute"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:prompt="@string/time_end_minute"
+                android:entries="@array/minute_items"/>
+            <Spinner
+                style="@style/cfut_value"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/time_end_formate"
+                android:prompt="@string/time_end_minute"
+                android:entries="@array/time_formate_items"/>
+        </LinearLayout>
+
+        <!-- Check box for all day option -->
+        <LinearLayout
+            android:id="@+id/all_day_setting"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:addStatesFromChildren="true"
+            android:gravity="center_vertical"
+            android:baselineAligned="false"
+            android:paddingStart="10dip"
+            android:paddingEnd="10dip">
+            <CheckBox
+                android:id="@+id/all_day"
+                android:visibility="gone"
+                android:paddingStart="10dip"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:singleLine="true"
+                android:checked="true"
+                android:text="@string/all_day"/>
+        </LinearLayout>
     </LinearLayout>
-
-</LinearLayout>
+</ScrollView>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 8c413e3..64d84c8 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -598,6 +598,8 @@
     <string name="rtt_mode_summary" msgid="8631541375609989562">"允许在语音通话中发送信息"</string>
     <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT 可为以下类型的来电者提供协助:失聪者、听力障碍人士、语言障碍人士或需要语音以外服务的人。&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;了解详情&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; - 系统会以信息转录的方式存储 RTT 通话\n       &lt;br&gt; - 视频通话不支持 RTT"</string>
     <string name="no_rtt_when_roaming" msgid="5268008247378355389">"注意:漫游时无法使用 RTT 功能"</string>
+    <string name="vibrating_for_outgoing_call_accepted_title">"当拨出电话接通时震动设备"</string>
+    <string name="vibrating_for_outgoing_call_accepted_summary">"启用当拨出电话接通时震动设备"</string>
   <string-array name="tty_mode_entries">
     <item msgid="3238070884803849303">"TTY 关闭"</item>
     <item msgid="1449091874731375214">"TTY 完整"</item>
@@ -927,4 +929,40 @@
     <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"您的蓝牙信号较弱。请尝试切换为扬声器模式。"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"通话质量通知"</string>
     <string name="notification_channel_sip_account" msgid="1261816025156179637">"已弃用的 SIP 帐号"</string>
+    <string name="srvcc_video_message">"4G网络不可用,视频电话降级到2G/3G语音电话."</string>
+    <string name="srvcc_message">"4G网络不可用,4G语音电话降级到2G/3G语音电话."</string>
+    <string name="ut_not_support">UT补充业务不可用</string>
+    <string name="ct_ut_not_support_close_4glte">UT补充业务不可用,请在设置中关闭增强4G LTE选项</string>
+    <!-- Call forwarding settings screen, setting timer info for cfut case -->
+    <string name="set_time_period">设置时间区间</string>
+    <string name="all_day">" 全天"</string>
+    <string name="time_interval_char">: </string>
+    <string name="time_start">" 自 "</string>
+    <string name="time_start_hour">"起始小时"</string>
+    <string name="time_start_minute">"起始分钟"</string>
+    <string name="time_end">" 到 "</string>
+    <string name="time_next_day">" 第二天"</string>
+    <string name="time_end_hour">"结束小时"</string>
+    <string name="time_end_minute">"结束分钟"</string>
+    <string name="time_formate_am">"上午"</string>
+    <string name="time_formate_pm">"下午"</string>
+    <string name="labelCFType">"呼叫转移设置"</string>
+    <string name="labelCFVoice">"语音"</string>
+    <string name="labelCFVideo">"视频"</string>
+    <string name="call_forward_option">"呼叫转移选项"</string>
+
+    <string name="cf_setting_mobile_data_alert">请开启手机数据连接,并确保WLAN已关闭</string>
+    <string name="no_mobile_data">移动数据连接不可用</string>
+    <string name="cf_setting_mobile_data_alert_roaming">请开启数据漫游</string>
+    <string name="no_mobile_data_roaming">数据漫游不可用</string>
+    <!--alert user to switch DDS when user is making UT supplementary service on non-DDS sub -->
+    <string name="switch_dds_to_sub_alert">请将移动数据切到卡槽</string>
+    <string name="mobile_data_alert">副卡可能会产生数据流量</string>
+    <string name="mobile_data">移动数据</string>
+    <string name="sim_is_not_ready">SIM卡未就绪</string>
+    <string name="switch_dds_to_sub_alert_msg">请将移动数据切到卡</string>
+    <string name="labelCommonMore" msgid="5930842194056092107">"通话设置"</string>
+    <string name="labelCommonMore_with_label" msgid="2674012918829238902">"通话设置(<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
+    <string name="sum_common_settings" msgid="284753265979035550">"其他通话设置"</string>
+    <string name="incall_error_outgoing_call_failed">"呼叫失败"</string>
 </resources>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 8187ff3..06a655d 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -29,4 +29,67 @@
         <item>"9"</item>
     </string-array>
 
+    <!-- Array resource for call fowarding timer settings-->
+    <string-array name="hour_24_items" translatable="false">
+        <item>00</item>
+        <item>01</item>
+        <item>02</item>
+        <item>03</item>
+        <item>04</item>
+        <item>05</item>
+        <item>06</item>
+        <item>07</item>
+        <item>08</item>
+        <item>09</item>
+        <item>10</item>
+        <item>11</item>
+        <item>12</item>
+        <item>13</item>
+        <item>14</item>
+        <item>15</item>
+        <item>16</item>
+        <item>17</item>
+        <item>18</item>
+        <item>19</item>
+        <item>20</item>
+        <item>21</item>
+        <item>22</item>
+        <item>23</item>
+    </string-array>
+    <string-array name="hour_12_items" translatable="false">
+        <item>12</item>
+        <item>01</item>
+        <item>02</item>
+        <item>03</item>
+        <item>04</item>
+        <item>05</item>
+        <item>06</item>
+        <item>07</item>
+        <item>08</item>
+        <item>09</item>
+        <item>10</item>
+        <item>11</item>
+    </string-array>
+    <string-array name="minute_items" translatable="false">
+        <item>00</item> <item>01</item> <item>02</item> <item>03</item>
+        <item>04</item> <item>05</item> <item>06</item> <item>07</item>
+        <item>08</item> <item>09</item> <item>10</item> <item>11</item>
+        <item>12</item> <item>13</item> <item>14</item> <item>15</item>
+        <item>16</item> <item>17</item> <item>18</item> <item>19</item>
+        <item>20</item> <item>21</item> <item>22</item> <item>23</item>
+        <item>24</item> <item>25</item> <item>26</item> <item>27</item>
+        <item>28</item> <item>29</item> <item>30</item> <item>31</item>
+        <item>32</item> <item>33</item> <item>34</item> <item>35</item>
+        <item>36</item> <item>37</item> <item>38</item> <item>38</item>
+        <item>40</item> <item>41</item> <item>42</item> <item>43</item>
+        <item>44</item> <item>45</item> <item>46</item> <item>47</item>
+        <item>48</item> <item>49</item> <item>50</item> <item>51</item>
+        <item>52</item> <item>53</item> <item>54</item> <item>55</item>
+        <item>56</item> <item>57</item> <item>58</item> <item>59</item>
+    </string-array>
+    <string-array name="time_formate_items" >
+        <item>@string/time_formate_am</item>
+        <item>@string/time_formate_pm</item>
+    </string-array>
+    <!-- Array resource for call fowarding timer settings-->
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index fca8acf..9194a17 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -47,6 +47,8 @@
             <enum name="no_reply" value="2" />
             <!-- not_reachable -->
             <enum name="not_reachable" value="3" />
+            <!-- not_logged_in -->
+            <enum name="not_logged_in" value="6" />
         </attr>
     </declare-styleable>
 
diff --git a/res/values/config.xml b/res/values/config.xml
index 9f8cc81..84fbcb8 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -215,7 +215,7 @@
     <bool name="config_pstn_phone_accounts_enabled">true</bool>
 
     <!-- Flag indicating whether the the emergency phone account should be emergency calls only -->
-    <bool name="config_emergency_account_emergency_calls_only">false</bool>
+    <bool name="config_emergency_account_emergency_calls_only">true</bool>
 
     <!-- Whether the emergency only account can make emergency calls -->
     <bool name="config_pstnCanPlaceEmergencyCalls">true</bool>
@@ -253,6 +253,10 @@
          When this is true, the Telephony stack is able to add additional audio to the outgoing
          audio stream which the remote party will be able to hear. -->
     <bool name="config_support_telephony_audio_device">false</bool>
+    <!-- To disable/enable CallForwardUnconditional Timer -->
+    <bool name="config_enable_cfu_time">false</bool>
+    <!-- To disable clir query when ut enable under CMCC -->
+    <bool name="config_disable_clir_over_ut">false</bool>
 
     <!-- Whether the device supports dialing emergency RTT calls when there's no SIM card installed
     -->
diff --git a/res/values/qtistrings.xml b/res/values/qtistrings.xml
new file mode 100755
index 0000000..cc8f634
--- /dev/null
+++ b/res/values/qtistrings.xml
@@ -0,0 +1,260 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (c) 2017 - 2018, 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>
+    <string name="ims_settings">IMS Settings</string>
+
+    <string name="call_barring">Call barring</string>
+    <!-- Icc depersonalization related strings -->
+    <!-- Label text for PIN entry widget on SIM Network Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY">SIM network unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM Network Subset Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY">SIM network subset unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY">SIM corporate unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY">SIM service provider unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM SIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SIM_ENTRY">SIM unlock PIN</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for Subset PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for Corporate PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for SIM service provider PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for SIM PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PIN entry widget on RUIM Network1 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY">RUIM network1 unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Network2 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY">RUIM network2 unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Hrpd Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY">RUIM hrpd unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY">RUIM corporate unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY">RUIM service provider unlock PIN</string>
+    <!-- Label text for PIN entry widget on RUIM RUIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY">RUIM unlock PIN</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY">Enter PUK</string>
+    <!-- Label text for PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY">Enter PUK</string>
+
+    <!-- Label text for PIN entry widget on SIM SPN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SPN_ENTRY">SPN unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM SP EHPLMN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY">SP EHPLMN unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM ICCID Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY">ICCID unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM IMPI Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_ENTRY">IMPI unlock PIN</string>
+    <!-- Label text for PIN entry widget on SIM NS_SP Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY">NS_SP unlock PIN</string>
+
+    <!-- Status message displayed on SIM Network Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS">Requesting SIM network unlock\u2026</string>
+    <!-- Status message displayed on SIM Network Subset Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_IN_PROGRESS">Requesting SIM network subset unlock
+\u2026</string>
+    <!-- Status message displayed on SIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_IN_PROGRESS">Requesting SIM service provider un
+lock\u2026</string>
+    <!-- Status message displayed on SIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_IN_PROGRESS">Requesting SIM corporate unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on Corporate PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on SIM Service provider PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on SIM PUK entry widget on Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on SIM SIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SIM_IN_PROGRESS">Requesting SIM unlock\u2026</string>
+    <!-- Status message displayed on RUIM Network1 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_IN_PROGRESS">Requesting RUIM network1 unlock\u2026</string>
+    <!-- Status message displayed on RUIM Network2 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_IN_PROGRESS">Requesting RUIM network2 unlock\u2026</string>
+    <!-- Status message displayed on RUIM Hrpd Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_IN_PROGRESS">Requesting RUIM hrpd unlock\u2026</string>
+    <!-- Status message displayed on RUIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_IN_PROGRESS">Requesting RUIM service provider
+unlock\u2026</string>
+    <!-- Status message displayed on RUIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_IN_PROGRESS">Requesting RUIM corporate unlock\u2026</string>
+
+    <!-- Status message displayed on SIM SPN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SPN_IN_PROGRESS">Requesting SPN unlock\u2026</string>
+    <!-- Status message displayed on SIM SP EHPLMN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_IN_PROGRESS">Requesting SP EHPLMN unlock\u2026</string>
+    <!-- Status message displayed on SIM ICCID Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_IN_PROGRESS">Requesting ICCID unlock\u2026</string>
+    <!-- Status message displayed on SIM IMPI Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_IN_PROGRESS">Requesting IMPI unlock\u2026</string>
+    <!-- Status message displayed on SIM NS_SP Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_IN_PROGRESS">Requesting NS_SP unlock\u2026</string>
+
+    <!-- Status message displayed on RUIM RUIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_IN_PROGRESS">Requesting RUIM unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Status message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS">Requesting PUK unlock\u2026</string>
+    <!-- Error message displayed on SIM Network Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR">SIM Network unlock request unsuccessful.</string>
+    <!-- Error message displayed on SIM Network Subset Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR">SIM Network Subset unlock request unsucces
+sful.</string>
+    <!-- Error message displayed on SIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR">SIM Service Provider unlock request unsu
+ccessful.</string>
+    <!-- Error message displayed on SIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR">SIM Corporate unlock request unsuccessful.</string>
+    <!-- Error message displayed on SIM SIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SIM_ERROR">SIM unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Network1 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR">RUIM Network1 unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Network2 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR">RUIM Network2 unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Hrpd Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR">RUIM Hrpd unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR">RUIM Corporate unlock request unsuccessful.</string>
+    <!-- Error message displayed on RUIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR">RUIM Service Provider unlock request un
+successful.</string>
+    <!-- Error message displayed on RUIM RUIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR">RUIM unlock request unsuccessful.</string>
+    <!-- Error message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR">PUK unlock unsuccessful.</string>
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR">PUK unlock unsuccessful.</string>
+
+    <!--  Error message displayed on SIM SPN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SPN_ERROR">SPN unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM SP EHPLMN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR">SP EHPLMN unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM ICCID Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_ERROR">ICCID unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM IMPI Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_ERROR">IMPI unlock request unsuccessful.</string>
+    <!--  Error message displayed on SIM NS_SP Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR">NS_SP unlock request unsuccessful.</string>
+
+    <!-- Success message displayed on SIM Network Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS">SIM Network unlock successful.</string>
+    <!-- Success message displayed on SIM Network Subset Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS">SIM Network Subset unlock successful.</string>
+    <!-- Success message displayed on SIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS">SIM Service Provider unlock successful
+.</string>
+    <!-- Success message displayed on SIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_SUCCESS">SIM Corporate unlock successful.</string>
+    <!-- Success message displayed on SIM SIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SIM_SUCCESS">SIM unlock successful.</string>
+    <!-- Success message displayed on RUIM Network1 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS">RUIM Network1 unlock successful.</string>
+    <!-- Success message displayed on RUIM Network2 Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS">RUIM Network2 unlock successful.</string>
+    <!-- Success message displayed on RUIM Hrpd Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS">RUIM Hrpd unlock successful.</string>
+    <!-- Success message displayed on RUIM Service Provider Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS">RUIM Service Provider unlock successf
+ul.</string>
+    <!-- Success message displayed on RUIM Corporate Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS">RUIM Corporate unlock successful.</string>
+    <!-- Success message displayed on RUIM RUIM Depersonalization panel -->
+    <string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS">RUIM unlock successful.</string>
+    <!-- Success message displayed on PUK Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_SIM_SIM_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_SUCCESS">PUK unlock successful.</string>
+    <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_SUCCESS">PUK unlock successful.</string>
+
+    <!-- Success message displayed on SIM SPN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SPN_SUCCESS">SPN unlock successful.</string>
+    <!-- Success message displayed on SIM SP EHPLMN Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_SUCCESS">SP EHPLMN unlock successful.</string>
+    <!-- Success message displayed on SIM ICCID Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_ICCID_SUCCESS">ICCID unlock successful.</string>
+    <!-- Success message displayed on SIM IMPI Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_IMPI_SUCCESS">IMPI unlock successful.</string>
+    <!-- Success message displayed on SIM NS_SP Depersonalization panel -->
+    <string name="PERSOSUBSTATE_SIM_NS_SP_SUCCESS">NS_SP unlock successful.</string>
+
+    <string name="cf_setting_mobile_data_alert">Please turn on mobile data and make sure that WLAN is off</string>
+    <string name="cf_setting_mobile_data_roaming_alert">Please turn on data roaming</string>
+    <string name="no_mobile_data">Mobile data is not available</string>
+    <string name="mobile_data">Mobile data</string>
+    <string name="no_mobile_data_roaming">Data roaming is not available</string>
+
+    <!--alert user to switch DDS when user is making UT supplementary service on non-DDS sub -->
+    <string name="switch_dds_to_sub_alert">Please switch mobile data to SIM</string>
+    <string name="mobile_data_alert">Data traffic may be generated on secondary card</string>
+    <string name="sim_is_not_ready">SIM card is not ready</string>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6fda189..bb76508 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -46,6 +46,8 @@
     <string name="carrier_mmi_msg_title"><xliff:g id="mmicarrier" example="T-Mobile">%s</xliff:g> Message</string>
     <!-- Default title for carrier MMI message -->
     <string name="default_carrier_mmi_msg_title">Carrier Message</string>
+    <!-- Title for USSD code initiated -->
+    <string name="ussdinitiated_title">USSD Initiated</string>
     <!-- Possible error messages with outgoing calls --><skip/>
     <string name="mmiStarted">MMI code started</string>
     <!-- Dialog label when a USSD code starts running -->
@@ -116,9 +118,9 @@
     <!-- Status message displayed on SIM Network Depersonalization panel -->
     <string name="requesting_unlock">Requesting network unlock\u2026</string>
     <!-- Error message displayed on SIM Network Depersonalization panel -->
-    <string name="unlock_failed">Network unlock request unsuccessful.</string>
+    <string name="unlock_failed"> Network unlock request unsuccessful.</string>
     <!-- Success message displayed on SIM Network Depersonalization panel -->
-    <string name="unlock_success">Network unlock successful.</string>
+    <string name="unlock_success">SIM Network unlock successful.</string>
 
     <!-- settings strings -->
 
@@ -129,6 +131,9 @@
     <!-- GSM Call settings screen, setting option name with label indicating the SIM the settings
          are applied to. [CHAR LIMIT=40] -->
     <string name="labelGsmMore_with_label">GSM call settings (<xliff:g id="subscriptionlabel" example="T-Mobile">%s</xliff:g>)</string>
+    <string name="labelCFType">Call Forward Setting</string>
+    <string name="labelCFVoice">Voice</string>
+    <string name="labelCFVideo">Video</string>
     <!-- CDMA Call settings screen, setting option name -->
     <string name="labelCDMAMore">CDMA call settings</string>
     <!-- CDMA Call settings screen, setting option name with label indicating the SIM the settings
@@ -243,6 +248,22 @@
     <!-- Call forwarding settings screen, setting summary text when Always forward is disabled -->
     <string name="sum_cfu_disabled">Off</string>
 
+    <!-- Call forwarding settings screen, setting timer info for cfut case -->
+    <string name="set_time_period">Set time period</string>
+    <string name="all_day">All day</string>
+    <string name="time_interval_char">: </string>
+    <string name="time_start">From</string>
+    <string name="time_start_hour">time start hour</string>
+    <string name="time_start_minute">time start minute</string>
+    <string name="time_end">To</string>
+    <string name="time_next_day">next day</string>
+    <string name="time_end_hour">time end hour</string>
+    <string name="time_end_minute">time end minute</string>
+    <string name="time_formate_am">AM</string>
+    <string name="time_formate_pm">PM</string>
+    <string name="cf_setting_mobile_data_alert">Please turn on mobile data and make sure that WLAN is off</string>
+    <string name="cf_setting_mobile_data_roaming_alert">Please turn on data roaming</string>
+
     <!-- Call forwarding settings screen, setting option name -->
     <string name="labelCFB">When busy</string>
     <!-- Call forwarding dialog box, text field label -->
@@ -277,6 +298,15 @@
     <string name="disable_cfnrc_forbidden">Your carrier doesn\'t support disabling call forwarding when your phone is unreachable.</string>
     <string name="registration_cf_forbidden">Your carrier doesn\'t support call forwarding.</string>
 
+    <!-- Call forwarding settings screen, setting option name -->
+    <string name="labelCFNL">When not logged in</string>
+    <!-- Call forwarding dialog box, text field label -->
+    <string name="messageCFNL">Number when not logged in</string>
+    <!-- Call forwarding settings screen, setting summary text when forwarding to a specific number when not logged in-->
+    <string name="sum_cfnl_enabled">Forwarding to <xliff:g id="phonenumber" example="555-1212">{0}</xliff:g></string>
+    <!-- Call forwarding settings screen, setting summary text when Forward when not logged in is disabled -->
+    <string name="sum_cfnl_disabled">Off</string>
+
     <!-- Cdma Call waiting settings screen, setting option name -->
     <string name="cdma_call_waiting">Turn on call waiting?</string>
     <string name="enable_cdma_call_waiting_setting">During a call, you\'ll be notified about incoming calls</string>
@@ -423,6 +453,11 @@
     <string name="network_connecting">Connecting...</string>
     <!-- Available networks screen, text when a network cannot be connected -->
     <string name="network_could_not_connect">Couldn’t connect</string>
+    <!-- In-call screen: call failure reason (call denied because call barring
+            is ON on MT side ) -->
+    <string name="callFailed_incoming_cb_enabled">Party has barred all Incoming calls.</string>
+    <!-- In-call screen: status label for an incoming call that is not answered and forwarded  -->
+    <string name="callUnanswered_forwarded">Call Unanswered and Forwarded</string>
 
     <!-- The preferred network modes in Mobile network settings -->
     <string-array name="preferred_network_mode_choices">
@@ -685,6 +720,7 @@
     <string name="ussd_dialog_sep" translatable="false">----------</string>
 
     <string name="gsm_umts_options">GSM/UMTS Options</string>
+    <string name="call_forward_option">CallForward Options</string>
     <string name="cdma_options">CDMA Options</string>
 
     <!-- Screen option on the mobile network settings to go into data usage settings -->
@@ -1004,6 +1040,8 @@
     <string name="tty_mode_option_summary">Set TTY mode</string>
     <string name="auto_retry_mode_title">Auto-retry</string>
     <string name="auto_retry_mode_summary">Enable Auto-retry mode</string>
+    <string name="vibrating_for_outgoing_call_accepted_title">Vibrating for outgoing call accepted</string>
+    <string name="vibrating_for_outgoing_call_accepted_summary">Enable vibrating mode for outgoing call accepted</string>
     <!-- TTY Mode change is NOT allowed during a video call -->
     <string name="tty_mode_not_allowed_video_call">TTY Mode change is not allowed during a video call</string>
     <!-- FDN list screen: menu item label -->
@@ -1211,6 +1249,8 @@
     <!-- In-call screen: call failure message displayed in an error dialog -->
     <string name="incall_error_call_failed">Call failed.</string>
     <!-- In-call screen: call failure message displayed in an error dialog -->
+    <string name="incall_error_outgoing_call_failed">Outgoing call failed.</string>
+    <!-- In-call screen: call failure message displayed in an error dialog -->
     <string name="incall_error_cannot_add_call">Call cannot be added at this time.  You can try to reach out by sending a message.</string>
     <!-- In-call screen: message displayed in an error dialog -->
     <string name="incall_error_supp_service_unknown">Service not supported</string>
@@ -1481,6 +1521,34 @@
         <item quantity="other">The phone will be in emergency callback mode for <xliff:g id="count">%s</xliff:g> minutes.\nDo you want to exit now?</item>
     </plurals>
 
+    <!-- Sms Callback Mode (SCM) -->
+    <string name="scm_exit_dialog">ScmExitDialog</string>
+    <!-- SCM: Status bar notification message -->
+    <string name="phone_entered_scm_text">Entered Sms Callback Mode</string>
+    <!-- SCM: Notification title -->
+    <string name="phone_in_scm_notification_title">Sms Callback Mode</string>
+    <!-- SCM: Displays the time when SCM will end, Example: "No Data Connection until 10:45 AM" -->
+    <string name="phone_in_scm_notification_complete_time">until <xliff:g id="completeTime">%s</xliff:g></string>
+    <!-- SCM: Dialog box message for exiting from the notifications screen -->
+    <plurals name="alert_dialog_exit_scm">
+        <!-- number of minutes is one -->
+        <item quantity="one">The phone will be in Sms Callback mode for <xliff:g id="count">%s</xliff:g> minute. While in this mode, no apps using a data connection can be used. Do you want to exit now?</item>
+        <!-- number of minutes is not equal to one -->
+        <item quantity="other">The phone will be in Sms Callback mode for <xliff:g id="count">%s</xliff:g> minutes. While in this mode, no applications using a data connection can be used. Do you want to exit now?</item>
+    </plurals>
+    <!-- SCM: Dialog box message for exiting from any other app -->
+    <plurals name="alert_dialog_not_avaialble_in_scm">
+        <!-- number of minutes is one -->
+        <item quantity="one">The selected action isn\'t available while in the Sms Callback mode. The phone will be in this mode for <xliff:g id="count">%s</xliff:g> minute. Do you want to exit now?</item>
+        <!-- number of minutes is not equal to one -->
+        <item quantity="other">The selected action isn\'t available while in the Sms Callback mode. The phone will be in this mode for <xliff:g id="count">%s</xliff:g> minutes. Do you want to exit now?</item>
+    </plurals>
+    <!-- SCM: Dialog box message while in emergency call -->
+    <string name="alert_dialog_in_scm_call">The selected action isn\'t available while in an emergency call.</string>
+    <!-- SCM: Progress text -->
+    <string name="progress_dialog_exiting_scm">Exiting Sms Callback mode</string>
+    <!-- SCM: Notification body wihout data restriction hint -->
+    <string name="phone_in_scm_call_notification_text_without_data_restriction_hint">The phone is in sms callback mode</string>
     <!-- For incoming calls, this is a string we can get from a CDMA network instead of
          the actual phone number, to indicate there's no number present.  DO NOT TRANSLATE. -->
     <string-array name="absent_num" translatable="false">
@@ -1619,6 +1687,11 @@
     <string name="callFailed_cdma_call_limit">
         There are too many active calls. Please end or merge existing calls before placing a new one.
     </string>
+     <!-- Smart Divert Settings -->
+     <!-- Title for the Smart Divert Feature displayed in Call Settings -->
+     <string name="smart_divert_title">Smart-Divert</string>
+     <!-- Summary for the Smart Divert Feature displayed in Call Settings -->
+     <string name="smart_divert_summary">CFNRc Settings</string>
 
     <!-- Call failure reason: If the network does not accept the emergency call request, e.g., because IMEI was used as identification and this capability is not supported -->
     <string name="callFailed_imei_not_accepted">
@@ -1883,6 +1956,8 @@
          does not have enough resources (e.g. it is congested) and the call cannot be placed. -->
     <string name="callFailed_NetworkCongested">Network is congested.  Contact your mobile operator
     for assistance.</string>
+    <string name="callFailed_concurrent_calls_not_possible">Call ended due to concurrent calls not
+    possible.</string>
     <!-- Message displayed to the user when an outgoing call is deflected.  This means that the
          party the user is calling has chosen to send the call to another phone number. -->
     <string name="supp_service_notification_call_deflected">Call deflected.</string>
@@ -1936,6 +2011,12 @@
     <!-- Message displayed to the user to indicate that a held call has been released /
          disconnected. -->
     <string name="supp_service_held_call_released">Held call has been released.</string>
+    <string name="srvcc_video_message">"Video call is downgraded to 2G/3G voice call due to 4G network is not available."</string>
+    <string name="srvcc_message">"4G voice call is downgraded to 2G/3G voice call due to 4G network is not available."</string>
+    <string name="ut_not_support">UT is not available</string>
+    <string name="ct_ut_not_support_close_4glte">UT is not available, please turn off Enhanced 4G LTE Mode in Settings</string>
+    <string name="switch_dds_to_sub_alert_msg">Please switch mobile data to SIM</string>
+
     <!-- In-call screen: error message shown when the user attempts to place a call, but the device
          is currently in the process of being provisioned for service.  Provisioning is the process
          by which a device confirms which services are available to the user by contacting the
@@ -2196,4 +2277,15 @@
     <!-- Telephony notification channel name for a channel containing SIP accounts removed
      notificatios -->
     <string name="notification_channel_sip_account">Deprecated SIP accounts</string>
+
+    <string name="labelCommonMore">Call settings</string>
+    <string name="labelCommonMore_with_label">Call settings (<xliff:g id="subscriptionlabel" example="T-Mobile">%s</xliff:g>)</string>
+    <string name="sum_common_call_settings">Additional call settings</string>
+    <string name="ut_unavailable_to_set_video_cf_toast">UT is unavailable to set call forwarding for video, please close this page and exit.</string>
+    <!-- Instruction text to notify user that emergency calls may not be possible when voice is enabled over wifi. [CHAR LIMIT=NONE] -->
+    <string name="limited_service_alert_dialog_description"><b>Emergency calls via WiFi Calling</b>\nEmergency calls are not possible via WiFi Calling.
+\n\nThe device switches automatically to a cellular network (2G/3G) to place an emergency call.
+\n\nEmergency calls are only possible in areas with cellular coverage.</string>
+    <!-- Option to hide the popup dialog if it is not necessary for the user. [CHAR LIMIT=40] -->
+    <string name="do_not_show_again">Do not show again</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8218a84..e924ba3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -315,6 +315,13 @@
         <item name="android:listDivider">@null</item>
     </style>
 
+    <style name="cfut_value">
+        <item name="android:layout_width">80dip</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+        <item name="android:singleLine">true</item>
+    </style>
+
     <style name="EmergencyInfoNameTextAppearance"
            parent="@android:style/TextAppearance.Material.Subhead">
         <item name="android:textColor">@color/primary_text_default_material_dark</item>
diff --git a/res/xml/call_feature_setting.xml b/res/xml/call_feature_setting.xml
index fe0ea43..57aff65 100644
--- a/res/xml/call_feature_setting.xml
+++ b/res/xml/call_feature_setting.xml
@@ -59,6 +59,12 @@
         android:persistent="false"
         android:summary="@string/auto_retry_mode_summary"/>
 
+    <SwitchPreference
+        android:key="button_vibrating_for_outgoing_call_accepted_key"
+        android:title="@string/vibrating_for_outgoing_call_accepted_title"
+        android:persistent="false"
+        android:summary="@string/vibrating_for_outgoing_call_accepted_summary"/>
+
     <PreferenceScreen
         android:key="button_gsm_more_expand_key"
         android:title="@string/labelGSMMore"
@@ -71,4 +77,15 @@
         android:summary="@string/sum_cdma_call_settings"
         android:persistent="false" />
 
+    <PreferenceScreen
+        android:key="button_common_more_expand_key"
+        android:title="@string/labelCommonMore"
+        android:summary="@string/sum_common_call_settings"
+        android:persistent="false" />
+
+    <PreferenceScreen
+        android:key="ims_settings_key"
+        android:title="@string/ims_settings"
+        android:persistent="false">
+    </PreferenceScreen>
 </PreferenceScreen>
diff --git a/res/xml/call_forward_type.xml b/res/xml/call_forward_type.xml
new file mode 100644
index 0000000..4128a7c
--- /dev/null
+++ b/res/xml/call_forward_type.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <Preference
+        android:key="button_cf_key_voice"
+        android:title="@string/labelCFVoice">
+    </Preference>
+
+    <Preference
+        android:key="button_cf_key_video"
+        android:title="@string/labelCFVideo">
+    </Preference>
+
+</PreferenceScreen>
diff --git a/res/xml/callforward_options.xml b/res/xml/callforward_options.xml
index 775151e..84de032 100644
--- a/res/xml/callforward_options.xml
+++ b/res/xml/callforward_options.xml
@@ -78,4 +78,21 @@
         android:singleLine="true"
         android:autoText="false"
         android:enabled="false" />
+
+    <!-- See note on com.android.phone.EditPreference above -->
+    <com.android.phone.CallForwardEditPreference
+        android:key="button_cfnl_key"
+        android:title="@string/labelCFNL"
+        android:persistent="false"
+        android:summaryOn="@string/sum_cfnl_enabled"
+        android:summaryOff="@string/sum_cfnl_disabled"
+        android:dialogTitle="@string/labelCFNL"
+        android:dialogMessage="@string/messageCFNL"
+        phone:confirmMode="activation"
+        phone:serviceClass="voice"
+        phone:reason="not_logged_in"
+        android:dependency="button_cfu_key"
+        android:singleLine="true"
+        android:autoText="false"
+        android:enabled="false" />
 </PreferenceScreen>
diff --git a/res/xml/cdma_call_privacy.xml b/res/xml/cdma_call_privacy.xml
index a16a504..9fc972b 100644
--- a/res/xml/cdma_call_privacy.xml
+++ b/res/xml/cdma_call_privacy.xml
@@ -2,6 +2,26 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   android:title="@string/additional_cdma_call_settings">
 
+    <PreferenceScreen
+        android:key="button_cf_expand_key"
+        android:title="@string/labelCF"
+        android:persistent="false">
+    </PreferenceScreen>
+
+    <PreferenceScreen
+        android:key="button_cw_key"
+        android:title="@string/labelCW"
+        android:persistent="false">
+    </PreferenceScreen>
+
+    <com.android.phone.CallWaitingSwitchPreference
+        android:key="button_cw_ut_key"
+        android:title="@string/labelCW"
+        android:persistent="false"
+        android:summaryOn="@string/sum_cw_enabled"
+        android:summaryOff="@string/sum_cw_disabled"
+        android:enabled="false"/>
+
     <com.android.phone.CdmaVoicePrivacySwitchPreference
         android:key="button_voice_privacy_key"
         android:title="@string/voice_privacy"
diff --git a/res/xml/phone_account_settings.xml b/res/xml/phone_account_settings.xml
index 8722761..015c365 100644
--- a/res/xml/phone_account_settings.xml
+++ b/res/xml/phone_account_settings.xml
@@ -32,6 +32,17 @@
                 android:targetClass="com.android.server.telecom.settings.EnableAccountPreferenceActivity" />
         </PreferenceScreen>
 
+        <PreferenceScreen
+          android:key="button_smart_divert"
+          android:title="@string/smart_divert_title"
+          android:summary="@string/smart_divert_summary"
+          android:persistent="false"
+          android:order="1500">
+          <intent android:action="android.intent.action.MAIN"
+             android:targetPackage="com.qti.xdivert"
+             android:targetClass="com.qti.xdivert.XDivertSetting" />
+        </PreferenceScreen>
+
     </PreferenceCategory>
 
     <PreferenceCategory
@@ -53,6 +64,12 @@
 
         </PreferenceScreen>
 
+        <SwitchPreference
+            android:key="button_vibrating_for_outgoing_call_accepted_key"
+            android:title="@string/vibrating_for_outgoing_call_accepted_title"
+            android:persistent="false"
+            android:summary="@string/vibrating_for_outgoing_call_accepted_summary"/>
+
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/res/xml/telephony_injection.xml b/res/xml/telephony_injection.xml
index 2af425c..3237a98 100644
--- a/res/xml/telephony_injection.xml
+++ b/res/xml/telephony_injection.xml
@@ -21,13 +21,22 @@
      "/system/framework/" should be the target directory.
         e.g. "/system/framework/eg-telephony-common.jar"
 -->
-<injection package=""
-           jar="">
+<injection package="com.qualcomm.qti.internal.telephony.QtiTelephonyComponentFactory"
+           jar="/system/framework/qti-telephony-common.jar:/product/framework/qti-telephony-hidl-wrapper.jar:/product/framework/qti-telephony-utils.jar:/system/framework/qti-telephony-utils.jar">
     <components>
         <!-- Components use injected component factory,
             e.g. com.android.internal.telephony.ServiceStateTracker
         -->
         <!--<component>com.example.componentA</component>-->
         <!--<component>com.example.componentB</component>-->
+        <component>com.android.internal.telephony.ServiceStateTracker</component>
+        <component>com.android.internal.telephony.SubscriptionController</component>
+        <component>com.android.internal.telephony.SubscriptionInfoUpdater</component>
+        <component>com.android.internal.telephony.GsmCdmaPhone</component>
+        <component>com.android.internal.telephony.PhoneSwitcher</component>
+        <component>com.android.internal.telephony.IccPhoneBookInterfaceManager</component>
+        <component>com.android.internal.telephony.dataconnection.DcTracker</component>
+        <component>com.android.internal.telephony.RIL</component>
+        <component>com.android.internal.telephony.TelephonyComponentFactory</component>
     </components>
 </injection>
diff --git a/src/com/android/phone/CallBarringEditPreference.java b/src/com/android/phone/CallBarringEditPreference.java
index 757600e..32877d4 100644
--- a/src/com/android/phone/CallBarringEditPreference.java
+++ b/src/com/android/phone/CallBarringEditPreference.java
@@ -28,6 +28,9 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.stub.ImsUtImplBase;
 import android.text.method.DigitsKeyListener;
 import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
@@ -37,11 +40,33 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import org.codeaurora.ims.QtiImsException;
+import org.codeaurora.ims.QtiImsExtListenerBaseImpl;
+import org.codeaurora.ims.QtiImsExtConnector;
+import org.codeaurora.ims.QtiImsExtManager;
+
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BIC_ACR;
+
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.phone.settings.fdn.EditPinPreference;
 
+import com.qti.extphone.Client;
+import com.qti.extphone.ExtPhoneCallbackBase;
+import com.qti.extphone.ExtTelephonyManager;
+import com.qti.extphone.IExtPhoneCallback;
+import com.qti.extphone.Status;
+
+
 import java.lang.ref.WeakReference;
 
 /**
@@ -54,6 +79,8 @@
 
     private String mFacility;
     boolean mIsActivated = false;
+    private boolean mExpectMore;
+    private ExtTelephonyManager mExtTelephonyManager;
     private CharSequence mEnableText;
     private CharSequence mDisableText;
     private CharSequence mSummaryOn;
@@ -62,6 +89,9 @@
     private final MyHandler mHandler = new MyHandler(this);
     private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
+    private Client mClient;
+    private QtiImsExtConnector mQtiImsExtConnector;
+    private QtiImsExtManager mQtiImsExtManager;
 
     private static final int PW_LENGTH = 4;
 
@@ -100,6 +130,50 @@
         this(context, null);
     }
 
+    private QtiImsExtListenerBaseImpl imsInterfaceListener =
+            new QtiImsExtListenerBaseImpl() {
+                @Override
+                public void onUTReqFailed(int phoneId, int errCode, String errString) {
+                    if (DBG) Log.d(LOG_TAG, "onUTReqFailed phoneId=" + phoneId + " errCode= "
+                            +errCode + "errString ="+ errString);
+
+                    if (errCode == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED) {
+                        getCallBarringWithExpectMore();
+                    } else {
+                        Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_BARRING);
+                        AsyncResult.forMessage(msg, null, PhoneUtils.getCommandException(errCode));
+                        msg.sendToTarget();
+                    }
+                }
+
+                @Override
+                public void queryCallBarringResponse(int[] response) {
+                    Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_BARRING);
+                    AsyncResult.forMessage(msg, response, null);
+                    msg.sendToTarget();
+                }
+            };
+
+    private void createQtiImsExtConnector(Context context) {
+        try {
+            mQtiImsExtConnector = new QtiImsExtConnector(context,
+                    new QtiImsExtConnector.IListener() {
+                        @Override
+                        public void onConnectionAvailable(QtiImsExtManager qtiImsExtManager) {
+                            Log.i(LOG_TAG, "QtiImsExtConnector onConnectionAvailable");
+                            mQtiImsExtManager = qtiImsExtManager;
+                            queryImsCallBarringStatus();
+                        }
+                        @Override
+                        public void onConnectionUnavailable() {
+                            mQtiImsExtManager = null;
+                        }
+                    });
+        } catch (QtiImsException e) {
+            Log.e(LOG_TAG, "Unable to create QtiImsExtConnector");
+        }
+    }
+
     void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
         if (DBG) {
             Log.d(LOG_TAG, "init: phone id = " + phone.getPhoneId());
@@ -107,16 +181,108 @@
         mPhone = phone;
 
         mTcpListener = listener;
+        mExtTelephonyManager = ExtTelephonyManager.getInstance(getContext());
         if (!skipReading) {
             // Query call barring status
-            mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
-                    MyHandler.MESSAGE_GET_CALL_BARRING), getServiceClassForCallBarring(mPhone));
+            if (!mPhone.isUtEnabled()) {
+                if (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM &&
+                        PhoneUtils.isBacktoBackSSFeatureSupported()) {
+                    getCallBarringWithExpectMore();
+                } else {
+                    mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
+                            MyHandler.MESSAGE_GET_CALL_BARRING),
+                            getServiceClassForCallBarring(mPhone));
+                }
+            } else {
+                createQtiImsExtConnector(getContext());
+                //Connect will get the QtiImsExtManager instance.
+                mQtiImsExtConnector.connect();
+            }
             if (mTcpListener != null) {
                 mTcpListener.onStarted(this, true);
             }
         }
     }
 
+    private void queryImsCallBarringStatus() {
+        try {
+            mQtiImsExtManager.queryCallBarring(mPhone.getPhoneId(),
+                    getCBTypeFromFacility(mFacility), "", getServiceClassForCallBarring(mPhone),
+                    mExpectMore, imsInterfaceListener);
+        } catch (QtiImsException e) {
+            Log.d(LOG_TAG, "queryCallForwardStatus failed. " +
+                    "Exception = " + e);
+            sendErrorResponse();
+        }
+    }
+
+    private void sendErrorResponse() {
+        Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_BARRING);
+        AsyncResult.forMessage(msg, null, new CommandException
+               (CommandException.Error.GENERIC_FAILURE));
+        msg.sendToTarget();
+    }
+
+    private int getCBTypeFromFacility(String facility) {
+        if (CB_FACILITY_BAOC.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_ALL_OUTGOING;
+        } else if (CB_FACILITY_BAOIC.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL;
+        } else if (CB_FACILITY_BAOICxH.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL_EXCL_HOME;
+        } else if (CB_FACILITY_BAIC.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_ALL_INCOMING;
+        } else if (CB_FACILITY_BAICr.equals(facility)) {
+            return ImsUtImplBase.CALL_BLOCKING_INCOMING_WHEN_ROAMING;
+        } else if (CB_FACILITY_BA_ALL.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_ALL;
+        } else if (CB_FACILITY_BA_MO.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_OUTGOING_ALL_SERVICES;
+        } else if (CB_FACILITY_BA_MT.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_INCOMING_ALL_SERVICES;
+        } else if (CB_FACILITY_BIC_ACR.equals(facility)) {
+            return ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING;
+        }
+
+        return 0;
+    }
+
+    private void getCallBarringWithExpectMore() {
+        if (!mExtTelephonyManager.isServiceConnected()) {
+            sendErrorResponse();
+            return;
+        }
+
+        try {
+            mClient = mExtTelephonyManager.registerCallback(
+                    getContext().getPackageName(), mExtPhoneCallBarringCallback);
+            mExtTelephonyManager.getFacilityLockForApp(mPhone.getPhoneId(), mFacility,
+                    "" /*password*/, getServiceClassForCallBarring(mPhone), null /*appId*/,
+                    mExpectMore, mClient);
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Exception " + e);
+            sendErrorResponse();
+        }
+    }
+
+    private IExtPhoneCallback mExtPhoneCallBarringCallback = new ExtPhoneCallbackBase() {
+        @Override
+        public void getFacilityLockForAppResponse(Status status, int[] response) {
+            Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_BARRING);
+            if (status.get() == Status.SUCCESS) {
+                AsyncResult.forMessage(msg, response, null);
+            } else {
+                AsyncResult.forMessage(msg, response,
+                        new CommandException(CommandException.Error.GENERIC_FAILURE));
+            }
+            msg.sendToTarget();
+        }
+    };
+
+    void setExpectMore(boolean expectMore) {
+        mExpectMore = expectMore;
+    }
+
     @Override
     public void onClick(DialogInterface dialog, int which) {
         super.onClick(dialog, which);
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index d249fae..41dc193 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
@@ -64,6 +65,8 @@
 
 import java.util.List;
 
+import org.codeaurora.ims.QtiCallConstants;
+
 /**
  * Top level "Call settings" UI; see res/xml/call_feature_setting.xml
  *
@@ -95,12 +98,19 @@
     private static final String BUTTON_RETRY_KEY       = "button_auto_retry_key";
     private static final String BUTTON_GSM_UMTS_OPTIONS = "button_gsm_more_expand_key";
     private static final String BUTTON_CDMA_OPTIONS = "button_cdma_more_expand_key";
+    //Hides radio technology, e.g. CDMA or GSM, details from the settings text.
+    //For example uses "Call Settings", instead of "Gsm Call Settings".
+    //PhoneType is used to display the correct settings when user clicks on the button.
+    private static final String BUTTON_COMMON_OPTIONS = "button_common_more_expand_key";
 
     private static final String PHONE_ACCOUNT_SETTINGS_KEY =
             "phone_account_settings_preference_screen";
 
     private static final String ENABLE_VIDEO_CALLING_KEY = "button_enable_video_calling";
     private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
+    private static final String BUTTON_IMS_SETTINGS_KEY   = "ims_settings_key";
+    private static final String BUTTON_VIBRATING_KEY =
+            "button_vibrating_for_outgoing_call_accepted_key";
 
     private Phone mPhone;
     private ImsManager mImsMgr;
@@ -112,6 +122,8 @@
     private PreferenceScreen mVoicemailSettingsScreen;
     private SwitchPreference mEnableVideoCalling;
     private Preference mButtonWifiCalling;
+    private PreferenceScreen mImsSettingsScreen;
+    private SwitchPreference mButtonVibratingForMoCallAccepted;
 
     /*
      * Click Listeners, handle click based on objects attached to UI.
@@ -169,6 +181,11 @@
                     android.provider.Settings.Global.CALL_AUTO_RETRY,
                     mButtonAutoRetry.isChecked() ? 1 : 0);
             return true;
+        } else if (preference == mButtonVibratingForMoCallAccepted) {
+            Settings.Global.putInt(mPhone.getContext().getContentResolver(),
+                    android.provider.Settings.Global.VIBRATING_FOR_OUTGOING_CALL_ACCEPTED,
+                    mButtonVibratingForMoCallAccepted.isChecked() ? 1 : 0);
+            return true;
         } else if (preference == preferenceScreen.findPreference(
                 GsmUmtsCallOptions.CALL_FORWARDING_KEY)) {
             return doSsOverUtPrecautions(preference);
@@ -352,6 +369,7 @@
         TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mPhone.getSubId());
 
+        mButtonVibratingForMoCallAccepted = (SwitchPreference) findPreference(BUTTON_VIBRATING_KEY);
         // Note: The PhoneAccountSettingsActivity accessible via the
         // android.telecom.action.CHANGE_PHONE_ACCOUNTS intent is accessible directly from
         // the AOSP Dialer settings page on multi-sim devices.
@@ -361,6 +379,12 @@
         if (telephonyManager.isMultiSimEnabled()) {
             Preference phoneAccountSettingsPreference = findPreference(PHONE_ACCOUNT_SETTINGS_KEY);
             getPreferenceScreen().removePreference(phoneAccountSettingsPreference);
+            getPreferenceScreen().removePreference(mButtonVibratingForMoCallAccepted);
+        } else {
+            final int vibrating = Settings.Global.getInt(getContentResolver(),
+                    Settings.Global.VIBRATING_FOR_OUTGOING_CALL_ACCEPTED, 1);
+            mButtonVibratingForMoCallAccepted.setChecked(vibrating != 0);
+            mButtonVibratingForMoCallAccepted.setOnPreferenceChangeListener(this);
         }
 
         PreferenceScreen prefSet = getPreferenceScreen();
@@ -390,17 +414,28 @@
             mButtonAutoRetry = null;
         }
 
+        Preference commonOptions = prefSet.findPreference(BUTTON_COMMON_OPTIONS);
         Preference cdmaOptions = prefSet.findPreference(BUTTON_CDMA_OPTIONS);
         Preference gsmOptions = prefSet.findPreference(BUTTON_GSM_UMTS_OPTIONS);
         Preference fdnButton = prefSet.findPreference(BUTTON_FDN_KEY);
         fdnButton.setIntent(mSubscriptionInfoHelper.getIntent(FdnSetting.class));
         if (carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
-            cdmaOptions.setIntent(mSubscriptionInfoHelper.getIntent(CdmaCallOptions.class));
-            gsmOptions.setIntent(mSubscriptionInfoHelper.getIntent(GsmUmtsCallOptions.class));
+            if (carrierConfig.getBoolean("config_common_callsettings_support_bool")) {
+                prefSet.removePreference(cdmaOptions);
+                prefSet.removePreference(gsmOptions);
+                boolean isCdmaPhone = mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA;
+                commonOptions.setIntent(mSubscriptionInfoHelper.getIntent(
+                            isCdmaPhone ? CdmaCallOptions.class : GsmUmtsCallOptions.class));
+            } else {
+                prefSet.removePreference(commonOptions);
+                cdmaOptions.setIntent(mSubscriptionInfoHelper.getIntent(CdmaCallOptions.class));
+                gsmOptions.setIntent(mSubscriptionInfoHelper.getIntent(GsmUmtsCallOptions.class));
+            }
         } else {
             // Remove GSM options and repopulate the preferences in this Activity if phone type is
             // GSM.
             prefSet.removePreference(gsmOptions);
+            prefSet.removePreference(commonOptions);
 
             int phoneType = mPhone.getPhoneType();
             if (carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)) {
@@ -415,6 +450,70 @@
                     cdmaOptions.setSummary(null);
                     cdmaOptions.setTitle(R.string.additional_gsm_call_settings);
                     cdmaOptions.setIntent(mSubscriptionInfoHelper.getIntent(CdmaCallOptions.class));
+
+                    if (carrierConfig.getBoolean(
+                            CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
+                        CdmaVoicePrivacySwitchPreference prefPri =
+                                (CdmaVoicePrivacySwitchPreference)prefSet.findPreference(
+                                "button_voice_privacy_key");
+                        if (prefPri != null) {
+                             prefSet.removePreference(prefPri);
+                        }
+                    }
+
+                    if (carrierConfig.getBoolean(
+                                CarrierConfigManager.KEY_CDMA_CW_CF_ENABLED_BOOL)
+                                && CdmaCallOptions.isCdmaCallWaitingActivityPresent(
+                                mPhone.getContext())) {
+                        Log.d(LOG_TAG, "Enabled CW CF");
+                        PreferenceScreen prefCW = (PreferenceScreen)
+                                prefSet.findPreference("button_cw_key");
+                        if (prefCW != null) {
+                            prefCW.setOnPreferenceClickListener(
+                                    new Preference.OnPreferenceClickListener() {
+                                        @Override
+                                        public boolean onPreferenceClick(Preference preference) {
+                                            Intent intent = new Intent(CdmaCallOptions.
+                                                    CALL_WAITING_INTENT);
+                                            intent.putExtra(
+                                                SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                                                mPhone.getSubId());
+                                            startActivity(intent);
+                                            return true;
+                                        }
+                                    });
+                        }
+                        PreferenceScreen prefCF = (PreferenceScreen)
+                                prefSet.findPreference("button_cf_expand_key");
+                        if (prefCF != null) {
+                            prefCF.setOnPreferenceClickListener(
+                                    new Preference.OnPreferenceClickListener() {
+                                        @Override
+                                        public boolean onPreferenceClick(Preference preference) {
+                                             Intent intent = mPhone.isUtEnabled() ?
+                                                mSubscriptionInfoHelper.getIntent(CallForwardType.class)
+                                                : new Intent(CdmaCallOptions.CALL_FORWARD_INTENT);
+                                            intent.putExtra(
+                                                SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                                                mPhone.getSubId());
+                                            startActivity(intent);
+                                            return true;
+                                        }
+                                    });
+                        }
+                    } else {
+                        Log.d(LOG_TAG, "Disabled CW CF");
+                        PreferenceScreen prefCW = (PreferenceScreen)
+                                prefSet.findPreference("button_cw_key");
+                        if (prefCW != null) {
+                            prefSet.removePreference(prefCW);
+                        }
+                        PreferenceScreen prefCF = (PreferenceScreen)
+                                prefSet.findPreference("button_cf_expand_key");
+                        if (prefCF != null) {
+                            prefSet.removePreference(prefCF);
+                        }
+                    }
                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                     prefSet.removePreference(cdmaOptions);
                     if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
@@ -452,6 +551,36 @@
                 .createForSubscriptionId(mPhone.getSubId());
         PersistableBundle carrierConfig =
                 PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+
+        mImsSettingsScreen = (PreferenceScreen) findPreference(BUTTON_IMS_SETTINGS_KEY);
+        if (isImsSettingsApkAvailable(mPhone.getContext())) {
+            ImsManager imsManager = ImsManager.getInstance(mPhone.getContext(),
+                    mPhone.getPhoneId());
+            try {
+                if ((imsManager.isVolteEnabledByPlatform() || imsManager.isVtEnabledByPlatform() ||
+                        imsManager.isWfcEnabledByPlatform()) &&
+                        imsManager.getImsServiceState() == ImsFeature.STATE_READY) {
+                    mImsSettingsScreen.setOnPreferenceClickListener(
+                            new Preference.OnPreferenceClickListener() {
+                         @Override
+                         public boolean onPreferenceClick(Preference preference) {
+                             Intent intent = new Intent("org.codeaurora.IMS_SETTINGS");
+                             intent.putExtra(QtiCallConstants.EXTRA_PHONE_ID, mPhone.getPhoneId());
+                             startActivity(intent);
+                             return true;
+                         }
+                    });
+                } else {
+                    prefSet.removePreference(mImsSettingsScreen);
+                }
+            } catch (ImsException ex) {
+                log("Exception when trying to get ImsServiceStatus: " + ex);
+                prefSet.removePreference(mImsSettingsScreen);
+            }
+        } else {
+            prefSet.removePreference(mImsSettingsScreen);
+        }
+
         boolean useWfcHomeModeForRoaming = carrierConfig.getBoolean(
                     CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL,
                     false);
@@ -496,7 +625,7 @@
 
             int resId = com.android.internal.R.string.wifi_calling_off_summary;
             if (mImsMgr.isWfcEnabledByUser()) {
-                boolean isRoaming = telephonyManager.isNetworkRoaming();
+                boolean isRoaming = telephonyManager.isNetworkRoaming(mPhone.getSubId());
                 // Also check carrier config for roaming mode
                 int wfcMode = mImsMgr.getWfcMode(isRoaming && !useWfcHomeModeForRoaming);
                 switch (wfcMode) {
@@ -509,6 +638,9 @@
                     case ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED:
                         resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
                         break;
+                    case ImsConfig.WfcModeFeatureValueConstants.IMS_PREFERRED:
+                        resId = com.android.internal.R.string.wfc_mode_ims_preferred_summary;
+                        break;
                     default:
                         if (DBG) log("Unexpected WFC mode value: " + wfcMode);
                 }
@@ -614,4 +746,24 @@
             return getResources();
         }
     }
+
+    /**
+     * check whether ImsSettings apk exist in system, if yes, return true, else
+     * return false.
+     */
+    public static boolean isImsSettingsApkAvailable(Context context) {
+        // check whether the target handler exist in system
+        Intent intent = new Intent("org.codeaurora.IMS_SETTINGS");
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+        for (ResolveInfo resolveInfo : list) {
+            // check is it installed in system.img, exclude the application
+            // installed by user
+            if ((resolveInfo.activityInfo.applicationInfo.flags &
+                    ApplicationInfo.FLAG_SYSTEM) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 2cbb7c5..906b09a 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -2,6 +2,12 @@
 
 import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR;
 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
+import static com.android.phone.TimeConsumingPreferenceActivity.RADIO_OFF_ERROR;
+import static com.android.phone.TimeConsumingPreferenceActivity.FDN_CHECK_FAILURE;
+import static com.android.phone.TimeConsumingPreferenceActivity.STK_CC_SS_TO_DIAL_ERROR;
+import static com.android.phone.TimeConsumingPreferenceActivity.STK_CC_SS_TO_USSD_ERROR;
+import static com.android.phone.TimeConsumingPreferenceActivity.STK_CC_SS_TO_SS_ERROR;
+import static com.android.phone.TimeConsumingPreferenceActivity.STK_CC_SS_TO_DIAL_VIDEO_ERROR;
 
 import android.app.AlertDialog;
 import android.content.Context;
@@ -11,9 +17,14 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.SystemProperties;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneNumberUtils;
+import android.telephony.ims.ImsCallForwardInfo;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.TelephonyManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
 import android.text.BidiFormatter;
 import android.text.SpannableString;
 import android.text.TextDirectionHeuristics;
@@ -21,16 +32,32 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
+import android.widget.Toast;
+
+import org.codeaurora.ims.QtiImsException;
+import org.codeaurora.ims.QtiImsExtListenerBaseImpl;
+import org.codeaurora.ims.QtiImsExtConnector;
+import org.codeaurora.ims.QtiImsExtManager;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+import org.codeaurora.ims.QtiCallConstants;
 
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
 
+import com.qti.extphone.Client;
+import com.qti.extphone.ExtPhoneCallbackBase;
+import com.qti.extphone.ExtTelephonyManager;
+import com.qti.extphone.IExtPhoneCallback;
+import com.qti.extphone.QtiCallForwardInfo;
+import com.qti.extphone.Status;
+
 import java.util.HashMap;
 
 public class CallForwardEditPreference extends EditPhoneNumberPreference {
     private static final String LOG_TAG = "CallForwardEditPreference";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private static final String SRC_TAGS[]       = {"{0}"};
     private CharSequence mSummaryOnTemplate;
@@ -60,15 +87,31 @@
     private HashMap<String, String> mCfInfo;
     private long mDelayMillisAfterUssdSet = 1000;
 
+    private boolean mExpectMore;
+    private boolean mIsTimerEnabled;
+    private boolean mAllowSetCallFwding = false;
+    private boolean mUtEnabled = false;
+    /*Variables which holds CFUT response data*/
+    private int mStartHour;
+    private int mStartMinute;
+    private int mEndHour;
+    private int mEndMinute;
+    private int mStatus;
+    private String mNumber;
+    private QtiImsExtConnector mQtiImsExtConnector;
+    private QtiImsExtManager mQtiImsExtManager;
+    private ExtTelephonyManager mExtTelephonyManager;
+    private Client mClient;
+    private Context mContext;
+
     public CallForwardEditPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         mSummaryOnTemplate = this.getSummaryOn();
+        mContext = context;
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.CallForwardEditPreference, 0, R.style.EditPhoneNumberPreference);
-        mServiceClass = a.getInt(R.styleable.CallForwardEditPreference_serviceClass,
-                CommandsInterface.SERVICE_CLASS_VOICE);
         reason = a.getInt(R.styleable.CallForwardEditPreference_reason,
                 CommandsInterface.CF_REASON_UNCONDITIONAL);
         a.recycle();
@@ -81,14 +124,17 @@
     }
 
     void init(TimeConsumingPreferenceListener listener, Phone phone,
-            boolean replaceInvalidCFNumber, boolean callForwardByUssd) {
+            boolean replaceInvalidCFNumber, int serviceClass, boolean callForwardByUssd) {
         mPhone = phone;
         mTcpListener = listener;
         mReplaceInvalidCFNumber = replaceInvalidCFNumber;
+        mServiceClass = serviceClass;
+        mUtEnabled = mPhone.isUtEnabled();
         mCallForwardByUssd = callForwardByUssd;
         Log.d(LOG_TAG,
                 "init :mReplaceInvalidCFNumber " + mReplaceInvalidCFNumber + ", mCallForwardByUssd "
                         + mCallForwardByUssd);
+        mExtTelephonyManager = ExtTelephonyManager.getInstance(getContext());
         if (mCallForwardByUssd) {
             mCfInfo = new HashMap<String, String>();
             TelephonyManager telephonyManager = new TelephonyManager(getContext(),
@@ -98,11 +144,83 @@
         }
     }
 
+    private void createQtiImsExtConnector(Context context) {
+        try {
+            mQtiImsExtConnector = new QtiImsExtConnector(context,
+                    new QtiImsExtConnector.IListener() {
+                        @Override
+                        public void onConnectionAvailable(QtiImsExtManager qtiImsExtManager) {
+                            Log.i(LOG_TAG, "QtiImsExtConnector onConnectionAvailable");
+                            mQtiImsExtManager = qtiImsExtManager;
+                            queryImsCallForwardStatus();
+                        }
+                        @Override
+                        public void onConnectionUnavailable() {
+                            mQtiImsExtManager = null;
+                            //QtiImsExtManager is not available so set
+                            //mIsTimerEnabled to false so that no Timer related operations will hit
+                            //and remove spinner.
+                            mIsTimerEnabled = false;
+                            mTcpListener.onFinished(CallForwardEditPreference.this, false);
+                        }
+                    });
+        } catch (QtiImsException e) {
+            Log.e(LOG_TAG, "Unable to create QtiImsExtConnector");
+        }
+    }
+
+    public void deInit() {
+        if (mQtiImsExtConnector != null) {
+            mQtiImsExtConnector.disconnect();
+            mQtiImsExtConnector = null;
+            mQtiImsExtManager = null;
+            mIsTimerEnabled = false;
+        }
+    }
+
+    private boolean isUtUnavailableForVideoCallForward() {
+        return !mPhone.isUtEnabled() && (mServiceClass == CommandsInterface.SERVICE_CLASS_DATA_SYNC
+                + CommandsInterface.SERVICE_CLASS_PACKET);
+    }
+
+    public boolean isAutoRetryCfu() {
+        CarrierConfigManager cfgManager = (CarrierConfigManager)
+            mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        boolean autoRetryCfu = false;
+        if (cfgManager != null) {
+            autoRetryCfu = cfgManager.getConfigForSubId(mPhone.getSubId())
+                .getBoolean("config_auto_retry_cfu_bool");
+        }
+        /**
+         * if UT is true at begginning and after query CFU fail with NW error 403 at
+         * Modem side,  Modem update UT to false at first and rasie error response
+         * to AP.
+         * At this condition, switch to query SS over cdma method UI.
+         */
+        return autoRetryCfu && mUtEnabled && !mPhone.isUtEnabled();
+    }
+
+    //Used to check if CFUT(CFU with timer) is supported
+    private boolean isTimerEnabled() {
+        //Timer is enabled only when UT services are enabled
+        CarrierConfigManager cfgManager = (CarrierConfigManager)
+                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        return (SystemProperties.getBoolean("persist.radio.ims.cmcc", false)
+                || (cfgManager != null) ?
+                cfgManager.getConfigForSubId(mPhone.getSubId())
+                    .getBoolean("config_enable_cfu_time") : false)
+                && mPhone.isUtEnabled();
+    }
+
     void restoreCallForwardInfo(CallForwardInfo cf) {
         handleCallForwardResult(cf);
         updateSummaryText();
     }
 
+    void setExpectMore(boolean expectMore) {
+        mExpectMore = expectMore;
+    }
+
     @Override
     protected void onBindDialogView(View view) {
         // default the button clicked to be the cancel button.
@@ -121,6 +239,11 @@
         super.onDialogClosed(positiveResult);
 
         Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked + ", positiveResult=" + positiveResult);
+        if (isUtUnavailableForVideoCallForward()) {
+            Toast.makeText(mContext, R.string.ut_unavailable_to_set_video_cf_toast,
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
         // Ignore this event if the user clicked the cancel button, or if the dialog is dismissed
         // without any button being pressed (back button press or click event outside the dialog).
         if (isUnknownStatus() && this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
@@ -159,14 +282,34 @@
             final String number = getPhoneNumber();
 
             Log.d(LOG_TAG, "callForwardInfo=" + callForwardInfo);
+            final int editStartHour = isAllDayChecked()? 0 : getStartTimeHour();
+            final int editStartMinute = isAllDayChecked()? 0 : getStartTimeMinute();
+            final int editEndHour = isAllDayChecked()? 0 : getEndTimeHour();
+            final int editEndMinute = isAllDayChecked()? 0 : getEndTimeMinute();
 
+            boolean isCFSettingChanged = true;
             if (action == CommandsInterface.CF_ACTION_REGISTRATION
                     && callForwardInfo != null
                     && callForwardInfo.status == 1
                     && number.equals(callForwardInfo.number)) {
-                // no change, do nothing
-                Log.d(LOG_TAG, "no change, do nothing");
-            } else {
+                if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL){
+                    // need to check if the time period for CFUT is changed
+                    if (isAllDayChecked()){
+                        isCFSettingChanged = isTimerValid();
+                    } else {
+                        isCFSettingChanged = mStartHour != editStartHour
+                                || mStartMinute != editStartMinute
+                                || mEndHour != editEndHour
+                                || mEndMinute != editEndMinute;
+                    }
+                } else {
+                    // no change, do nothing
+                    if (DBG) Log.d(LOG_TAG, "no change, do nothing");
+                    isCFSettingChanged = false;
+                }
+            }
+            if (DBG) Log.d(LOG_TAG, "isCFSettingChanged = " + isCFSettingChanged);
+            if (isCFSettingChanged) {
                 // set to network
                 Log.d(LOG_TAG, "reason=" + reason + ", action=" + action
                         + ", number=" + number);
@@ -174,7 +317,34 @@
                 // Display no forwarding number while we're waiting for
                 // confirmation
                 setSummaryOn("");
-                if (!mCallForwardByUssd) {
+
+                // the interface of Phone.setCallForwardingOption has error:
+                // should be action, reason...
+                if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL
+                        && !isAllDayChecked() && mIsTimerEnabled
+                        && (action != CommandsInterface.CF_ACTION_DISABLE)) {
+
+                    Log.d(LOG_TAG, "setCallForwardingUncondTimerOption,"
+                                                +"starthour = " + editStartHour
+                                                + "startminute = " + editStartMinute
+                                                + "endhour = " + editEndHour
+                                                + "endminute = " + editEndMinute);
+                    try {
+                        mQtiImsExtManager.setCallForwardUncondTimer(mPhone.getPhoneId(),
+                                editStartHour,
+                                editStartMinute,
+                                editEndHour,
+                                editEndMinute,
+                                action,
+                                reason,
+                                mServiceClass,
+                                number,
+                                imsInterfaceListener);
+                    } catch (QtiImsException e) {
+                        Log.d(LOG_TAG, "setCallForwardUncondTimer exception!" +e);
+                    }
+                    mAllowSetCallFwding = true;
+                } else if (!mCallForwardByUssd) {
                     // the interface of Phone.setCallForwardingOption has error:
                     // should be action, reason...
                     mPhone.setCallForwardingOption(action,
@@ -202,7 +372,18 @@
         }
     }
 
-    private void handleCallForwardResult(CallForwardInfo cf) {
+    void handleCallForwardTimerResult() {
+        setToggled(mStatus == 1);
+        setPhoneNumber(mNumber);
+        /*Setting Timer*/
+        if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL) {
+            setAllDayCheckBox(!(mStatus == 1 && isTimerValid()));
+            //set timer info even all be zero
+            setPhoneNumberWithTimePeriod(mNumber, mStartHour, mStartMinute, mEndHour, mEndMinute);
+        }
+    }
+
+    void handleCallForwardResult(CallForwardInfo cf) {
         callForwardInfo = cf;
         Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
         // In some cases, the network can send call forwarding URIs for voicemail that violate the
@@ -215,6 +396,13 @@
             Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
         }
 
+        if (DBG) Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
+        if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL) {
+            mStartHour = 0;
+            mStartMinute = 0;
+            mEndHour = 0;
+            mEndMinute = 0;
+        }
         setUnknownStatus(callForwardInfo.status == CommandsInterface.SS_STATUS_UNKNOWN);
         setToggled(callForwardInfo.status == 1);
         boolean displayVoicemailNumber = false;
@@ -239,11 +427,32 @@
      */
     void startCallForwardOptionsQuery() {
         if (!mCallForwardByUssd) {
-            mPhone.getCallForwardingOption(reason, mServiceClass,
-                    mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
+            if (isUtUnavailableForVideoCallForward()) {
+                Log.d(LOG_TAG, "Video CF query cannot be triggered due to UT is false now");
+                return;
+            }
+            mIsTimerEnabled = isTimerEnabled();
+            Log.d(LOG_TAG, "isTimerEnabled=" + mIsTimerEnabled);
+            if (mPhone != null &&  mPhone.isUtEnabled()) {
+                if (mQtiImsExtConnector == null) {
+                    createQtiImsExtConnector(mContext);
+                    //Connect will get the QtiImsExtManager instance.
+                    mQtiImsExtConnector.connect();
+                } else {
+                    queryImsCallForwardStatus();
+                }
+            } else {
+                if (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM &&
+                        PhoneUtils.isBacktoBackSSFeatureSupported()) {
+                    queryCallForwardStatus();
+                } else {
+                    mPhone.getCallForwardingOption(reason, mServiceClass,
+                            mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
                             // unused in this case
                             CommandsInterface.CF_ACTION_DISABLE,
                             MyHandler.MESSAGE_GET_CF, null));
+                }
+            }
         } else {
             mHandler.sendMessage(mHandler.obtainMessage(mHandler.MESSAGE_GET_CF_USSD,
                     // unused in this case
@@ -254,9 +463,87 @@
         }
     }
 
+    private void queryCallForwardStatus() {
+        if (!mExtTelephonyManager.isServiceConnected()) {
+            sendErrorResponse();
+            return;
+        }
+
+        try {
+            mClient = mExtTelephonyManager.registerCallback(
+                    mContext.getPackageName(), mExtPhoneCallFwdInfoCallback);
+            mExtTelephonyManager.queryCallForwardStatus(mPhone.getPhoneId(), reason,
+                    mServiceClass, null /*number*/, mExpectMore,
+                    mClient);
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Exception " + e);
+            sendErrorResponse();
+        }
+    }
+
+    private void sendErrorResponse() {
+        Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
+                // unused in this case
+                CommandsInterface.CF_ACTION_DISABLE, MyHandler.MESSAGE_GET_CF, null);
+        AsyncResult.forMessage(msg, null, new CommandException
+               (CommandException.Error.GENERIC_FAILURE));
+        msg.sendToTarget();
+    }
+
+    private IExtPhoneCallback mExtPhoneCallFwdInfoCallback = new ExtPhoneCallbackBase() {
+        @Override
+        public void queryCallForwardStatusResponse(Status status, QtiCallForwardInfo[] infos) {
+            Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
+                    // unused in this case
+                    CommandsInterface.CF_ACTION_DISABLE, MyHandler.MESSAGE_GET_CF, null);
+            if (status.get() == Status.SUCCESS) {
+                CallForwardInfo[] cfInfo = new CallForwardInfo[infos.length];
+                for (int i = 0; i < infos.length; i++) {
+                    cfInfo[i] = new CallForwardInfo();
+                    cfInfo[i].status = infos[i].status;
+                    cfInfo[i].reason = infos[i].reason;
+                    cfInfo[i].serviceClass = infos[i].serviceClass;
+                    cfInfo[i].toa = infos[i].toa;
+                    cfInfo[i].number = infos[i].number;
+                    cfInfo[i].timeSeconds = infos[i].timeSeconds;
+                }
+                AsyncResult.forMessage(msg, cfInfo, null);
+            } else {
+                AsyncResult.forMessage(msg, null,
+                        new CommandException(CommandException.Error.GENERIC_FAILURE));
+            }
+            msg.sendToTarget();
+        }
+    };
+
+    private void queryImsCallForwardStatus() {
+        if (mQtiImsExtManager != null) {
+            try {
+                if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL &&
+                        mIsTimerEnabled) {
+                    setTimeSettingVisibility(true);
+                    mQtiImsExtManager.getCallForwardUncondTimer(mPhone.getPhoneId(),
+                            reason, mServiceClass, imsInterfaceListener);
+                } else {
+                    mQtiImsExtManager.queryCallForwardStatus(mPhone.getPhoneId(),
+                            reason, mServiceClass, mExpectMore, imsInterfaceListener);
+                }
+            } catch (QtiImsException e){
+                Log.d(LOG_TAG, "queryCallForwardStatus failed. " +
+                        "Exception = " + e);
+                sendErrorResponse();
+            }
+        }
+    }
+
     private void updateSummaryText() {
+        if (DBG) Log.d(LOG_TAG, "updateSummaryText, complete fetching for reason " + reason);
         if (isToggled()) {
-            final String number = getRawPhoneNumber();
+            String number = getRawPhoneNumber();
+            if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL
+                    && mIsTimerEnabled && isTimerValid()){
+                number = getRawPhoneNumberWithTime();
+            }
             if (number != null && number.length() > 0) {
                 // Wrap the number to preserve presentation in RTL languages.
                 String wrappedNumber = BidiFormatter.getInstance().unicodeWrap(
@@ -290,6 +577,144 @@
         return telephonyManager.getNetworkCountryIso().toUpperCase();
     }
 
+    private QtiImsExtListenerBaseImpl imsInterfaceListener =
+            new QtiImsExtListenerBaseImpl() {
+
+        @Override
+        public void onSetCallForwardUncondTimer(int phoneId, int status) {
+            if (DBG) Log.d(LOG_TAG, "onSetCallForwardTimer phoneId=" + phoneId +" status= "+status);
+
+            try {
+                mQtiImsExtManager.getCallForwardUncondTimer(phoneId,
+                        reason,
+                        mServiceClass,
+                        imsInterfaceListener);
+            } catch (QtiImsException e) {
+                if (DBG) Log.d(LOG_TAG, "setCallForwardUncondTimer exception! ");
+            }
+        }
+
+        @Override
+        public void onGetCallForwardUncondTimer(int phoneId, int startHour, int endHour,
+                int startMinute, int endMinute, int reason, int status, String number,
+                int service) {
+            Log.d(LOG_TAG,"onGetCallForwardUncondTimer phoneId=" + phoneId + " startHour= "
+                    + startHour + " endHour = " + endHour + "endMinute = " + endMinute
+                    + "status = " + status + "number = " + number + "service= " +service);
+            mStartHour = startHour;
+            mStartMinute = startMinute;
+            mEndHour = endHour;
+            mEndMinute = endMinute;
+            mStatus = status;
+            mNumber = number;
+
+            mHandler.sendMessage(mHandler.obtainMessage(mHandler.MESSAGE_GET_CFUT));
+        }
+
+        @Override
+        public void queryCallForwardStatusResponse(int phoneId, ImsCallForwardInfo[] cfInfoList) {
+            Log.d(LOG_TAG, "queryCallForwardStatusResponse phoneId=" + phoneId);
+
+            int size = cfInfoList.length;
+            CallForwardInfo[] cfInfo = new CallForwardInfo[size];
+            for (int i = 0; i < size; i++) {
+                cfInfo[i] = new CallForwardInfo();
+                cfInfo[i].status = cfInfoList[i].getStatus();
+                cfInfo[i].reason = cfInfoList[i].getCondition();
+                cfInfo[i].toa = cfInfoList[i].getToA();
+                cfInfo[i].number = cfInfoList[i].getNumber();
+                cfInfo[i].timeSeconds = cfInfoList[i].getTimeSeconds();
+
+                //Check if the service class signifies Video call forward
+                //As per 3GPP TS 29002 MAP Specification : Section 17.7.10, the BearerServiceCode
+                // for "allDataCircuitAsynchronous" is '01010000' ( i.e. 80).
+                //Hence, SERVICE_CLASS_DATA_SYNC (1<<4) and SERVICE_CLASS_PACKET (1<<6)
+                //together make video service class.
+
+                if (cfInfoList[i].getServiceClass() == (CommandsInterface.SERVICE_CLASS_DATA_SYNC +
+                        CommandsInterface.SERVICE_CLASS_PACKET)) {
+                    cfInfo[i].serviceClass = cfInfoList[i].getServiceClass();
+                } else {
+                    cfInfo[i].serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
+                }
+            }
+
+            Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
+                    // unused in this case
+                    CommandsInterface.CF_ACTION_DISABLE, MyHandler.MESSAGE_GET_CF, null);
+            AsyncResult.forMessage(msg, cfInfo, null);
+            msg.sendToTarget();
+        }
+
+        @Override
+        public void onUTReqFailed(int phoneId, int errCode, String errString) {
+            if (DBG) Log.d(LOG_TAG, "onUTReqFailed phoneId=" + phoneId + " errCode= "
+                    + errCode + "errString =" + errString);
+            Message msg;
+            if (reason == CommandsInterface.CF_REASON_UNCONDITIONAL && mIsTimerEnabled) {
+                msg = mHandler.obtainMessage(mHandler.MESSAGE_GET_UT_FAILED);
+                msg.arg1 = errCode;
+                msg.sendToTarget();
+            } else {
+                if (errCode == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED) {
+                    queryCallForwardStatus();
+                } else {
+                    msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
+                            // unused in this case
+                            CommandsInterface.CF_ACTION_DISABLE, MyHandler.MESSAGE_GET_CF, null);
+                    if (errCode == QtiCallConstants.CODE_UT_CF_SERVICE_NOT_REGISTERED) {
+                        AsyncResult.forMessage(msg, null,
+                                new QtiImsException("Service Not Registered", errCode));
+                    } else {
+                        AsyncResult.forMessage(msg, null, PhoneUtils.getCommandException(errCode));
+                    }
+                    msg.sendToTarget();
+                }
+            }
+
+        }
+    };
+
+    private void handleUtReqFailed(int errCode) {
+        if (mAllowSetCallFwding) {
+            mTcpListener.onFinished(CallForwardEditPreference.this, false);
+            mAllowSetCallFwding = false;
+        } else {
+            mTcpListener.onFinished(CallForwardEditPreference.this, true);
+        }
+        int error = RESPONSE_ERROR;
+        if (errCode == ImsReasonInfo.CODE_FDN_BLOCKED) {
+            error = FDN_CHECK_FAILURE;
+        } else if (errCode == ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL) {
+            error = STK_CC_SS_TO_DIAL_ERROR;
+        } else if (errCode == ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO) {
+            error = STK_CC_SS_TO_DIAL_VIDEO_ERROR;
+        } else if(errCode == ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD) {
+            error = STK_CC_SS_TO_USSD_ERROR;
+        } else if (errCode == ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS) {
+            error = STK_CC_SS_TO_SS_ERROR;
+        } else if (errCode == ImsReasonInfo.CODE_RADIO_OFF) {
+            error = RADIO_OFF_ERROR;
+        }
+        mTcpListener.onError(CallForwardEditPreference.this, error);
+    }
+
+    private void handleGetCFTimerResponse() {
+        if (mAllowSetCallFwding) {
+            mTcpListener.onFinished(CallForwardEditPreference.this, false);
+            mAllowSetCallFwding = false;
+        } else {
+            mTcpListener.onFinished(CallForwardEditPreference.this, true);
+        }
+        handleCallForwardTimerResult();
+        updateSummaryText();
+    }
+
+    //used to check if timer infor is valid
+    private boolean isTimerValid() {
+        return mStartHour != 0 || mStartMinute != 0 || mEndHour != 0 || mEndMinute != 0;
+    }
+
     // Message protocol:
     // what: get vs. set
     // arg1: action -- register vs. disable
@@ -299,6 +724,8 @@
         static final int MESSAGE_SET_CF = 1;
         static final int MESSAGE_GET_CF_USSD = 2;
         static final int MESSAGE_SET_CF_USSD = 3;
+        static final int MESSAGE_GET_CFUT = 4;
+        static final int MESSAGE_GET_UT_FAILED = 5;
 
         TelephonyManager.UssdResponseCallback mUssdCallback =
                 new TelephonyManager.UssdResponseCallback() {
@@ -341,6 +768,7 @@
 
         @Override
         public void handleMessage(Message msg) {
+            Log.i(LOG_TAG, "handleMessage : " + msg.what);
             switch (msg.what) {
                 case MESSAGE_GET_CF:
                     handleGetCFResponse(msg);
@@ -354,6 +782,12 @@
                 case MESSAGE_SET_CF_USSD:
                     prepareUssdCommand(msg, CarrierXmlParser.SsEntry.SSAction.UNKNOWN);
                     break;
+                case MESSAGE_GET_CFUT:
+                    handleGetCFTimerResponse();
+                    break;
+                case MESSAGE_GET_UT_FAILED:
+                    handleUtReqFailed(msg.arg1);
+                    break;
             }
         }
 
@@ -369,8 +803,15 @@
             if (ar.exception != null) {
                 Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
                 if (ar.exception instanceof CommandException) {
-                    mTcpListener.onException(CallForwardEditPreference.this,
-                            (CommandException) ar.exception);
+                    if (isAutoRetryCfu() && reason == CommandsInterface.CF_REASON_UNCONDITIONAL) {
+                         mUtEnabled = mPhone.isUtEnabled();
+                    } else {
+                        mTcpListener.onException(CallForwardEditPreference.this,
+                                (CommandException) ar.exception);
+                    }
+                } else if (ar.exception instanceof QtiImsException) {
+                    mTcpListener.onError(CallForwardEditPreference.this,
+                            ((QtiImsException) ar.exception).getCode());
                 } else {
                     // Most likely an ImsException and we can't handle it the same way as
                     // a CommandException. The best we can do is to handle the exception
diff --git a/src/com/android/phone/CallForwardType.java b/src/com/android/phone/CallForwardType.java
new file mode 100644
index 0000000..5bb79f3
--- /dev/null
+++ b/src/com/android/phone/CallForwardType.java
@@ -0,0 +1,222 @@
+/* Copyright (c) 2015, 2017-2018, 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.phone;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import android.preference.Preference.OnPreferenceClickListener;
+
+public class CallForwardType extends PreferenceActivity {
+    private static final String LOG_TAG = "CallForwardType";
+    private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    private static final String BUTTON_CF_KEY_VOICE = "button_cf_key_voice";
+    private static final String BUTTON_CF_KEY_VIDEO = "button_cf_key_video";
+
+    private Preference mVoicePreference;
+    private Preference mVideoPreference;
+    private Phone mPhone;
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private boolean mIsUtCapable = false;
+    private boolean mIsVtCapable = false;
+    boolean mHideVtCfOption = false;
+    private FeatureConnector<ImsManager> mFeatureConnector;
+    private int mPhoneId;
+    private IntentFilter mIntentFilter;
+
+     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+         @Override
+         public void onReceive(Context context, Intent intent) {
+             Log.d(LOG_TAG, "onReceive intent : " + intent);
+                 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
+                         SubscriptionManager.INVALID_PHONE_INDEX);
+             if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+                 if (phoneId == mPhoneId &&
+                         IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
+                             intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
+                     Log.d(LOG_TAG, "onSimAbsent, exit");
+                     finish();
+                 }
+             }
+         }
+     };
+
+    private ImsMmTelManager.CapabilityCallback mCapabilityCallback =
+        new ImsMmTelManager.CapabilityCallback() {
+            @Override
+            public void onCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities capabilities) {
+                    boolean isUtCapable = capabilities.isCapable(
+                            MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
+                    boolean isVtCapable = capabilities.isCapable(
+                            MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
+                    if (isUtCapable ==  mIsUtCapable && isVtCapable == mIsVtCapable) {
+                        return;
+                    }
+                    mIsUtCapable = isUtCapable;
+                    mIsVtCapable = isVtCapable;
+                    showVideoOption(mIsUtCapable && mIsVtCapable && !mHideVtCfOption);
+            }
+    };
+
+    private void setListeners() throws ImsException {
+        ImsManager imsMgr = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
+        imsMgr.addCapabilitiesCallback(mCapabilityCallback, mPhone.getContext().getMainExecutor());
+    }
+
+    private void removeListeners() {
+        ImsManager imsMgr = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
+        imsMgr.removeCapabilitiesCallback(mCapabilityCallback);
+    }
+
+    private void showVideoOption(boolean show) {
+        if (!show){
+            Log.d(LOG_TAG, "remove video option");
+            getPreferenceScreen().removePreference(mVideoPreference);
+        } else {
+            Log.d(LOG_TAG, "enable video option");
+            getPreferenceScreen().addPreference(mVideoPreference);
+        }
+    }
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        Log.d(LOG_TAG, "onCreate..");
+        /*Loading CallForward Setting page*/
+        addPreferencesFromResource(R.xml.call_forward_type);
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+        mPhone = mSubscriptionInfoHelper.getPhone();
+        mPhoneId = mPhone.getPhoneId();
+        mIsUtCapable = mPhone.isUtEnabled();
+        mIsVtCapable = mPhone.isVideoEnabled();
+        mFeatureConnector = ImsManager.getConnector(
+            mPhone.getContext(), mPhone.getPhoneId(), LOG_TAG,
+            new FeatureConnector.Listener<ImsManager>() {
+                @Override
+                public void connectionReady(ImsManager manager) throws ImsException {
+                    Log.d(LOG_TAG, "ImsManager: connection ready.");
+                    setListeners();
+                }
+
+                @Override
+                public void connectionUnavailable(int reason) {
+                    Log.d(LOG_TAG, "ImsManager: connection unavailable.");
+                    removeListeners();
+                }
+            }, mPhone.getContext().getMainExecutor());
+
+        /*Voice Button*/
+        mVoicePreference = (Preference) findPreference(BUTTON_CF_KEY_VOICE);
+        mVoicePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+
+            /*onClicking Voice Button*/
+            public boolean onPreferenceClick(Preference pref) {
+                Intent intent = mSubscriptionInfoHelper.getIntent(GsmUmtsCallForwardOptions.class);
+                Log.d(LOG_TAG, "Voice button clicked!");
+                intent.putExtra(PhoneUtils.SERVICE_CLASS,
+                        CommandsInterface.SERVICE_CLASS_VOICE);
+                startActivity(intent);
+                return true;
+            }
+        });
+
+         /*Video Button*/
+         mVideoPreference = (Preference) findPreference(BUTTON_CF_KEY_VIDEO);
+         mVideoPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+
+             /*onClicking Video Button*/
+             public boolean onPreferenceClick(Preference pref) {
+                Intent intent = mSubscriptionInfoHelper.getIntent(GsmUmtsCallForwardOptions.class);
+                Log.d(LOG_TAG, "Video button clicked!");
+                intent.putExtra(PhoneUtils.SERVICE_CLASS,
+                       (CommandsInterface.SERVICE_CLASS_DATA_SYNC +
+                        CommandsInterface.SERVICE_CLASS_PACKET));
+                startActivity(intent);
+                return true;
+             }
+        });
+        mIntentFilter = new IntentFilter();
+        mIntentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        CarrierConfigManager cfgManager = (CarrierConfigManager)
+                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (cfgManager != null) {
+            mHideVtCfOption = cfgManager.getConfigForSubId(mPhone.getSubId())
+                .getBoolean("config_hide_vt_callforward_option");
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        registerReceiver(mBroadcastReceiver, mIntentFilter);
+        mFeatureConnector.connect();
+
+        if (mHideVtCfOption || !(mPhone.isUtEnabled() && mPhone.isVideoEnabled())) {
+            Log.d(LOG_TAG, "VT or/and Ut Service is not enabled");
+            showVideoOption(false);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mFeatureConnector.disconnect();
+        unregisterReceiver(mBroadcastReceiver);
+    }
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int itemId = item.getItemId();
+        if (itemId == android.R.id.home) {
+            onBackPressed();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/src/com/android/phone/CallWaitingSwitchPreference.java b/src/com/android/phone/CallWaitingSwitchPreference.java
index 01dd3b2..a6c990c 100644
--- a/src/com/android/phone/CallWaitingSwitchPreference.java
+++ b/src/com/android/phone/CallWaitingSwitchPreference.java
@@ -7,11 +7,14 @@
 import android.os.Handler;
 import android.os.Message;
 import android.preference.SwitchPreference;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
 
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -28,6 +31,7 @@
     private boolean mIsDuringUpdateProcess = false;
     private int mUpdateStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
     private int mQueryStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+    private boolean mUtEnabled = false;
 
     public CallWaitingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -48,6 +52,7 @@
         mExecutor = Executors.newSingleThreadExecutor();
         mTelephonyManager = getContext().getSystemService(
                 TelephonyManager.class).createForSubscriptionId(phone.getSubId());
+        mUtEnabled = mPhone.isUtEnabled();
 
         if (!skipReading) {
             Log.d(LOG_TAG, "init getCallWaitingStatus");
@@ -80,6 +85,18 @@
         }
     }
 
+    public boolean isAutoRetrySsoverCdma() {
+        CarrierConfigManager cfgManager = (CarrierConfigManager)
+            getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        boolean autoRetryCfu = false;
+        if (cfgManager != null) {
+            autoRetryCfu = cfgManager.getConfigForSubId(mPhone.getSubId())
+                .getBoolean("config_auto_retry_cfu_bool");
+        }
+        boolean isCdma = mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA;
+        return autoRetryCfu && isCdma && mUtEnabled && !mPhone.isUtEnabled();
+    }
+
     private class MyHandler extends Handler {
         static final int MESSAGE_UPDATE_CALL_WAITING = 0;
 
@@ -106,7 +123,9 @@
                             || mUpdateStatus
                             == TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR)) {
                 Log.d(LOG_TAG, "handleSetCallWaitingResponse: Exception");
-                if (mTcpListener != null) {
+                if (isAutoRetrySsoverCdma()) {
+                    mUtEnabled = false;
+                } else if (mTcpListener != null) {
                     mTcpListener.onError(CallWaitingSwitchPreference.this, EXCEPTION_ERROR);
                 }
             } else if (mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 45ca974..8a45d47 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -801,24 +801,12 @@
         Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
                 Intent.FLAG_RECEIVER_FOREGROUND);
+        // Include subId extra only if SIM records are loaded
         if (addSubIdExtra) {
-            int simApplicationState = TelephonyManager.SIM_STATE_UNKNOWN;
-            int[] subIds = SubscriptionManager.getSubId(phoneId);
-            if (!ArrayUtils.isEmpty(subIds)) {
-                TelephonyManager telMgr = TelephonyManager.from(mContext)
-                        .createForSubscriptionId(subIds[0]);
-                simApplicationState = telMgr.getSimApplicationState();
-            }
-            logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId
-                    + " simApplicationState " + simApplicationState);
-            // Include subId/carrier id extra only if SIM records are loaded
-            if (simApplicationState != TelephonyManager.SIM_STATE_UNKNOWN
-                    && simApplicationState != TelephonyManager.SIM_STATE_NOT_READY) {
-                SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
-                intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_ID,
-                        getSpecificCarrierIdForPhoneId(phoneId));
-                intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, getCarrierIdForPhoneId(phoneId));
-            }
+            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
+            intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_ID,
+                    getSpecificCarrierIdForPhoneId(phoneId));
+            intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, getCarrierIdForPhoneId(phoneId));
         }
         intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, phoneId);
         intent.putExtra(CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK,
@@ -870,6 +858,7 @@
         String imsi = "";
         String gid1 = "";
         String gid2 = "";
+        String iccid = "";
         String spn = TelephonyManager.from(mContext).getSimOperatorNameForPhone(phoneId);
         String simOperator = TelephonyManager.from(mContext).getSimOperatorNumericForPhone(phoneId);
         int carrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
@@ -884,10 +873,11 @@
             imsi = phone.getSubscriberId();
             gid1 = phone.getGroupIdLevel1();
             gid2 = phone.getGroupIdLevel2();
+            iccid = phone.getIccSerialNumber();
             carrierId = phone.getCarrierId();
             specificCarrierId = phone.getSpecificCarrierId();
         }
-        return new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2, carrierId, specificCarrierId);
+        return new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2, iccid, carrierId, specificCarrierId);
     }
 
     /** Returns the package name of a priveleged carrier app, or null if there is none. */
diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java
index a8d2e93..45019d8 100644
--- a/src/com/android/phone/CdmaCallForwardOptions.java
+++ b/src/com/android/phone/CdmaCallForwardOptions.java
@@ -149,11 +149,14 @@
     public void onResume() {
         super.onResume();
 
+        // TODO(b/168714925) this variable is a placeholder
+        int tempServiceClass = 0;
+
         if (mFirstResume) {
             if (mIcicle == null) {
                 Log.d(LOG_TAG, "start to init ");
                 CallForwardEditPreference pref = mPreferences.get(mInitIndex);
-                pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                pref.init(this, mPhone, mReplaceInvalidCFNumbers, tempServiceClass, mCallForwardByUssd);
                 pref.startCallForwardOptionsQuery();
 
             } else {
@@ -166,7 +169,7 @@
                     CallForwardInfo cf = new CallForwardInfo();
                     cf.number = bundle.getString(KEY_NUMBER);
                     cf.status = bundle.getInt(KEY_STATUS);
-                    pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                    pref.init(this, mPhone, mReplaceInvalidCFNumbers, tempServiceClass, mCallForwardByUssd);
                     pref.restoreCallForwardInfo(cf);
                 }
             }
@@ -193,10 +196,13 @@
 
     @Override
     public void onFinished(Preference preference, boolean reading) {
+        // TODO(b/168714925) this variable is a placeholder
+        int tempServiceClass = 0;
+
         if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
             mInitIndex++;
             CallForwardEditPreference pref = mPreferences.get(mInitIndex);
-            pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+            pref.init(this, mPhone, mReplaceInvalidCFNumbers, tempServiceClass, mCallForwardByUssd);
             pref.startCallForwardOptionsQuery();
         }
 
diff --git a/src/com/android/phone/CdmaCallOptions.java b/src/com/android/phone/CdmaCallOptions.java
index 6145870..c6760e0 100644
--- a/src/com/android/phone/CdmaCallOptions.java
+++ b/src/com/android/phone/CdmaCallOptions.java
@@ -16,27 +16,183 @@
 
 package com.android.phone;
 
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.view.MenuItem;
 
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.SubscriptionController;
 
-public class CdmaCallOptions extends TimeConsumingPreferenceActivity {
+import java.util.List;
+
+public class CdmaCallOptions extends TimeConsumingPreferenceActivity
+               implements DialogInterface.OnClickListener,
+               DialogInterface.OnCancelListener {
     private static final String LOG_TAG = "CdmaCallOptions";
 
+    public static final int CALL_WAITING = 7;
     private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
     private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
     private static final String CALL_WAITING_KEY = "call_waiting_key";
+    public static final String CALL_FORWARD_INTENT = "org.codeaurora.settings.CDMA_CALL_FORWARDING";
+    public static final String CALL_WAITING_INTENT = "org.codeaurora.settings.CDMA_CALL_WAITING";
+
+    private CallWaitingSwitchPreference mCWButton;
+    private PreferenceScreen mPrefCW;
+    private boolean mUtEnabled = false;
+    private boolean mCommon = false;
+    private Phone mPhone = null;
+    private boolean mCdmaCfCwEnabled = false;
+    private static final String BUTTON_CW_KEY = "button_cw_ut_key";
+
+    private static boolean isActivityPresent(Context context, String intentName) {
+        PackageManager pm = context.getPackageManager();
+        // check whether the target handler exist in system
+        Intent intent = new Intent(intentName);
+        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+        for (ResolveInfo resolveInfo : list){
+            if ((resolveInfo.activityInfo.applicationInfo.flags &
+                    ApplicationInfo.FLAG_SYSTEM) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean isCdmaCallForwardingActivityPresent(Context context) {
+        return isActivityPresent(context, CALL_FORWARD_INTENT);
+    }
+
+    public static boolean isCdmaCallWaitingActivityPresent(Context context) {
+        return isActivityPresent(context, CALL_WAITING_INTENT);
+    }
+
+    //prompt dialog to notify user turn off Enhance 4G LTE switch
+    private boolean isPromptTurnOffEnhance4GLTE(Phone phone) {
+        if (phone == null || phone.getImsPhone() == null) {
+            return false;
+        }
+
+        com.android.ims.ImsManager imsMgr = com.android.ims.ImsManager.getInstance(this, phone.getPhoneId());
+        try {
+            if (imsMgr.getImsServiceState() != ImsFeature.STATE_READY) {
+                Log.d(LOG_TAG, "ImsServiceStatus is not ready!");
+                return false;
+            }
+        } catch (com.android.ims.ImsException ex) {
+            Log.d(LOG_TAG, "Exception when trying to get ImsServiceStatus: " + ex);
+            return false;
+        }
+
+        return imsMgr.isEnhanced4gLteModeSettingEnabledByUser()
+            && imsMgr.isNonTtyOrTtyOnVolteEnabled()
+            && !phone.isUtEnabled()
+            && !phone.isVolteEnabled()
+            && !phone.isVideoEnabled();
+    }
+
+    /*
+     * Some operators ask to prompt user to switch DDS to sub which query CF/CW over UT
+     */
+    private  boolean maybePromptUserToSwitchDds() {
+        // check the active data sub.
+        int sub = mPhone.getSubId();
+        final SubscriptionManager subMgr = SubscriptionManager.from(this);
+        int slotId = subMgr.getSlotIndex(sub);
+        int defaultDataSub = subMgr.getDefaultDataSubscriptionId();
+        Log.d(LOG_TAG, "isUtEnabled = " + mPhone.isUtEnabled() + ", need to check DDS ");
+        if (mPhone != null && sub != defaultDataSub && !mPhone.isUtEnabled()) {
+            Log.d(LOG_TAG, "Show dds switch dialog if data sub is not on current sub");
+            showSwitchDdsDialog(slotId);
+            return true;
+        }
+        return false;
+    }
+
+    private void showSwitchDdsDialog(int slotId) {
+        String title = (String)this.getResources().getText(R.string.no_mobile_data);
+        int simId = slotId + 1;
+        String message = (String)this.getResources()
+            .getText(R.string.switch_dds_to_sub_alert_msg) + String.valueOf(simId);
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(title);
+        builder.setMessage(message);
+        builder.setIconAttribute(android.R.attr.alertDialogIcon);
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                Intent newIntent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+                newIntent.putExtra(Settings.EXTRA_SUB_ID,mPhone.getSubId());
+                newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(newIntent);
+                finish();
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel,
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+                finish();
+            }
+        });
+        builder.create().show();
+    }
+
+    private void showAlertDialog(String title, String message) {
+        Dialog dialog = new AlertDialog.Builder(this)
+            .setTitle(title)
+            .setMessage(message)
+            .setIconAttribute(android.R.attr.alertDialogIcon)
+            .setPositiveButton(android.R.string.ok, this)
+            .setNegativeButton(android.R.string.cancel, this)
+            .setOnCancelListener(this)
+            .create();
+        dialog.show();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int id) {
+        if (id == DialogInterface.BUTTON_POSITIVE) {
+            Intent newIntent = new Intent("android.settings.SETTINGS");
+            newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(newIntent);
+        }
+        finish();
+        return;
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        finish();
+        return;
+    }
 
     private class UtCallback extends ImsMmTelManager.CapabilityCallback {
         @Override
@@ -59,12 +215,6 @@
         addPreferencesFromResource(R.xml.cdma_call_privacy);
 
         SubscriptionInfoHelper subInfoHelper = new SubscriptionInfoHelper(this, getIntent());
-        subInfoHelper.setActionBarTitle(
-                getActionBar(), getResources(), R.string.labelCdmaMore_with_label);
-
-        CdmaVoicePrivacySwitchPreference buttonVoicePrivacy =
-                (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
-        buttonVoicePrivacy.setPhone(subInfoHelper.getPhone());
         PersistableBundle carrierConfig;
         int subId;
         if (subInfoHelper.hasSubId()) {
@@ -73,9 +223,113 @@
             subId = SubscriptionManager.getDefaultSubscriptionId();
         }
         carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(subId);
-        if (subInfoHelper.getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA
-                || carrierConfig.getBoolean(CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
-            buttonVoicePrivacy.setEnabled(false);
+        mCommon = carrierConfig.getBoolean("config_common_callsettings_support_bool");
+        subInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(),
+                mCommon ? R.string.labelCommonMore_with_label : R.string.labelCdmaMore_with_label);
+
+        CdmaVoicePrivacySwitchPreference buttonVoicePrivacy =
+            (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+        mPhone = subInfoHelper.getPhone();
+        buttonVoicePrivacy.setPhone(mPhone);
+        Log.d(LOG_TAG, "sub id = " + subInfoHelper.getSubId() + " phone id = " +
+                mPhone.getPhoneId());
+
+        mCdmaCfCwEnabled = carrierConfig
+            .getBoolean(CarrierConfigManager.KEY_CDMA_CW_CF_ENABLED_BOOL);
+        PreferenceScreen prefScreen = getPreferenceScreen();
+        if (mPhone.getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA ||
+                carrierConfig.getBoolean(CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
+            CdmaVoicePrivacySwitchPreference prefPri = (CdmaVoicePrivacySwitchPreference)
+                    prefScreen.findPreference("button_voice_privacy_key");
+            if (prefPri != null) {
+                prefPri.setEnabled(false);
+            }
+        }
+
+        if(carrierConfig.getBoolean("check_mobile_data_for_cf") && maybePromptUserToSwitchDds()) {
+            return;
+        }
+        if(mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
+                && isPromptTurnOffEnhance4GLTE(mPhone)
+                && carrierConfig.getBoolean(CarrierConfigManager.KEY_CDMA_CW_CF_ENABLED_BOOL)) {
+            String title = (String)this.getResources()
+                .getText(R.string.ut_not_support);
+            String msg = (String)this.getResources()
+                .getText(R.string.ct_ut_not_support_close_4glte);
+            showAlertDialog(title, msg);
+        }
+
+        mCWButton = (CallWaitingSwitchPreference) prefScreen.findPreference(BUTTON_CW_KEY);
+        if (mPhone.getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA
+                || !carrierConfig.getBoolean(CarrierConfigManager.KEY_CDMA_CW_CF_ENABLED_BOOL)
+                || !isCdmaCallWaitingActivityPresent(this)) {
+            Log.d(LOG_TAG, "Disabled CW CF");
+            mPrefCW = (PreferenceScreen) prefScreen.findPreference("button_cw_key");
+            if (mCWButton != null) {
+                 prefScreen.removePreference(mCWButton);
+            }
+
+            if (mPrefCW != null) {
+                mPrefCW.setEnabled(false);
+            }
+            PreferenceScreen prefCF = (PreferenceScreen)
+                    prefScreen.findPreference("button_cf_expand_key");
+            if (prefCF != null) {
+                prefCF.setEnabled(false);
+            }
+        } else {
+            Log.d(LOG_TAG, "Enabled CW CF");
+            mPrefCW = (PreferenceScreen) prefScreen.findPreference("button_cw_key");
+
+            com.android.ims.ImsManager imsMgr = com.android.ims.ImsManager.getInstance(this, mPhone.getPhoneId());
+            Boolean isEnhanced4G = imsMgr.isEnhanced4gLteModeSettingEnabledByUser();
+            if (mPhone.isUtEnabled() && isEnhanced4G) {
+                mUtEnabled = mPhone.isUtEnabled();
+                prefScreen.removePreference(mPrefCW);
+                mCWButton.init(this, false, mPhone);
+            } else {
+                if (mCWButton != null) {
+                    prefScreen.removePreference(mCWButton);
+                }
+                if (mPrefCW != null) {
+                    mPrefCW.setOnPreferenceClickListener(
+                            new Preference.OnPreferenceClickListener() {
+                                @Override
+                                public boolean onPreferenceClick(Preference preference) {
+                                    Intent intent = new Intent(CALL_WAITING_INTENT);
+                                    intent.putExtra(
+                                        SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                                        mPhone.getSubId());
+                                    startActivity(intent);
+                                    return true;
+                                }
+                            });
+                }
+            }
+            PreferenceScreen prefCF = (PreferenceScreen)
+                    prefScreen.findPreference("button_cf_expand_key");
+            if (prefCF != null) {
+                prefCF.setOnPreferenceClickListener(
+                        new Preference.OnPreferenceClickListener() {
+                            @Override
+                            public boolean onPreferenceClick(Preference preference) {
+                                Intent intent = mPhone.isUtEnabled() ?
+                                    subInfoHelper.getIntent(GsmUmtsCallForwardOptions.class)
+                                    : new Intent(CALL_FORWARD_INTENT);
+                                if (mPhone.isUtEnabled()) {
+                                    intent.putExtra(PhoneUtils.SERVICE_CLASS,
+                                        CommandsInterface.SERVICE_CLASS_VOICE);
+                                } else {
+                                    intent.putExtra(
+                                        SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                                        mPhone.getSubId());
+                                }
+                                startActivity(intent);
+                                return true;
+                            }
+                        });
+            }
         }
 
         mCallForwardingPref = getPreferenceScreen().findPreference(CALL_FORWARDING_KEY);
@@ -171,6 +425,40 @@
     }
 
     @Override
+    public void onFinished(Preference preference, boolean reading) {
+        if (mCdmaCfCwEnabled && mUtEnabled && mPhone != null && !mPhone.isUtEnabled()) {
+            if (isPromptTurnOffEnhance4GLTE(mPhone)) {
+                String title = (String)this.getResources()
+                    .getText(R.string.ut_not_support);
+                String msg = (String)this.getResources()
+                    .getText(R.string.ct_ut_not_support_close_4glte);
+                showAlertDialog(title, msg);
+            }
+            mUtEnabled = false;
+            if (mCWButton != null) {
+                PreferenceScreen prefScreen = getPreferenceScreen();
+                prefScreen.removePreference(mCWButton);
+                prefScreen.addPreference(mPrefCW);
+                if (mPrefCW != null) {
+                    mPrefCW.setOnPreferenceClickListener(
+                            new Preference.OnPreferenceClickListener() {
+                                @Override
+                                public boolean onPreferenceClick(Preference preference) {
+                                    Intent intent = new Intent(CALL_WAITING_INTENT);
+                                    intent.putExtra(
+                                        SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                                        mPhone.getSubId());
+                                    startActivity(intent);
+                                    return true;
+                                }
+                            });
+                }
+            }
+        }
+        super.onFinished(preference, reading);
+    }
+
+    @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         final int itemId = item.getItemId();
         if (itemId == android.R.id.home) {
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 505c284..fdc5e11 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -29,6 +29,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.telephony.PhoneNumberUtils;
 import android.text.BidiFormatter;
+import android.text.format.DateFormat;
 import android.text.TextDirectionHeuristics;
 import android.text.TextUtils;
 import android.text.method.ArrowKeyMovementMethod;
@@ -36,14 +37,26 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.ImageButton;
+import android.widget.Spinner;
 import android.widget.TextView;
+import android.widget.Toast;
+import android.util.Log;
+import java.util.Calendar;
+import java.util.Date;
 
 import com.android.internal.telephony.CommandsInterface;
 
-public class EditPhoneNumberPreference extends EditTextPreference {
+public class EditPhoneNumberPreference extends EditTextPreference
+        implements AdapterView.OnItemSelectedListener {
 
+    private String TAG = "EditPhoneNumberPreference";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
     //allowed modes for this preference.
     /** simple confirmation (OK / CANCEL) */
     private static final int CM_CONFIRM = 0;
@@ -64,6 +77,41 @@
     //UI layout
     private ImageButton mContactPickButton;
 
+    //UI for time settings
+    private Calendar mStartDate = Calendar.getInstance();
+    private Calendar mEndDate = Calendar.getInstance();
+    private TextView mTimeStartTextView;
+    private View mStartTimeSetting;
+    private Spinner mTimeStartHourSpinner;
+    private Spinner mTimeStartMinuteSpinner;
+    private TextView mTimeEndTextView;
+    private View mEndTimeSetting;
+    private Spinner mTimeEndHourSpinner;
+    private Spinner mTimeEndMinuteSpinner;
+    private CheckBox mTimePeriodAllDay;
+    private Spinner mTimeStartFormate;
+    private Spinner mTimeEndFormate;
+    private ArrayAdapter time24hour;
+    private ArrayAdapter time12hour;
+
+    //relevant (parsed) value of the mText
+    private boolean canShowTimerSetting = false;
+    private int mValidStartTimeHour = 22;
+    private int mValidStartTimeMinute = 0;
+    private int mValidEndTimeHour = 8;
+    private int mValidEndTimeMinute = 0;
+
+    private int mStartTimeHour = 22;
+    private int mStartTimeMinute = 0;
+    private int mStartTimeAPMP = 0;
+    private int mEndTimeHour = 8;
+    private int mEndTimeMinute = 0;
+    private int mEndTimeAMPM = 0;
+    private String mPhoneNumber;
+    private String mTimePeriodString;
+    private boolean mChecked;
+    private boolean mTimePeriodAllDayChecked = true;
+
     //Listeners
     /** Called when focus is changed between fields */
     private View.OnFocusChangeListener mDialogFocusChangeListener;
@@ -91,10 +139,6 @@
     // button that was clicked on dialog close.
     private int mButtonClicked;
 
-    //relevant (parsed) value of the mText
-    private String mPhoneNumber;
-    private boolean mChecked;
-
     private boolean mIsUnknownStatus;
 
     /**
@@ -233,6 +277,9 @@
                 }
             });
         }
+        //set timer settings
+        initTimeSettingsView(view);
+
     }
 
     /**
@@ -341,7 +388,24 @@
         // A positive result is technically either button1 or button3.
         if ((mButtonClicked == DialogInterface.BUTTON_POSITIVE) ||
                 (mButtonClicked == DialogInterface.BUTTON_NEUTRAL)){
-            setPhoneNumber(getEditText().getText().toString());
+            EditText editText = getEditText();
+            String number = "";
+            if (editText != null) {
+                number = editText.getText().toString();
+            }
+            if (mPrefId == CommandsInterface.CF_REASON_UNCONDITIONAL){
+                if (isAllDayChecked() && mTimePeriodAllDayChecked){
+                    setPhoneNumber(number);
+                } else {
+                    setPhoneNumberWithTimePeriod(number, mStartTimeHour,
+                        mStartTimeMinute, mEndTimeHour, mEndTimeMinute);
+                    if (DBG) Log.d(TAG, "onDialogClosed, phonenumber = " + number
+                                    + "timePeriodString = " + mTimePeriodString);
+                }
+            } else {
+                setPhoneNumber(number);
+            }
+            if (DBG) dumpSpinnerSelectedTimePeriodInfo();
             super.onDialogClosed(positiveResult);
             setText(getStringValue());
         } else {
@@ -383,11 +447,35 @@
         return PhoneNumberUtils.stripSeparators(mPhoneNumber);
     }
 
+    public int getStartTimeHour() {
+        return mStartTimeHour;
+    }
+
+    public int getStartTimeMinute() {
+        return mStartTimeMinute;
+    }
+
+    public int getEndTimeHour() {
+        return mEndTimeHour;
+    }
+
+    public int getEndTimeMinute() {
+        return mEndTimeMinute;
+    }
+
+    public boolean isAllDayChecked() {
+        return mTimePeriodAllDay.isChecked();
+    }
+
     /** The phone number including any formatting characters */
     protected String getRawPhoneNumber() {
         return mPhoneNumber;
     }
 
+    protected String getRawPhoneNumberWithTime(){
+        return mPhoneNumber + mTimePeriodString;
+    }
+
     //set the phone number value.
     // return the current preference to allow for chaining preferences.
     public EditPhoneNumberPreference setPhoneNumber(String number) {
@@ -398,6 +486,88 @@
         return this;
     }
 
+    public void setAllDayCheckBox(boolean checked){
+        if (DBG) Log.d(TAG, "setAllDayCheckBox,"
+                +"mTimePeriodAllDayChecked" + checked);
+        mTimePeriodAllDayChecked = checked;
+    }
+
+    public void setTimeSettingVisibility(boolean enable) {
+        canShowTimerSetting = enable;
+    }
+
+    /*
+     *    set the phone number with time period info.
+     *1. save timer info from network
+     *2. call when timer info changed after timer edit finish
+     */
+    public EditPhoneNumberPreference setPhoneNumberWithTimePeriod(String number,
+                int starthour, int startminute, int endhour, int endminute) {
+        mPhoneNumber = number;
+        setText(getStringValue());
+        setTimePeriodInfo(starthour, startminute, endhour, endminute);
+        notifyChanged();
+
+        return this;
+    }
+
+    public void setTimePeriodInfo(int starthour, int startminute,
+            int endhour, int endminute) {
+        if (DBG) Log.d(TAG, "setTimePeriodInfo, starthour = " + starthour
+                    + "startminute = " + startminute
+                    + "endhour = " + endhour
+                    + "endminute = " + endminute);
+        mValidStartTimeHour = starthour;
+        mValidStartTimeMinute = startminute;
+        mValidEndTimeHour = endhour;
+        mValidEndTimeMinute = endminute;
+        if (mTimePeriodAllDayChecked){
+            mTimePeriodString = getContext().getResources().getString(R.string.all_day);
+        } else {
+            String fomatedStartTimeString = formateTime(mStartDate,
+                    mValidStartTimeHour, mValidStartTimeMinute);
+            String fomatedEndTimeString = formateTime(mEndDate,
+                    mValidEndTimeHour, mValidEndTimeMinute);
+            mTimePeriodString = getContext().getResources().getString(R.string.time_start)
+                    + fomatedStartTimeString
+                    + getContext().getResources().getString(R.string.time_end)
+                    + fomatedEndTimeString;
+            if (mValidEndTimeHour*60 + mValidEndTimeMinute
+                    < mValidStartTimeHour*60 + mValidStartTimeMinute){
+                mTimePeriodString = mTimePeriodString
+                + getContext().getResources().getString(R.string.time_next_day);
+            }
+        }
+    }
+
+    //set the phone number with time period info.
+    private String formateTime(Calendar mDate, int hour, int minute) {
+        String fomatedTimeString;
+        Calendar now = Calendar.getInstance();
+        java.text.DateFormat mDateFormat = DateFormat.getDateFormat(getContext());
+        mDate.set(now.get(Calendar.YEAR),
+                now.get(Calendar.MONTH),
+                now.get(Calendar.DAY_OF_MONTH),
+                hour,
+                minute,
+                0);
+        Date mDateTimeOnly = mDate.getTime();
+        fomatedTimeString = DateFormat.getTimeFormat(getContext()).format(mDateTimeOnly);
+        if (DBG) Log.d(TAG, "formatedTime =" + fomatedTimeString);
+        return fomatedTimeString;
+    }
+
+    //set the phone number with time period info.
+    public EditPhoneNumberPreference setPhoneNumberWithTimePeriod(String number,
+                String timeperiod) {
+        mPhoneNumber = number;
+        mTimePeriodString = timeperiod;
+        setText(getStringValue());
+        notifyChanged();
+
+        return this;
+    }
+
 
     /*
      * Other code relevant to preference framework
@@ -498,11 +668,19 @@
     protected void setValueFromString(String value) {
         String[] inValues = value.split(":", 2);
         setToggled(inValues[0].equals(VALUE_ON));
-        setPhoneNumber(inValues[1]);
+        if (inValues.length == 3){
+            setPhoneNumberWithTimePeriod(inValues[1], inValues[2]);
+        } else {
+            setPhoneNumber(inValues[1]);
+        }
     }
 
     //retrieve the state of this preference in the form of an encoded string
     protected String getStringValue() {
+        if (mPrefId == CommandsInterface.CF_REASON_UNCONDITIONAL){
+            return ((isToggled() ? VALUE_ON : VALUE_OFF) + VALUE_SEPARATOR + getPhoneNumber()
+                    + mTimePeriodString);
+        }
         return ((isToggled() ? VALUE_ON : VALUE_OFF) + VALUE_SEPARATOR + getPhoneNumber());
     }
 
@@ -522,4 +700,167 @@
     public boolean isUnknownStatus() {
         return mIsUnknownStatus;
     }
+
+    @Override
+    public void onNothingSelected(AdapterView<?> parent) {
+    }
+
+    @Override
+    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+        String selectedItem = parent.getItemAtPosition(position).toString();
+        if (DBG) Log.d(TAG, "onItemSelected"
+                + ", selectedItem = " + selectedItem
+                + ", position = " + position
+                + ", parent" + parent);
+        setSelectionStartTimePreiod();
+        setSelectionEndTimePreiod();
+        if (DBG) dumpSpinnerSelectedTimePeriodInfo();
+    }
+
+    private void initTimeSettingsView(View view){
+        Log.d(TAG, "initTimeSettingsView, mPrefId = " + mPrefId);
+        //all day default for time period
+        mTimePeriodAllDay = (CheckBox) view.findViewById(R.id.all_day);
+        mTimePeriodAllDay.setChecked(mTimePeriodAllDayChecked);
+        mTimePeriodAllDay.setOnCheckedChangeListener(new CompoundButton
+                .OnCheckedChangeListener(){
+                    @Override
+                    public void onCheckedChanged(CompoundButton buttonView,
+                            boolean isChecked) {
+                        onAllDayChecked(isChecked);
+                    }
+                });
+        //set start time
+        mTimeStartTextView = (TextView) view.findViewById(R.id.time_start);
+        mStartTimeSetting = (View) view.findViewById(R.id.start_time_setting);
+        mTimeStartHourSpinner = (Spinner) view.findViewById(
+                is24Hour()? R.id.time_start_hour_24 : R.id.time_start_hour_12);
+        mTimeStartHourSpinner.setOnItemSelectedListener(this);
+        mTimeStartMinuteSpinner = (Spinner) view.findViewById(R.id.time_start_minute);
+        mTimeStartMinuteSpinner.setOnItemSelectedListener(this);
+
+        //set end time
+        mTimeEndTextView = (TextView) view.findViewById(R.id.time_end);
+        mEndTimeSetting = (View) view.findViewById(R.id.end_time_setting);
+        mTimeEndHourSpinner = (Spinner) view.findViewById(
+                is24Hour()? R.id.time_end_hour_24 : R.id.time_end_hour_12);
+        mTimeEndHourSpinner.setOnItemSelectedListener(this);
+        mTimeEndMinuteSpinner = (Spinner) view.findViewById(R.id.time_end_minute);
+        mTimeEndMinuteSpinner.setOnItemSelectedListener(this);
+
+        //set time formate: 12-hour or 24-hour
+        mTimeStartFormate = (Spinner) view.findViewById(R.id.time_start_formate);
+        mTimeStartFormate.setOnItemSelectedListener(this);
+        mTimeEndFormate = (Spinner) view.findViewById(R.id.time_end_formate);
+        mTimeEndFormate.setOnItemSelectedListener(this);
+
+        if ((mPrefId == CommandsInterface.CF_REASON_UNCONDITIONAL)
+                && canShowTimerSetting) {
+            Log.d(TAG, "show time setting for cfut");
+            mTimePeriodAllDay.setVisibility(View.VISIBLE);
+            mTimeStartTextView.setVisibility(View.VISIBLE);
+            mTimeEndTextView.setVisibility(View.VISIBLE);
+            mStartTimeSetting.setVisibility(View.VISIBLE);
+            mEndTimeSetting.setVisibility(View.VISIBLE);
+            mTimeStartHourSpinner.setVisibility(View.VISIBLE);
+            mTimeEndHourSpinner.setVisibility(View.VISIBLE);
+
+            mTimeStartMinuteSpinner.setSelection(mValidStartTimeMinute);
+            mTimeEndMinuteSpinner.setSelection(mValidEndTimeMinute);
+
+            if (is24Hour()){
+                mTimeStartHourSpinner.setSelection(mValidStartTimeHour);
+                mTimeEndHourSpinner.setSelection(mValidEndTimeHour);
+            } else {
+                mTimeStartFormate.setVisibility(View.VISIBLE);
+                mTimeEndFormate.setVisibility(View.VISIBLE);
+                showTimeWith12Formate(mValidStartTimeHour, mTimeStartHourSpinner, mTimeStartFormate);
+                showTimeWith12Formate(mValidEndTimeHour, mTimeEndHourSpinner, mTimeEndFormate);
+            }
+            onAllDayChecked(mTimePeriodAllDay.isChecked());
+        }
+    }
+
+    private void onAllDayChecked(boolean isChecked){
+        mTimeStartHourSpinner.setEnabled(!isChecked);
+        mTimeStartHourSpinner.setClickable(!isChecked);
+        mTimeStartMinuteSpinner.setEnabled(!isChecked);
+        mTimeStartMinuteSpinner.setClickable(!isChecked);
+        mTimeEndHourSpinner.setEnabled(!isChecked);
+        mTimeEndHourSpinner.setClickable(!isChecked);
+        mTimeEndMinuteSpinner.setEnabled(!isChecked);
+        mTimeEndMinuteSpinner.setClickable(!isChecked);
+        if (!is24Hour()){
+            mTimeStartFormate.setEnabled(!isChecked);
+            mTimeStartFormate.setClickable(!isChecked);
+            mTimeEndFormate.setEnabled(!isChecked);
+            mTimeEndFormate.setClickable(!isChecked);
+        }
+        mTimeStartTextView.setTextColor(isChecked? 0xFF888888 : 0xFF000000);
+        mTimeEndTextView.setTextColor(isChecked? 0xFF888888 : 0xFF000000);
+    }
+
+    /*Get value from the system settings*/
+    private boolean is24Hour() {
+        return DateFormat.is24HourFormat(getContext());
+    }
+
+    private void showTimeWith12Formate(int hour, Spinner hourSpinner, Spinner hourFormate){
+        if (hour < 12){
+            hourSpinner.setSelection(hour);
+            hourFormate.setSelection(0);
+        } else if (hour >= 12){
+            hourSpinner.setSelection(hour-12);
+            hourFormate.setSelection(1);
+        }
+    }
+
+    private void setSelectionStartTimePreiod( ){
+        mStartTimeMinute = (int) mTimeStartMinuteSpinner.getSelectedItemId();
+        if (DBG) Log.d(TAG, "setSelectionTimePreiod, mStartTimeMinute = "+mStartTimeMinute);
+        int starthourposion = (int) mTimeStartHourSpinner.getSelectedItemId();
+        if (DBG) Log.d(TAG, "setSelectionTimePreiod, starthourposion = "+starthourposion);
+        if (is24Hour()){
+            mStartTimeHour = starthourposion;
+        } else {
+            mStartTimeAPMP = (int) mTimeStartFormate.getSelectedItemId();
+            Log.d(TAG, "setSelectionTimePreiod, mStartTimeAPMP = " + mStartTimeAPMP);
+            if (mStartTimeAPMP == 0){
+                mStartTimeHour = starthourposion;
+            } else if (mStartTimeAPMP ==1){
+                mStartTimeHour = 12 + starthourposion;
+            }
+        }
+        Log.d(TAG, "setSelectionTimePreiod, mStartTimeHour = "
+                + mStartTimeHour + ", mStartTimeMinute = " + mStartTimeMinute);
+    }
+
+    private void setSelectionEndTimePreiod(){
+        mEndTimeMinute = (int) mTimeEndMinuteSpinner.getSelectedItemId();
+        if (DBG) Log.d(TAG, "setSelectionTimePreiod, mEndTimeMinute = "+mEndTimeMinute);
+        int endhourposion = (int) mTimeEndHourSpinner.getSelectedItemId();
+        if (DBG) Log.d(TAG, "setSelectionTimePreiod, endhourposion = "+ endhourposion);
+        if (is24Hour()){
+            mEndTimeHour = (int) mTimeEndHourSpinner.getSelectedItemId();
+        } else {
+            mEndTimeAMPM = (int) mTimeEndFormate.getSelectedItemId();
+            if (DBG) Log.d(TAG, "setSelectionTimePreiod, mEndTimeAMPM = " + mEndTimeAMPM);
+            if (mEndTimeAMPM == 0){
+                mEndTimeHour = endhourposion;
+            } else if (mEndTimeAMPM ==1){
+                mEndTimeHour = 12 + endhourposion;
+            }
+        }
+        if (DBG) Log.d(TAG, "setSelectionTimePreiod, mEndTimeHour = "
+                    + mEndTimeHour + ", mEndTimeMinute = " + mEndTimeMinute);
+    }
+
+    private void dumpSpinnerSelectedTimePeriodInfo(){
+        Log.d(TAG, "dumpSpinnerSelectedTimePeriodIfo: "
+            + "mStartTimeHour = " + mStartTimeHour
+            + ", mStartTimeMinute = " + mStartTimeMinute
+            + ", mEndTimeHour = " + mEndTimeHour
+            + ", mEndTimeMinute = " + mEndTimeMinute
+            + ", mTimePeriodString = " + mTimePeriodString);
+    }
 }
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index adba850..f545b8a 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -40,6 +40,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.telephony.EcbmHandler;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
 
@@ -72,12 +73,14 @@
     private static final int ECM_TIMER_RESET = 1;
     private Phone mPhone = null;
     private boolean mIsResumed = false;
+    private EcbmHandler mEcbmHandler;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
+        mEcbmHandler = EcbmHandler.getInstance();
         // Check if phone is in Emergency Callback Mode. If not, exit.
         if (mPhone == null || !mPhone.isInEcm()) {
             Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone);
@@ -95,7 +98,7 @@
         waitForConnectionCompleteThread.start();
 
         // Register ECM timer reset notfication
-        mPhone.registerForEcmTimerReset(mTimerResetHandler, ECM_TIMER_RESET, null);
+        mEcbmHandler.registerForEcmTimerReset(mTimerResetHandler, ECM_TIMER_RESET, null);
 
         // Register receiver for intent closing the dialog
         IntentFilter filter = new IntentFilter();
@@ -124,8 +127,8 @@
             // Receiver was never registered - silently ignore.
         }
         // Unregister ECM timer reset notification
-        if (mPhone != null) {
-            mPhone.unregisterForEcmTimerReset(mHandler);
+        if (mEcbmHandler != null) {
+            mEcbmHandler.unregisterForEcmTimerReset(mHandler);
         }
     }
 
@@ -242,7 +245,11 @@
                             new DialogInterface.OnClickListener() {
                                 public void onClick(DialogInterface dialog,int whichButton) {
                                     // User clicked Yes. Exit Emergency Callback Mode.
-                                    mPhone.exitEmergencyCallbackMode();
+                                    try {
+                                        mEcbmHandler.exitEmergencyCallbackMode();
+                                    } catch (Exception e) {
+                                        e.printStackTrace();
+                                    }
 
                                     // Show progress dialog
                                     showDialog(EXIT_ECM_PROGRESS_DIALOG);
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index 464db6f..edbd20f 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -35,6 +35,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.telephony.EcbmHandler;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
@@ -59,6 +60,7 @@
     private long mTimeLeft = 0;
     private Phone mPhone = null;
     private boolean mInEmergencyCall = false;
+    private EcbmHandler mEcbmHandler;
 
     private static final int ECM_TIMER_RESET = 1;
 
@@ -74,7 +76,8 @@
 
     @Override
     public void onCreate() {
-         Phone phoneInEcm = PhoneGlobals.getInstance().getPhoneInEcm();
+        Phone phoneInEcm = PhoneGlobals.getInstance().getPhoneInEcm();
+        mEcbmHandler = EcbmHandler.getInstance();
         // Check if it is CDMA phone
         if (phoneInEcm == null || ((phoneInEcm.getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA)
                 && (phoneInEcm.getImsPhone() == null))) {
@@ -93,7 +96,7 @@
 
         // Register ECM timer reset notfication
         mPhone = phoneInEcm;
-        mPhone.registerForEcmTimerReset(mHandler, ECM_TIMER_RESET, null);
+        mEcbmHandler.registerForEcmTimerReset(mHandler, ECM_TIMER_RESET, null);
 
         startTimerNotification();
     }
@@ -104,7 +107,7 @@
             // Unregister receiver
             unregisterReceiver(mEcmReceiver);
             // Unregister ECM timer reset notification
-            mPhone.unregisterForEcmTimerReset(mHandler);
+            mEcbmHandler.unregisterForEcmTimerReset(mHandler);
 
             // Cancel the notification and timer
             mNotificationManager.cancelAsUser(null, R.string.phone_in_ecm_notification_title,
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 42bfb24..4ee2806 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -26,6 +26,7 @@
 import android.app.Dialog;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -174,6 +175,7 @@
     private boolean mDTMFToneEnabled;
 
     private EmergencyActionGroup mEmergencyActionGroup;
+    private StatusBarManager mStatusBarManager;
 
     private EmergencyInfoGroup mEmergencyInfoGroup;
 
@@ -283,6 +285,7 @@
         }
 
         setContentView(R.layout.emergency_dialer);
+        mStatusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
 
         mDigits = (ResizingTextEditText) findViewById(R.id.digits);
         mDigits.setKeyListener(DialerKeyListener.getInstance());
@@ -635,6 +638,10 @@
     protected void onResume() {
         super.onResume();
 
+        if (null != mStatusBarManager) {
+            mStatusBarManager.disable(
+                     StatusBarManager.DISABLE_RECENT|StatusBarManager.DISABLE_HOME);
+        }
         // retrieve the DTMF tone play back setting.
         mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
                 Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
@@ -659,6 +666,9 @@
     @Override
     public void onPause() {
         super.onPause();
+        if (null != mStatusBarManager) {
+            mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
+        }
     }
 
     @Override
diff --git a/src/com/android/phone/GsmUmtsAdditionalCallOptions.java b/src/com/android/phone/GsmUmtsAdditionalCallOptions.java
index 6e28922..9884dfb 100644
--- a/src/com/android/phone/GsmUmtsAdditionalCallOptions.java
+++ b/src/com/android/phone/GsmUmtsAdditionalCallOptions.java
@@ -2,6 +2,8 @@
 
 import android.app.ActionBar;
 import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.preference.Preference;
@@ -122,7 +124,12 @@
                         }
                         mCLIRButton.handleGetCLIRResult(clirArray);
                     } else {
-                        mCLIRButton.init(this, false, mPhone);
+                        if (isUtEnabledToDisableClir()) {
+                            mCLIRButton.setSummary(R.string.sum_default_caller_id);
+                            mCWButton.init(this, false, mPhone);
+                        } else {
+                            mCLIRButton.init(this, false, mPhone);
+                        }
                     }
                 }
             }
@@ -135,6 +142,16 @@
         }
     }
 
+    private boolean isUtEnabledToDisableClir() {
+        boolean skipClir = false;
+        CarrierConfigManager configManager = (CarrierConfigManager)
+            getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle pb = configManager.getConfigForSubId(mPhone.getSubId());
+        if (pb != null) {
+            skipClir = pb.getBoolean("config_disable_clir_over_ut");
+        }
+        return mPhone.isUtEnabled() && skipClir;
+    }
     @Override
     public void onResume() {
         super.onResume();
@@ -192,10 +209,7 @@
 
     @Override
     public void onFinished(Preference preference, boolean reading) {
-        if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
-            mInitIndex++;
-            doPreferenceInit(mInitIndex);
-        }
+        doNextPreferenceInit();
         super.onFinished(preference, reading);
     }
 
@@ -209,13 +223,25 @@
         return super.onOptionsItemSelected(item);
     }
 
+    private void doNextPreferenceInit() {
+        if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
+            mInitIndex++;
+            doPreferenceInit(mInitIndex);
+        }
+    }
+
     private void doPreferenceInit(int index) {
         if (mPreferences.size() > index) {
             Preference pref = mPreferences.get(index);
             if (pref instanceof CallWaitingSwitchPreference) {
                 ((CallWaitingSwitchPreference) pref).init(this, false, mPhone);
             } else if (pref instanceof CLIRListPreference) {
-                ((CLIRListPreference) pref).init(this, false, mPhone);
+                if (isUtEnabledToDisableClir()) {
+                  ((CLIRListPreference) pref).setSummary(R.string.sum_default_caller_id);
+                  doNextPreferenceInit();
+                } else {
+                  ((CLIRListPreference) pref).init(this, false, mPhone);
+                }
             }
         }
     }
diff --git a/src/com/android/phone/GsmUmtsCallBarringOptions.java b/src/com/android/phone/GsmUmtsCallBarringOptions.java
index 30e9b5c..de10055 100644
--- a/src/com/android/phone/GsmUmtsCallBarringOptions.java
+++ b/src/com/android/phone/GsmUmtsCallBarringOptions.java
@@ -17,15 +17,25 @@
 package com.android.phone;
 
 import android.app.ActionBar;
+import android.app.AlertDialog;
 import android.app.Dialog;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.os.AsyncResult;
 import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -37,6 +47,9 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.phone.settings.fdn.EditPinPreference;
 
 import java.util.ArrayList;
@@ -46,7 +59,8 @@
  * the dialogs to change the passward.
  */
 public class GsmUmtsCallBarringOptions extends TimeConsumingPreferenceActivity
-        implements EditPinPreference.OnPinEnteredListener {
+        implements EditPinPreference.OnPinEnteredListener,
+        DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
     private static final String LOG_TAG = "GsmUmtsCallBarringOptions";
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
@@ -111,8 +125,12 @@
     private boolean mFirstResume;
     private Bundle mIcicle;
 
+    private BroadcastReceiver mReceiver = null;
+    private boolean mCheckData = false;
     private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private SubscriptionManager mSubscriptionManager;
     private Dialog mProgressDialog;
+    AlertDialog.Builder mBuilder = null;
 
     @Override
     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
@@ -343,6 +361,10 @@
         return password != null && password.length() == PW_LENGTH;
     }
 
+    private boolean canExpectMoreCallFwdReq() {
+        return (mInitIndex < mPreferences.size()-1);
+    }
+
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -412,18 +434,46 @@
         mPreferences.add(mButtonBAIC);
         mPreferences.add(mButtonBAICr);
 
+        boolean useDisableaAll = true;
+        boolean disableOutCallBarringOverIms = false;
+        boolean disableChangePasswordOverIms = false;
+
+        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
+        if (imsPhone != null && imsPhone.isUtEnabled()) {
+            useDisableaAll = false;
+            disableOutCallBarringOverIms = isDisableOutCallBarringOverIms();
+            disableChangePasswordOverIms = isDisableChangePasswordOverIms();
+        }
+
         // Find out if the sim card is ready.
         boolean isSimReady = TelephonyManager.from(this).getSimState(
                 SubscriptionManager.getSlotIndex(mPhone.getSubId()))
                         == TelephonyManager.SIM_STATE_READY;
 
-        // Deactivate all option and Change password option are unavailable
-        // when sim card is not ready.
-        if (isSimReady) {
+        // Change outgoing CB options are unavailable when sim card is not reay or when the carrier
+        // config is true.
+        if (isSimReady && !disableOutCallBarringOverIms) {
+            mButtonBAOC.setEnabled(true);
+            mButtonBAOIC.setEnabled(true);
+            mButtonBAOICxH.setEnabled(true);
+        } else {
+            mButtonBAOC.setEnabled(false);
+            mButtonBAOIC.setEnabled(false);
+            mButtonBAOICxH.setEnabled(false);
+        }
+
+        // Deactivate all option is unavailable when sim card is not ready or Ut is enabled.
+        if (isSimReady && useDisableaAll) {
             mButtonDisableAll.setEnabled(true);
-            mButtonChangePW.setEnabled(true);
         } else {
             mButtonDisableAll.setEnabled(false);
+        }
+
+        // Change password option is unavailable when sim card is not ready or when the password is
+        // not used.
+        if (isSimReady && !disableChangePasswordOverIms) {
+            mButtonChangePW.setEnabled(true);
+        } else {
             mButtonChangePW.setEnabled(false);
             mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled);
         }
@@ -459,18 +509,48 @@
             displayPwChangeDialog(mIcicle.getInt(DIALOG_MESSAGE_KEY, mPwChangeDialogStrId), false);
             mButtonChangePW.setText(mIcicle.getString(DIALOG_PW_ENTRY_KEY));
         }
+
+        PersistableBundle pb = configManager.getConfigForSubId(mPhone.getSubId());
+        mCheckData = pb.getBoolean("check_mobile_data_for_cf");
     }
 
     @Override
     public void onResume() {
         super.onResume();
 
+        if (mCheckData) {
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+            mReceiver = new PhoneAppBroadcastReceiver();
+            registerReceiver(mReceiver, intentFilter);
+            final SubscriptionManager mSubscriptionManager = SubscriptionManager.from(this);
+            checkDataStatus();
+        } else {
+            initCallBarring();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver);
+        }
+    }
+
+    private void initCallBarring () {
         if (mFirstResume) {
             if (mIcicle == null || mIcicle.getBoolean(SAVED_BEFORE_LOAD_COMPLETED_KEY)) {
                 if (DBG) {
                     Log.d(LOG_TAG, "onResume: start to init ");
                 }
                 resetPwChangeState();
+                if (isDisableOutCallBarringOverIms() && mPhone.isUtEnabled()) {
+                    //if disable outgoing call barring over ims, ignore all outgoing query
+                    // and start query from incoming barring
+                    mInitIndex = 3;
+                }
+                mPreferences.get(mInitIndex).setExpectMore(canExpectMoreCallFwdReq());
                 mPreferences.get(mInitIndex).init(this, false, mPhone);
 
                 // Request removing BUSY_SAVING_DIALOG because reading is restarted.
@@ -512,6 +592,7 @@
     public void onFinished(Preference preference, boolean reading) {
         if (mInitIndex < mPreferences.size() - 1 && !isFinishing()) {
             mInitIndex++;
+            mPreferences.get(mInitIndex).setExpectMore(canExpectMoreCallFwdReq());
             mPreferences.get(mInitIndex).init(this, false, mPhone);
         }
         super.onFinished(preference, reading);
@@ -537,4 +618,165 @@
             mProgressDialog = dialog;
         }
     }
+
+    private boolean isDisableOutCallBarringOverIms() {
+        CarrierConfigManager configManager = (CarrierConfigManager)getSystemService(
+                 Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle pb = configManager.getConfigForSubId(mPhone.getSubId());
+
+        return pb != null ? pb.getBoolean("config_disable_outgoing_callbarring_over_ims") : false;
+    }
+
+    private boolean isDisableChangePasswordOverIms() {
+        CarrierConfigManager configManager = (CarrierConfigManager)getSystemService(
+                 Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle pb = configManager.getConfigForSubId(mPhone.getSubId());
+
+        return pb != null ? pb.getBoolean("config_disable_change_password_over_ims") : false;
+    }
+    /**
+     * Receiver for intent broadcasts the Phone app cares about.
+     */
+    private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+                String state = intent.getStringExtra(PhoneConstants.STATE_KEY);
+                final String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
+                Log.d(LOG_TAG, "apntype is: " + apnType + " state is: " + state);
+                if (PhoneConstants.DataState.DISCONNECTED.name().equals(state) &&
+                        PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
+                    Log.d(LOG_TAG, "default data is disconnected.");
+                    checkDataStatus();
+                }
+            }
+        }
+    }
+
+    private void showAlertDialog(String title, String message) {
+        Dialog dialog = new AlertDialog.Builder(this)
+            .setTitle(title)
+            .setMessage(message)
+            .setIconAttribute(android.R.attr.alertDialogIcon)
+            .setPositiveButton(android.R.string.ok, this)
+            .setNegativeButton(android.R.string.cancel, this)
+            .setOnCancelListener(this)
+            .create();
+        dialog.show();
+    }
+
+    public void checkDataStatus() {
+        // check the active data sub.
+        int sub = mPhone.getSubId();
+        int defaultDataSub = mSubscriptionManager.getDefaultDataSubscriptionId();
+        int slotId = mSubscriptionManager.getSlotIndex(sub);
+        Log.d(LOG_TAG, "isUtEnabled = " + mPhone.isUtEnabled() +
+                ", checkMobileDataForCb = " + mCheckData + " defaultDataSub is : " +
+                defaultDataSub + " current sub is : " + sub);
+
+        if (sub != defaultDataSub) {
+            if (mPhone.isUtEnabled()) {
+                Log.d(LOG_TAG, "Show data in use indication if data sub is not on current sub");
+                showDataInuseToast();
+                initCallBarring();
+                return;
+            } else {
+                Log.d(LOG_TAG, "Show dds switch dialog if data sub is not on current sub");
+                showSwitchDdsDialog(slotId);
+                return;
+            }
+        }
+
+        if (mPhone.isUtEnabled() && mCheckData) {
+            int activeNetworkType = getActiveNetworkType();
+            boolean isDataRoaming = mPhone.getServiceState().getDataRoaming();
+            boolean isDataRoamingEnabled = mPhone.getDataRoamingEnabled();
+            boolean promptForDataRoaming = isDataRoaming && !isDataRoamingEnabled;
+            Log.d(LOG_TAG, "activeNetworkType = " + getActiveNetworkType() + ", sub = " + sub +
+                    ", defaultDataSub = " + defaultDataSub + ", isDataRoaming = " +
+                    isDataRoaming + ", isDataRoamingEnabled= " + isDataRoamingEnabled);
+            if ((activeNetworkType != ConnectivityManager.TYPE_MOBILE
+                        || sub != defaultDataSub)
+                    && !(activeNetworkType == ConnectivityManager.TYPE_NONE
+                        && promptForDataRoaming)) {
+                if (DBG) Log.d(LOG_TAG, "Show alert dialog if mobile network is disabled");
+                String title = (String)this.getResources().getText(R.string.no_mobile_data);
+                String message = (String)this.getResources()
+                    .getText(R.string.cf_setting_mobile_data_alert);
+                showAlertDialog(title, message);
+                return;
+            } else if (promptForDataRoaming) {
+                if (DBG) Log.d(LOG_TAG, "Show alert dialog if data roaming is disabled");
+                String title = (String)this.getResources()
+                    .getText(R.string.no_mobile_data_roaming);
+                String message = (String)this.getResources()
+                    .getText(R.string.cf_setting_mobile_data_roaming_alert);
+                showAlertDialog(title, message);
+                return;
+            }
+        }
+        initCallBarring();
+    }
+
+    private int getActiveNetworkType() {
+        ConnectivityManager cm = (ConnectivityManager) getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            NetworkInfo ni = cm.getActiveNetworkInfo();
+            if ((ni == null) || !ni.isConnected()){
+                return ConnectivityManager.TYPE_NONE;
+            }
+            return ni.getType();
+        }
+        return ConnectivityManager.TYPE_NONE;
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int id) {
+        if (id == DialogInterface.BUTTON_POSITIVE) {
+            Intent newIntent = new Intent("android.settings.SETTINGS");
+            newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(newIntent);
+        }
+        finish();
+        return;
+    }
+
+    private void showDataInuseToast() {
+        String message = (String)this.getResources()
+            .getText(R.string.mobile_data_alert);
+        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+    }
+
+    private void showSwitchDdsDialog(int slotId) {
+        String title = (String)this.getResources().getText(R.string.no_mobile_data);
+        int simId = slotId + 1;
+        String message = (String)this.getResources()
+            .getText(R.string.switch_dds_to_sub_alert) + String.valueOf(simId);
+        if (mBuilder == null) {
+            mBuilder=new AlertDialog.Builder(this);
+            mBuilder.setTitle(title);
+            mBuilder.setMessage(message);
+            mBuilder.setIconAttribute(android.R.attr.alertDialogIcon);
+            mBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    Intent newIntent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+                    newIntent.putExtra(Settings.EXTRA_SUB_ID,mPhone.getSubId());
+                    newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(newIntent);
+                }
+            });
+            mBuilder.setNegativeButton(android.R.string.cancel,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            dialog.dismiss();
+                            finish();
+                        }
+            });
+            mBuilder.create().show();
+        }
+    }
 }
diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java
index fda0ea5..420d615 100644
--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java
+++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java
@@ -1,23 +1,45 @@
 package com.android.phone;
 
 import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.SystemProperties;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
+import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.view.MenuItem;
+import android.widget.Toast;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
 
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
 
+import org.codeaurora.ims.QtiCallConstants;
+
 import java.util.ArrayList;
 
-public class GsmUmtsCallForwardOptions extends TimeConsumingPreferenceActivity {
+public class GsmUmtsCallForwardOptions extends TimeConsumingPreferenceActivity
+    implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
     private static final String LOG_TAG = "GsmUmtsCallForwardOptions";
 
@@ -25,10 +47,13 @@
         android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
     };
 
+    public static final String CALL_FORWARD_INTENT = "org.codeaurora.settings.CDMA_CALL_FORWARDING";
+
     private static final String BUTTON_CFU_KEY   = "button_cfu_key";
     private static final String BUTTON_CFB_KEY   = "button_cfb_key";
     private static final String BUTTON_CFNRY_KEY = "button_cfnry_key";
     private static final String BUTTON_CFNRC_KEY = "button_cfnrc_key";
+    private static final String BUTTON_CFNL_KEY  = "button_cfnl_key";
 
     private static final String KEY_TOGGLE = "toggle";
     private static final String KEY_STATUS = "status";
@@ -39,6 +64,9 @@
     private CallForwardEditPreference mButtonCFB;
     private CallForwardEditPreference mButtonCFNRy;
     private CallForwardEditPreference mButtonCFNRc;
+    private CallForwardEditPreference mButtonCFNL;
+
+    private boolean mSupportCFNL = true;
 
     private final ArrayList<CallForwardEditPreference> mPreferences =
             new ArrayList<CallForwardEditPreference> ();
@@ -49,6 +77,12 @@
     private Phone mPhone;
     private SubscriptionInfoHelper mSubscriptionInfoHelper;
     private boolean mReplaceInvalidCFNumbers;
+    private int mServiceClass;
+    private BroadcastReceiver mReceiver = null;
+    private SubscriptionManager mSubscriptionManager;
+    private boolean mCheckData = false;
+    AlertDialog.Builder builder = null;
+    private CarrierConfigManager mCarrierConfig;
     private boolean mCallForwardByUssd;
 
     @Override
@@ -62,6 +96,13 @@
                 getActionBar(), getResources(), R.string.call_forwarding_settings_with_label);
         mPhone = mSubscriptionInfoHelper.getPhone();
 
+        mCarrierConfig = (CarrierConfigManager)
+                getSystemService(CARRIER_CONFIG_SERVICE);
+
+        if (mCarrierConfig != null) {
+            PersistableBundle pb = mCarrierConfig.getConfigForSubId(mPhone.getSubId());
+            mCheckData = pb.getBoolean("check_mobile_data_for_cf");
+        }
         PersistableBundle b = null;
         boolean supportCFB = true;
         boolean supportCFNRc = true;
@@ -83,6 +124,13 @@
                     CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL);
             supportCFNRy = b.getBoolean(
                     CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_UNANSWERED_SUPPORTED_BOOL);
+            mSupportCFNL = b.getBoolean(
+                    CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_NOT_LOGGED_IN_SUPPORTED_BOOL);
+        }
+        // Disable mSupportCFNL if IMS UT is not registered or build version is older than S.
+        if (!mPhone.isUtEnabled() ||
+                SystemProperties.getInt("ro.board.api_level", 0) < Build.VERSION_CODES.S) {
+            mSupportCFNL = false;
         }
 
         PreferenceScreen prefSet = getPreferenceScreen();
@@ -90,16 +138,19 @@
         mButtonCFB = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
         mButtonCFNRy = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
         mButtonCFNRc = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
+        mButtonCFNL  = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNL_KEY);
 
         mButtonCFU.setParentActivity(this, mButtonCFU.reason);
         mButtonCFB.setParentActivity(this, mButtonCFB.reason);
         mButtonCFNRy.setParentActivity(this, mButtonCFNRy.reason);
         mButtonCFNRc.setParentActivity(this, mButtonCFNRc.reason);
+        mButtonCFNL.setParentActivity(this, mButtonCFNL.reason);
 
         mPreferences.add(mButtonCFU);
         layoutCallForwardItem(supportCFB, mButtonCFB, prefSet);
         layoutCallForwardItem(supportCFNRy, mButtonCFNRy, prefSet);
         layoutCallForwardItem(supportCFNRc, mButtonCFNRc, prefSet);
+        layoutCallForwardItem(mSupportCFNL, mButtonCFNL, prefSet);
 
         if (mCallForwardByUssd) {
             //the call forwarding ussd command's behavior is similar to the call forwarding when
@@ -107,9 +158,11 @@
             prefSet.removePreference(mButtonCFU);
             prefSet.removePreference(mButtonCFB);
             prefSet.removePreference(mButtonCFNRc);
+            prefSet.removePreference(mButtonCFNL);
             mPreferences.remove(mButtonCFU);
             mPreferences.remove(mButtonCFB);
             mPreferences.remove(mButtonCFNRc);
+            mPreferences.remove(mButtonCFNL);
             mButtonCFNRy.setDependency(null);
         }
 
@@ -117,6 +170,13 @@
         // TimeConsumingPreferenceActivity dialog can display as it
         // relies on onResume / onPause to maintain its foreground state.
 
+        /*Retrieve Call Forward ServiceClass*/
+        Intent intent = getIntent();
+        Log.d(LOG_TAG, "Intent is " + intent);
+        mServiceClass = intent.getIntExtra(PhoneUtils.SERVICE_CLASS,
+                CommandsInterface.SERVICE_CLASS_VOICE);
+        Log.d(LOG_TAG, "serviceClass: " + mServiceClass);
+
         mFirstResume = true;
         mIcicle = icicle;
 
@@ -136,15 +196,170 @@
         }
     }
 
+    /**
+     * Receiver for intent broadcasts the Phone app cares about.
+     */
+    private class PhoneAppBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+                String state = intent.getStringExtra(PhoneConstants.STATE_KEY);
+                final String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
+                Log.d(LOG_TAG, "apntype is: " + apnType + " state is: " + state);
+                if (PhoneConstants.DataState.DISCONNECTED.name().equals(state) &&
+                            PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)) {
+                    Log.d(LOG_TAG, "default data is disconnected.");
+                    checkDataStatus();
+                }
+            }
+        }
+    }
+
+    public void checkDataStatus() {
+        // check the active data sub.
+        int sub = mPhone.getSubId();
+        int slotId = mSubscriptionManager.getSlotIndex(sub);
+        int defaultDataSub = mSubscriptionManager.getDefaultDataSubscriptionId();
+        Log.d(LOG_TAG, "isUtEnabled = " + mPhone.isUtEnabled() + ", checkData= " + mCheckData);
+        // Find out if the sim card is ready.
+        boolean isSimReady = TelephonyManager.from(this).getSimState(slotId)
+                == TelephonyManager.SIM_STATE_READY;
+        if (!isSimReady) {
+            Log.d(LOG_TAG, "SIM is not ready!");
+            String title = (String)this.getResources().getText(R.string.sim_is_not_ready);
+            String message = (String)this.getResources()
+                .getText(R.string.sim_is_not_ready);
+            showAlertDialog(title, message);
+            return;
+        }
+        if (mPhone != null) {
+            int activeNetworkType = getActiveNetworkType();
+            boolean isDataRoaming = mPhone.getServiceState().getDataRoaming();
+            boolean isDataRoamingEnabled = mPhone.getDataRoamingEnabled();
+            boolean promptForDataRoaming = isDataRoaming && !isDataRoamingEnabled;
+            Log.d(LOG_TAG, "activeNetworkType = " + getActiveNetworkType() + ", sub = " + sub +
+                    ", defaultDataSub = " + defaultDataSub + ", isDataRoaming = " +
+                    isDataRoaming + ", isDataRoamingEnabled= " + isDataRoamingEnabled);
+            if ((sub != defaultDataSub) && !mPhone.isUtEnabled()) {
+                Log.d(LOG_TAG, "Show dds switch dialog if data sub is not on current sub");
+                showSwitchDdsDialog(slotId);
+                return;
+            }
+
+            if (mPhone.isUtEnabled() && mCheckData) {
+                boolean isDataEnabled = TelephonyManager.from(this).getDataEnabled(sub);
+                Log.d(LOG_TAG, "isDataEnabled: " + isDataEnabled);
+                if ((!isDataEnabled || activeNetworkType != ConnectivityManager.TYPE_MOBILE)
+                        && !(activeNetworkType == ConnectivityManager.TYPE_NONE
+                        && promptForDataRoaming)) {
+                    Log.d(LOG_TAG,
+                            "Show alert dialog if data sub is not on current sub or WLAN is on");
+                    String title = (String)this.getResources().getText(R.string.no_mobile_data);
+                    String message = (String)this.getResources()
+                            .getText(R.string.cf_setting_mobile_data_alert);
+                    showAlertDialog(title, message);
+                    return;
+                }
+                if (promptForDataRoaming) {
+                       Log.d(LOG_TAG, "Show alert dialog if data roaming is disabled");
+                       String title = (String)this.getResources()
+                               .getText(R.string.no_mobile_data_roaming);
+                       String message = (String)this.getResources()
+                               .getText(R.string.cf_setting_mobile_data_roaming_alert);
+                       showAlertDialog(title, message);
+                       return;
+                }
+                if (sub != defaultDataSub) {
+                    Log.d(LOG_TAG, "Show data in use indication if data sub is not on current sub");
+                    showDataInuseToast();
+                }
+            }
+        }
+        initCallforwarding();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int id) {
+        if (id == DialogInterface.BUTTON_POSITIVE) {
+            Intent newIntent = new Intent("android.settings.SETTINGS");
+            newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(newIntent);
+        }
+        finish();
+        return;
+    }
+
+    private int getActiveNetworkType() {
+        ConnectivityManager cm = (ConnectivityManager) getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            NetworkInfo ni = cm.getActiveNetworkInfo();
+            if ((ni == null) || !ni.isConnected()){
+                return ConnectivityManager.TYPE_NONE;
+            }
+            return ni.getType();
+        }
+        return ConnectivityManager.TYPE_NONE;
+    }
+
+    //prompt dialog to notify user turn off Enhance 4G LTE switch
+    private boolean isPromptTurnOffEnhance4GLTE(Phone phone) {
+        if (phone == null || phone.getImsPhone() == null) {
+            return false;
+        }
+
+        ImsManager imsMgr = ImsManager.getInstance(this, phone.getPhoneId());
+        try {
+            if (imsMgr.getImsServiceState() != ImsFeature.STATE_READY) {
+                Log.d(LOG_TAG, "ImsServiceStatus is not ready!");
+                return false;
+            }
+        } catch (ImsException ex) {
+            Log.d(LOG_TAG, "Exception when trying to get ImsServiceStatus: " + ex);
+            return false;
+        }
+
+        return imsMgr.isEnhanced4gLteModeSettingEnabledByUser()
+            && imsMgr.isNonTtyOrTtyOnVolteEnabled()
+            && !phone.isUtEnabled()
+            && !phone.isVolteEnabled()
+            && !phone.isVideoEnabled();
+    }
+
     @Override
     public void onResume() {
         super.onResume();
+        if (mCarrierConfig.getConfigForSubId(mPhone.getSubId())
+                .getBoolean(CarrierConfigManager.KEY_CDMA_CW_CF_ENABLED_BOOL)
+                && isPromptTurnOffEnhance4GLTE(mPhone)) {
+            String title = (String)this.getResources()
+                .getText(R.string.ut_not_support);
+            String msg = (String)this.getResources()
+                .getText(R.string.ct_ut_not_support_close_4glte);
+            showAlertDialog(title, msg);
+            return;
+        }
 
+        if (mCheckData) {
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+            mReceiver = new PhoneAppBroadcastReceiver();
+            registerReceiver(mReceiver, intentFilter);
+            final SubscriptionManager mSubscriptionManager = SubscriptionManager.from(this);
+            checkDataStatus();
+        } else {
+            initCallforwarding();
+        }
+    }
+
+    private void initCallforwarding () {
         if (mFirstResume) {
             if (mIcicle == null) {
                 Log.d(LOG_TAG, "start to init ");
                 CallForwardEditPreference pref = mPreferences.get(mInitIndex);
-                pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                pref.setExpectMore(canExpectMoreCallFwdReq());
+                pref.init(this, mPhone, mReplaceInvalidCFNumbers, mServiceClass, mCallForwardByUssd);
                 pref.startCallForwardOptionsQuery();
 
             } else {
@@ -157,7 +372,7 @@
                     CallForwardInfo cf = new CallForwardInfo();
                     cf.number = bundle.getString(KEY_NUMBER);
                     cf.status = bundle.getInt(KEY_STATUS);
-                    pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                    pref.init(this, mPhone, mReplaceInvalidCFNumbers, mServiceClass, mCallForwardByUssd);
                     pref.restoreCallForwardInfo(cf);
                 }
             }
@@ -166,6 +381,59 @@
         }
     }
 
+    private void showDataInuseToast() {
+        String message = (String)this.getResources()
+                .getText(R.string.mobile_data_alert);
+        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+    }
+
+    private void showSwitchDdsDialog(int slotId) {
+        String title = (String)this.getResources().getText(R.string.no_mobile_data);
+        int simId = slotId + 1;
+        String message = (String)this.getResources()
+                .getText(R.string.switch_dds_to_sub_alert) + String.valueOf(simId);
+        if (builder == null) {
+            builder=new AlertDialog.Builder(this);
+            builder.setTitle(title);
+            builder.setMessage(message);
+            builder.setIconAttribute(android.R.attr.alertDialogIcon);
+            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    Intent newIntent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
+                    newIntent.putExtra(Settings.EXTRA_SUB_ID,mPhone.getSubId());
+                    newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(newIntent);
+                }
+            });
+            builder.setNegativeButton(android.R.string.cancel,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                        finish();
+                    }
+            });
+            builder.create().show();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mCheckData && mReceiver != null) {
+            unregisterReceiver(mReceiver);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        for (CallForwardEditPreference pref : mPreferences) {
+            pref.deInit();
+        }
+    }
+
     @Override
     protected void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -185,13 +453,61 @@
     @Override
     public void onFinished(Preference preference, boolean reading) {
         if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
-            mInitIndex++;
-            CallForwardEditPreference pref = mPreferences.get(mInitIndex);
-            pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
-            pref.startCallForwardOptionsQuery();
+            if (mInitIndex == 0 && mButtonCFU.isAutoRetryCfu()) {
+                Log.i(LOG_TAG, "auto retry case: ");
+                CarrierConfigManager carrierConfig = (CarrierConfigManager)
+                    getSystemService(CARRIER_CONFIG_SERVICE);
+                if(carrierConfig != null && mPhone != null
+                        && carrierConfig.getConfigForSubId(mPhone.getSubId())
+                            .getBoolean(CarrierConfigManager.KEY_CDMA_CW_CF_ENABLED_BOOL)) {
+                    if (isPromptTurnOffEnhance4GLTE(mPhone)) {
+                        String title = (String)this.getResources()
+                            .getText(R.string.ut_not_support);
+                        String msg = (String)this.getResources()
+                            .getText(R.string.ct_ut_not_support_close_4glte);
+                        showAlertDialog(title, msg);
+                    }else if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA ){
+                        Log.i(LOG_TAG, "auto retry and switch to cmda method UI.");
+                        Intent intent = new Intent(CALL_FORWARD_INTENT);
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        startActivity(intent);
+                        finish();
+                    }
+                }
+            } else {
+                mInitIndex++;
+                CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+                pref.setExpectMore(canExpectMoreCallFwdReq());
+                pref.init(this, mPhone, mReplaceInvalidCFNumbers, mServiceClass, mCallForwardByUssd);
+                pref.startCallForwardOptionsQuery();
+            }
         }
 
         super.onFinished(preference, reading);
+
+        // Update CFNL also if CFNRc is changed
+        if (preference == mButtonCFNRc && !reading && mSupportCFNL) {
+            Log.d(LOG_TAG, "CFNRc is changed, updating CFNL also");
+            mButtonCFNL.setExpectMore(canExpectMoreCallFwdReq());
+            mButtonCFNL.init(this, mPhone, mReplaceInvalidCFNumbers, mServiceClass,
+                    mCallForwardByUssd);
+            mButtonCFNL.startCallForwardOptionsQuery();
+        }
+    }
+
+    public void onError(Preference preference, int error) {
+        if (preference == mButtonCFNL &&
+                error == QtiCallConstants.CODE_UT_CF_SERVICE_NOT_REGISTERED) {
+            Log.d(LOG_TAG, "CFNL failed with CODE_UT_CF_SERVICE_NOT_REGISTERED");
+            mSupportCFNL = false;
+            getPreferenceScreen().removePreference(preference);
+            return;
+        }
+        super.onError(preference, error);
+    }
+
+    private boolean canExpectMoreCallFwdReq() {
+        return (mInitIndex < mPreferences.size()-1);
     }
 
     @Override
@@ -242,4 +558,16 @@
         }
         return super.onOptionsItemSelected(item);
     }
+
+    private void showAlertDialog(String title, String message) {
+        Dialog dialog = new AlertDialog.Builder(this)
+                .setTitle(title)
+                .setMessage(message)
+                .setIconAttribute(android.R.attr.alertDialogIcon)
+                .setPositiveButton(android.R.string.ok, this)
+                .setNegativeButton(android.R.string.cancel, this)
+                .setOnCancelListener(this)
+                .create();
+        dialog.show();
+    }
 }
diff --git a/src/com/android/phone/GsmUmtsCallOptions.java b/src/com/android/phone/GsmUmtsCallOptions.java
index 51d1b66..495a69c 100644
--- a/src/com/android/phone/GsmUmtsCallOptions.java
+++ b/src/com/android/phone/GsmUmtsCallOptions.java
@@ -35,6 +35,7 @@
     public static final String CALL_BARRING_KEY = "call_barring_key";
     public static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
 
+    private boolean mCommon = false;
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -42,10 +43,18 @@
         addPreferencesFromResource(R.xml.gsm_umts_call_options);
 
         SubscriptionInfoHelper subInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+        PersistableBundle pb = null;
+        if (subInfoHelper.hasSubId()) {
+            pb = PhoneGlobals.getInstance().getCarrierConfigForSubId(subInfoHelper.getSubId());
+        } else {
+            pb = PhoneGlobals.getInstance().getCarrierConfig();
+        }
+        mCommon = pb != null && pb.getBoolean("config_common_callsettings_support_bool");
         subInfoHelper.setActionBarTitle(
-                getActionBar(), getResources(), R.string.labelGsmMore_with_label);
-        init(getPreferenceScreen(), subInfoHelper);
+                getActionBar(), getResources(),
+                mCommon ? R.string.labelCommonMore_with_label : R.string.labelGsmMore_with_label);
 
+        init(getPreferenceScreen(), subInfoHelper);
         if (subInfoHelper.getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) {
             //disable the entire screen
             getPreferenceScreen().setEnabled(false);
@@ -84,7 +93,7 @@
             if (b != null && b.getBoolean(
                     CarrierConfigManager.KEY_CALL_FORWARDING_VISIBILITY_BOOL)) {
                 callForwardingPref.setIntent(
-                        subInfoHelper.getIntent(GsmUmtsCallForwardOptions.class));
+                        subInfoHelper.getIntent(CallForwardType.class));
                 callForwardingPref.setEnabled(isAirplaneModeOff);
             } else {
                 prefScreen.removePreference(callForwardingPref);
diff --git a/src/com/android/phone/IccNetworkDepersonalizationPanel.java b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
index a4ec8a4..4ce1e11 100644
--- a/src/com/android/phone/IccNetworkDepersonalizationPanel.java
+++ b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
@@ -22,6 +22,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -40,8 +42,10 @@
 import android.widget.TextView;
 
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 
+import com.qti.extphone.IDepersoResCallback;
 /**
  * "SIM network unlock" PIN entry screen.
  *
@@ -65,6 +69,8 @@
     //events
     private static final int EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT = 100;
 
+    //this enum value should match with error value being propagated from vendor
+    private int ERROR = 1;
     private Phone mPhone;
     private int mPersoSubtype;
     private static IccNetworkDepersonalizationPanel [] sNdpPanel =
@@ -141,30 +147,61 @@
     private Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             if (msg.what == EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT) {
-                AsyncResult res = (AsyncResult) msg.obj;
-                if (res.exception != null) {
-                    if (DBG) log("network depersonalization request failure.");
-                    displayStatus(statusType.ERROR.name());
-                    postDelayed(new Runnable() {
-                        public void run() {
-                            hideAlert();
-                            mPinEntry.getText().clear();
-                            mPinEntry.requestFocus();
+                if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+                    AsyncResult res = (AsyncResult) msg.obj;
+                        if (res.exception != null) {
+                            if (DBG) log("network depersonalization request failure.");
+                            displayStatus(statusType.ERROR.name());
+                            postDelayed(new Runnable() {
+                                public void run() {
+                                    hideAlert();
+                                    mPinEntry.getText().clear();
+                                    mPinEntry.requestFocus();
+                                }
+                            }, 3000);
+                        } else {
+                            if (DBG) log("network depersonalization success.");
+                            displayStatus(statusType.SUCCESS.name());
+                            postDelayed(new Runnable() {
+                                public void run() {
+                                    dismiss();
+                                }
+                            }, 3000);
                         }
-                    }, 3000);
                 } else {
-                    if (DBG) log("network depersonalization success.");
-                    displayStatus(statusType.SUCCESS.name());
-                    postDelayed(new Runnable() {
-                        public void run() {
-                            dismiss();
-                        }
-                    }, 3000);
+                    //DepersoResult received ERROR/SUCCESS from vendor side
+                    if (msg.arg1 == ERROR) {
+                        if (DBG) log("network depersonalization request failure.");
+                        displayStatus(statusType.ERROR.name());
+                        postDelayed(new Runnable() {
+                            public void run() {
+                                hideAlert();
+                                mPinEntry.getText().clear();
+                                mPinEntry.requestFocus();
+                            }
+                        }, 3000);
+                    } else {
+                        if (DBG) log("network depersonalization success.");
+                        displayStatus(statusType.SUCCESS.name());
+                        postDelayed(new Runnable() {
+                            public void run() {
+                                dismiss();
+                            }
+                        }, 3000);
+                    }
                 }
             }
         }
     };
 
+    private final IDepersoResCallback mCallback = new IDepersoResCallback.Stub() {
+        public void onDepersoResult(int result, int phoneId) {
+           log("on deperso result received: " +result);
+           Message msg = mHandler.obtainMessage(EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT, result,
+                   phoneId);
+           msg.sendToTarget();
+        }
+    };
 
     //constructor
     public IccNetworkDepersonalizationPanel(Context context) {
@@ -261,13 +298,22 @@
                 return;
             }
 
-            log("Requesting De-Personalization for subtype " + mPersoSubtype);
+            int persoState = mPersoSubState.getState();
+            log("Requesting De-Personalization for subtype " + mPersoSubtype
+                    + " subtype val " + persoState);
 
             try {
-                mPhone.getIccCard().supplySimDepersonalization(mPersoSubState,pin,
-                        Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
+                // If 1.5 or above HAL Version, then functionality uses IRadio.hal
+                // else follow legacy procedure
+                if(mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+                    mPhone.getIccCard().supplySimDepersonalization(mPersoSubState,pin,
+                           Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
+                } else {
+                    PhoneUtils.getExtTelManager().supplyIccDepersonalization(pin,
+                            Integer.toString(persoState), mCallback, mPhone.getPhoneId());
+                }
             } catch (NullPointerException ex) {
-                log("NullPointerException @supplySimDepersonalization" + ex);
+                log("NullPointerException @supplyIccDepersonalization" + ex);
             }
             displayStatus(statusType.IN_PROGRESS.name());
         }
@@ -281,7 +327,6 @@
 
         label = getContext().getResources().getIdentifier(mPersoSubState.name()
                 + "_" + type, "string", "android");
-
         if (label == 0) {
             log ("Unable to get the PersoSubType string");
             return;
@@ -303,6 +348,14 @@
             }
         }
 
+       CharSequence displayName = mSir.getDisplayName();
+       log("Operator displayName is: " + displayName + "phoneId: " + mPhone.getPhoneId());
+
+        // Displaying Operator displayName  on UI
+        String phoneIdText = getContext().getString(R.string.label_phoneid)
+                + ": " + displayName;
+        mPhoneIdText.setText(phoneIdText);
+
         if (type == statusType.ENTRY.name()) {
             String displayText = getContext().getString(label);
             mPersoSubtypeText.setText(displayText);
diff --git a/src/com/android/phone/ImsUtil.java b/src/com/android/phone/ImsUtil.java
index ba4ad38..564e534 100644
--- a/src/com/android/phone/ImsUtil.java
+++ b/src/com/android/phone/ImsUtil.java
@@ -61,7 +61,7 @@
      * @return {@code true} if WFC is supported per Slot and has been enabled by the user.
      */
     public static boolean isWfcEnabled(Context context, int phoneId) {
-        ImsManager imsManager = ImsManager.getInstance(context, phoneId);
+        final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
         boolean isEnabledByPlatform = imsManager.isWfcEnabledByPlatform();
         boolean isEnabledByUser = imsManager.isWfcEnabledByUser();
         if (DBG) Log.d(LOG_TAG, "isWfcEnabled :: isEnabledByPlatform=" + isEnabledByPlatform
@@ -84,7 +84,7 @@
      * enabled, this will return {@code false}.
      */
     public static boolean isWfcModeWifiOnly(Context context, int phoneId) {
-        ImsManager imsManager = ImsManager.getInstance(context, phoneId);
+        final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
         boolean isWifiOnlyMode =
                 imsManager.getWfcMode() == ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY;
         if (DBG) Log.d(LOG_TAG, "isWfcModeWifiOnly :: isWifiOnlyMode" + isWifiOnlyMode
@@ -116,7 +116,7 @@
         CarrierConfigManager cfgManager = (CarrierConfigManager) context
                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
 
-        ImsManager imsManager = ImsManager.getInstance(context, phoneId);
+        final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
         if (!imsManager.isWfcEnabledByPlatform()) {
             return false;
         }
@@ -149,10 +149,6 @@
         return false;
     }
 
-    private static ImsManager getDefaultImsManagerInstance(Context context) {
-        return ImsManager.getInstance(context, SubscriptionManager.getDefaultVoicePhoneId());
-    }
-
     private static int getSubId(int phoneId) {
         final int[] subIds = SubscriptionManager.getSubId(phoneId);
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/src/com/android/phone/LimitedServiceActivity.java b/src/com/android/phone/LimitedServiceActivity.java
new file mode 100644
index 0000000..baea452
--- /dev/null
+++ b/src/com/android/phone/LimitedServiceActivity.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2020, 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.phone;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.CheckBox;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentActivity;
+import com.android.internal.telephony.CarrierServiceStateTracker;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+
+public class LimitedServiceActivity extends FragmentActivity {
+
+    private static final String LOG_TAG = "LimitedServiceActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.d(LOG_TAG, "Started LimitedServiceActivity");
+        int phoneId = getIntent().getExtras().getInt(PhoneConstants.PHONE_KEY);
+        LimitedServiceAlertDialogFragment newFragment = LimitedServiceAlertDialogFragment.
+                newInstance(phoneId);
+        newFragment.show(getSupportFragmentManager(), null);
+    }
+
+    public static class LimitedServiceAlertDialogFragment extends DialogFragment {
+        private static final String TAG = "LimitedServiceAlertDialog";
+        private static final int EVENT_IMS_CAPABILITIES_CHANGED = 1;
+        private static final String KEY_PHONE_ID = "key_phone_id";
+        private Phone mPhone;
+        private int mPhoneId;
+        private TelephonyManager mTelephonyManager;
+        private Handler mHandler;
+
+        public static LimitedServiceAlertDialogFragment newInstance(int phoneId) {
+            LimitedServiceAlertDialogFragment frag = new LimitedServiceAlertDialogFragment();
+            Log.i(TAG, "LimitedServiceAlertDialog for phoneId:" + phoneId);
+            Bundle args = new Bundle();
+            args.putInt(KEY_PHONE_ID, phoneId);
+            frag.setArguments(args);
+            return frag;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle bundle) {
+            mPhoneId = getArguments().getInt(KEY_PHONE_ID);
+            mPhone = PhoneFactory.getPhone(mPhoneId);
+            mTelephonyManager = getContext().getSystemService(TelephonyManager.class).
+                    createForSubscriptionId(mPhone.getSubId());
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case EVENT_IMS_CAPABILITIES_CHANGED:
+                            if (!mTelephonyManager.isWifiCallingAvailable()) {
+                                cleanUp();
+                            }
+                            break;
+                    }
+                }
+            };
+            mPhone.getServiceStateTracker().registerForImsCapabilityChanged(mHandler,
+                    EVENT_IMS_CAPABILITIES_CHANGED, null);
+            if (!SubscriptionManager.isValidPhoneId(mPhoneId)) return null;
+            super.onCreateDialog(bundle);
+            View dialogView = View.inflate(getActivity(),
+                    R.layout.frag_limited_service_alert_dialog, null);
+            CheckBox alertCheckBox = dialogView.findViewById(R.id.do_not_show);
+            SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(
+                    PhoneFactory.getPhone(mPhoneId).getContext());
+            Log.i(TAG, "onCreateDialog " + Phone.KEY_DO_NOT_SHOW_LIMITED_SERVICE_ALERT +
+                    PhoneFactory.getPhone(mPhoneId).getSubId()  + ":" + pref.getBoolean
+                    (Phone.KEY_DO_NOT_SHOW_LIMITED_SERVICE_ALERT + PhoneFactory.getPhone
+                    (mPhoneId).getSubId(), false));
+
+            AlertDialog alertDialog =
+                new AlertDialog.Builder(getActivity())
+                    .setView(dialogView)
+                    .setNegativeButton(
+                        android.R.string.cancel,
+                        (dialog, which) -> onNegativeButtonClicked())
+                    .setPositiveButton(
+                        android.R.string.ok,
+                        (dialog, which) -> onPositiveButtonClicked(pref, alertCheckBox.isChecked()))
+                    .create();
+            this.setCancelable(false);
+            return alertDialog;
+        }
+
+        private void onNegativeButtonClicked() {
+            Log.d(TAG, "onNegativeButtonClicked");
+            SubscriptionManager subscriptionManager = (SubscriptionManager) getContext().
+                    getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+            int[] subIds = subscriptionManager.getSubscriptionIds(mPhoneId);
+            if (subIds != null && subIds.length > 0 && SubscriptionManager.
+                    isValidSubscriptionId(subIds[0])) {
+                ImsMmTelManager imsMmTelMgr = ImsMmTelManager.
+                        createForSubscriptionId(subIds[0]);
+                Log.i(TAG, "Disabling WFC setting");
+                imsMmTelMgr.setVoWiFiSettingEnabled(false);
+            }
+            cleanUp();
+        }
+
+        private void onPositiveButtonClicked(@NonNull SharedPreferences preferences,
+                boolean isChecked) {
+            SharedPreferences.Editor editor = preferences.edit();
+            editor.putBoolean(Phone.KEY_DO_NOT_SHOW_LIMITED_SERVICE_ALERT + PhoneFactory.
+                    getPhone(mPhoneId).getSubId(), isChecked);
+            editor.commit();
+            Log.i(TAG, "onPositiveButtonClicked isChecked:" + isChecked + " phoneId:" + mPhoneId
+                    + " do not show preference:" + preferences.getBoolean
+                    (Phone.KEY_DO_NOT_SHOW_LIMITED_SERVICE_ALERT +
+                    PhoneFactory.getPhone(mPhoneId).getSubId(), false));
+            if (isChecked) {
+                NotificationManager sNotificationManager = (NotificationManager) getContext().
+                        getSystemService(NOTIFICATION_SERVICE);
+                sNotificationManager.cancel(CarrierServiceStateTracker.EMERGENCY_NOTIFICATION_TAG,
+                        PhoneFactory.getPhone(mPhoneId).getSubId());
+            }
+            cleanUp();
+        }
+
+        private void cleanUp() {
+            mPhone.getServiceStateTracker().unregisterForImsCapabilityChanged(mHandler);
+            dismiss();
+            getActivity().finish();
+        }
+  }
+}
diff --git a/src/com/android/phone/MMIDialogActivity.java b/src/com/android/phone/MMIDialogActivity.java
index c9be2ac..7153096 100644
--- a/src/com/android/phone/MMIDialogActivity.java
+++ b/src/com/android/phone/MMIDialogActivity.java
@@ -102,7 +102,7 @@
             final MmiCode mmiCode = codes.get(0);
             final Message message = Message.obtain(mHandler, PhoneGlobals.MMI_CANCEL);
             Log.d(TAG, "showMMIDialog: mmiCode = " + mmiCode);
-            mMMIDialog = PhoneUtils.displayMMIInitiate(this, mmiCode, message, mMMIDialog);
+            mMMIDialog = PhoneUtils.displayMMIInitiate(mPhone, this, mmiCode, message, mMMIDialog);
         } else {
             Log.d(TAG, "showMMIDialog: no pending MMIs; finishing");
             finish();
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index f2641a1..fbe5635 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -88,6 +88,8 @@
     private static final String MWI_SHOULD_CHECK_VVM_CONFIGURATION_KEY_PREFIX =
             "mwi_should_check_vvm_configuration_state_";
 
+    private static final String EXTRA_SUB_ID = "sub_id";
+
     // notification types
     static final int MMI_NOTIFICATION = 1;
     static final int NETWORK_SELECTION_NOTIFICATION = 2;
@@ -388,7 +390,7 @@
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
                         && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
-                            pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
+                            pendingIntent, isSettingsIntent, userHandle, isRefresh, subId)) {
                         notifyAsUser(
                                 Integer.toString(subId) /* tag */,
                                 VOICEMAIL_NOTIFICATION,
@@ -404,7 +406,7 @@
                         UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
                         && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
                     if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
-                            false, userHandle, isRefresh)) {
+                            false, userHandle, isRefresh, subId)) {
                         cancelAsUser(
                                 Integer.toString(subId) /* tag */,
                                 VOICEMAIL_NOTIFICATION,
@@ -449,7 +451,7 @@
      */
     private boolean maybeSendVoicemailNotificationUsingDefaultDialer(Phone phone, Integer count,
             String number, PendingIntent pendingIntent, boolean isSettingsIntent,
-            UserHandle userHandle, boolean isRefresh) {
+            UserHandle userHandle, boolean isRefresh, int subId) {
 
         if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
             Intent intent = getShowVoicemailIntentForDefaultDialer(userHandle);
@@ -458,6 +460,7 @@
             intent.putExtra(TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                     PhoneUtils.makePstnPhoneAccountHandle(phone));
             intent.putExtra(TelephonyManager.EXTRA_IS_REFRESH, isRefresh);
+            intent.putExtra(EXTRA_SUB_ID, subId);
             if (count != null) {
                 intent.putExtra(TelephonyManager.EXTRA_NOTIFICATION_COUNT, count);
             }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index e7cb28c..2ec0a84 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -44,6 +44,7 @@
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
 import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
@@ -129,6 +130,7 @@
     private static final int EVENT_DATA_ROAMING_SETTINGS_CHANGED = 15;
     private static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED = 16;
     private static final int EVENT_CARRIER_CONFIG_CHANGED = 17;
+    private static final int EVENT_DATA_CONNECTION_ATTACHED = 18;
 
     // The MMI codes are also used by the InCallScreen.
     public static final int MMI_INITIATE = 51;
@@ -284,7 +286,6 @@
                     Phone phone = (Phone) ((AsyncResult) msg.obj).userObj;
                     handleSimLock(subType, phone);
                     break;
-
                 case EVENT_DATA_ROAMING_DISCONNECTED:
                     notificationMgr.showDataRoamingNotification(msg.arg1, false);
                     break;
@@ -362,6 +363,21 @@
                         handleSimLock(-1, phone);
                     }
                     break;
+                case EVENT_DATA_CONNECTION_ATTACHED:
+                    subId = (Integer)((AsyncResult)msg.obj).userObj;
+                    phone = getPhone(subId);
+                    if (phone != null) {
+                        DataConnectionReasons reasons = new DataConnectionReasons();
+                        boolean dataAllowed = phone.isDataAllowed(ApnSetting.TYPE_DEFAULT, reasons);
+                        if (!dataAllowed && dataIsNowRoaming(subId)
+                                && subId == mDefaultDataSubId) {
+                            if (VDBG) Log.v(LOG_TAG, "EVENT_DATA_CONNECTION_ATTACHED");
+                                updateDataRoamingStatus();
+                        }
+                    } else {
+                        Log.w(LOG_TAG, "phone object is null subId: " + subId);
+                    }
+                    break;
             }
         }
     };
@@ -499,6 +515,7 @@
             intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
             intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
             intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+            intentFilter.addAction(SmsCallbackModeService.ACTION_SMS_CALLBACK_MODE_CHANGED);
             intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
             intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
             intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
@@ -540,6 +557,8 @@
             new BinderCallsStats(
                     new BinderCallsStats.Injector(),
                     com.android.internal.os.BinderLatencyProto.Dims.TELEPHONY));
+
+        PhoneUtils.connectExtTelephonyManager(this);
     }
 
     /**
@@ -744,6 +763,9 @@
                 // re-register as it may be a new IccCard
                 int phoneId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
                         SubscriptionManager.INVALID_PHONE_INDEX);
+                int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+                        SubscriptionManager.INVALID_SIM_SLOT_INDEX);
+                String simStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
                 if (SubscriptionManager.isValidPhoneId(phoneId)) {
                     PhoneUtils.unregisterIccStatus(mHandler, phoneId);
                     PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED, phoneId);
@@ -751,6 +773,17 @@
                 String iccStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
                 mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
                         new EventSimStateChangedBag(phoneId, iccStatus)));
+                Phone phone = PhoneFactory.getPhone(phoneId);
+                if (phone != null) {
+                    if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
+                        phone.getServiceStateTracker().registerForDataConnectionAttached(
+                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, mHandler, EVENT_DATA_CONNECTION_ATTACHED, subId);
+                    } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)
+                            || IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {
+                        phone.getServiceStateTracker()
+                                .unregisterForDataConnectionAttached(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, mHandler);
+                    }
+                }
             } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
                 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
                 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active.");
@@ -782,6 +815,12 @@
                 } else {
                     Log.w(LOG_TAG, "phoneInEcm is null.");
                 }
+            } else if (action.equals(SmsCallbackModeService.ACTION_SMS_CALLBACK_MODE_CHANGED)) {
+                if (intent.getBooleanExtra(
+                                SmsCallbackModeService.EXTRA_PHONE_IN_SCM_STATE, false)) {
+                   // Start Sms Callback Mode service
+                    context.startService(new Intent(context, SmsCallbackModeService.class));
+                }
             } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
                 // Roaming status could be overridden by carrier config, so we need to update it.
                 if (VDBG) Log.v(LOG_TAG, "carrier config changed.");
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 7dbfea5..fbad702 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -7587,8 +7587,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             boolean isRttSupported = isRttSupported(subscriptionId);
-            boolean isUserRttSettingOn = Settings.Secure.getInt(
-                    mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+            boolean isUserRttSettingOn = isUserRttSettingOn(subscriptionId);
             boolean shouldIgnoreUserRttSetting = mApp.getCarrierConfigForSubId(subscriptionId)
                     .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
             return isRttSupported && (isUserRttSettingOn || shouldIgnoreUserRttSetting);
@@ -7597,6 +7596,21 @@
         }
     }
 
+    private boolean isUserRttSettingOn(int subscriptionId) {
+        int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            loge("phoneId " + phoneId + " is not valid.");
+            return false;
+        }
+        return Settings.Secure.getInt(
+                mApp.getContentResolver(),
+                Settings.Secure.RTT_CALLING_MODE + convertRttPhoneId(phoneId) , 0) != 0;
+    }
+
+    private static String convertRttPhoneId(int phoneId) {
+        return phoneId != 0 ? Integer.toString(phoneId) : "";
+    }
+
     @Deprecated
     @Override
     public String getDeviceId(String callingPackage) {
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 35cfdd3..7428491 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -22,6 +22,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
@@ -30,15 +32,21 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.PersistableBundle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -51,6 +59,7 @@
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.MmiCode;
@@ -61,9 +70,14 @@
 import com.android.phone.settings.SuppServicesUiUtil;
 import com.android.telephony.Rlog;
 
+import com.qti.extphone.ExtTelephonyManager;
+import com.qti.extphone.ServiceCallback;
+
 import java.io.IOException;
 import java.util.List;
 
+import org.codeaurora.internal.IExtTelephony;
+
 /**
  * Misc utilities for the Phone app.
  */
@@ -90,6 +104,10 @@
     /** Define for default vibrate pattern if res cannot be found */
     private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
 
+    private static final int INVALID = -1;
+    private static int mBackToBackSSFeature = INVALID;
+    private static final int BACK_BACK_SS_REQ = 1;
+
     /**
      * Theme to use for dialogs displayed by utility methods in this class. This is needed
      * because these dialogs are displayed using the application context, which does not resolve
@@ -97,8 +115,16 @@
      */
     private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
 
+    /** Extra key to identify the service class voice or video */
+    public static final String SERVICE_CLASS = "service_class";
+
+    private static final int PRIMARY_STACK_MODEM_ID = 0;
+
     /** USSD information used to aggregate all USSD messages */
     private static StringBuilder sUssdMsg = new StringBuilder();
+    private static ExtTelephonyManager mExtTelephonyManager;
+    private static TelephonyManager sTelephonyManager;
+    private static TelecomManager sTelecomManager;
 
     private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT =
             new ComponentName("com.android.phone",
@@ -224,13 +250,15 @@
      * Handle the MMIInitiate message and put up an alert that lets
      * the user cancel the operation, if applicable.
      *
+     * @param phone the Phone object.
      * @param context context to get strings.
      * @param mmiCode the MmiCode object being started.
      * @param buttonCallbackMessage message to post when button is clicked.
      * @param previousAlert a previous alert used in this activity.
      * @return the dialog handle
      */
-    static Dialog displayMMIInitiate(Context context,
+    static Dialog displayMMIInitiate(Phone phone,
+                                          Context context,
                                           MmiCode mmiCode,
                                           Message buttonCallbackMessage,
                                           Dialog previousAlert) {
@@ -281,6 +309,13 @@
 
             // create the indeterminate progress dialog and display it.
             ProgressDialog pd = new ProgressDialog(context, THEME);
+            if (isMultiSimMode() && phone != null) {
+                pd.setTitle(context.getText(R.string.ussdinitiated_title));
+                PhoneAccount account = getPhoneAccount(phone.getSubId());
+                if (account != null && account.getIcon() != null) {
+                    pd.setIcon(account.getIcon().loadDrawable(context));
+                }
+            }
             pd.setMessage(context.getText(R.string.ussdRunning));
             pd.setCancelable(false);
             pd.setIndeterminate(true);
@@ -477,6 +512,20 @@
                         .setCancelable(false)
                         .create();
 
+                if (isMultiSimMode() && phone != null) {
+                    PhoneAccount account = getPhoneAccount(phone.getSubId());
+                    if (account != null && account.getIcon() != null) {
+                        newDialog.setIcon(account.getIcon().loadDrawable(context));
+                    }
+                    if (phone.getCarrierName() != null) {
+                        newDialog.setTitle(app.getResources().getString(
+                                R.string.carrier_mmi_msg_title, phone.getCarrierName()));
+                    } else {
+                        newDialog.setTitle(app.getResources().getString(
+                               R.string.default_carrier_mmi_msg_title));
+                    }
+                }
+
                 // attach the key listener to the dialog's input field and make
                 // sure focus is set.
                 final View.OnKeyListener mUSSDDialogInputListener =
@@ -612,6 +661,12 @@
             ussdDialog
                     .setTitle(app.getResources().getString(R.string.default_carrier_mmi_msg_title));
         }
+        if (isMultiSimMode() && phone != null) {
+            PhoneAccount account = getPhoneAccount(phone.getSubId());
+            if (account != null && account.getIcon() != null) {
+                ussdDialog.setIcon(account.getIcon().loadDrawable(context));
+            }
+        }
         sUssdMsg.insert(0, text);
         ussdDialog.setMessage(sUssdMsg.toString());
         ussdDialog.show();
@@ -645,7 +700,6 @@
         return canceled;
     }
 
-
     //
     // Misc UI policy helper functions
     //
@@ -749,6 +803,11 @@
         return null;
     }
 
+    public static boolean isValidPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+        return phoneAccountHandle != null && !TextUtils.isEmpty(phoneAccountHandle.getId())
+                && !phoneAccountHandle.getId().equals("null");
+    }
+
     /**
      * Determine if a given phone account corresponds to an active SIM
      *
@@ -829,4 +888,162 @@
             phone.setRadioPower(enabled);
         }
     }
+
+    private static IExtTelephony getIExtTelephony() {
+        return IExtTelephony.Stub.asInterface(ServiceManager.getService("qti.radio.extphone"));
+    }
+
+    public static int getPhoneIdForECall() {
+        int phoneId = 0;
+        try {
+            phoneId = getIExtTelephony().getPhoneIdForECall();
+        } catch (RemoteException ex) {
+            Log.e("TelephonyConnectionService", "Exceptions : " + ex);
+        } catch (NullPointerException ex) {
+            Log.e("TelephonyConnectionService", "Exception : " + ex);
+        }
+        return phoneId;
+    }
+
+    public static int getPrimaryStackPhoneId() {
+        String modemUuId = null;
+        int primayStackPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
+
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (phone == null) continue;
+
+            Log.d(LOG_TAG, "Logical Modem id: " + phone.getModemUuId()
+                    + " phoneId: " + phone.getPhoneId());
+            modemUuId = phone.getModemUuId();
+            if ((modemUuId == null) || (modemUuId.length() <= 0) ||
+                    modemUuId.isEmpty()) {
+                continue;
+            }
+            // Select the phone id based on modemUuid
+            // if modemUuid is 0 for any phone instance, primary stack is mapped
+            // to it so return the phone id as the primary stack phone id.
+            int modemUuIdValue = PRIMARY_STACK_MODEM_ID;
+            try {
+                modemUuIdValue = Integer.parseInt(modemUuId);
+            } catch (NumberFormatException e) {
+                Log.w(LOG_TAG, "modemUuId is not an integer: " + modemUuId);
+            }
+            if (modemUuIdValue == PRIMARY_STACK_MODEM_ID) {
+                primayStackPhoneId = phone.getPhoneId();
+                Log.d(LOG_TAG, "Primay Stack phone id: " + primayStackPhoneId + " selected");
+                break;
+            }
+        }
+
+        // If phone id is invalid return default phone id
+        if (primayStackPhoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+            Log.d(LOG_TAG, "Returning default phone id");
+            primayStackPhoneId = 0;
+        }
+
+        return primayStackPhoneId;
+    }
+
+    public static void connectExtTelephonyManager(Context context) {
+        mExtTelephonyManager = ExtTelephonyManager.getInstance(context);
+
+        mExtTelephonyManager.connectService(mExtTelManagerServiceCallback);
+    }
+
+    public static ExtTelephonyManager getExtTelManager() {
+        return mExtTelephonyManager;
+    }
+
+    private static ServiceCallback mExtTelManagerServiceCallback = new ServiceCallback() {
+
+        @Override
+        public void onConnected() {
+            Log.d(LOG_TAG, "mExtTelManagerServiceCallback: service connected");
+        }
+
+        @Override
+        public void onDisconnected() {
+            Log.d(LOG_TAG, "mExtTelManagerServiceCallback: service disconnected");
+        }
+    };
+
+    static boolean isBacktoBackSSFeatureSupported() {
+        if (mBackToBackSSFeature == INVALID) {
+            mBackToBackSSFeature =
+                   (mExtTelephonyManager.isFeatureSupported(BACK_BACK_SS_REQ)) ? 1 : 0;
+        }
+        return (mBackToBackSSFeature == 1);
+    }
+
+    public static CommandException getCommandException(int code) {
+            CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
+
+        switch(code) {
+            case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
+                error = CommandException.Error.REQUEST_NOT_SUPPORTED;
+                break;
+            case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
+                error = CommandException.Error.PASSWORD_INCORRECT;
+                break;
+            case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
+                error = CommandException.Error.RADIO_NOT_AVAILABLE;
+                break;
+            case ImsReasonInfo.CODE_FDN_BLOCKED:
+                error = CommandException.Error.FDN_CHECK_FAILURE;
+                break;
+            case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL:
+                error = CommandException.Error.SS_MODIFIED_TO_DIAL;
+                break;
+            case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD:
+                error = CommandException.Error.SS_MODIFIED_TO_USSD;
+                break;
+            case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS:
+                error = CommandException.Error.SS_MODIFIED_TO_SS;
+                break;
+            case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO:
+                error = CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO;
+                break;
+            default:
+                break;
+        }
+
+        return new CommandException(error);
+    }
+
+    /**
+     * To get the phone account using subscription ID.
+     */
+    private static PhoneAccount getPhoneAccount(int subId) {
+        PhoneAccountHandle handle = getTelephonyManager() != null ?
+                getTelephonyManager().getPhoneAccountHandleForSubscriptionId(subId) : null;
+        return getTelecomManager() != null && handle != null ?
+                getTelecomManager().getPhoneAccount(handle) : null;
+    }
+
+    /**
+     * Returns true if device is in Single Standby mode, false otherwise.
+     */
+    private static boolean isMultiSimMode() {
+        return getTelephonyManager() != null && getTelephonyManager().getActiveModemCount() > 1;
+    }
+
+    /**
+     * To get the instance of TelephonyManager.
+     */
+    private static TelephonyManager getTelephonyManager() {
+        if (sTelephonyManager == null) {
+            sTelephonyManager = PhoneGlobals.getInstance().getSystemService(TelephonyManager.class);
+        }
+        return sTelephonyManager;
+    }
+
+    /**
+     * To get the instance of TelecomManager.
+     */
+    private static TelecomManager getTelecomManager() {
+        if (sTelecomManager == null) {
+            sTelecomManager = PhoneGlobals.getInstance().getSystemService(TelecomManager.class);
+        }
+        return sTelecomManager;
+    }
 }
diff --git a/src/com/android/phone/SmsCallbackModeExitDialog.java b/src/com/android/phone/SmsCallbackModeExitDialog.java
new file mode 100644
index 0000000..c994e85
--- /dev/null
+++ b/src/com/android/phone/SmsCallbackModeExitDialog.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
+ *
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.EcbmHandler;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyIntents;
+
+
+/**
+ * Displays dialog that enables users to exit Sms Callback Mode
+ *
+ * @see SmsCallbackModeService
+ */
+public class SmsCallbackModeExitDialog extends Activity implements OnCancelListener {
+
+    private static final String TAG = "SmsCallbackMode";
+
+    /** Intent to trigger the Sms Callback Mode exit dialog */
+    static final String ACTION_SHOW_SCM_EXIT_DIALOG =
+            "org.codeaurora.intent.action.ACTION_SHOW_SCM_EXIT_DIALOG";
+
+    public static final int EXIT_SCM_BLOCK_OTHERS = 1;
+    public static final int EXIT_SCM_DIALOG = 2;
+    public static final int EXIT_SCM_PROGRESS_DIALOG = 3;
+    public static final int EXIT_SCM_IN_EMERGENCY_SMS_DIALOG = 4;
+
+    AlertDialog mAlertDialog = null;
+    ProgressDialog mProgressDialog = null;
+    CountDownTimer mTimer = null;
+    SmsCallbackModeService mService = null;
+    Handler mHandler = null;
+    int mDialogType = 0;
+    long mScmTimeout = 0;
+    private boolean mInEmergencySms = false;
+    private static final int SCM_TIMER_RESET = 1;
+    private Phone mPhone = null;
+    private boolean mIsResumed = false;
+    //private EcbmHandler mEcbmHandler;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+        mPhone = PhoneFactory.getDefaultPhone();
+
+        // Check if phone is in Emergency Callback Mode or
+        // Exit SCBM feature supported or not. If not, exit.
+        if (mPhone == null || !mPhone.isInScbm() ||
+                !mPhone.isExitScbmFeatureSupported()) {
+            SmsCallbackModeExitDialog.this.setResult(RESULT_OK);
+            finish();
+            return;
+        }
+        Log.i(TAG, "SCMExitDialog launched - isInScm: true" + " phone:" + mPhone);
+
+        mHandler = new Handler();
+
+        // Start thread that will wait for the connection completion so that it can get
+        // timeout value from the service
+        Thread waitForConnectionCompleteThread = new Thread(null, mTask,
+                "ScmExitDialogWaitThread");
+        waitForConnectionCompleteThread.start();
+
+        // Register SCM timer reset notfication
+        mPhone.registerForScbmTimerReset(mTimerResetHandler, SCM_TIMER_RESET, null);
+
+        // Register receiver for intent closing the dialog
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(SmsCallbackModeService.ACTION_SMS_CALLBACK_MODE_CHANGED);
+        registerReceiver(mScmExitReceiver, filter);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mIsResumed = true;
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mIsResumed = false;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        try {
+            unregisterReceiver(mScmExitReceiver);
+        } catch (IllegalArgumentException e) {
+            // Receiver was never registered - silently ignore.
+        }
+        // Unregister ECM timer reset notification
+        if (mPhone != null) {
+            mPhone.unregisterForScbmTimerReset(mHandler);
+        }
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mDialogType = savedInstanceState.getInt("DIALOG_TYPE");
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt("DIALOG_TYPE", mDialogType);
+    }
+
+    /**
+     * Waits until bind to the service completes
+     */
+    private Runnable mTask = new Runnable() {
+        public void run() {
+            Looper.prepare();
+
+            // Bind to the remote service
+            bindService(new Intent(SmsCallbackModeExitDialog.this,
+                    SmsCallbackModeService.class), mConnection, Context.BIND_AUTO_CREATE);
+
+            // Wait for bind to finish
+            synchronized (SmsCallbackModeExitDialog.this) {
+                try {
+                    if (mService == null) {
+                        SmsCallbackModeExitDialog.this.wait();
+                    }
+                } catch (InterruptedException e) {
+                    Log.d("ECM", "SmsCallbackModeExitDialog InterruptedException: "
+                            + e.getMessage());
+                    e.printStackTrace();
+                }
+            }
+
+            // Get timeout value and call state from the service
+            if (mService != null) {
+                mScmTimeout = mService.getSmsCallbackModeTimeout();
+                mInEmergencySms = mService.getSmsCallbackModeSmsState();
+                try {
+                    // Unbind from remote service
+                    unbindService(mConnection);
+                } catch (IllegalArgumentException e) {
+                    // Failed to unbind from service.
+                    Log.w(TAG, "Failed to unbind from SmsCallbackModeService");
+                }
+            }
+
+            // Show dialog
+            mHandler.post(new Runnable() {
+                public void run() {
+                    showSmsCallbackModeExitDialog();
+                }
+            });
+        }
+    };
+
+    /**
+     * Shows Sms Callback Mode dialog and starts countdown timer
+     */
+    private void showSmsCallbackModeExitDialog() {
+        if (isDestroyed()) {
+            Log.w(TAG, "Tried to show dialog, but activity was already finished");
+            return;
+        }
+        if (mInEmergencySms) {
+            mDialogType = EXIT_SCM_IN_EMERGENCY_SMS_DIALOG;
+            showDialog(EXIT_SCM_IN_EMERGENCY_SMS_DIALOG);
+        } else {
+            if (getIntent().getAction().equals(
+                    SmsCallbackModeService.ACTION_SHOW_NOTICE_SCM_BLOCK_OTHERS)) {
+                mDialogType = EXIT_SCM_BLOCK_OTHERS;
+                showDialog(EXIT_SCM_BLOCK_OTHERS);
+            } else if (getIntent().getAction().equals(ACTION_SHOW_SCM_EXIT_DIALOG)) {
+                mDialogType = EXIT_SCM_DIALOG;
+                showDialog(EXIT_SCM_DIALOG);
+            }
+
+            mTimer = new CountDownTimer(mScmTimeout, 1000) {
+                @Override
+                public void onTick(long millisUntilFinished) {
+                    CharSequence text = getDialogText(millisUntilFinished);
+                    mAlertDialog.setMessage(text);
+                }
+
+                @Override
+                public void onFinish() {
+                    //Do nothing
+                }
+            }.start();
+        }
+    }
+
+    /**
+     * Creates dialog that enables users to exit Sms Callback Mode
+     */
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+            case EXIT_SCM_BLOCK_OTHERS:
+            case EXIT_SCM_DIALOG:
+                CharSequence text = getDialogText(mScmTimeout);
+                mAlertDialog = new AlertDialog.Builder(SmsCallbackModeExitDialog.this,
+                        android.R.style.Theme_DeviceDefault_Dialog_Alert)
+                        .setIcon(R.drawable.ic_emergency_callback_mode)
+                        .setTitle(R.string.phone_in_scm_notification_title)
+                        .setMessage(text)
+                        .setPositiveButton(R.string.alert_dialog_yes,
+                                new DialogInterface.OnClickListener() {
+                                    public void onClick(DialogInterface dialog,int whichButton) {
+                                        // User clicked Yes. Exit Sms Callback Mode.
+                                        try {
+                                            mPhone.exitScbm();
+                                        } catch (Exception e) {
+                                            e.printStackTrace();
+                                        }
+
+                                        // Show progress dialog
+                                        showDialog(EXIT_SCM_PROGRESS_DIALOG);
+                                        mTimer.cancel();
+                                    }
+                                })
+                        .setNegativeButton(R.string.alert_dialog_no,
+                                new DialogInterface.OnClickListener() {
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                        // User clicked No
+                                        setResult(RESULT_CANCELED);
+                                        finish();
+                                    }
+                                }).create();
+                mAlertDialog.setOnCancelListener(this);
+                return mAlertDialog;
+
+            case EXIT_SCM_IN_EMERGENCY_SMS_DIALOG:
+                mAlertDialog = new AlertDialog.Builder(SmsCallbackModeExitDialog.this,
+                        android.R.style.Theme_DeviceDefault_Dialog_Alert)
+                        .setIcon(R.drawable.ic_emergency_callback_mode)
+                        .setTitle(R.string.phone_in_scm_notification_title)
+                        .setMessage(R.string.alert_dialog_in_scm_call)
+                        .setNeutralButton(R.string.alert_dialog_dismiss,
+                                new DialogInterface.OnClickListener() {
+                                    public void onClick(DialogInterface dialog, int whichButton) {
+                                        // User clicked Dismiss
+                                        setResult(RESULT_CANCELED);
+                                        finish();
+                                    }
+                                }).create();
+                mAlertDialog.setOnCancelListener(this);
+                return mAlertDialog;
+
+            case EXIT_SCM_PROGRESS_DIALOG:
+                mProgressDialog = new ProgressDialog(SmsCallbackModeExitDialog.this);
+                mProgressDialog.setMessage(getText(R.string.progress_dialog_exiting_scm));
+                mProgressDialog.setIndeterminate(true);
+                mProgressDialog.setCancelable(false);
+                return mProgressDialog;
+
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Returns dialog box text with updated timeout value
+     */
+    private CharSequence getDialogText(long millisUntilFinished) {
+        // Format time
+        int minutes = (int)(millisUntilFinished / 60000);
+        String time = String.format("%d:%02d", minutes,
+                (millisUntilFinished % 60000) / 1000);
+
+        switch (mDialogType) {
+            case EXIT_SCM_BLOCK_OTHERS:
+                return String.format(getResources().getQuantityText(
+                        R.plurals.alert_dialog_not_avaialble_in_scm, minutes).toString(), time);
+            case EXIT_SCM_DIALOG:
+                return String.format(getResources().getQuantityText(
+                        R.plurals.alert_dialog_exit_scm, minutes).toString(), time);
+        }
+        return null;
+    }
+
+    /**
+     * Closes activity when dialog is canceled
+     */
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        SmsCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
+        finish();
+    }
+
+    /**
+     * Listen for Sms Callback Mode state change intents
+     */
+    private BroadcastReceiver mScmExitReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Received exit Sms Callback Mode notification close all dialogs
+            if (intent.getAction().equals(
+                    SmsCallbackModeService.ACTION_SMS_CALLBACK_MODE_CHANGED)) {
+                // Cancel if the sticky broadcast extra for whether or not we are in SCM is false.
+                if (!intent.getBooleanExtra(SmsCallbackModeService.EXTRA_PHONE_IN_SCM_STATE, false)) {
+                    if (mAlertDialog != null)
+                        mAlertDialog.dismiss();
+                    if (mProgressDialog != null)
+                        mProgressDialog.dismiss();
+                    SmsCallbackModeExitDialog.this.setResult(RESULT_OK);
+                    finish();
+                }
+            }
+        }
+    };
+
+    /**
+     * Class for interacting with the interface of the service
+     */
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mService = ((SmsCallbackModeService.LocalBinder)service).getService();
+            // Notify thread that connection is ready
+            synchronized (SmsCallbackModeExitDialog.this) {
+                SmsCallbackModeExitDialog.this.notify();
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            mService = null;
+        }
+    };
+
+    /**
+     * Class for receiving framework timer reset notifications
+     */
+    private Handler mTimerResetHandler = new Handler () {
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SCM_TIMER_RESET:
+                    if(!((Boolean)((AsyncResult) msg.obj).result).booleanValue()) {
+                        SmsCallbackModeExitDialog.this.setResult(RESULT_CANCELED);
+                        finish();
+                    }
+                    break;
+            }
+        }
+    };
+}
diff --git a/src/com/android/phone/SmsCallbackModeService.java b/src/com/android/phone/SmsCallbackModeService.java
new file mode 100644
index 0000000..226f923
--- /dev/null
+++ b/src/com/android/phone/SmsCallbackModeService.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
+ *
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.UserHandle;
+import android.sysprop.TelephonyProperties;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.util.NotificationChannelController;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * Application service that inserts/removes SMS Callback Mode notification and
+ * updates SMS Callback Mode countdown clock in the notification
+ *
+ * @see SmsCallbackModeExitDialog
+ */
+public class SmsCallbackModeService extends Service {
+
+    // Default Emergency Callback Mode timeout value
+    private static final long DEFAULT_SCM_EXIT_TIMER_VALUE = 300000L;
+    private static final String LOG_TAG = "SmsCallbackModeService";
+
+    private NotificationManager mNotificationManager = null;
+    private CountDownTimer mTimer = null;
+    private long mTimeLeft = 0;
+    private Phone mPhone = null;
+    private boolean mInEmergencySms = false;
+
+    private static final int SCM_TIMER_RESET = 1;
+
+    /**
+     * Intent action broadcasted when Sms Callback Mode changed.
+     */
+    public static final String ACTION_SMS_CALLBACK_MODE_CHANGED =
+            "org.codeaurora.intent.action.SMS_CALLBACK_MODE_CHANGED";
+    /**
+     * Extra included in {@link #ACTION_SMS_CALLBACK_MODE_CHANGED}.
+     * Indicates whether the phone is in an sms callback mode.
+     */
+    public static final String EXTRA_PHONE_IN_SCM_STATE =
+            "org.codeaurora.extra.PHONE_IN_SCM_STATE";
+
+    /**
+     * Intent broadcasted to indicate the sms callback mode blocks
+     * datacall/sms.
+     */
+    public static final String ACTION_SHOW_NOTICE_SCM_BLOCK_OTHERS =
+            "org.codeaurora.intent.action.SHOW_NOTICE_SCM_BLOCK_OTHERS";
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SCM_TIMER_RESET:
+                    resetScmTimer((AsyncResult) msg.obj);
+                    break;
+            }
+        }
+    };
+
+    @Override
+    public void onCreate() {
+        mPhone = PhoneFactory.getDefaultPhone();
+
+        // Register receiver for intents
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_SMS_CALLBACK_MODE_CHANGED);
+        registerReceiver(mScmReceiver, filter);
+
+        mNotificationManager = getSystemService(NotificationManager.class);
+
+        mPhone.registerForScbmTimerReset(mHandler, SCM_TIMER_RESET, null);
+
+        startTimerNotification();
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mPhone != null) {
+            // Unregister receiver
+            unregisterReceiver(mScmReceiver);
+            // Unregister SCM timer reset notification
+            mPhone.unregisterForScbmTimerReset(mHandler);
+
+            // Cancel the notification and timer
+            mNotificationManager.cancelAsUser(null, R.string.phone_in_scm_notification_title,
+                    UserHandle.ALL);
+            mTimer.cancel();
+        }
+    }
+
+    /**
+     * Listens for SMS Callback Mode intents
+     */
+    private BroadcastReceiver mScmReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Stop the service when phone exits SMS Callback Mode
+            if (intent.getAction().equals(ACTION_SMS_CALLBACK_MODE_CHANGED)) {
+                if (!intent.getBooleanExtra(EXTRA_PHONE_IN_SCM_STATE, false)) {
+                    stopSelf();
+                }
+            }
+        }
+    };
+
+    /**
+     * Start timer notification for Sms Callback Mode
+     */
+    private void startTimerNotification() {
+        // Get Sms Callback Mode timeout value
+        long scmTimeout = TelephonyProperties.ecm_exit_timer().
+                orElse(DEFAULT_SCM_EXIT_TIMER_VALUE);
+
+        // Show the notification
+        showNotification(scmTimeout);
+
+        // Start countdown timer for the notification updates
+        if (mTimer != null) {
+            mTimer.cancel();
+        } else {
+            mTimer = new CountDownTimer(scmTimeout, 1000) {
+
+                @Override
+                public void onTick(long millisUntilFinished) {
+                    mTimeLeft = millisUntilFinished;
+                }
+
+                @Override
+                public void onFinish() {
+                    //Do nothing
+                }
+
+            };
+        }
+        mTimer.start();
+    }
+
+    /**
+     * Shows notification for Sms Callback Mode
+     */
+    private void showNotification(long millisUntilFinished) {
+        boolean isInScm = mPhone.isInScbm();
+        if (!isInScm) {
+            Log.i(LOG_TAG, "Asked to show notification but not in SCM mode");
+            if (mTimer != null) {
+                mTimer.cancel();
+            }
+            return;
+        }
+        final Notification.Builder builder = new Notification.Builder(getApplicationContext());
+        builder.setOngoing(true);
+        builder.setPriority(Notification.PRIORITY_HIGH);
+        builder.setSmallIcon(R.drawable.ic_emergency_callback_mode);
+        builder.setTicker(getText(R.string.phone_entered_scm_text));
+        builder.setContentTitle(getText(R.string.phone_in_scm_notification_title));
+        builder.setColor(getResources().getColor(R.color.dialer_theme_color));
+
+        // PendingIntent to launch Sms Callback Mode Exit activity if the user selects
+        // this notification
+        Intent intent = new Intent(this, SmsCallbackModeExitDialog.class);
+        intent.setAction(SmsCallbackModeExitDialog.ACTION_SHOW_SCM_EXIT_DIALOG);
+        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent,
+                PendingIntent.FLAG_IMMUTABLE);
+        builder.setContentIntent(contentIntent);
+
+        // Format notification string
+        String text = null;
+        if(mInEmergencySms) {
+            text = getText(
+                    R.string.phone_in_scm_call_notification_text_without_data_restriction_hint)
+                    .toString();
+        } else {
+            // Calculate the time in ms when the notification will be finished.
+            long finishedCountMs = millisUntilFinished + System.currentTimeMillis();
+            builder.setShowWhen(true);
+            builder.setChronometerCountDown(true);
+            builder.setUsesChronometer(true);
+            builder.setWhen(finishedCountMs);
+
+            String completeTime = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(
+                    finishedCountMs);
+            text = getResources().getString(
+                    // During IMS SCM, data restriction hint should be removed.
+                   R.string.phone_in_scm_notification_complete_time,
+                   completeTime);
+        }
+        builder.setContentText(text);
+        builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
+
+        // Show notification
+        mNotificationManager.notifyAsUser(null, R.string.phone_in_scm_notification_title,
+                builder.build(), UserHandle.ALL);
+    }
+
+    /**
+     * Handle SCM_TIMER_RESET notification
+     */
+    private void resetScmTimer(AsyncResult r) {
+        boolean isTimerCanceled = ((Boolean)r.result).booleanValue();
+        Log.i(LOG_TAG, "resetScmTimer: " + isTimerCanceled);
+
+        if (isTimerCanceled) {
+            mInEmergencySms = true;
+            mTimer.cancel();
+            showNotification(0);
+        } else {
+            mInEmergencySms = false;
+            startTimerNotification();
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    // This is the object that receives interactions from clients.
+    private final IBinder mBinder = new LocalBinder();
+
+    /**
+     * Class for clients to access
+     */
+    public class LocalBinder extends Binder {
+        SmsCallbackModeService getService() {
+            return SmsCallbackModeService.this;
+        }
+    }
+
+    /**
+     * Returns Sms Callback Mode timeout value
+     */
+    public long getSmsCallbackModeTimeout() {
+        return mTimeLeft;
+    }
+
+    /**
+     * Returns Sms Callback Mode Sms state
+     */
+    public boolean getSmsCallbackModeSmsState() {
+        return mInEmergencySms;
+    }
+}
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 6bc71dc..2b85ad0 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -7,10 +7,14 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserManager;
 import android.preference.Preference;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceFragment;
+import android.preference.SwitchPreference;
+import android.provider.Settings;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -26,6 +30,8 @@
 import com.android.phone.R;
 import com.android.phone.SubscriptionInfoHelper;
 
+import android.content.pm.PackageManager.NameNotFoundException;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -42,6 +48,8 @@
 
     private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_accounts_all_calling_accounts";
 
+    private static final String BUTTON_SMART_DIVERT_KEY = "button_smart_divert";
+
     private static final String MAKE_AND_RECEIVE_CALLS_CATEGORY_KEY =
             "make_and_receive_calls_settings_category_key";
     private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
@@ -51,6 +59,9 @@
     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
 
+    private static final String BUTTON_VIBRATING_KEY =
+            "button_vibrating_for_outgoing_call_accepted_key";
+
     /**
      * Value to start ordering of phone accounts relative to other preferences. By setting this
      * value on the phone account listings, we ensure that anything that is ordered before
@@ -61,6 +72,8 @@
 
     private static final String LOG_TAG = PhoneAccountSettingsFragment.class.getSimpleName();
 
+    private boolean isXdivertAvailable = false;
+
     private TelecomManager mTelecomManager;
     private TelephonyManager mTelephonyManager;
     private SubscriptionManager mSubscriptionManager;
@@ -69,8 +82,10 @@
 
     private AccountSelectionPreference mDefaultOutgoingAccount;
     private Preference mAllCallingAccounts;
+    private Preference mSmartDivertPref;
 
     private PreferenceCategory mMakeAndReceiveCallsCategory;
+    private SwitchPreference mButtonVibratingForMoCallAccepted;
     private boolean mMakeAndReceiveCallsCategoryPresent;
 
     private final SubscriptionManager.OnSubscriptionsChangedListener
@@ -92,6 +107,15 @@
         mTelecomManager = getActivity().getSystemService(TelecomManager.class);
         mTelephonyManager = TelephonyManager.from(getActivity());
         mSubscriptionManager = SubscriptionManager.from(getActivity());
+
+        // check whether the target handler exist in system
+        PackageManager pm = getActivity().getPackageManager();
+        try {
+            pm.getPackageInfo("com.qti.xdivert", 0);
+            isXdivertAvailable = true;
+        } catch (NameNotFoundException e) {
+            Log.w(LOG_TAG, " com.qti.xdivert Vendor apk not available for ");
+        }
     }
 
     @Override
@@ -134,9 +158,12 @@
         mDefaultOutgoingAccount = (AccountSelectionPreference)
                 getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
         mAllCallingAccounts = getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
+        mSmartDivertPref = getPreferenceScreen().findPreference(BUTTON_SMART_DIVERT_KEY);
 
         mMakeAndReceiveCallsCategory = (PreferenceCategory) getPreferenceScreen().findPreference(
                 MAKE_AND_RECEIVE_CALLS_CATEGORY_KEY);
+        mButtonVibratingForMoCallAccepted = (SwitchPreference)
+                mMakeAndReceiveCallsCategory.findPreference(BUTTON_VIBRATING_KEY);
         mMakeAndReceiveCallsCategoryPresent = false;
 
         updateAccounts();
@@ -162,6 +189,12 @@
      */
     @Override
     public boolean onPreferenceChange(Preference pref, Object objValue) {
+        if (pref == mButtonVibratingForMoCallAccepted) {
+            Settings.Global.putInt(getActivity().getContentResolver(),
+                    android.provider.Settings.Global.VIBRATING_FOR_OUTGOING_CALL_ACCEPTED,
+                    mButtonVibratingForMoCallAccepted.isChecked() ? 0 : 1);
+            return true;
+        }
         return false;
     }
 
@@ -351,6 +384,14 @@
                 mAccountList.addPreference(mAllCallingAccounts);
             } else {
                 mAccountList.removePreference(mAllCallingAccounts);
+                mMakeAndReceiveCallsCategory.removePreference(mDefaultOutgoingAccount);
+            }
+
+            if (isXdivertAvailable) {
+                if (mSmartDivertPref != null) {
+                    Log.d(LOG_TAG, "Add smart divert preference");
+                    mAccountList.addPreference(mSmartDivertPref);
+                }
             }
         }
     }
@@ -452,6 +493,17 @@
                     getPreferenceScreen().findPreference(SMART_FORWARDING_CONFIGURATION_PREF_KEY));
         }
 
+        SwitchPreference vibratingButton = (SwitchPreference)
+                mMakeAndReceiveCallsCategory.findPreference(BUTTON_VIBRATING_KEY);
+        if (vibratingButton != null) {
+            mMakeAndReceiveCallsCategoryPresent = true;
+            final int vibrating = Settings.Global.getInt(
+                    getActivity().getContentResolver(),
+                    Settings.Global.VIBRATING_FOR_OUTGOING_CALL_ACCEPTED, 1);
+            vibratingButton.setChecked(vibrating != 0);
+            vibratingButton.setOnPreferenceChangeListener(this);
+        }
+
         if (!mMakeAndReceiveCallsCategoryPresent) {
             getPreferenceScreen().removePreference(mMakeAndReceiveCallsCategory);
         }
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 17adef3..78fdb2e 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -47,15 +47,18 @@
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
 import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityNr;
 import android.telephony.CellIdentityWcdma;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoCdma;
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoLte;
+import android.telephony.CellInfoNr;
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellSignalStrengthCdma;
 import android.telephony.CellSignalStrengthGsm;
 import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.NetworkRegistrationInfo;
@@ -951,6 +954,23 @@
                 getCellInfoDisplayString(ssLte.getTimingAdvance()));
     }
 
+    private String buildNrInfoString(CellInfoNr ci) {
+        CellIdentityNr cidNr = (CellIdentityNr) ci.getCellIdentity();
+        CellSignalStrengthNr ssNr = (CellSignalStrengthNr) ci.getCellSignalStrength();
+
+        return String.format(
+                "%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s %-6.6s %-4.4s %-4.4s\n",
+                getConnectionStatusString(ci),
+                cidNr.getMccString(),
+                cidNr.getMncString(),
+                getCellInfoDisplayString(cidNr.getTac()),
+                getCellInfoDisplayString(cidNr.getNci()),
+                getCellInfoDisplayString(cidNr.getPci()),
+                getCellInfoDisplayString(cidNr.getNrarfcn()),
+                getCellInfoDisplayString(ssNr.getDbm()),
+                getCellInfoDisplayString(ssNr.getSsRsrq()));
+    }
+
     private String buildWcdmaInfoString(CellInfoWcdma ci) {
         CellIdentityWcdma cidWcdma = ci.getCellIdentity();
         CellSignalStrengthWcdma ssWcdma = ci.getCellSignalStrength();
@@ -971,7 +991,8 @@
         StringBuilder cdmaCells = new StringBuilder(),
                 gsmCells = new StringBuilder(),
                 lteCells = new StringBuilder(),
-                wcdmaCells = new StringBuilder();
+                wcdmaCells = new StringBuilder(),
+                nrCells = new StringBuilder();
 
         if (arrayCi != null) {
             for (CellInfo ci : arrayCi) {
@@ -984,8 +1005,19 @@
                     gsmCells.append(buildGsmInfoString((CellInfoGsm) ci));
                 } else if (ci instanceof CellInfoCdma) {
                     cdmaCells.append(buildCdmaInfoString((CellInfoCdma) ci));
+                } else if (ci instanceof CellInfoNr) {
+                    nrCells.append(buildNrInfoString((CellInfoNr) ci));
                 }
             }
+            if (nrCells.length() != 0) {
+                value += String.format(
+                        "NR\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s"
+                                + " %-6.6s %-4.4s %-4.4s\n",
+                        "SRV", "MCC", "MNC", "TAC", "NCI", "PCI",
+                        "NRARFCN", "RSRP", "RSRQ");
+                value += nrCells.toString();
+            }
+
             if (lteCells.length() != 0) {
                 value += String.format(
                         "LTE\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s"
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index 468d38f..c7ffde6 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -75,8 +75,7 @@
     /** static intent to invoke phone number picker */
     private static final Intent CONTACT_IMPORT_INTENT;
     static {
-        CONTACT_IMPORT_INTENT = new Intent(Intent.ACTION_GET_CONTENT);
-        CONTACT_IMPORT_INTENT.setType(CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+        CONTACT_IMPORT_INTENT = new Intent(Intent.ACTION_PICK, CommonDataKinds.Phone.CONTENT_URI);
     }
     /** flag to track saving state */
     private boolean mDataBusy;
diff --git a/src/com/android/phone/vvm/VvmSimStateTracker.java b/src/com/android/phone/vvm/VvmSimStateTracker.java
index a77bd7b..8e9fbe1 100644
--- a/src/com/android/phone/vvm/VvmSimStateTracker.java
+++ b/src/com/android/phone/vvm/VvmSimStateTracker.java
@@ -138,7 +138,8 @@
                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
-                if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
+                if (!subscriptionManager.isActiveSubId(subId)) {
                     VvmLog.i(TAG, "Received SIM change for invalid subscription id.");
                     checkRemovedSim(context);
                     return;
@@ -241,6 +242,10 @@
     }
 
     private void listenToAccount(Context context, PhoneAccountHandle phoneAccountHandle) {
+        if (sListeners.get(phoneAccountHandle) != null) {
+            VvmLog.i(TAG, "Listener is registered for " + phoneAccountHandle);
+            return;
+        }
         ServiceStateListener listener = new ServiceStateListener(context, phoneAccountHandle);
         listener.listen();
         sListeners.put(phoneAccountHandle, listener);
diff --git a/src/com/android/services/telephony/AnswerAndReleaseHandler.java b/src/com/android/services/telephony/AnswerAndReleaseHandler.java
new file mode 100644
index 0000000..f3e7498
--- /dev/null
+++ b/src/com/android/services/telephony/AnswerAndReleaseHandler.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2020, 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.services.telephony;
+
+import android.os.Bundle;
+import android.telecom.Conference;
+import android.telecom.Connection;
+import android.telecom.StatusHints;
+import android.telecom.VideoProfile;
+
+import com.android.ims.internal.ConferenceParticipant;
+
+import java.util.Collection;
+import java.util.List;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class AnswerAndReleaseHandler extends TelephonyConnection.TelephonyConnectionListener {
+
+    private List<Connection> mConnectionList = new CopyOnWriteArrayList<>();
+    private List<Conference> mConferenceList = new CopyOnWriteArrayList<>();
+    private int mVideoState;
+    private Connection mIncomingConnection = null;
+    private List<Listener> mListeners = new CopyOnWriteArrayList<>();
+
+    public AnswerAndReleaseHandler(Connection incomingConnection, int answerWithVideoState) {
+        mVideoState = answerWithVideoState;
+        mIncomingConnection = incomingConnection;
+    }
+
+    public interface Listener {
+        void onAnswered();
+    }
+
+    public static class ListenerBase implements Listener {
+        @Override
+        public void onAnswered() {}
+    }
+
+    public void addListener(Listener listener) {
+        synchronized (mListeners) {
+            mListeners.add(listener);
+        }
+    }
+
+    public void removeListener(Listener listener) {
+        synchronized (mListeners) {
+            mListeners.remove(listener);
+        }
+    }
+
+    private void notifyOnAnswered() {
+        synchronized (mListeners) {
+            for (Listener l : mListeners) {
+                l.onAnswered();
+            }
+        }
+    }
+
+    private final TelephonyConferenceBase.TelephonyConferenceListener mTelephonyConferenceListener =
+            new TelephonyConferenceBase.TelephonyConferenceListener() {
+        @Override
+        public void onDestroyed(Conference conference) {
+            removeConference(conference);
+            maybeAnswer();
+        }
+    };
+
+    public void checkAndAnswer(Collection<Connection> allConnections,
+            Collection<Conference> allConferences) {
+        for (Connection current : allConnections) {
+            // Connection list could contain other types like conference
+            // participant connections which need to be ignored
+            if (!(current instanceof TelephonyConnection)) {
+                continue;
+            }
+            int state = current.getState();
+            if (state == Connection.STATE_RINGING ||
+                    state == Connection.STATE_DISCONNECTED) {
+                continue;
+            }
+            boolean containsConnection = false;
+            synchronized(mConnectionList) {
+                containsConnection = mConnectionList.contains(current);
+            }
+            if (!containsConnection) {
+                addConnection(current);
+                TelephonyConnection conn = (TelephonyConnection) current;
+                conn.addTelephonyConnectionListener(this);
+                conn.onDisconnect();
+            }
+        }
+        for (Conference current : allConferences) {
+            if (!(current instanceof TelephonyConferenceBase)) {
+                continue;
+            }
+            if (current.getState() == Connection.STATE_DISCONNECTED) {
+                continue;
+            }
+            boolean containsConference = false;
+            synchronized(mConferenceList) {
+                containsConference = mConferenceList.contains(current);
+            }
+            if (!containsConference) {
+                addConference(current);
+                TelephonyConferenceBase conf = (TelephonyConferenceBase) current;
+                conf.addTelephonyConferenceListener(mTelephonyConferenceListener);
+                current.onDisconnect();
+            }
+        }
+        maybeAnswer();
+    }
+
+    private void maybeAnswer() {
+        boolean isConnectionListEmpty = false;
+        synchronized(mConnectionList) {
+            isConnectionListEmpty = mConnectionList.isEmpty();
+        }
+        boolean isConferenceListEmpty = false;
+        synchronized(mConferenceList) {
+            isConferenceListEmpty = mConferenceList.isEmpty();
+        }
+        if (isConnectionListEmpty && isConferenceListEmpty) {
+            if (mIncomingConnection.getState() == Connection.STATE_RINGING) {
+                mIncomingConnection.onAnswer(mVideoState);
+            }
+            notifyOnAnswered();
+        }
+    }
+
+    private void addConnection(Connection conn) {
+        synchronized(mConnectionList) {
+            mConnectionList.add(conn);
+        }
+    }
+
+    private void removeConnection(Connection conn) {
+        synchronized(mConnectionList) {
+            mConnectionList.remove(conn);
+        }
+    }
+
+    private void addConference(Conference conf) {
+        synchronized(mConferenceList) {
+            mConferenceList.add(conf);
+        }
+    }
+
+    private void removeConference(Conference conf) {
+        synchronized(mConferenceList) {
+            mConferenceList.remove(conf);
+        }
+    }
+
+    @Override
+    public void onOriginalConnectionConfigured(TelephonyConnection c) {}
+
+    @Override
+    public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
+
+    @Override
+    public void onConferenceParticipantsChanged(Connection c,
+            List<ConferenceParticipant> participants) {}
+
+    @Override
+    public void onConferenceStarted() {}
+
+    @Override
+    public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+
+    @Override
+    public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {}
+
+    @Override
+    public void onConnectionEvent(Connection c, String event, Bundle extras) {}
+
+    @Override
+    public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {}
+
+    @Override
+    public void onExtrasChanged(Connection c, Bundle extras) {}
+
+    @Override
+    public void onExtrasRemoved(Connection c, List<String> keys) {}
+
+    @Override
+    public void onStateChanged(android.telecom.Connection c, int state) {}
+
+    @Override
+    public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
+
+    @Override
+    public void onDestroyed(Connection c) {}
+
+    @Override
+    public void onDisconnected(android.telecom.Connection c,
+            android.telecom.DisconnectCause disconnectCause) {
+        removeConnection(c);
+        maybeAnswer();
+    }
+
+    @Override
+    public void onVideoProviderChanged(android.telecom.Connection c,
+            Connection.VideoProvider videoProvider) {}
+
+    @Override
+    public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
+
+    @Override
+    public void onRingbackRequested(Connection c, boolean ringback) {}
+}
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index c7b324d..66c7081 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -27,6 +27,7 @@
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneUtils;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.phone.settings.SettingsConstants;
 
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
old mode 100644
new mode 100755
index b7ecd48..6526495
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -59,7 +59,8 @@
     public ConferenceParticipantConnection(
             com.android.internal.telephony.Connection parentConnection,
             ConferenceParticipant participant,
-            boolean isRemotelyHosted) {
+            boolean isRemotelyHosted,
+            boolean isParticipantHost) {
 
         mParentConnection = parentConnection;
 
@@ -79,7 +80,8 @@
         mUserEntity = participant.getHandle();
         mEndpoint = participant.getEndpoint();
 
-        setCapabilitiesAndProperties(isRemotelyHosted);
+        setCapabilitiesAndProperties(isRemotelyHosted, isParticipantHost);
+        updateState(participant.getState());
     }
 
     /**
@@ -157,13 +159,18 @@
      * @param isRemotelyHosted {@code true} if this participant is part of a conference hosted
      *                         hosted on a remote device, {@code false} otherwise.
      */
-    private void setCapabilitiesAndProperties(boolean isRemotelyHosted) {
+    private void setCapabilitiesAndProperties(boolean isRemotelyHosted,
+            boolean isParticipantHost) {
         int capabilities = CAPABILITY_DISCONNECT_FROM_CONFERENCE;
         setConnectionCapabilities(capabilities);
 
         if (isRemotelyHosted) {
             setConnectionProperties(PROPERTY_REMOTELY_HOSTED);
         }
+
+        if (isParticipantHost) {
+            setConnectionProperties(PROPERTY_IS_PARTICIPANT_HOST);
+        }
     }
 
     /**
@@ -189,7 +196,11 @@
         // The SubscriptionInfo reports ISO country codes in lower case.  Convert to upper case,
         // since ultimately we use this ISO when formatting the CEP phone number, and the phone
         // number formatting library expects uppercase ISO country codes.
-        return subInfo.getCountryIso().toUpperCase();
+        final String country = subInfo.getCountryIso();
+        if (country == null) {
+            return null;
+        }
+        return country.toUpperCase();
     }
 
     /**
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 9321e1e..e9b165d 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -32,8 +32,13 @@
 import com.android.phone.PhoneGlobals;
 import com.android.phone.common.R;
 
+import com.android.internal.telephony.gsm.SuppServiceNotification;
+
 public class DisconnectCauseUtil {
 
+    public static int mNotificationCode = 0xFF;
+    public static int mNotificationType = 0xFF;
+
    /**
     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
     * generic {@link android.telecom.DisconnectCause} object, possibly populated with a localized
@@ -46,6 +51,13 @@
                 CallFailCause.NOT_VALID, null /* reason */);
     }
 
+    public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+            String reason, int type, int code, int phoneId) {
+        mNotificationCode = code;
+        mNotificationType = type;
+        return toTelecomDisconnectCause(telephonyDisconnectCause, reason, phoneId);
+    }
+
    /**
     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
@@ -207,12 +219,55 @@
             case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
             case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
             case android.telephony.DisconnectCause.MEDIA_TIMEOUT:
+            case android.telephony.DisconnectCause.CONCURRENT_CALLS_NOT_POSSIBLE:
                 return DisconnectCause.ERROR;
 
             case android.telephony.DisconnectCause.DIALED_MMI:
             case android.telephony.DisconnectCause.EXITED_ECM:
             case android.telephony.DisconnectCause.MMI:
             case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
+            case android.telephony.DisconnectCause.NO_CIRCUIT_AVAIL:
+            case android.telephony.DisconnectCause.NO_ROUTE_TO_DESTINATION:
+            case android.telephony.DisconnectCause.OPERATOR_DETERMINED_BARRING:
+            case android.telephony.DisconnectCause.CALL_FAIL_NO_USER_RESPONDING:
+            case android.telephony.DisconnectCause.CALL_FAIL_NO_ANSWER_FROM_USER:
+            case android.telephony.DisconnectCause.CALL_FAIL_DESTINATION_OUT_OF_ORDER:
+            case android.telephony.DisconnectCause.BEARER_CAPABILITY_NOT_AUTHORIZED:
+            case android.telephony.DisconnectCause.CHANNEL_UNACCEPTABLE:
+            case android.telephony.DisconnectCause.CALL_REJECTED:
+            case android.telephony.DisconnectCause.NUMBER_CHANGED:
+            case android.telephony.DisconnectCause.PREEMPTION:
+            case android.telephony.DisconnectCause.FACILITY_REJECTED:
+            case android.telephony.DisconnectCause.RESP_TO_STATUS_ENQUIRY:
+            case android.telephony.DisconnectCause.NETWORK_OUT_OF_ORDER:
+            case android.telephony.DisconnectCause.TEMPORARY_FAILURE:
+            case android.telephony.DisconnectCause.SWITCHING_EQUIPMENT_CONGESTION:
+            case android.telephony.DisconnectCause.ACCESS_INFORMATION_DISCARDED:
+            case android.telephony.DisconnectCause.REQUESTED_CIRCUIT_OR_CHANNEL_NOT_AVAILABLE:
+            case android.telephony.DisconnectCause.RESOURCES_UNAVAILABLE_OR_UNSPECIFIED:
+            case android.telephony.DisconnectCause.QOS_UNAVAILABLE:
+            case android.telephony.DisconnectCause.REQUESTED_FACILITY_NOT_SUBSCRIBED:
+            case android.telephony.DisconnectCause.INCOMING_CALLS_BARRED_WITHIN_CUG:
+            case android.telephony.DisconnectCause.BEARER_CAPABILITY_UNAVAILABLE:
+            case android.telephony.DisconnectCause.SERVICE_OPTION_NOT_AVAILABLE:
+            case android.telephony.DisconnectCause.BEARER_SERVICE_NOT_IMPLEMENTED:
+            case android.telephony.DisconnectCause.REQUESTED_FACILITY_NOT_IMPLEMENTED:
+            case android.telephony.DisconnectCause.ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE:
+            case android.telephony.DisconnectCause.SERVICE_OR_OPTION_NOT_IMPLEMENTED:
+            case android.telephony.DisconnectCause.INVALID_TRANSACTION_IDENTIFIER:
+            case android.telephony.DisconnectCause.USER_NOT_MEMBER_OF_CUG:
+            case android.telephony.DisconnectCause.INCOMPATIBLE_DESTINATION:
+            case android.telephony.DisconnectCause.INVALID_TRANSIT_NW_SELECTION:
+            case android.telephony.DisconnectCause.SEMANTICALLY_INCORRECT_MESSAGE:
+            case android.telephony.DisconnectCause.INVALID_MANDATORY_INFORMATION:
+            case android.telephony.DisconnectCause.MESSAGE_TYPE_NON_IMPLEMENTED:
+            case android.telephony.DisconnectCause.MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE:
+            case android.telephony.DisconnectCause.INFORMATION_ELEMENT_NON_EXISTENT:
+            case android.telephony.DisconnectCause.CONDITIONAL_IE_ERROR:
+            case android.telephony.DisconnectCause.MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE:
+            case android.telephony.DisconnectCause.RECOVERY_ON_TIMER_EXPIRED:
+            case android.telephony.DisconnectCause.PROTOCOL_ERROR_UNSPECIFIED:
+            case android.telephony.DisconnectCause.INTERWORKING_UNSPECIFIED:
                 return DisconnectCause.OTHER;
 
             case android.telephony.DisconnectCause.NOT_VALID:
@@ -384,6 +439,9 @@
             case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
                 resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
                 break;
+            case android.telephony.DisconnectCause.CONCURRENT_CALLS_NOT_POSSIBLE:
+                resourceId = R.string.callFailed_concurrent_calls_not_possible;
+                break;
             default:
                 break;
         }
@@ -585,9 +643,32 @@
 
         Integer resourceId = null;
         switch (telephonyDisconnectCause) {
-            case android.telephony.DisconnectCause.CALL_BARRED:
-                resourceId = R.string.callFailed_cb_enabled;
+            case android.telephony.DisconnectCause.INCOMING_MISSED: {
+                // If the network sends SVC Notification then this dialog will be displayed
+                // in case of B when the incoming call at B is not answered and gets forwarded
+                // to C
+                if (mNotificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2 &&
+                        mNotificationCode ==
+                        SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED) {
+                    resourceId = R.string.callUnanswered_forwarded;
+                }
                 break;
+            }
+
+            case android.telephony.DisconnectCause.CALL_BARRED:{
+                // When call is disconnected with this code then it can either be barring from
+                // MO side or MT side.
+                // In MT case, if network sends SVC Notification then this dialog will be
+                // displayed when A is calling B & incoming is barred on B.
+                if (mNotificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1 &&
+                        mNotificationCode ==
+                        SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED) {
+                    resourceId = R.string.callFailed_incoming_cb_enabled;
+                } else {
+                    resourceId = R.string.callFailed_cb_enabled;
+                }
+                break;
+            }
 
             case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
                 resourceId = R.string.callFailed_cdma_activation;
@@ -654,7 +735,7 @@
                 // failure in the telephony layer.
                 // TODO: Need UI spec for this failure case; for now just
                 // show a generic error.
-                resourceId = R.string.incall_error_call_failed;
+                resourceId = R.string.incall_error_outgoing_call_failed;
                 break;
 
             case android.telephony.DisconnectCause.POWER_OFF:
@@ -729,6 +810,201 @@
 
             case android.telephony.DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED:
                 resourceId = R.string.callFailed_maximum_reached;
+            // Supplementary for Clear Code
+            case android.telephony.DisconnectCause.BUSY:
+                resourceId = R.string.callFailed_userBusy;
+                break;
+
+            case android.telephony.DisconnectCause.NO_CIRCUIT_AVAIL:
+                resourceId = R.string.callFailed_no_circuit_avail;
+                break;
+
+            case android.telephony.DisconnectCause.TEMPORARY_FAILURE:
+                resourceId = R.string.callFailed_tempopary_failure;
+                break;
+
+            case android.telephony.DisconnectCause.SWITCHING_EQUIPMENT_CONGESTION:
+                resourceId = R.string.callFailed_switching_equipment_congestion;
+                break;
+
+            case android.telephony.DisconnectCause.REQUESTED_CIRCUIT_OR_CHANNEL_NOT_AVAILABLE:
+                resourceId = R.string.callFailed_requested_circuit_or_channel_not_available;
+                break;
+
+            case android.telephony.DisconnectCause.QOS_UNAVAILABLE:
+                resourceId = R.string.callFailed_QOS_unavailable;
+                break;
+
+            case android.telephony.DisconnectCause.BEARER_CAPABILITY_UNAVAILABLE:
+                resourceId = R.string.callFailed_bearer_capability_unavailable;
+                break;
+
+            case android.telephony.DisconnectCause.LIMIT_EXCEEDED:
+                resourceId = R.string.callFailed_limitExceeded;
+
+            case android.telephony.DisconnectCause.INVALID_NUMBER:
+                resourceId = R.string.callFailed_invalid_number_format;
+                break;
+
+            case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
+                resourceId = R.string.callFailed_unobtainable_number;
+                break;
+
+            case android.telephony.DisconnectCause.NO_ROUTE_TO_DESTINATION:
+                resourceId = R.string.callFailed_no_route_to_destination;
+                break;
+
+            case android.telephony.DisconnectCause.CALL_FAIL_NO_USER_RESPONDING:
+                resourceId = R.string.callFailed_no_user_responding;
+                break;
+
+            case android.telephony.DisconnectCause.CALL_FAIL_NO_ANSWER_FROM_USER:
+                resourceId = R.string.callFailed_no_answer_from_user;
+                break;
+
+            case android.telephony.DisconnectCause.CALL_FAIL_DESTINATION_OUT_OF_ORDER:
+                resourceId = R.string.callFailed_destination_out_of_order;
+                break;
+
+            case android.telephony.DisconnectCause.BEARER_CAPABILITY_NOT_AUTHORIZED:
+                resourceId = R.string.callFailed_bearer_capability_not_authorized;
+                break;
+
+            case android.telephony.DisconnectCause.CHANNEL_UNACCEPTABLE:
+                resourceId = R.string.callFailed_channel_unacceptable;
+                break;
+
+            case android.telephony.DisconnectCause.OPERATOR_DETERMINED_BARRING:
+                resourceId = R.string.callFailed_operator_determined_barring;
+                break;
+
+            case android.telephony.DisconnectCause.CALL_REJECTED:
+                resourceId = R.string.callFailed_call_rejected;
+                break;
+
+            case android.telephony.DisconnectCause.NUMBER_CHANGED:
+                resourceId = R.string.callFailed_number_changed;
+                break;
+
+            case android.telephony.DisconnectCause.PREEMPTION:
+                resourceId = R.string.callFailed_preemption;
+                break;
+
+            case android.telephony.DisconnectCause.FACILITY_REJECTED:
+                resourceId = R.string.callFailed_facility_rejected;
+                break;
+
+            case android.telephony.DisconnectCause.RESP_TO_STATUS_ENQUIRY:
+                resourceId = R.string.callFailed_resp_to_status_enquiry;
+                break;
+
+            case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED:
+                resourceId = R.string.callFailed_normal_unspecified;
+                break;
+
+            case android.telephony.DisconnectCause.NETWORK_OUT_OF_ORDER:
+                resourceId = R.string.callFailed_network_out_of_order;
+                break;
+
+            case android.telephony.DisconnectCause.ACCESS_INFORMATION_DISCARDED:
+                resourceId = R.string.callFailed_access_information_discarded;
+                break;
+
+            case android.telephony.DisconnectCause.RESOURCES_UNAVAILABLE_OR_UNSPECIFIED:
+                resourceId = R.string.callFailed_resources_unavailable_or_unspecified;
+                break;
+
+            case android.telephony.DisconnectCause.REQUESTED_FACILITY_NOT_SUBSCRIBED:
+                resourceId = R.string.callFailed_requested_facility_not_subscribed;
+                break;
+
+            case android.telephony.DisconnectCause.INCOMING_CALLS_BARRED_WITHIN_CUG:
+                resourceId = R.string.callFailed_incoming_calls_barred_within_CUG;
+                break;
+
+            case android.telephony.DisconnectCause.SERVICE_OPTION_NOT_AVAILABLE:
+                resourceId = R.string.callFailed_service_option_not_available;
+                break;
+
+            case android.telephony.DisconnectCause.BEARER_SERVICE_NOT_IMPLEMENTED:
+                resourceId = R.string.callFailed_bearer_service_not_implemented;
+                break;
+
+            case android.telephony.DisconnectCause.REQUESTED_FACILITY_NOT_IMPLEMENTED:
+                resourceId = R.string.callFailed_requested_facility_not_implemented;
+                break;
+
+            case android.telephony.DisconnectCause.ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE:
+                resourceId = R.string.callFailed_only_digital_information_bearer_available;
+                break;
+
+            case android.telephony.DisconnectCause.SERVICE_OR_OPTION_NOT_IMPLEMENTED:
+                resourceId = R.string.callFailed_service_or_option_not_implemented;
+                break;
+
+            case android.telephony.DisconnectCause.INVALID_TRANSACTION_IDENTIFIER:
+                resourceId = R.string.callFailed_invalid_transaction_identifier;
+                break;
+
+            case android.telephony.DisconnectCause.USER_NOT_MEMBER_OF_CUG:
+                resourceId = R.string.callFailed_user_not_member_of_CUG;
+                break;
+
+            case android.telephony.DisconnectCause.INCOMPATIBLE_DESTINATION:
+                resourceId = R.string.callFailed_incompatible_destination;
+                break;
+
+            case android.telephony.DisconnectCause.INVALID_TRANSIT_NW_SELECTION:
+                resourceId = R.string.callFailed_invalid_transit_NW_selection;
+                break;
+
+            case android.telephony.DisconnectCause.SEMANTICALLY_INCORRECT_MESSAGE:
+                resourceId = R.string.callFailed_semantically_incorrect_message;
+                break;
+
+            case android.telephony.DisconnectCause.INVALID_MANDATORY_INFORMATION:
+                resourceId = R.string.callFailed_invalid_mandatory_information;
+                break;
+
+            case android.telephony.DisconnectCause.MESSAGE_TYPE_NON_IMPLEMENTED:
+                resourceId = R.string.callFailed_message_type_non_implemented;
+                break;
+
+            case android.telephony.DisconnectCause.MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE:
+                resourceId = R.string.callFailed_message_type_not_compatible_with_protocol_state;
+                break;
+
+            case android.telephony.DisconnectCause.INFORMATION_ELEMENT_NON_EXISTENT:
+                resourceId = R.string.callFailed_information_element_non_existent;
+                break;
+
+            case android.telephony.DisconnectCause.CONDITIONAL_IE_ERROR:
+                resourceId = R.string.callFailed_conditional_IE_error;
+                break;
+
+            case android.telephony.DisconnectCause.MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE:
+                resourceId = R.string.callFailed_message_not_compatible_with_protocol_state;
+                break;
+
+            case android.telephony.DisconnectCause.RECOVERY_ON_TIMER_EXPIRED:
+                resourceId = R.string.callFailed_recovery_on_timer_expired;
+                break;
+
+            case android.telephony.DisconnectCause.PROTOCOL_ERROR_UNSPECIFIED:
+                resourceId = R.string.callFailed_protocol_error_unspecified;
+                break;
+
+            case android.telephony.DisconnectCause.INTERWORKING_UNSPECIFIED:
+                resourceId = R.string.callFailed_interworking_unspecified;
+                break;
+
+            case android.telephony.DisconnectCause.NORMAL:
+                resourceId = R.string.callFailed_normal;
+                break;
+
+            case android.telephony.DisconnectCause.NON_SELECTED_USER_CLEARING:
+                resourceId = R.string.callFailed_non_selected_user_clearing;
+                break;
 
             case android.telephony.DisconnectCause.OUTGOING_CANCELED:
                 // We don't want to show any dialog for the canceled case since the call was
@@ -784,6 +1060,9 @@
             case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
                 resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
                 break;
+            case android.telephony.DisconnectCause.CONCURRENT_CALLS_NOT_POSSIBLE:
+                resourceId = R.string.callFailed_concurrent_calls_not_possible;
+                break;
             default:
                 break;
         }
@@ -869,6 +1148,13 @@
             case android.telephony.DisconnectCause.OUT_OF_SERVICE:
                 return ToneGenerator.TONE_CDMA_CALLDROP_LITE;
 
+            case android.telephony.DisconnectCause.NO_ROUTE_TO_DESTINATION:
+            case android.telephony.DisconnectCause.OPERATOR_DETERMINED_BARRING:
+            case android.telephony.DisconnectCause.CALL_FAIL_NO_USER_RESPONDING:
+            case android.telephony.DisconnectCause.NUMBER_CHANGED:
+            case android.telephony.DisconnectCause.CALL_FAIL_DESTINATION_OUT_OF_ORDER:
+            case android.telephony.DisconnectCause.BEARER_CAPABILITY_NOT_AUTHORIZED:
+            case android.telephony.DisconnectCause.USER_NOT_MEMBER_OF_CUG:
             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
                 return ToneGenerator.TONE_SUP_ERROR;
 
diff --git a/src/com/android/services/telephony/HoldAndAnswerHandler.java b/src/com/android/services/telephony/HoldAndAnswerHandler.java
new file mode 100644
index 0000000..9463397
--- /dev/null
+++ b/src/com/android/services/telephony/HoldAndAnswerHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2021, 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.services.telephony;
+
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telecom.StatusHints;
+import android.telecom.VideoProfile;
+
+import com.android.ims.internal.ConferenceParticipant;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/* Handles answering an incoming call when there is an ACTIVE call on the other sub */
+public class HoldAndAnswerHandler extends HoldHandlerBase {
+    private TelephonyConnection mConnToAnswer = null;
+    int mVideoState;
+
+    public HoldAndAnswerHandler(TelephonyConnection connToHold, TelephonyConnection connToAnswer,
+                                int videoState) {
+        mConnToAnswer = connToAnswer;
+        mConnToHold = connToHold;
+        mVideoState = videoState;
+        mConnToHold.addTelephonyConnectionListener(this);
+    }
+
+    // Holds ACTIVE call
+    @Override
+    public void accept() {
+        Log.i(this, "hold " + mConnToHold.getTelecomCallId() + " to answer " +
+                mConnToAnswer.getTelecomCallId());
+        mConnToHold.onHold();
+    }
+
+    private void cleanup() {
+        mConnToHold.removeTelephonyConnectionListener(this);
+        mConnToHold = null;
+        mConnToAnswer = null;
+    }
+
+    private void onHoldFail() {
+        cleanup();
+        notifyOnCompleted(false);
+    }
+
+    private void onHoldSuccess() {
+        // Make sure waiting call still exists
+        if (mConnToAnswer != null && mConnToAnswer.getState() == Connection.STATE_RINGING) {
+            if (mVideoState == VideoProfile.STATE_AUDIO_ONLY) {
+                mConnToAnswer.onAnswer();
+            } else {
+                mConnToAnswer.onAnswer(mVideoState);
+            }
+        }
+        cleanup();
+        notifyOnCompleted(true);
+    }
+
+    @Override
+    public void onStateChanged(android.telecom.Connection c, int state) {
+        Log.d(this, "onStateChanged state = " + state);
+        // We are ready to answer if the connection pending HOLD is successfully held or
+        // got disconnected
+        if (c == mConnToHold &&
+                (state == Connection.STATE_HOLDING || state == Connection.STATE_DISCONNECTED)) {
+            onHoldSuccess();
+        }
+    }
+
+    @Override
+    public void onConnectionEvent(Connection c, String event, Bundle extras) {
+        Log.d(this, "onConnectionEvent event = " + event);
+        if (c == mConnToHold && event ==
+                android.telecom.Connection.EVENT_CALL_HOLD_FAILED) {
+            //If hold fails, incoming call will remain ringing
+            onHoldFail();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/services/telephony/HoldAndDialHandler.java b/src/com/android/services/telephony/HoldAndDialHandler.java
new file mode 100644
index 0000000..222aae6
--- /dev/null
+++ b/src/com/android/services/telephony/HoldAndDialHandler.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2021, 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.services.telephony;
+
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telephony.DisconnectCause;
+import android.telecom.StatusHints;
+
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhone.ImsDialArgs.DeferDial;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.ims.internal.ConferenceParticipant;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/* Handles dialing an outgoing call when there is an ACTIVE call on the other sub */
+public class HoldAndDialHandler extends HoldHandlerBase {
+    private TelephonyConnection mConnToDial = null;
+    private TelephonyConnectionService mConnectionService;
+    private Phone mPhone;
+    private int mVideoState;
+    private Bundle mExtras;
+    String[] mParticipants;
+    boolean mIsConference = false;
+    private com.android.internal.telephony.Connection mOriginalConnection = null;
+
+    public HoldAndDialHandler(TelephonyConnection connToHold, TelephonyConnection connToDial,
+                TelephonyConnectionService connectionService, Phone phone, int videoState,
+                Bundle extras) {
+        this(connToHold, connToDial, connectionService, phone, videoState);
+        mExtras = extras;
+    }
+
+    public HoldAndDialHandler(TelephonyConnection connToHold, TelephonyConnection connToDial,
+                TelephonyConnectionService connectionService, Phone phone, int videoState,
+                String[] participants) {
+        this(connToHold, connToDial, connectionService, phone, videoState);
+        mParticipants = participants;
+        mIsConference = true;
+    }
+
+    private HoldAndDialHandler(TelephonyConnection connToHold, TelephonyConnection connToDial,
+                TelephonyConnectionService connectionService, Phone phone, int videoState) {
+        mConnToDial = connToDial;
+        mConnToHold = connToHold;
+        mConnectionService = connectionService;
+        mPhone = phone;
+        mVideoState = videoState;
+        mConnToHold.addTelephonyConnectionListener(this);
+    }
+
+    // Holds ACTIVE call and invokes dial to get a pending connection
+    @Override
+    public com.android.internal.telephony.Connection dial() throws CallStateException {
+        // DeferDial is enabled which means that ImsPhoneCallTracker will only return a pending
+        // connection without placing the call. Call will be placed after hold completes and
+        // DeferDial is disabled
+        try {
+            mOriginalConnection = (mIsConference ? startConferenceInternal(DeferDial.ENABLE) :
+                    dialInternal(DeferDial.ENABLE));
+            if (mOriginalConnection == null) {
+                // Tracker was able to process MMI code. Do not hold active call
+                onCompleted(true);
+            } else {
+                holdInternal();
+            }
+            return mOriginalConnection;
+        } catch (CallStateException e) {
+            if (e.getError() == CallStateException.ERROR_HOLD_ACTIVE_CALL_ON_OTHER_SUB) {
+                // This error means that the tracker can process the MMI code only after the
+                // active call on the other sub is held
+                holdInternal();
+                return null;
+            }
+            onCompleted(false);
+            throw e;
+        }
+    }
+
+    private void holdInternal() {
+        if (mConnToHold.getState() == Connection.STATE_ACTIVE) {
+            mConnToHold.onHold();
+        }
+    }
+
+    private void onCompleted(boolean success) {
+        cleanup();
+        notifyOnCompleted(success);
+    }
+
+    private void cleanup() {
+        mConnToHold.removeTelephonyConnectionListener(this);
+        mConnToHold = null;
+        mConnToDial = null;
+    }
+
+    private com.android.internal.telephony.Connection dialInternal(DeferDial deferDial)
+            throws CallStateException {
+        Log.d(this, "HoldAndDialHandler dialInternal deferDial = " + deferDial);
+        String number = (mConnToDial.getAddress() != null)
+                ? mConnToDial.getAddress().getSchemeSpecificPart()
+                : "";
+        com.android.internal.telephony.Connection originalConnection =
+                mPhone.dial(number, new ImsPhone.ImsDialArgs.Builder()
+                        .setVideoState(mVideoState)
+                        .setIntentExtras(mExtras)
+                        .setRttTextStream(mConnToDial.getRttTextStream())
+                        .setDeferDial(deferDial)
+                        .build());
+        if (originalConnection == null) {
+            Log.d(this, "HoldAndDialHandler originalconnection = null. MMI use case");
+        }
+        return originalConnection;
+    }
+
+    private com.android.internal.telephony.Connection startConferenceInternal(DeferDial deferDial)
+            throws CallStateException {
+        Log.d(this, "HoldAndDialHandler startConferenceInternal deferDial = " + deferDial);
+        com.android.internal.telephony.Connection originalConnection = mPhone.startConference(
+                mParticipants, new ImsPhone.ImsDialArgs.Builder()
+                        .setVideoState(mVideoState)
+                        .setRttTextStream(mConnToDial.getRttTextStream())
+                        .setDeferDial(deferDial)
+                        .build());
+        return originalConnection;
+    }
+
+    private void handleDialException(CallStateException e, String msg) {
+        Log.e(this, e, msg + " exception: " + e);
+        mConnectionService.handleCallStateException(e, mConnToDial, mPhone);
+        onCompleted(false);
+    }
+
+    @Override
+    public void onStateChanged(android.telecom.Connection c, int state) {
+        Log.d(this, "onStateChanged state = " + state);
+        if (c != mConnToHold || !(state == Connection.STATE_HOLDING ||
+                state == Connection.STATE_DISCONNECTED)) {
+            return;
+        }
+        if (mOriginalConnection != null &&
+                mConnToDial.getState() == Connection.STATE_DISCONNECTED) {
+            // Pending MO call was hung up
+            onCompleted(false);
+            return;
+        }
+        try {
+            com.android.internal.telephony.Connection originalConnection =
+                    (mIsConference ? startConferenceInternal(DeferDial.DISABLE) :
+                            dialInternal(DeferDial.DISABLE));
+            if (mOriginalConnection != originalConnection) {
+                // Safe check. This should not happen
+                Log.e(this, null,
+                        "original connection is different " + mOriginalConnection
+                                + " " + originalConnection);
+                mConnToDial.setTelephonyConnectionDisconnected(
+                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                                android.telephony.DisconnectCause.OUTGOING_FAILURE,
+                                "original connection is different", mPhone.getPhoneId()));
+                // This clears mOriginalConnection as that is the on associated with
+                // TelephonyConnection. originalConnection will not be cleared
+                mConnToDial.close();
+                onCompleted(false);
+                return;
+            }
+            onCompleted(true);
+        } catch (CallStateException e) {
+            handleDialException(e, "conference failed");
+        }
+    }
+
+    @Override
+    public void onConnectionEvent(Connection c, String event, Bundle extras) {
+        if (c != mConnToHold) return;
+        if (event == android.telecom.Connection.EVENT_CALL_HOLD_FAILED) {
+            // Disconnect dialing call
+            ImsPhoneConnection conn = (ImsPhoneConnection)mOriginalConnection;
+            //set cause similar to same sub hold fail case
+            conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
+            mConnToDial.onDisconnect();
+            onCompleted(false);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/services/telephony/HoldAndSwapHandler.java b/src/com/android/services/telephony/HoldAndSwapHandler.java
new file mode 100644
index 0000000..37533e1
--- /dev/null
+++ b/src/com/android/services/telephony/HoldAndSwapHandler.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2021, 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.services.telephony;
+
+import android.os.Bundle;
+import android.telecom.Connection;
+import android.telecom.StatusHints;
+
+import com.android.ims.internal.ConferenceParticipant;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/* Handles DSDA across sub call swap use case */
+public class HoldAndSwapHandler extends HoldHandlerBase {
+    private TelephonyConnection mConnToResume = null;
+
+    public HoldAndSwapHandler(TelephonyConnection connToHold, TelephonyConnection connToResume) {
+        mConnToResume = connToResume;
+        mConnToHold = connToHold;
+        mConnToHold.addTelephonyConnectionListener(this);
+    }
+
+    // Holds active call
+    @Override
+    public void accept() {
+        Log.i(this, "hold " + mConnToHold.getTelecomCallId() + " resume " +
+                mConnToResume.getTelecomCallId());
+        mConnToHold.onHold();
+    }
+
+    private void cleanup() {
+        if (mConnToHold != null) {
+            mConnToHold.removeTelephonyConnectionListener(this);
+            mConnToHold = null;
+        }
+        if (mConnToResume != null) {
+            mConnToResume.removeTelephonyConnectionListener(this);
+            mConnToResume = null;
+        }
+    }
+
+    private void onFail() {
+        cleanup();
+        notifyOnCompleted(false);
+    }
+
+    private void onSuccess() {
+        cleanup();
+        notifyOnCompleted(true);
+    }
+
+    private void revertPreviousHold() {
+        // mConnToResume got terminated in the middle of a swap after the other call
+        // was held. So resume other call as swap failed
+        if (mConnToResume != null) {
+            mConnToResume.removeTelephonyConnectionListener(this);
+        }
+        if (mConnToHold == null) {
+            onFail();
+        } else if (mConnToHold.getState() == Connection.STATE_HOLDING) {
+            mConnToResume = mConnToHold;
+            mConnToHold = null;
+            mConnToResume.addTelephonyConnectionListener(this);
+            mConnToResume.onUnhold();
+        }
+    }
+
+    @Override
+    public void onStateChanged(android.telecom.Connection c, int state) {
+        if (c == null) return;
+        if (c.equals(mConnToHold)) {
+            Log.d(this,"onStateChanged callToHold state = " + state);
+            if (state == Connection.STATE_HOLDING || state == Connection.STATE_DISCONNECTED) {
+                mConnToHold.removeTelephonyConnectionListener(this);
+                if (mConnToResume.getState() == Connection.STATE_HOLDING) {
+                    mConnToResume.addTelephonyConnectionListener(this);
+                    mConnToResume.onUnhold();
+                }// Here we could possibly check for mConnToResume == DISCONNECTED and unhold
+                // mConnToHold but that is not in sync with ImsPhoneCallTracker handling
+            }
+        } else if (c.equals(mConnToResume)) {
+            Log.d(this,"onStateChanged callToResume state = " + state);
+            switch (state) {
+                case Connection.STATE_DISCONNECTED:
+                    revertPreviousHold();
+                    break;
+                case Connection.STATE_ACTIVE:
+                    onSuccess();
+                    break;
+                default:
+                    // Do nothing for other events
+            }
+        }
+    }
+
+    @Override
+    public void onConnectionEvent(Connection c, String event, Bundle extras) {
+        switch (event) {
+            case android.telecom.Connection.EVENT_CALL_HOLD_FAILED:
+                onFail();
+                break;
+            case android.telecom.Connection.EVENT_CALL_RESUME_FAILED://Add new event
+                revertPreviousHold();
+                break;
+            default:
+                // Do nothing for other events
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/services/telephony/HoldHandlerBase.java b/src/com/android/services/telephony/HoldHandlerBase.java
new file mode 100644
index 0000000..e38dcd7
--- /dev/null
+++ b/src/com/android/services/telephony/HoldHandlerBase.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2020-2021, 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.services.telephony;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.Conference;
+import android.telecom.Conferenceable;
+import android.telecom.Connection;
+import android.telecom.Connection.VideoProvider;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.StatusHints;
+
+import com.android.ims.internal.ConferenceParticipant;
+import com.android.internal.telephony.CallStateException;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/* Base class that handles across sub HOLD use cases for DSDA */
+public class HoldHandlerBase extends TelephonyConnection.TelephonyConnectionListener {
+    private List<Listener> mListeners = new CopyOnWriteArrayList<>();
+    protected TelephonyConnection mConnToHold = null;
+
+    public interface Listener {
+        // status true indicates operation succeeded, false indicates failure
+        void onCompleted(boolean status);
+    }
+
+    public void addListener(HoldHandlerBase.Listener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeListener(HoldHandlerBase.Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    protected void notifyOnCompleted(boolean status) {
+        for (HoldHandlerBase.Listener l : mListeners) {
+            l.onCompleted(status);
+        }
+    }
+
+    public void accept() {}
+    public com.android.internal.telephony.Connection dial() throws CallStateException {
+        return null;
+    }
+    public void onOriginalConnectionConfigured(TelephonyConnection c) {}
+    public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
+    public void onConferenceParticipantsChanged(Connection c,
+                                                List<ConferenceParticipant> participants) {}
+    public void onConferenceStarted() {}
+    public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+
+    public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {}
+    public void onConnectionEvent(Connection c, String event, Bundle extras) {}
+    public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {}
+    public void onExtrasChanged(Connection c, Bundle extras) {}
+    public void onExtrasRemoved(Connection c, List<String> keys) {}
+    public void onStateChanged(android.telecom.Connection c, int state) {}
+    public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
+    public void onDestroyed(Connection c) {}
+    public void onDisconnected(android.telecom.Connection c,
+                               android.telecom.DisconnectCause disconnectCause) {}
+    public void onVideoProviderChanged(android.telecom.Connection c,
+                                       Connection.VideoProvider videoProvider) {}
+    public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
+    public void onRingbackRequested(Connection c, boolean ringback) {}
+}
diff --git a/src/com/android/services/telephony/HoldTracker.java b/src/com/android/services/telephony/HoldTracker.java
index 5032b41..316227a 100644
--- a/src/com/android/services/telephony/HoldTracker.java
+++ b/src/com/android/services/telephony/HoldTracker.java
@@ -17,6 +17,7 @@
 package com.android.services.telephony;
 
 import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -79,7 +80,10 @@
         }
 
         Log.d(this, "topHoldableCount = " + topHoldableCount);
-        boolean isHoldable = topHoldableCount < 2;
+        final int maxHoldableCallCount = TelephonyManager.isConcurrentCallsPossible() ?
+                2 /* DSDA */ : 1 /* otherwise */;
+
+        boolean isHoldable = topHoldableCount <= maxHoldableCallCount;
         for (Holdable holdable : holdables) {
             holdable.setHoldable(holdable.isChildHoldable() ? false : isHoldable);
         }
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
old mode 100644
new mode 100755
index c62b4fa..b6b03ed
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -29,6 +29,9 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Pair;
 
@@ -90,6 +93,8 @@
             private int mMaximumConferenceSize = 5;
             private boolean mShouldLocalDisconnectEmptyConference = false;
             private boolean mIsHoldAllowed = false;
+            private boolean mIsMultiAnchorConferenceSupported = false;
+            private boolean mFilterOutConferenceHost = true;
 
             /**
              * Sets whether the maximum size of the conference is enforced.
@@ -135,13 +140,37 @@
             }
 
             /**
+             * Sets if the carrier associated with the conference supports multianchor.
+             * @param isMultiAnchorConferenceSupported {@code true} if the carrier associated with
+             * the conference supports multianchor.
+             * @return builder instance.
+             */
+            public Builder setIsMultiAnchorConferenceSupported(
+                    boolean isMultiAnchorConferenceSupported) {
+                mIsMultiAnchorConferenceSupported = isMultiAnchorConferenceSupported;
+                return this;
+            }
+
+            /**
+             * Sets whether the conference host should be filtered out.
+             * @param filterOutConferenceHost {@code true} if the conference host should be filtered
+             * out.
+             * @return builder instance.
+             */
+            public Builder setFilterOutConferenceHost(boolean filterOutConferenceHost) {
+                mFilterOutConferenceHost = filterOutConferenceHost;
+                return this;
+            }
+
+            /**
              * Build instance of {@link CarrierConfiguration}.
              * @return carrier config instance.
              */
             public ImsConference.CarrierConfiguration build() {
                 return new ImsConference.CarrierConfiguration(mIsMaximumConferenceSizeEnforced,
                         mMaximumConferenceSize, mShouldLocalDisconnectEmptyConference,
-                        mIsHoldAllowed);
+                        mIsHoldAllowed, mIsMultiAnchorConferenceSupported,
+                        mFilterOutConferenceHost);
             }
         }
 
@@ -153,13 +182,20 @@
 
         private boolean mIsHoldAllowed;
 
+        private boolean mIsMultiAnchorConferenceSupported;
+
+        private boolean mFilterOutConferenceHost;
+
         private CarrierConfiguration(boolean isMaximumConferenceSizeEnforced,
                 int maximumConferenceSize, boolean shouldLocalDisconnectEmptyConference,
-                boolean isHoldAllowed) {
+                boolean isHoldAllowed, boolean isMultiAnchorConferenceSupported,
+                boolean filterOutConferenceHost) {
             mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
             mMaximumConferenceSize = maximumConferenceSize;
             mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
             mIsHoldAllowed = isHoldAllowed;
+            mIsMultiAnchorConferenceSupported = isMultiAnchorConferenceSupported;
+            mFilterOutConferenceHost = filterOutConferenceHost;
         }
 
         /**
@@ -196,6 +232,23 @@
         public boolean isHoldAllowed() {
             return mIsHoldAllowed;
         }
+
+        /**
+         * @return {@code true} if the carrier associated with the conference supports multianchor
+         * conference, {@code false} otherwise.
+         */
+        public boolean isMultiAnchorConferenceSupported() {
+            return mIsMultiAnchorConferenceSupported;
+        }
+
+        /**
+         * Determines whether the conference host should be filtered out.
+         * @return {@code true} if the conference host should be filtered out, {@code false}
+         * otherwise.
+         */
+        public boolean shouldFilterOutConferenceHost() {
+            return mFilterOutConferenceHost;
+        }
     }
 
     /**
@@ -1014,7 +1067,9 @@
                     if (!mConferenceParticipantConnections.containsKey(userEntity)) {
                         // Some carriers will also include the conference host in the CEP.  We will
                         // filter that out here.
-                        if (!isParticipantHost(mConferenceHostAddress, participant.getHandle())) {
+                        if ((!isParticipantHost(mConferenceHostAddress, participant.getHandle())
+                               || !mCarrierConfig.shouldFilterOutConferenceHost())) {
+                            Log.i(this, "Create participant connection, participant = %s", participant);
                             createConferenceParticipantConnection(parent, participant);
                             newParticipants.add(participant);
                             newParticipantsAdded = true;
@@ -1238,7 +1293,8 @@
         // active call.
         ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
                 parent.getOriginalConnection(), participant,
-                !isConferenceHost() /* isRemotelyHosted */);
+                !isConferenceHost() /* isRemotelyHosted */,
+                isParticipantHost(mConferenceHostAddress, participant.getHandle()));
 
         if (participant.getConnectTime() == 0) {
             connection.setConnectTimeMillis(parent.getConnectTimeMillis());
@@ -1441,7 +1497,7 @@
                         c.getConnectionProperties() | Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
                 c.updateState();
                 // Copy the connect time from the conferenceHost
-                c.setConnectTimeMillis(mConferenceHost.getConnectTimeMillis());
+                c.setConnectTimeMillis(originalConnection.getConnectTime());
                 c.setConnectionStartElapsedRealtimeMillis(
                         mConferenceHost.getConnectionStartElapsedRealtimeMillis());
                 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c);
@@ -1523,8 +1579,19 @@
             Phone phone = mConferenceHost.getPhone();
             if (phone != null) {
                 Context context = phone.getContext();
+                String displaySubId = "";
+                if (TelephonyManager.getDefault().getActiveModemCount() > 1) {
+                    final int phoneId = mConferenceHost.getPhone().getPhoneId();
+                    SubscriptionInfo sub = SubscriptionManager.from(
+                            mConferenceHost.getPhone().getContext())
+                        .getActiveSubscriptionInfoForSimSlotIndex(phoneId);
+                    if (sub != null) {
+                        displaySubId = sub.getDisplayName().toString();
+                        displaySubId  = " " + displaySubId;
+                    }
+                }
                 StatusHints hints = new StatusHints(
-                        context.getString(R.string.status_hint_label_wifi_call),
+                        context.getString(R.string.status_hint_label_wifi_call) + displaySubId,
                         Icon.createWithResource(
                                 context, R.drawable.ic_signal_wifi_4_bar_24dp),
                         null /* extras */);
@@ -1625,6 +1692,14 @@
     }
 
     /**
+     * @return {@code true} if the carrier associated with the conference supports multianchor
+     * conference, {@code false} otherwise.
+     */
+    public boolean isMultiAnchorConferenceSupported() {
+        return mCarrierConfig.isMultiAnchorConferenceSupported();
+    }
+
+    /**
      * @return The number of participants in the conference.
      */
     public int getNumberOfParticipants() {
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 7cf9415..4631ab5 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -37,6 +37,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 /**
@@ -270,7 +271,8 @@
                 Log.d(this, "recalc - %s %s", conference.getState(), conference);
             }
 
-            if (!conference.isConferenceHost()) {
+            if (!conference.isConferenceHost() &&
+                    (!conference.isMultiAnchorConferenceSupported())) {
                 if (Log.VERBOSE) {
                     Log.v(this, "skipping conference (not hosted on this device): %s", conference);
                 }
@@ -300,10 +302,14 @@
 
         for (Conferenceable c : conferenceableSet) {
             if (c instanceof Connection) {
-                // Remove this connection from the Set and add all others
+                PhoneAccountHandle handle = getPhoneAccountHandle(c);
+                // Remove this connection from the Set and add ones with same PhoneAccountHandle
+                // and both connections are not in HELD state
                 List<Conferenceable> conferenceables = conferenceableSet
                         .stream()
-                        .filter(conferenceable -> c != conferenceable)
+                        .filter(conferenceable -> c != conferenceable &&
+                        Objects.equals(handle, getPhoneAccountHandle(conferenceable)) &&
+                        !(isHeld(c) && isHeld(conferenceable)))
                         .collect(Collectors.toList());
                 // TODO: Remove this once RemoteConnection#setConferenceableConnections is fixed.
                 // Add all conference participant connections as conferenceable with a standalone
@@ -317,6 +323,7 @@
 
                 ((Connection) c).setConferenceables(conferenceables);
             } else if (c instanceof ImsConference) {
+                PhoneAccountHandle handle = getPhoneAccountHandle(c);
                 ImsConference imsConference = (ImsConference) c;
 
                 // If the conference is full, don't allow anything to be conferenced with it.
@@ -328,7 +335,9 @@
                 // to another conference.
                 List<Connection> connections = conferenceableSet
                         .stream()
-                        .filter(conferenceable -> conferenceable instanceof Connection)
+                        .filter(conferenceable -> conferenceable instanceof Connection &&
+                        Objects.equals(handle, getPhoneAccountHandle(conferenceable)) &&
+                        !(isHeld(c) && isHeld(conferenceable)))
                         .map(conferenceable -> (Connection) conferenceable)
                         .collect(Collectors.toList());
                 // Conference equivalent to setConferenceables that only accepts Connections
@@ -338,6 +347,32 @@
     }
 
     /**
+     * Retrieves the PhoneAccountHandle for Conferenceable object
+     */
+    private static PhoneAccountHandle getPhoneAccountHandle(Conferenceable c) {
+        if (c instanceof Connection) {
+            return ((Connection) c).getPhoneAccountHandle();
+        } else if (c instanceof ImsConference) {
+            return ((ImsConference) c).getPhoneAccountHandle();
+        }
+        return null;
+    }
+
+    /*
+     * Checks if the Connection is in HELD state
+     */
+    private static boolean isHeld(Conferenceable conn) {
+        return conn instanceof Connection ?
+                isHoldingState(((Connection) conn).getState()) :
+                conn instanceof ImsConference ?
+                isHoldingState(((ImsConference) conn).getState()) : false;
+    }
+
+    private static boolean isHoldingState(int state) {
+        return state == Connection.STATE_HOLDING;
+    }
+
+    /**
      * Determines if a connection is a member of a conference hosted on another device.
      *
      * @param connection The connection.
@@ -474,12 +509,18 @@
                     CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
             boolean shouldLocalDisconnectOnEmptyConference = bundle.getBoolean(
                     CarrierConfigManager.KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL);
+            boolean isMultiAnchorConferenceSupported = bundle.getBoolean(
+                    CarrierConfigManager.KEY_CARRIER_SUPPORTS_MULTIANCHOR_CONFERENCE);
+            boolean filterOutConferenceHost = !cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean("disable_filter_out_conference_host");
 
             config.setIsMaximumConferenceSizeEnforced(isMaximumConferenceSizeEnforced)
                     .setMaximumConferenceSize(maximumConferenceSize)
                     .setIsHoldAllowed(isHoldAllowed)
                     .setShouldLocalDisconnectEmptyConference(
-                            shouldLocalDisconnectOnEmptyConference);
+                            shouldLocalDisconnectOnEmptyConference)
+                    .setIsMultiAnchorConferenceSupported(isMultiAnchorConferenceSupported)
+                    .setFilterOutConferenceHost(filterOutConferenceHost);
         }
         return config.build();
     }
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 93e1e3c..21c2394 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -314,6 +314,7 @@
     }
 
     private void registerForRadioOff() {
+        unregisterForServiceStateChanged();
         mPhone.mCi.registerForOffOrNotAvailable(mHandler, MSG_RADIO_OFF_OR_NOT_AVAILABLE, null);
     }
 
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 1d749f4..cdf1226 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -58,10 +58,13 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 
+import com.android.ims.FeatureConnector;
 import com.android.ims.ImsManager;
 import com.android.internal.telephony.ExponentialBackoff;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
@@ -69,6 +72,7 @@
 import com.android.telephony.Rlog;
 
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Optional;
@@ -116,10 +120,18 @@
 
     private Handler mHandler;
 
+    // Flag which decides whether SIM should power down due to APM,
+    private static final String APM_SIM_NOT_PWDN_PROPERTY = "persist.vendor.radio.apm_sim_not_pwdn";
+
+    private enum Count {
+        ZERO,
+        ONE,
+        TWO
+    }
+
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
         private PhoneAccount mAccount;
-        private final PstnIncomingCallNotifier mIncomingCallNotifier;
         private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
         private boolean mIsEmergency;
         private boolean mIsRttCapable;
@@ -141,6 +153,7 @@
         private boolean mIsManageImsConferenceCallSupported;
         private boolean mIsUsingSimCallManager;
         private boolean mIsShowPreciseFailedCause;
+        private final FeatureConnector<ImsManager> mImsManagerConnector;
 
         AccountEntry(Phone phone, boolean isEmergency, boolean isTest) {
             mPhone = phone;
@@ -150,9 +163,20 @@
             mAccount = registerPstnPhoneAccount(isEmergency, isTest);
             Log.i(this, "Registered phoneAccount: %s with handle: %s",
                     mAccount, mAccount.getAccountHandle());
-            mIncomingCallNotifier = new PstnIncomingCallNotifier((Phone) mPhone);
             mPhoneCapabilitiesNotifier = new PstnPhoneCapabilitiesNotifier((Phone) mPhone,
                     this);
+            mImsManagerConnector = ImsManager.getConnector(
+                mPhone.getContext(), mPhone.getPhoneId(), "TelecomAccountRegistry",
+                new FeatureConnector.Listener<ImsManager>() {
+                    @Override
+                    public void connectionReady(ImsManager manager){
+                        registerImsRegistrationCallback();
+                    }
+                    @Override
+                    public void connectionUnavailable(int reason) {
+                        unregisterImsRegistrationCallback();
+                    }
+                }, mPhone.getContext().getMainExecutor());
 
             if (mIsTestAccount || isEmergency) {
                 // For test and emergency entries, there is no sub ID that can be assigned, so do
@@ -198,21 +222,19 @@
                     updateAdhocConfCapability(false);
                 }
             };
-            registerImsRegistrationCallback();
+            mImsManagerConnector.connect();
         }
 
         void teardown() {
-            mIncomingCallNotifier.teardown();
             mPhoneCapabilitiesNotifier.teardown();
-            if (mMmTelManager != null) {
-                if (mMmtelCapabilityCallback != null) {
-                    mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
-                }
-
-                if (mImsRegistrationCallback != null) {
-                    mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
-                }
+            if (mMmTelManager != null && mMmtelCapabilityCallback != null) {
+                mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
             }
+            mImsManagerConnector.disconnect();
+        }
+
+        private boolean isMatched(SubscriptionInfo subInfo) {
+            return mPhone.getSubId() == subInfo.getSubscriptionId();
         }
 
         private void registerMmTelCapabilityCallback() {
@@ -244,6 +266,7 @@
             try {
                 mMmTelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
                         mImsRegistrationCallback);
+                Log.v(this, "registerImsRegistrationCallback: registration success");
             } catch (ImsException e) {
                 Log.w(this, "registerImsRegistrationCallback: registration failed, no ImsService"
                         + " available. Exception: " + e.getMessage());
@@ -255,6 +278,13 @@
             }
         }
 
+        private void unregisterImsRegistrationCallback() {
+            if (mMmTelManager == null || mImsRegistrationCallback == null) {
+                return;
+            }
+            mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+        }
+
         /**
          * Trigger re-registration of this account.
          */
@@ -788,7 +818,6 @@
          */
         @Override
         public void onVideoCapabilitiesChanged(boolean isVideoCapable) {
-            mIsVideoCapable = isVideoCapable;
             synchronized (mAccountsLock) {
                 if (!mAccounts.contains(this)) {
                     // Account has already been torn down, don't try to register it again.
@@ -797,7 +826,10 @@
                     // time we get here, the original phone account could have been torn down.
                     return;
                 }
-                mAccount = registerPstnPhoneAccount(mIsEmergency, mIsTestAccount);
+                if (isVideoCapable != mIsVideoCapable) {
+                    mIsVideoCapable = isVideoCapable;
+                    mAccount = registerPstnPhoneAccount(mIsEmergency, mIsTestAccount);
+                }
             }
         }
 
@@ -906,6 +938,7 @@
 
             boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr
                     .isRttEnabled(mPhone.getSubId());
+            boolean isUserRttSettingOn = isUserRttSettingOn();
 
             boolean isRoaming = mTelephonyManager.isNetworkRoaming(mPhone.getSubId());
             boolean isOnWfc = mPhone.getImsRegistrationTech()
@@ -918,11 +951,21 @@
             Log.i(this, "isRttCurrentlySupported -- regular acct,"
                     + " hasVoiceAvailability: " + hasVoiceAvailability + "\n"
                     + " isRttSupported: " + isRttSupported + "\n"
+                    + " isUserRttSettingOn: " + isUserRttSettingOn + "\n"
                     + " alwaysAllowWhileRoaming: " + alwaysAllowWhileRoaming + "\n"
                     + " isRoaming: " + isRoaming + "\n"
                     + " isOnWfc: " + isOnWfc + "\n");
 
-            return hasVoiceAvailability && isRttSupported && !shouldDisableBecauseRoamingOffWfc;
+            return hasVoiceAvailability && isRttSupported && !shouldDisableBecauseRoamingOffWfc
+                    && isUserRttSettingOn;
+        }
+
+        private boolean isUserRttSettingOn() {
+            int rttSetting = Settings.Secure.getInt(
+                    mContext.getContentResolver(),
+                    Settings.Secure.RTT_CALLING_MODE
+                    + convertRttPhoneId(mPhone.getPhoneId()) , 0);
+            return rttSetting != 0;
         }
 
         /**
@@ -1016,6 +1059,10 @@
                             ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM,
                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
         }
+
+        private boolean isSubAccount() {
+            return !(mIsTestAccount || mIsEmergency);
+        }
     }
 
     private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
@@ -1028,10 +1075,37 @@
             }
             mSubscriptionListenerState = LISTENER_STATE_REGISTERED;
 
-            // Any time the SubscriptionInfo changes rerun the setup
-            Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - update accounts");
-            tearDownAccounts();
-            setupAccounts();
+            List<SubscriptionInfo> subList =
+                    mSubscriptionManager.getActiveSubscriptionInfoList();
+
+            boolean isTearingDownNeeded = subList == null;
+            if (!isTearingDownNeeded) {
+                int subAccountCnt = subList.size();
+                synchronized (mAccountsLock) {
+                    subAccountCnt = mAccounts.stream()
+                            .filter(entry -> entry.isSubAccount())
+                            .collect(java.util.stream.Collectors.toList()).size();
+                }
+                isTearingDownNeeded |= subAccountCnt != subList.size();
+                if (!isTearingDownNeeded) {
+                    // Check if it is needed to be torn down for SUB refresh.
+                    for (SubscriptionInfo subInfo : subList) {
+                        isTearingDownNeeded |= !isAccountMatched(subInfo);
+                    }
+                }
+            }
+            if (isTearingDownNeeded) {
+                Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - update accounts");
+                tearDownAccounts();
+                setupAccounts();
+            } else {
+                Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - reregister accounts");
+                synchronized (mAccountsLock) {
+                    for (AccountEntry entry : mAccounts) {
+                        entry.reRegisterPstnPhoneAccount();
+                    }
+                }
+            }
         }
 
         @Override
@@ -1150,6 +1224,7 @@
             }
         }
     };
+    private PstnIncomingCallNotifier[] mPstnIncomingCallNotifiers;
 
     TelecomAccountRegistry(Context context) {
         mContext = context;
@@ -1165,6 +1240,8 @@
                 2, /* multiplier */
                 mHandlerThread.getLooper(),
                 mRegisterOnSubscriptionsChangedListenerRunnable);
+        mPstnIncomingCallNotifiers =
+                new PstnIncomingCallNotifier[mTelephonyManager.getActiveModemCount()];
     }
 
     /**
@@ -1403,6 +1480,10 @@
         mContext.registerReceiver(mLocaleChangeReceiver, localeChangeFilter);
 
         registerContentObservers();
+        // register for Pstn incoming call notifiers
+        for (int i = 0; i < mTelephonyManager.getActiveModemCount(); i++) {
+            mPstnIncomingCallNotifiers[i] = new PstnIncomingCallNotifier(PhoneFactory.getPhone(i));
+        }
     }
 
     private void registerContentObservers() {
@@ -1418,9 +1499,13 @@
             }
         };
 
-        Uri rttSettingUri = Settings.Secure.getUriFor(Settings.Secure.RTT_CALLING_MODE);
-        mContext.getContentResolver().registerContentObserver(
-                rttSettingUri, false, rttUiSettingObserver);
+        // register for all settings
+        for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
+            Uri rttSettingUri = Settings.Secure.getUriFor(
+                    Settings.Secure.RTT_CALLING_MODE + convertRttPhoneId(i));
+            mContext.getContentResolver().registerContentObserver(
+                    rttSettingUri, false, rttUiSettingObserver);
+        }
 
         // Listen to the changes to the user's Contacts Discovery Setting.
         ContentObserver contactDiscoveryObserver = new ContentObserver(mHandler) {
@@ -1439,6 +1524,10 @@
                 contactDiscUri, true /*notifyForDescendants*/, contactDiscoveryObserver);
     }
 
+    private static String convertRttPhoneId(int phoneId) {
+        return phoneId != 0 ? Integer.toString(phoneId) : "";
+    }
+
     /**
      * Determines if the list of {@link AccountEntry}(s) contains an {@link AccountEntry} with a
      * specified {@link PhoneAccountHandle}.
@@ -1500,18 +1589,36 @@
 
         final boolean phoneAccountsEnabled = mContext.getResources().getBoolean(
                 R.bool.config_pstn_phone_accounts_enabled);
+        int activeCount = 0;
+        int activeSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
         synchronized (mAccountsLock) {
             try {
                 if (phoneAccountsEnabled) {
+
                     for (Phone phone : phones) {
                         int subscriptionId = phone.getSubId();
-                        Log.i(this, "setupAccounts: Phone with subscription id %d", subscriptionId);
+                        int slotId = phone.getPhoneId();
+                        boolean isAccountAdded = false;
+
+                        Log.i(this, "setupAccounts: Phone with subscription id: " + subscriptionId +
+                                " slotId: " + slotId);
                         // setupAccounts can be called multiple times during service changes.
                         // Don't add an account if the Icc has not been set yet.
                         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)
                                 || phone.getFullIccSerialNumber() == null) {
                             Log.d(this, "setupAccounts: skipping invalid subid %d", subscriptionId);
+                            // If device configured in dsds mode, a SIM removed and if corresponding
+                            // phone is in ECM then add emergency account to that sub so that
+                            // incoming emergency call can be processed.
+                            Phone phoneInEcm = PhoneGlobals.getInstance().getPhoneInEcm();
+                            if ((mTelephonyManager.getPhoneCount() > 1)
+                                    && (phoneInEcm != null)
+                                    && phoneInEcm.getPhoneId() == phone.getPhoneId()) {
+                                mAccounts.add(new AccountEntry(phoneInEcm, true /* emergency */,
+                                        false /* isTest */));
+                                isAccountAdded = true;
+                            }
                             continue;
                         }
                         // Don't add account if it's opportunistic subscription, which is considered
@@ -1524,8 +1631,26 @@
                             continue;
                         }
 
-                        mAccounts.add(new AccountEntry(phone, false /* emergency */,
-                                false /* isTest */));
+                        if (mSubscriptionManager.isActiveSubId(subscriptionId)) {
+                            activeCount++;
+                            activeSubscriptionId = subscriptionId;
+                            mAccounts.add(new AccountEntry(phone, false /* emergency */,
+                                    false /* isTest */));
+                            isAccountAdded = true;
+                        }
+                        // Speacial case where one sub sim locked other sub reporting emergency service
+                        // emergency call placed will initiate on primary sub i.e sub which is reporting
+                        // limited/emergency service, if phone switches when emergency call is
+                        // in progress, there will missing phone account which can notify phantom call
+                        // to upper layer add emergency if for a phone no account added and if phone
+                        // reporting emergency service and other subs oos or if phone has running call
+                        if (!isAccountAdded && ((phone.getServiceState().isEmergencyOnly()
+                                    && !isOtherPhoneInService(phone))
+                                    || (phone.getState() == PhoneConstants.State.OFFHOOK))) {
+                            Log.i(this, "Adding emergency account to phone id: "+phone.getPhoneId());
+                            mAccounts.add(new AccountEntry(phone, true /* emergency */,
+                                    false /* isTest */));
+                        }
                     }
                 }
             } finally {
@@ -1534,9 +1659,9 @@
                 // numbers but a phone account is.
                 if (mAccounts.isEmpty()) {
                     Log.i(this, "setupAccounts: adding default");
-                    mAccounts.add(
-                            new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
-                                    false /* isTest */));
+                    mAccounts.add(new AccountEntry(PhoneFactory.getPhone(
+                            PhoneUtils.getPrimaryStackPhoneId()), true /* emergency */,
+                            false /* isTest */));
                 }
             }
 
@@ -1549,6 +1674,102 @@
 
         // Clean up any PhoneAccounts that are no longer relevant
         cleanupPhoneAccounts();
+
+        // Do not call setUserSelectedOutgoingPhoneAccount() from here when IRadio HAL version 1_5
+        // and later enabled as  MultiSimSettingController.updateUserPreferences() taking care of
+        // settings the default outgoing phone account.
+        if ((phones.length > 0) && phones[0].getHalVersion().less(RIL.RADIO_HAL_VERSION_1_5)) {
+            PhoneAccountHandle defaultPhoneAccount =
+                    mTelecomManager.getUserSelectedOutgoingPhoneAccount();
+
+            if ((defaultPhoneAccount == null)
+                        && (mTelephonyManager.getActiveModemCount() > Count.ONE.ordinal())
+                        && (activeCount == Count.ONE.ordinal())
+                        && (areAllSimAccountsFound()) && (isRadioInValidState(phones))) {
+                PhoneAccountHandle phoneAccountHandle =
+                        subscriptionIdToPhoneAccountHandle(activeSubscriptionId);
+                if (phoneAccountHandle != null) {
+                    Log.i(this, "setting default phone account, subId " + activeSubscriptionId);
+                    mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
+                }
+            }
+        }
+    }
+
+    private boolean areAllSimAccountsFound() {
+        final Iterator<PhoneAccountHandle> phoneAccounts =
+                mTelecomManager.getCallCapablePhoneAccounts().listIterator();
+        while (phoneAccounts.hasNext()) {
+            final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+            final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
+            if (mTelephonyManager.getSubIdForPhoneAccount(phoneAccount) ==
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isRadioInValidState(Phone[] phones) {
+        boolean isApmSimNotPwrDown = false;
+        try {
+            int propVal = PhoneUtils.getExtTelManager().
+                    getPropertyValueInt(APM_SIM_NOT_PWDN_PROPERTY, 0);
+            isApmSimNotPwrDown = (propVal == 1);
+            Log.d(this, "isRadioInValidState, propVal = " + propVal +
+                    " isApmSimNotPwrDown = " + isApmSimNotPwrDown);
+        } catch (NullPointerException ex) {
+            Log.w(this, "Failed to get property: + " + APM_SIM_NOT_PWDN_PROPERTY +
+                    " , Exception: " + ex);
+        }
+
+        int isAPMOn = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0);
+
+        // Do not update default Voice subId when SIM is pwdn due to APM
+        if ((isAPMOn == 1) && (!isApmSimNotPwrDown)) {
+            Log.d(this, "isRadioInValidState, isApmSimNotPwrDown = " + isApmSimNotPwrDown
+                    + ", isAPMOn:" + isAPMOn);
+            return false;
+        }
+
+        //Do not update default Voice subId when when device Shutdown is in progress
+        int  numPhones = mTelephonyManager.getActiveModemCount();
+        for (int i = 0; i < numPhones; i++) {
+            if (phones[i] != null && phones[i].isShuttingDown()) {
+                Log.d(this, " isRadioInValidState: device shutdown in progress ");
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
+        final Iterator<PhoneAccountHandle> phoneAccounts =
+                mTelecomManager.getCallCapablePhoneAccounts().listIterator();
+        while (phoneAccounts.hasNext()) {
+            final PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
+            final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle);
+            if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
+                return phoneAccountHandle;
+            }
+        }
+        return null;
+    }
+
+    private boolean isOtherPhoneInService(Phone currentPhone) {
+        TelephonyManager tm = TelephonyManager.getDefault();
+        int phoneCount = tm.getPhoneCount();
+        for (int phId = 0; phId < phoneCount; phId++) {
+            Phone phone = PhoneFactory.getPhone(phId);
+            if (currentPhone.getPhoneId() == phone.getPhoneId()) continue;
+            int ss = phone.getServiceState().getState();
+            Log.i(this, "Phone Id: %d Service State: %d",phone.getPhoneId(), ss);
+            if (ss == ServiceState.STATE_IN_SERVICE) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private void tearDownAccounts() {
@@ -1560,6 +1781,17 @@
         }
     }
 
+    private boolean isAccountMatched(SubscriptionInfo info) {
+        synchronized (mAccountsLock) {
+            for (AccountEntry entry : mAccounts) {
+                if (entry.isMatched(info)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Handles changes to the carrier configuration which may impact a phone account.  There are
      * some extras defined in the {@link PhoneAccount} which are based on carrier config options.
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index 7e4693f..a4a7977 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -33,6 +33,7 @@
 public class TelephonyConference extends TelephonyConferenceBase implements Holdable {
 
     private boolean mIsHoldable;
+    private boolean mIsDisconnecting;
 
     public TelephonyConference(PhoneAccountHandle phoneAccount) {
         super(phoneAccount);
@@ -66,9 +67,14 @@
     private boolean disconnectCall(Connection connection) {
         Call call = getMultipartyCallForConnection(connection, "onDisconnect");
         if (call != null) {
+            if (mIsDisconnecting) {
+                Log.i(this, "disconnectCall already called once");
+                return false;
+            }
             Log.d(this, "Found multiparty call to hangup for conference.");
             try {
                 call.hangup();
+                mIsDisconnecting = true;
                 return true;
             } catch (CallStateException e) {
                 Log.e(this, e, "Exception thrown trying to hangup conference");
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index ed07726..9a441ae 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -44,6 +44,8 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.ServiceState;
 import android.telephony.ServiceState.RilRadioTechnology;
 import android.telephony.SubscriptionManager;
@@ -56,6 +58,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Pair;
+import android.widget.Toast;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsException;
@@ -90,6 +93,10 @@
 import com.android.phone.callcomposer.CallComposerPictureTransfer;
 import com.android.telephony.Rlog;
 
+import org.codeaurora.ims.QtiCallConstants;
+import org.codeaurora.ims.QtiCallExtras;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -105,7 +112,8 @@
 /**
  * Base class for CDMA and GSM connections.
  */
-abstract class TelephonyConnection extends Connection implements Holdable, Communicator.Callback {
+abstract class TelephonyConnection extends Connection implements Holdable,
+          Communicator.Callback, TelephonyConnectionService.ConnectionRemovedListener {
     private static final String LOG_TAG = "TelephonyConnection";
 
     private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
@@ -141,6 +149,7 @@
     private static final int MSG_REJECT = 21;
     private static final int MSG_DTMF_DONE = 22;
     private static final int MSG_MEDIA_ATTRIBUTES_CHANGED = 23;
+    private static final int MSG_CONNECTION_REMOVED = 24;
 
     private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
     private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
@@ -148,6 +157,16 @@
     private List<Uri> mParticipants;
     private boolean mIsAdhocConferenceCall;
 
+    private boolean mIsEmergencyNumber = false;
+
+    private SuppServiceNotification mSsNotification = null;
+
+    /* Flag indicates if context based swap is disabled
+     * @param true means DSDA specific APIs should be invoked
+     * @param false means ImsPhoneCallTracker can use context to swap (legacy behavior)
+     */
+    private boolean mContextBasedSwapDisabled = false;
+
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
@@ -199,8 +218,15 @@
                     Log.v(TelephonyConnection.this, "MSG_SUPP_SERVICE_NOTIFY on phoneId : "
                             + (phone != null ? Integer.toString(phone.getPhoneId())
                             : "null"));
-                    SuppServiceNotification mSsNotification = null;
+                    if (phone == null) {
+                        break;
+                    }
                     if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
+                        if (mOriginalConnection != null && ((SuppServiceNotification)((AsyncResult)
+                                msg.obj).result).history != null && !(mConnectionState ==
+                                Call.State.DIALING || mConnectionState == Call.State.ALERTING)) {
+                           return;
+                        }
                         mSsNotification =
                                 (SuppServiceNotification)((AsyncResult) msg.obj).result;
                         if (mOriginalConnection != null) {
@@ -294,6 +320,11 @@
                 case MSG_DTMF_DONE:
                     Log.i(this, "MSG_DTMF_DONE");
                     break;
+                case MSG_CONNECTION_REMOVED:
+                    Log.d(this, "MSG_CONNECTION_REMOVED");
+                    // Some connection has disconnected. Re fresh disable add call property.
+                    refreshDisableAddCall();
+                    break;
 
                 case MSG_SET_CALL_RADIO_TECH:
                     int vrat = (int) msg.obj;
@@ -359,6 +390,24 @@
                         + mOriginalConnection.toString()
                         + ", new original connection="
                         + connection.toString());
+                boolean isShowToast = false;
+                Phone phone = getPhone();
+                if (phone != null) {
+                    CarrierConfigManager cfgManager = (CarrierConfigManager) phone
+                            .getContext().getSystemService(Context
+                            .CARRIER_CONFIG_SERVICE);
+                    if (cfgManager != null) {
+                        isShowToast = cfgManager.getConfigForSubId(phone.getSubId())
+                                .getBoolean("config_show_srvcc_toast");
+                    }
+                }
+                if (isShowToast && !shouldTreatAsEmergencyCall()) {
+                    int srvccMessageRes = VideoProfile.isVideo(
+                            mOriginalConnection.getVideoState()) ?
+                            R.string.srvcc_video_message : R.string.srvcc_message;
+                    Toast.makeText(phone.getContext(),
+                            srvccMessageRes, Toast.LENGTH_LONG).show();
+                }
                 setOriginalConnection(connection);
                 mWasImsConnection = false;
                 if (mHangupDisconnectCause != DisconnectCause.NOT_VALID) {
@@ -404,11 +453,23 @@
      * @param code the {@link SuppServiceNotification#code}.
      */
     private void sendSuppServiceNotificationEvent(int type, int code) {
+       CharSequence notificationMessage = getSuppServiceMessage(type, code);
+       if (notificationMessage == null || notificationMessage.length() == 0) {
+           return;
+       }
+
+       if (TelephonyManager.getDefault().getPhoneCount() > 1) {
+           SubscriptionInfo sub = SubscriptionManager.from(getPhone().getContext())
+                   .getActiveSubscriptionInfoForSimSlotIndex(getPhone().getPhoneId());
+           if (sub != null && !TextUtils.isEmpty(sub.getDisplayName().toString())) {
+               notificationMessage = sub.getDisplayName().toString() + ":" + notificationMessage;
+           }
+        }
+
         Bundle extras = new Bundle();
         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
         extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
-        extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
-                getSuppServiceMessage(type, code));
+        extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE, notificationMessage);
         sendTelephonyConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION,
                 extras);
     }
@@ -890,6 +951,11 @@
     private boolean mIsTtyEnabled;
 
     /**
+     * Indicates whether this connection is VT capable.
+     */
+    private boolean mAllowVideoCall = true;
+
+    /**
      * Indicates whether this call is using assisted dialing.
      */
     private boolean mIsUsingAssistedDialing;
@@ -1269,9 +1335,16 @@
     public void performAnswer(int videoState) {
         Log.v(this, "performAnswer");
         if (isValidRingingCall() && getPhone() != null) {
-            try {
-                mTelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+            if (TelephonyManager.isConcurrentCallsPossible()) {
+                // Disconnect dialing call when incoming call is accepted.
+                // Follow AOSP's approach for now. TODO:answer after disconnect completes
+                mTelephonyConnectionService.maybeDisconnectDialingCallsOnOtherSubs(
                         getPhoneAccountHandle());
+            } else {
+                mTelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+                            getPhoneAccountHandle());
+            }
+            try {
                 getPhone().acceptCall(videoState);
             } catch (CallStateException e) {
                 Log.e(this, e, "Failed to accept call.");
@@ -1279,6 +1352,13 @@
         }
     }
 
+    @Override
+    public void onConnectionRemoved(TelephonyConnection conn) {
+        if (conn != this) {
+            mHandler.obtainMessage(MSG_CONNECTION_REMOVED).sendToTarget();
+        }
+    }
+
     public void performHold() {
         Log.v(this, "performHold");
         // TODO: Can dialing calls be put on hold as well since they take up the
@@ -1304,7 +1384,14 @@
                     // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
                     if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
                         ImsPhone imsPhone = (ImsPhone) phone;
-                        imsPhone.holdActiveCall();
+                        if (isContextBasedSwapDisabled()) {
+                            // Invoke new API for DSDA only. This API makes sure that the
+                            // connection is only held and not swapped if there is another held
+                            // connection on that sub
+                            imsPhone.holdActiveCallOnly();
+                        } else {
+                            imsPhone.holdActiveCall();
+                        }
                         return;
                     }
                     phone.switchHoldingAndActive();
@@ -1327,7 +1414,20 @@
                 // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
                 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
                     ImsPhone imsPhone = (ImsPhone) phone;
-                    imsPhone.unholdHeldCall();
+                    if (isContextBasedSwapDisabled()) {
+                        if (hasActiveCallOnThisSub(imsPhone)) {
+                            // Same sub swap use case: Mimic CallsManager behavior of calling hold
+                            // and letting ImsPhoneCallTracker manage swap
+                            imsPhone.holdActiveCall();
+                        } else {
+                            // Unhold specific connection in single unhold or across sub swap use
+                            // case where hold already has been completed by
+                            // TelephonyConnectionService
+                            imsPhone.unholdHeldCall((ImsPhoneConnection)mOriginalConnection);
+                        }
+                    } else { // legacy unhold
+                        imsPhone.unholdHeldCall();
+                    }
                     return;
                 }
                 // Here's the deal--Telephony hold/unhold is weird because whenever there exists
@@ -1521,12 +1621,6 @@
                 setCallerDisplayName(name, namePresentation);
             }
 
-            TelephonyManager tm = (TelephonyManager) getPhone().getContext()
-                    .getSystemService(Context.TELEPHONY_SERVICE);
-            if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
-                mTreatAsEmergencyCall = true;
-            }
-
             // Changing the address of the connection can change whether it is an emergency call or
             // not, which can impact whether it can be part of a conference.
             refreshConferenceSupported();
@@ -1574,7 +1668,9 @@
         mOriginalConnectionExtras.clear();
         mOriginalConnection = originalConnection;
         mOriginalConnection.setTelecomCallId(getTelecomCallId());
-        registerForCallEvents(getPhone());
+        if (getPhone() != null) {
+            registerForCallEvents(getPhone());
+        }
 
         mOriginalConnection.addPostDialListener(mPostDialListener);
         mOriginalConnection.addListener(mOriginalConnectionListener);
@@ -1606,22 +1702,34 @@
         // Propagate VERSTAT for IMS calls.
         setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus());
 
+        Bundle extrasToPut = new Bundle();
+        List<String> extrasToRemove = new ArrayList<>();
+
         if (isImsConnection()) {
             mWasImsConnection = true;
+        } else {
+            extrasToRemove.add(QtiImsExtUtils.QTI_IMS_PHONE_ID_EXTRA_KEY);
+            extrasToRemove.add(QtiImsExtUtils.EXTRA_TIR_OVERWRITE_ALLOWED);
+            extrasToRemove.add(QtiCallConstants.ORIENTATION_MODE_EXTRA_KEY);
+            extrasToRemove.add(QtiCallConstants.EXTRAS_CALL_PROGRESS_INFO_TYPE);
+            extrasToRemove.add(QtiCallConstants.EXTRAS_CALL_PROGRESS_REASON_CODE);
+            extrasToRemove.add(QtiCallConstants.EXTRAS_CALL_PROGRESS_REASON_TEXT);
+            extrasToRemove.add(QtiCallConstants.EXTRA_CRS_TYPE);
+            extrasToRemove.add(QtiCallConstants.EXTRA_ORIGINAL_CALL_TYPE);
+            extrasToRemove.add(QtiCallConstants.EXTRA_IS_PREPARATORY);
+            extrasToRemove.add(QtiCallExtras.EXTRAS_CALL_AUDIO_QUALITY);
         }
         if (originalConnection instanceof ImsPhoneConnection) {
             maybeConfigureDeviceToDeviceCommunication();
         }
         mIsMultiParty = mOriginalConnection.isMultiparty();
 
-        Bundle extrasToPut = new Bundle();
         // Also stash the number verification status in a hidden extra key in the connection.
         // We do this because a RemoteConnection DOES NOT include a getNumberVerificationStatus
         // method and we need to be able to pass the number verification status up to Telecom
         // despite the missing pathway in the RemoteConnectionService API surface.
         extrasToPut.putInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS,
                 mOriginalConnection.getNumberVerificationStatus());
-        List<String> extrasToRemove = new ArrayList<>();
         if (mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
             extrasToPut.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
         } else {
@@ -1779,6 +1887,21 @@
         }
     }
 
+    private void maybeRemoveAnsweringDropsFgCallExtra() {
+        if(mOriginalConnection == null || !mOriginalConnection.isActiveCallDisconnectedOnAnswer()) {
+            return;
+        }
+
+        Call.State state = mOriginalConnection.getState();
+
+        if (state == Call.State.INCOMING || state == Call.State.WAITING) {
+            return;
+        }
+
+        Log.v(TelephonyConnection.this, "maybeRemoveAnsweringDropsFgCallExtra removing extra");
+        removeExtras(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
+    }
+
     private int transformCodec(int codec) {
         switch (codec) {
             case ImsStreamMediaProfile.AUDIO_QUALITY_NONE:
@@ -1844,13 +1967,21 @@
         boolean wasVideoCall = false;
         boolean isVowifiEnabled = false;
         if (phone instanceof ImsPhone) {
-            ImsPhoneCall foregroundCall = ((ImsPhone) phone).getForegroundCall();
-            if (foregroundCall != null) {
-                ImsCall call = foregroundCall.getImsCall();
-                if (call != null) {
-                    isCurrentVideoCall = call.isVideoCall();
-                    wasVideoCall = call.wasVideoCall();
-                }
+            ImsPhone imsPhone = (ImsPhone) phone;
+            ImsCall call = null;
+            if (imsPhone.getForegroundCall() != null
+                    && imsPhone.getForegroundCall().getImsCall() != null) {
+                call = imsPhone.getForegroundCall().getImsCall();
+            } else if (imsPhone.getBackgroundCall() != null
+                    && imsPhone.getBackgroundCall().getImsCall() != null) {
+                call = imsPhone.getBackgroundCall().getImsCall();
+            } else if (imsPhone.getRingingCall() != null
+                    && imsPhone.getRingingCall().getImsCall() != null) {
+                call = imsPhone.getRingingCall().getImsCall();
+            }
+            if (call != null) {
+                isCurrentVideoCall = call.isVideoCall();
+                wasVideoCall = call.wasVideoCall();
             }
 
             isVowifiEnabled = isWfcEnabled(phone);
@@ -2069,7 +2200,9 @@
             for (Connection current : getTelephonyConnectionService().getAllConnections()) {
                 if (current != this && current instanceof TelephonyConnection) {
                     TelephonyConnection other = (TelephonyConnection) current;
-                    if (canTransfer(other)) {
+                    if (getPhone() != null && other.getPhone() != null
+                            && (getPhone().getSubId() == other.getPhone().getSubId())
+                            && canTransfer(other)) {
                         canConsultativeTransfer = true;
                         break;
                     }
@@ -2144,6 +2277,10 @@
     @VisibleForTesting
     public void hangup(int telephonyDisconnectCode) {
         if (mOriginalConnection != null) {
+            if (mHangupDisconnectCause != DisconnectCause.NOT_VALID) {
+                Log.i(this, "hangup already called once");
+                return;
+            }
             mHangupDisconnectCause = telephonyDisconnectCode;
             try {
                 // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
@@ -2337,7 +2474,7 @@
                     // Ensure extras are propagated to Telecom.
                     putTelephonyExtras(mOriginalConnectionExtras);
                     // If extras contain Conference support information,
-                    // then ensure capabilities are updated.
+                    // then ensure capabilities are updated and propagated to Telecom.
                     if (mOriginalConnectionExtras.containsKey(
                             ImsCallProfile.EXTRA_EXTENDING_TO_CONFERENCE_SUPPORTED)
                             || mOriginalConnectionExtras.containsKey(
@@ -2391,6 +2528,13 @@
         if (mOriginalConnection == null) {
             return;
         }
+
+        TelephonyManager tm = (TelephonyManager) getPhone().getContext()
+                .getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm.isEmergencyNumber(mOriginalConnection.getAddress())) {
+            mTreatAsEmergencyCall = true;
+        }
+
         Call.State newState;
         // If the state is overridden and the state of the original connection hasn't changed since,
         // then we continue in the overridden state, else we go to the original connection's state.
@@ -2440,30 +2584,43 @@
                         fireOnOriginalConnectionRetryDial(cause
                                 == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE);
                     } else {
-                        int preciseDisconnectCause = CallFailCause.NOT_VALID;
-                        if (mShowPreciseFailedCause) {
-                            preciseDisconnectCause =
-                                    mOriginalConnection.getPreciseDisconnectCause();
+                        if (mSsNotification != null) {
+                            setTelephonyConnectionDisconnected(
+                                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                                    mOriginalConnection.getDisconnectCause(),
+                                    mOriginalConnection.getVendorDisconnectCause(),
+                                    mSsNotification.notificationType,
+                                    mSsNotification.code,
+                                    getPhone().getPhoneId()));
+                            mSsNotification = null;
+                            DisconnectCauseUtil.mNotificationCode = 0xFF;
+                            DisconnectCauseUtil.mNotificationType = 0xFF;
+                        } else {
+                            int preciseDisconnectCause = CallFailCause.NOT_VALID;
+                            if (mShowPreciseFailedCause) {
+                                preciseDisconnectCause =
+                                        mOriginalConnection.getPreciseDisconnectCause();
+                            }
+                            int disconnectCause = mOriginalConnection.getDisconnectCause();
+                            if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID)
+                                    && (mHangupDisconnectCause != disconnectCause)) {
+                                Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause
+                                        + " -> " + mHangupDisconnectCause);
+                                disconnectCause = mHangupDisconnectCause;
+                            }
+                            ImsReasonInfo imsReasonInfo = null;
+                            if (isImsConnection()) {
+                                ImsPhoneConnection imsPhoneConnection =
+                                        (ImsPhoneConnection) mOriginalConnection;
+                                imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
+                            }
+                            setTelephonyConnectionDisconnected(
+                                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                                            disconnectCause,
+                                            preciseDisconnectCause,
+                                            mOriginalConnection.getVendorDisconnectCause(),
+                                            getPhone().getPhoneId(), imsReasonInfo));
                         }
-                        int disconnectCause = mOriginalConnection.getDisconnectCause();
-                        if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID)
-                                && (mHangupDisconnectCause != disconnectCause)) {
-                            Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause
-                                    + " -> " + mHangupDisconnectCause);
-                            disconnectCause = mHangupDisconnectCause;
-                        }
-                        ImsReasonInfo imsReasonInfo = null;
-                        if (isImsConnection()) {
-                            ImsPhoneConnection imsPhoneConnection =
-                                    (ImsPhoneConnection) mOriginalConnection;
-                            imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
-                        }
-                        setTelephonyConnectionDisconnected(
-                                DisconnectCauseUtil.toTelecomDisconnectCause(
-                                        disconnectCause,
-                                        preciseDisconnectCause,
-                                        mOriginalConnection.getVendorDisconnectCause(),
-                                        getPhone().getPhoneId(), imsReasonInfo));
                         close();
                     }
                     break;
@@ -2490,6 +2647,7 @@
         updateMultiparty();
         refreshDisableAddCall();
         refreshCodec();
+        maybeRemoveAnsweringDropsFgCallExtra();
     }
 
     /**
@@ -2502,7 +2660,6 @@
 
         if (mIsMultiParty != mOriginalConnection.isMultiparty()) {
             mIsMultiParty = mOriginalConnection.isMultiparty();
-
             if (mIsMultiParty) {
                 notifyConferenceStarted();
             }
@@ -2701,9 +2858,14 @@
                 (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
                         == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
 
-        boolean isLocalVideoSupported = (mOriginalConnectionCapabilities
+        PersistableBundle pb = getCarrierConfig();
+        boolean vtTtySupported = false;
+        if(pb != null) {
+            vtTtySupported = pb.getBoolean(CarrierConfigManager.KEY_CARRIER_VT_TTY_SUPPORT_BOOL);
+        }
+        boolean isLocalVideoSupported = mAllowVideoCall && (mOriginalConnectionCapabilities
                 & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
-                == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled;
+                == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && (vtTtySupported || !mIsTtyEnabled);
         capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
                 isLocalVideoSupported);
 
@@ -2856,6 +3018,15 @@
     }
 
     /**
+     * This function is used to disables VT capability.
+     * @param allowVideoCall true disables VT capability
+     */
+    public void allowVideoCall(boolean allowVideoCall) {
+        mAllowVideoCall = allowVideoCall;
+        updateConnectionCapabilities();
+    }
+
+    /**
      * Whether the original connection is an IMS connection.
      * @return {@code True} if the original connection is an IMS connection, {@code false}
      *     otherwise.
@@ -2934,10 +3105,20 @@
             int labelId = isValidRingingCall()
                     ? R.string.status_hint_label_incoming_wifi_call
                     : R.string.status_hint_label_wifi_call;
+            String displaySubId = "";
+            if (TelephonyManager.getDefault().getPhoneCount() > 1) {
+                final int phoneId = getPhone().getPhoneId();
+                SubscriptionInfo sub = SubscriptionManager.from(getPhone().getContext())
+                    .getActiveSubscriptionInfoForSimSlotIndex(phoneId);
+                if (sub != null) {
+                    displaySubId = sub.getDisplayName().toString();
+                    displaySubId  = " " + displaySubId;
+                }
+            }
 
             Context context = getPhone().getContext();
             setTelephonyStatusHints(new StatusHints(
-                    getResourceString(labelId),
+                    getResourceString(labelId) + displaySubId,
                     Icon.createWithResource(
                             context, R.drawable.ic_signal_wifi_4_bar_24dp),
                     null /* extras */));
@@ -3767,6 +3948,11 @@
                         .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
     }
 
+    private boolean hasActiveCallOnThisSub(ImsPhone imsPhone) {
+        // Active foreground call on same sub means same sub swap
+        return imsPhone.getForegroundCall().getState() == Call.State.ACTIVE;
+    }
+
     /**
      * Handles a device to device message which a {@link CallDiagnostics} wishes to send.
      * @param extras the call event extras bundle.
@@ -3823,4 +4009,14 @@
     public List<TelephonyConnectionListener> getTelephonyConnectionListeners() {
         return new ArrayList<>(mTelephonyListeners);
     }
+
+    /* Disables context based swap to make use of new DSDA hold APIs */
+    public void disableContextBasedSwap(boolean contextBasedSwapDisabled) {
+        mContextBasedSwapDisabled = contextBasedSwapDisabled;
+    }
+
+    /* Determines if context based swap is disabled */
+    public boolean isContextBasedSwapDisabled() {
+        return mContextBasedSwapDisabled;
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index a392876..937680f 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -68,6 +68,7 @@
 import com.android.phone.FrameworksUtils;
 import com.android.phone.MMIDialogActivity;
 import com.android.phone.PhoneUtils;
+import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
 import com.android.phone.callcomposer.CallComposerPictureManager;
 import com.android.phone.settings.SuppServicesUiUtil;
@@ -80,6 +81,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -166,6 +168,9 @@
     /** Set to true when there is an emergency call pending which will potential trigger a dial.
      * This must be set to false when the call is dialed. */
     private volatile boolean mIsEmergencyCallPending;
+    private AnswerAndReleaseHandler mAnswerAndReleaseHandler = null;
+    /** Handler for hold across sub use case */
+    private HoldHandlerBase mHoldHandler = null;
 
     // Contains one TelephonyConnection that has placed a call and a memory of which Phones it has
     // already tried to connect with. There should be only one TelephonyConnection trying to place a
@@ -206,6 +211,25 @@
         int getPhoneId(int subId);
     }
 
+    private AnswerAndReleaseHandler.ListenerBase mAnswerAndReleaseListener =
+            new AnswerAndReleaseHandler.ListenerBase() {
+        @Override
+        public void onAnswered() {
+            mAnswerAndReleaseHandler.removeListener(this);
+            mAnswerAndReleaseHandler = null;
+        }
+    };
+
+    private HoldHandlerBase.Listener mHoldListener =
+            new HoldHandlerBase.Listener() {
+        @Override
+        public void onCompleted(boolean status) {
+            mHoldHandler.removeListener(this);
+            mHoldHandler = null;
+            Log.i(this, "onCompleted " + status);
+        }
+    };
+
     private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
         @Override
         public int getDefaultVoicePhoneId() {
@@ -464,12 +488,78 @@
             new TelephonyConnection.TelephonyConnectionListener() {
         @Override
         public void onOriginalConnectionConfigured(TelephonyConnection c) {
-            addConnectionToConferenceController(c);
+            if (!c.isAdhocConferenceCall()) {
+                addConnectionToConferenceController(c);
+            }
         }
 
         @Override
         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
-            retryOutgoingOriginalConnection(c, isPermanentFailure);
+            if (!c.isAdhocConferenceCall()) {
+                retryOutgoingOriginalConnection(c, isPermanentFailure);
+            }
+        }
+
+        @Override
+        public void onStateChanged(android.telecom.Connection c, int state) {
+            /*
+             * Special handling for Incoming + Incoming call scenario where we transitioned
+             * to Active + Incoming call scenario.
+             */
+            if (state != Connection.STATE_ACTIVE) {
+                return;
+            }
+            // Check for DSDA mode and Connection type
+            if (!isConcurrentCallsPossible() || !isTelephonyConnection(c)) {
+                return;
+            }
+            TelephonyConnection conn = (TelephonyConnection) c;
+            // Check if we need to disable VT capability based on carrier requirements.
+            maybeDisableVideo(conn);
+            /*
+             * Check if we need to update Incoming connection extra using
+             * handleIncomingDsdaCall().
+             */
+            Connection ringingConnection = getRingingConnection();
+            if (ringingConnection != null) {
+                handleIncomingDsdaCall((TelephonyConnection) ringingConnection);
+            }
+        }
+
+        @Override
+        public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
+            /*
+             * Special handling for Video + Voice call case where the Video call
+             * is downgraded or Voice + Voice call case where the Voice call
+             * is upgraded.
+             */
+            if (c.getState() != Connection.STATE_ACTIVE) {
+                return;
+            }
+            // Check for DSDA mode and Connection type
+            if (!isConcurrentCallsPossible() || !isTelephonyConnection(c)) {
+                return;
+            }
+            TelephonyConnection conn = (TelephonyConnection) c;
+            /*
+             * As per carrier requirement we need to disable swap when Active call is
+             * VT call and enable swap if that Video call is downgraded to Voice
+             * call.
+             */
+             if (!isConcurrentCallAllowedDuringVideoCall(conn.getPhone())) {
+                 return;
+             }
+            /*
+             * Either there is no call present on the other SUB or there is
+             * a connected Video call on other SUB then no need to check
+             * further since here we only handle Voice/Video + Voice use-cases.
+             */
+            PhoneAccountHandle accountHandle = c.getPhoneAccountHandle();
+            if (!isCallPresentOnOtherSub(accountHandle) ||
+                    hasConnectedVideoCallOnOtherSub(accountHandle)) {
+                return;
+            }
+            disableSwap(conn, VideoProfile.isVideo(videoState));
         }
     };
 
@@ -481,6 +571,17 @@
         }
     };
 
+    private List<ConnectionRemovedListener> mConnectionRemovedListeners =
+            new CopyOnWriteArrayList<>();
+
+    /**
+     * A listener to be invoked whenever a TelephonyConnection is removed
+     * from connection service.
+     */
+    public interface ConnectionRemovedListener {
+        public void onConnectionRemoved(TelephonyConnection conn);
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -522,12 +623,29 @@
         updatePhoneAccount(conferenceHostConnection, phone);
         com.android.internal.telephony.Connection originalConnection = null;
         try {
-            originalConnection = phone.startConference(
-                    getParticipantsToDial(request.getParticipants()),
-                    new ImsPhone.ImsDialArgs.Builder()
-                    .setVideoState(request.getVideoState())
-                    .setRttTextStream(conferenceHostConnection.getRttTextStream())
-                    .build());
+            if (isAcrossSubHoldInProgress()) {
+                throw new CallStateException("Cannot dial as holding in progress");
+            }
+            // Get connection to hold if any
+            Pair<TelephonyConnection, PhoneAccountHandle> pairToHold =
+                    getActiveDsdaConnectionPhoneAccountPair();
+            TelephonyConnection connToHold = pairToHold.first;
+            if (connToHold == null || Objects.equals(pairToHold.second,
+                    conferenceHostConnection.getPhoneAccountHandle())) {
+                originalConnection = phone.startConference(getParticipantsToDial(
+                        request.getParticipants()),
+                        new ImsPhone.ImsDialArgs.Builder()
+                                .setVideoState(request.getVideoState())
+                                .setRttTextStream(conferenceHostConnection.getRttTextStream())
+                                .build());
+            } else {
+                // DSDA use case: adhoc conference and active call are on different subs
+                mHoldHandler = new HoldAndDialHandler(connToHold, conferenceHostConnection, this,
+                        phone, request.getVideoState(),
+                        getParticipantsToDial(request.getParticipants()));
+                prepareForAcrossSubHold(connToHold);
+                originalConnection = mHoldHandler.dial();
+            }
         } catch (CallStateException e) {
             Log.e(this, e, "placeOutgoingConference, phone.startConference exception: " + e);
             handleCallStateException(e, conferenceHostConnection, phone);
@@ -576,6 +694,90 @@
     }
 
     @Override
+    protected void unhold(String callId) {
+        if (isAcrossSubHoldInProgress()) {
+            Log.e(this, null, "Cannot unhold call as holding in progress");
+            return;
+        }
+        if (!isConcurrentCallsPossible()) {
+            // follow legacy unhold behavior
+            super.unhold(callId);
+            return;
+        }
+        unholdDsdaCall(callId);
+    }
+
+    @Override
+    protected void hold(String callId) {
+        if (isAcrossSubHoldInProgress()) {
+            Log.e(this, null, "Cannot unhold call as holding in progress");
+            return;
+        }
+
+        // When concurrent calls are possible, this API is invoked only to hold and
+        // not to swap. This block takes care of holding a call in foll. use cases:
+        // ACTIVE or ACTIVE + HELD use case
+        if (isConcurrentCallsPossible()) {
+            try {
+                Log.d(this, "hold DSDA call");
+                Pair<TelephonyConnection, PhoneAccountHandle> pairToHold =
+                        getConnectionPhoneAccountPair(callId, "singleHold");
+                pairToHold.first.disableContextBasedSwap(true);
+            } catch (CallStateException ex) {
+                // Not an instance of TelephonyConnection/ImsConference. Just log and return similar
+                // to SS/DSDS handling
+                Log.e(this, ex, "hold " + ex);
+                return;
+            }
+        }
+        super.hold(callId);
+    }
+
+    @Override
+    protected void answer(String callId) {
+        answerVideo(callId, VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @Override
+    protected void answerVideo(String callId, int videoState) {
+        if (isAcrossSubHoldInProgress()) {
+            Log.e(this, null, "Cannot answer as holding in progress");
+            return;
+        }
+        if (mAnswerAndReleaseHandler != null) {
+            Log.e(this, null, "Cannot answer as AnswerAndRelease is in progress.");
+            return;
+        }
+        if(isConcurrentCallsPossible()) {
+            // DSDA answer across sub use case
+            answerDsdaCall(callId, videoState);
+            return;
+        }
+        Connection answerAndReleaseConnection = shallDisconnectOtherCalls();
+        boolean isAnswerAndReleaseConnection = answerAndReleaseConnection != null;
+        Log.i(this, "answerVideo: isAnswerAndReleaseConnection: " +
+                isAnswerAndReleaseConnection);
+        if (!isAnswerAndReleaseConnection) {
+            super.answerVideo(callId, videoState);
+            return;
+        }
+        // Pseudo DSDA use case
+        setupAnswerAndReleaseHandler(answerAndReleaseConnection, videoState);
+    }
+
+    private Connection shallDisconnectOtherCalls() {
+        for (Connection current : getAllConnections()) {
+            if (current.getState() == Connection.STATE_RINGING &&
+                    current.getExtras() != null &&
+                    current.getExtras().getBoolean(
+                        Connection.EXTRA_ANSWERING_DROPS_FG_CALL, false)) {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    @Override
     public @Nullable Conference onCreateIncomingConference(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
             @NonNull final ConnectionRequest request) {
@@ -647,6 +849,7 @@
             final ConnectionRequest request) {
         Log.i(this, "onCreateOutgoingConnection, request: " + request);
 
+        Bundle bundle = request.getExtras();
         Uri handle = request.getAddress();
         boolean isAdhocConference = request.isAdhocConferenceCall();
 
@@ -825,8 +1028,49 @@
             }
 
             if (!isEmergencyNumber) {
+                boolean disableSwap = false;
+                if (isConcurrentCallsPossible()) {
+                    Connection conn = getRingingOrDialingConnection();
+                    if (conn != null && !Objects.equals(
+                            request.getAccountHandle(), conn.getPhoneAccountHandle())) {
+                        // In DSDA, fail dial if there are dialing or ringing calls on the other
+                        // sub. Same sub dialing/ringing calls is handled by ImsPhoneCallTracker
+                        int disconnectCause = android.telephony.DisconnectCause.ALREADY_DIALING;
+                        if (conn.getState() == Connection.STATE_RINGING) {
+                            disconnectCause = android.telephony.
+                                    DisconnectCause.CANT_CALL_WHILE_RINGING;
+                        }
+                        return Connection.createFailedConnection(
+                                mDisconnectCauseFactory.toTelecomDisconnectCause(disconnectCause,
+                                        "Ongoing calls", phone.getPhoneId()));
+                    }
+                    /*
+                     * This is the case when we have Outgoing Video + Voice call and as per
+                     * carrier requirement we need to either disallow this operation or we
+                     * need to disable swap option if the Video call is permitted.
+                     * Note: In case of same SUB case, this will be blocked in
+                     *       ImsPhoneCallTracker#canAddVideoCallDuringImsAudioCall()
+                     */
+                    boolean hasOutgoingVideoCallDuringAudioCall =
+                            VideoProfile.isVideo(request.getVideoState()) &&
+                            hasActiveOrHeldAudioCall();
+                    if (!isVideoCallHoldAllowedOnAnySub() && hasOutgoingVideoCallDuringAudioCall) {
+                        if (!isConcurrentCallAllowedDuringVideoCall(phone)) {
+                            return Connection.createFailedConnection(
+                                    mDisconnectCauseFactory.toTelecomDisconnectCause(
+                                            android.telephony.DisconnectCause.OUTGOING_FAILURE,
+                                            "cannot dial in current state", phone.getPhoneId()));
+                        }
+                        disableSwap = true;
+                    }
+                }
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         false, handle, phone);
+                if (disableSwap) {
+                    if (resultConnection instanceof TelephonyConnection) {
+                        disableSwap((TelephonyConnection)resultConnection, true);
+                    }
+                }
                 if (isAdhocConference) {
                     if (resultConnection instanceof TelephonyConnection) {
                         TelephonyConnection conn = (TelephonyConnection)resultConnection;
@@ -1072,6 +1316,9 @@
                     if (phone.isUtEnabled() && number.endsWith("#")) {
                         Log.d(this, "onCreateOutgoingConnection dial for UT");
                         break;
+                    } else if (phone.isOutgoingImsVoiceAllowed()) {
+                        Log.d(this, "onCreateOutgoingConnection dial with PS only");
+                        break;
                     } else {
                         return Connection.createFailedConnection(
                                 mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -1102,9 +1349,18 @@
         final boolean isTtyModeEnabled = mDeviceState.isTtyModeEnabled(this);
         if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled
                 && !isEmergencyNumber) {
-            return Connection.createFailedConnection(mDisconnectCauseFactory.toTelecomDisconnectCause(
-                    android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,
-                    null, phone.getPhoneId()));
+            boolean vtTtySupported = false;
+            CarrierConfigManager cfgManager = (CarrierConfigManager)
+                    phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (cfgManager != null) {
+                vtTtySupported = cfgManager.getConfigForSubId(phone.getSubId())
+                        .getBoolean(CarrierConfigManager.KEY_CARRIER_VT_TTY_SUPPORT_BOOL);
+            }
+            if (!vtTtySupported) {
+                return Connection.createFailedConnection(mDisconnectCauseFactory.
+                        toTelecomDisconnectCause(android.telephony.DisconnectCause.
+                        VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,null, phone.getPhoneId()));
+            }
         }
 
         // Check for additional limits on CDMA phones.
@@ -1158,9 +1414,17 @@
                     "Treat as an Emergency Call.");
             isEmergency = true;
         }
-        Phone phone = getPhoneForAccount(accountHandle, isEmergency,
-                /* Note: when not an emergency, handle can be null for unknown callers */
-                request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
+
+        Phone phone;
+        if (isEmergency) {
+            phone = PhoneGlobals.getInstance().getPhoneInEcm();
+        } else {
+            phone = getPhoneForAccount(accountHandle, isEmergency,
+                    /* Note: when not an emergency, handle can be null for unknown callers */
+                    request.getAddress() == null ? null :
+                            request.getAddress().getSchemeSpecificPart());
+        }
+
         if (phone == null) {
             return Connection.createFailedConnection(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -1221,6 +1485,7 @@
             // Add extra to call if answering this incoming call would cause an in progress call on
             // another subscription to be disconnected.
             maybeIndicateAnsweringWillDisconnect(connection, request.getAccountHandle());
+            handleIncomingDsdaCall(connection);
 
             connection.setTtyEnabled(mDeviceState.isTtyModeEnabled(getApplicationContext()));
             return connection;
@@ -1259,6 +1524,209 @@
         imsOriginalConnection.getImsCall().setAnswerWithRtt();
     }
 
+    /*
+     * This handles certain incoming call DSDA use cases based on carrier requirements
+     * by updating Connection(s) extras to enable PseudoDsda behavior
+     * or disable/remove call swap option.
+     */
+    private void handleIncomingDsdaCall(TelephonyConnection incomingConnection) {
+        com.android.internal.telephony.Connection originalConnection =
+                incomingConnection.getOriginalConnection();
+        Phone phone = incomingConnection.getPhone();
+        PhoneAccountHandle incomingHandle = mPhoneUtilsProxy.makePstnPhoneAccountHandle(phone);
+
+        /*
+         * If we are not in DSDA mode or the incoming call is on the same phoneAccount or
+         * connection is not IMS then we return and let the legacy behavior take over.
+         */
+        if (!isConcurrentCallsPossible()
+                || !isCallPresentOnOtherSub(incomingHandle)
+                || originalConnection == null
+                || originalConnection.getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
+            return;
+        }
+
+        /*
+         * Check if the incoming call is a Voice call and there is no Video call on the other
+         * SUB in which case we do not have to do any special handling and let the incoming
+         * call pass as is.
+         */
+        boolean hasConnectedVideoCallOnOtherSub =
+                hasConnectedVideoCallOnOtherSub(incomingHandle);
+        if (!VideoProfile.isVideo(incomingConnection.getVideoState()) &&
+                !hasConnectedVideoCallOnOtherSub) {
+            return;
+        }
+
+        ImsPhoneConnection imsOriginalConnection = (ImsPhoneConnection) originalConnection;
+        /*
+         * If holding Video call is not allowed on the other SUB and there is a video call then
+         * answering the incoming call will end the call(s) on the other SUB.
+         */
+        if (hasConnectedVideoCallOnOtherSub && !isVideoCallHoldAllowedOnOtherSub(phone)) {
+            enableAnsweringWillDisconnect(imsOriginalConnection, incomingConnection);
+            return;
+        }
+
+        boolean isVideoCallHoldAllowed = isVideoCallHoldAllowed(phone);
+        boolean videoCallDuringVoiceCall =
+                VideoProfile.isVideo(incomingConnection.getVideoState()) &&
+                !hasConnectedVideoCallOnOtherSub;
+        // Check this is Voice Call (SUB1) + Incoming VT call (SUB2) scenario
+        if (isVideoCallHoldAllowed || !videoCallDuringVoiceCall) {
+            return;
+        }
+
+        if (!isConcurrentCallAllowedDuringVideoCall(phone)) {
+            // If concurrent call is NOT allowed then answering the incoming
+            // call should end the call(s) on other SUB.
+            enableAnsweringWillDisconnect(imsOriginalConnection, incomingConnection);
+        } else {
+            // If concurrent call is allowed then grey out the swap option on the UI.
+            disableSwap(incomingConnection, true);
+        }
+    }
+
+    /**
+     * Checks to see if there are video calls present on a sub other than the one passed in.
+     * @param accountHandle The new incoming connection {@link PhoneAccountHandle}
+     */
+    private boolean hasConnectedVideoCallOnOtherSub(@NonNull PhoneAccountHandle accountHandle) {
+        return getAllConnections().stream()
+                .filter(c ->
+                        // Exclude multiendpoint calls as they're not on this device.
+                        (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+                        // Include any calls not on same sub as current connection.
+                        && !Objects.equals(c.getPhoneAccountHandle(), accountHandle)
+                        && VideoProfile.isVideo(c.getVideoState())
+                        && (c.getState() == Connection.STATE_ACTIVE ||
+                            c.getState() == Connection.STATE_HOLDING))
+                .count() > 0;
+    }
+
+    /**
+     * Checks if video call hold is allowed on the other SUB
+     * @param phone The current phone {@link Phone}
+     * Note: This function assumes that we can only have device in
+     *       single sim / dual sim configuration.
+     */
+    private boolean isVideoCallHoldAllowedOnOtherSub(Phone phone) {
+        for (Phone ph : mPhoneFactoryProxy.getPhones()) {
+            if (ph.getSubId() !=  phone.getSubId()) {
+                return isVideoCallHoldAllowed(ph);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if video call hold is allowed on any SUB.
+     * This function checks if we have specific mcc mnc combo on
+     * each SUB.
+     * Note: This function assumes that we can only have device in
+     *       single sim / dual sim configuration.
+     */
+    private boolean isVideoCallHoldAllowedOnAnySub() {
+        for (Phone ph : mPhoneFactoryProxy.getPhones()) {
+            if (isVideoCallHoldAllowed(ph)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void enableAnsweringWillDisconnect(ImsPhoneConnection imsOriginalConnection,
+            TelephonyConnection connection) {
+        imsOriginalConnection.setActiveCallDisconnectedOnAnswer(true);
+        Bundle extras = new Bundle();
+        extras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
+        connection.putExtras(extras);
+    }
+
+    private void disableSwap(TelephonyConnection connection, boolean disable) {
+        Bundle extras = new Bundle();
+        extras.putBoolean(Connection.EXTRA_DISABLE_SWAP_CALL, disable);
+        connection.putExtras(extras);
+    }
+
+    private boolean isConcurrentCallAllowedDuringVideoCall(Phone phone) {
+         CarrierConfigManager cfgManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (cfgManager == null) {
+            // For some reason CarrierConfigManager is unavailable, return default
+            Log.w(this,
+                  "isConcurrentCallAllowedDuringVideoCall: couldn't get CarrierConfigManager");
+            return true;
+        }
+        return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+                CarrierConfigManager.KEY_ALLOW_CONCURRENT_CALL_DURING_VIDEO_CALL_BOOL, true);
+    }
+
+    /*
+     * Checks if we need to disable Video to prevent Video upgrades
+     * for any call.
+     * @param connection The connection is used to get phoneAccountHandle.
+     */
+    public void maybeDisableVideo(TelephonyConnection connection) {
+        // Checks if in DSDA mode and both mcc mnc has certain configs
+        // to disable VT capability or not.
+        if (connection == null || !isConcurrentCallsPossible() ||
+                isVideoCallHoldAllowedOnAnySub() ||
+                isConcurrentCallAllowedDuringVideoCall(connection.getPhone())) {
+            return;
+        }
+
+        PhoneAccountHandle phoneAccountHandle = connection.getPhoneAccountHandle();
+        // Checks if this is Voice call (SUB1) + Dialed Voice call (SUB2) case
+        if (phoneAccountHandle == null || !isCallPresentOnOtherSub(phoneAccountHandle) ||
+                hasConnectedVideoCallOnOtherSub(phoneAccountHandle) ||
+                VideoProfile.isVideo(connection.getVideoState())) {
+            return;
+        }
+
+        setAllowVideoCall(false);
+    }
+
+    /*
+     * Checks if there are no more calls on the {@connection} phone account handle in
+     * which case we enable VT capability for all remaining call(s).
+     * @param connection The connection is used to get phoneAccountHandle.
+     */
+    private void maybeEnableVideo(Connection connection) {
+        PhoneAccountHandle phoneAccountHandle = connection.getPhoneAccountHandle();
+        long count = getAllConnections().stream()
+                .filter(c ->
+                        // Exclude multiendpoint calls as they're not on this device.
+                        (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+                        // Include any calls on same sub as current connection.
+                        && Objects.equals(c.getPhoneAccountHandle(), phoneAccountHandle)
+                        && (c.getState() == Connection.STATE_ACTIVE ||
+                            c.getState() == Connection.STATE_HOLDING))
+                .count();
+        if (count > 0) {
+            return;
+        }
+
+        setAllowVideoCall(true);
+    }
+
+    private void setAllowVideoCall(boolean allowed) {
+        for (Connection conn : getAllConnections()) {
+            if (!isTelephonyConnection(conn)) {
+                continue;
+            }
+            ((TelephonyConnection) conn).allowVideoCall(allowed);
+        }
+
+        for (Conference current : getAllConferences()) {
+            if (!isImsConference(current)) {
+                continue;
+            }
+            Connection conn = ((ImsConference)current).getConferenceHost();
+            ((TelephonyConnection) conn).allowVideoCall(allowed);
+        }
+    }
+
     /**
      * Called by the {@link ConnectionService} when a newly created {@link Connection} has been
      * added to the {@link ConnectionService} and sent to Telecom.  Here it is safe to send
@@ -1271,6 +1739,7 @@
         if (connection instanceof TelephonyConnection) {
             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
             maybeSendInternationalCallEvent(telephonyConnection);
+            maybeSendPhoneAccountUpdateEvent(telephonyConnection);
         }
     }
 
@@ -1366,14 +1835,19 @@
         // Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
         // Emergency PhoneAccount
         PhoneAccountHandle accountHandle = request.getAccountHandle();
-        boolean isEmergency = false;
+        Phone phone = null;
         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
                 accountHandle.getId())) {
             Log.i(this, "Emergency PhoneAccountHandle is being used for unknown call... " +
                     "Treat as an Emergency Call.");
-            isEmergency = true;
+            for (Phone phoneSelected : mPhoneFactoryProxy.getPhones()) {
+                if (phoneSelected.getState() == PhoneConstants.State.OFFHOOK) {
+                    phone = phoneSelected;
+                    break;
+                }
+            }
         }
-        Phone phone = getPhoneForAccount(accountHandle, isEmergency,
+        if (phone == null) phone = getPhoneForAccount(accountHandle, false,
                 /* Note: when not an emergency, handle can be null for unknown callers */
                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
@@ -1688,8 +2162,13 @@
                     });
         }
 
+        updatePhoneAccount(connection, phone);
+
         final com.android.internal.telephony.Connection originalConnection;
         try {
+            if (isAcrossSubHoldInProgress()) {
+                throw new CallStateException("Cannot dial as holding in progress");
+            }
             if (phone != null) {
                 EmergencyNumber emergencyNumber =
                         phone.getEmergencyNumberTracker().getEmergencyNumber(number);
@@ -1730,7 +2209,16 @@
                         }
                     }
                 }
-                originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
+
+                // Get connection to hold if any
+                Pair<TelephonyConnection, PhoneAccountHandle> pairToHold =
+                        getActiveDsdaConnectionPhoneAccountPair();
+                TelephonyConnection connToHold = pairToHold.first;
+                if (connToHold == null || Objects.equals(pairToHold.second,
+                        connection.getPhoneAccountHandle())) {
+                    // Same sub dial and hold or dial without hold use case
+                    // Follow legacy behavior
+                    originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
                         .setVideoState(videoState)
                         .setIntentExtras(extras)
                         .setRttTextStream(connection.getRttTextStream())
@@ -1738,6 +2226,14 @@
                         // We need to wait until the phone has been chosen in GsmCdmaPhone to
                         // register for the associated TelephonyConnection call event listeners.
                         connection::registerForCallEvents);
+
+                } else {
+                    // Across sub hold and dial
+                    mHoldHandler = new HoldAndDialHandler(connToHold, connection, this, phone,
+                            videoState, extras);
+                    prepareForAcrossSubHold(connToHold);
+                    originalConnection = mHoldHandler.dial();
+                }
             } else {
                 originalConnection = null;
             }
@@ -1809,8 +2305,8 @@
                 CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
     }
 
-    private void handleCallStateException(CallStateException e, TelephonyConnection connection,
-            Phone phone) {
+    public static void handleCallStateException(CallStateException e, TelephonyConnection
+            connection, Phone phone) {
         int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
         switch (e.getError()) {
             case CallStateException.ERROR_OUT_OF_SERVICE:
@@ -1870,10 +2366,7 @@
                     allowsMute, callDirection, telecomCallId);
         }
         if (returnConnection != null) {
-            if (!isAdhocConference) {
-                // Listen to Telephony specific callbacks from the connection
-                returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
-            }
+            returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
             returnConnection.setVideoPauseSupported(
                     TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
                             phoneAccountHandle));
@@ -1884,6 +2377,7 @@
                     TelecomAccountRegistry.getInstance(this).isShowPreciseFailedCause(
                             phoneAccountHandle));
             returnConnection.setTelephonyConnectionService(this);
+            addConnectionRemovedListener(returnConnection);
         }
         return returnConnection;
     }
@@ -1918,10 +2412,20 @@
     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency,
                                      @Nullable String emergencyNumberAddress) {
         Phone chosenPhone = null;
+        if (isEmergency) {
+            return PhoneFactory.getPhone(PhoneUtils.getPhoneIdForECall());
+        }
         int subId = mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(accountHandle);
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
             int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
             chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
+        } else {
+            for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+                Call call = phone.getRingingCall();
+                if (call.getState().isRinging()) {
+                    return phone;
+                }
+            }
         }
         // If this is an emergency call and the phone we originally planned to make this call
         // with is not in service or was invalid, try to find one that is in service, using the
@@ -2296,6 +2800,16 @@
         return true;
     }
 
+    @Override
+    public void removeConnection(Connection connection) {
+        maybeEnableVideo(connection);
+        super.removeConnection(connection);
+        if (connection instanceof TelephonyConnection) {
+            removeConnectionRemovedListener((TelephonyConnection)connection);
+            fireOnConnectionRemoved((TelephonyConnection)connection);
+        }
+    }
+
     TelephonyConnection.TelephonyConnectionListener getTelephonyConnectionListener() {
         return mTelephonyConnectionListener;
     }
@@ -2339,6 +2853,22 @@
         }
     }
 
+    private void addConnectionRemovedListener(ConnectionRemovedListener l) {
+        mConnectionRemovedListeners.add(l);
+    }
+
+    private void removeConnectionRemovedListener(ConnectionRemovedListener l) {
+        if (l != null) {
+            mConnectionRemovedListeners.remove(l);
+        }
+    }
+
+    private void fireOnConnectionRemoved(TelephonyConnection conn) {
+        for (ConnectionRemovedListener l : mConnectionRemovedListeners) {
+            l.onConnectionRemoved(conn);
+        }
+    }
+
     /**
      * Create a new CDMA connection. CDMA connections have additional limitations when creating
      * additional calls which are handled in this method.  Specifically, CDMA has a "FLASH" command
@@ -2398,6 +2928,14 @@
         }
     }
 
+    private void maybeSendPhoneAccountUpdateEvent(TelephonyConnection telephonyConnection) {
+        if (telephonyConnection == null || telephonyConnection.getPhone() == null) {
+            return;
+        }
+        updatePhoneAccount(telephonyConnection,
+                mPhoneFactoryProxy.getPhone(telephonyConnection.getPhone().getPhoneId()));
+    }
+
     private void handleTtyModeChange(boolean isTtyEnabled) {
         Log.i(this, "handleTtyModeChange; isTtyEnabled=%b", isTtyEnabled);
         mIsTtyEnabled = isTtyEnabled;
@@ -2581,7 +3119,8 @@
      */
     public void maybeIndicateAnsweringWillDisconnect(@NonNull TelephonyConnection connection,
             @NonNull PhoneAccountHandle phoneAccountHandle) {
-        if (isCallPresentOnOtherSub(phoneAccountHandle)) {
+        if (isCallPresentOnOtherSub(phoneAccountHandle) &&
+            !isConcurrentCallsPossible()) {
             Log.i(this, "maybeIndicateAnsweringWillDisconnect; answering call %s will cause a call "
                     + "on another subscription to drop.", connection.getTelecomCallId());
             Bundle extras = new Bundle();
@@ -2645,4 +3184,270 @@
                     }
                 });
     }
+
+    /* Find if swap needs to be done on a connection or conference and send that information
+       to the handler for across sub use case
+     */
+    private void unholdDsdaCall(String callId) {
+        try {
+            Pair<TelephonyConnection, PhoneAccountHandle> pairToResume =
+                    getConnectionPhoneAccountPair(callId, "unhold");
+            // Let TelephonyConnection know that context based swap needs to be disabled so that
+            // it can invoke hold APIs based on that
+            TelephonyConnection connToResume = pairToResume.first;
+            connToResume.disableContextBasedSwap(true);
+
+            // Get connection to hold if any
+            Pair<TelephonyConnection, PhoneAccountHandle> pairToHold =
+                    getActiveDsdaConnectionPhoneAccountPair();
+            TelephonyConnection connToHold = pairToHold.first;
+            if (connToHold == null || Objects.equals(pairToHold.second,
+                    pairToResume.second)) {
+                // Single call unhold or same sub swap use case
+                // For same sub swap, let ImsPhoneCallTracker handle hold and resume
+                super.unhold(callId);
+                return;
+            }
+            // Let hold handler manage across sub swap (hold and resume)
+            mHoldHandler = new HoldAndSwapHandler(connToHold, connToResume);
+            prepareForAcrossSubHold(connToHold);
+            mHoldHandler.accept();
+        } catch (CallStateException e) {
+            // Not an instance of TelephonyConnection/ImsConference. Just log and return similar
+            // to SS/DSDS handling
+            Log.e(this, e, "unholdDsdaCall " + e);
+            return;
+        }
+    }
+
+    private void answerDsdaCall(String callId, int videoState) {
+        try {
+            Pair<TelephonyConnection, PhoneAccountHandle> pairToAnswer =
+                    getConnectionPhoneAccountPair(callId, "unhold");
+            TelephonyConnection connToAnswer = pairToAnswer.first;
+            // Let TelephonyConnection know that context based swap needs to be disabled so that
+            // it can invoke hold APIs based on that
+            connToAnswer.disableContextBasedSwap(true);
+            if (connToAnswer.getExtras() != null &&
+                connToAnswer.getExtras().getBoolean(
+                    Connection.EXTRA_ANSWERING_DROPS_FG_CALL, false)) {
+                // Pseudo DSDA use case
+                setupAnswerAndReleaseHandler(connToAnswer, videoState);
+                return;
+            }
+            // Get connection to hold if any
+            Pair<TelephonyConnection, PhoneAccountHandle> pairToHold =
+                    getActiveDsdaConnectionPhoneAccountPair();
+            TelephonyConnection connToHold = pairToHold.first;
+            if (connToHold == null || Objects.equals(pairToHold.second,
+                    pairToAnswer.second)) {
+                // Active call not there or is on the same sub as call to answer
+                // follow legacy behavior
+                super.answerVideo(callId, videoState);
+                return;
+            }
+            // Invoke handler as incoming call and active call are on different subs
+            mHoldHandler = new HoldAndAnswerHandler(connToHold, connToAnswer, videoState);
+            prepareForAcrossSubHold(connToHold);
+            mHoldHandler.accept();
+        } catch (CallStateException e) {
+            // Not an instance of TelephonyConnection/ImsConference. Just log and return similar
+            // to SS/DSDS handling
+            Log.e(this, e, "answerDsdaCall " + e);
+            return;
+        }
+    }
+
+    private void setupAnswerAndReleaseHandler(Connection conn, int videoState) {
+        mAnswerAndReleaseHandler =
+            new AnswerAndReleaseHandler(conn, videoState);
+        mAnswerAndReleaseHandler.addListener(mAnswerAndReleaseListener);
+        mAnswerAndReleaseHandler.checkAndAnswer(getAllConnections(),
+                getAllConferences());
+    }
+
+    /*
+     * Returns the Telephony connection with ACTIVE state.
+     */
+    private Connection getActiveConnection() {
+        for (Connection current : getAllConnections()) {
+            if (isTelephonyConnection(current) && current.getState() == Connection.STATE_ACTIVE) {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * Returns the instance of TelephonyConferenceBase with ACTIVE state.
+     */
+    private Conference getActiveConference() {
+        for (Conference current : getAllConferences()) {
+            if (isTelephonyConferenceBase(current) &&
+                    current.getState() == Connection.STATE_ACTIVE) {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * This function checks if there is an ACTIVE / HELD audio call.
+     */
+    private boolean hasActiveOrHeldAudioCall() {
+        for (Connection current : getAllConnections()) {
+            if (isTelephonyConnection(current) &&
+                (current.getState() == Connection.STATE_HOLDING ||
+                    current.getState() == Connection.STATE_ACTIVE) &&
+                VideoProfile.isAudioOnly(((TelephonyConnection)current).getVideoState())) {
+                return true;
+            }
+        }
+
+        for (Conference conference : getAllConferences()) {
+            if (isTelephonyConferenceBase(conference) &&
+                (conference.getState() == Connection.STATE_HOLDING ||
+                    conference.getState() == Connection.STATE_ACTIVE) &&
+                isImsConference(conference)) {
+                Connection conn = ((ImsConference)conference).getConferenceHost();
+                if (VideoProfile.isAudioOnly(((TelephonyConnection)conn).getVideoState())) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private Connection getRingingOrDialingConnection() {
+        for (Connection current : getAllConnections()) {
+            int state = current.getState();
+            if (state == Connection.STATE_RINGING || state == Connection.STATE_DIALING) {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    private Connection getRingingConnection() {
+        for (Connection current : getAllConnections()) {
+            if (isTelephonyConnection(current) &&
+                    current.getState() == Connection.STATE_RINGING) {
+                return current;
+            }
+        }
+        return null;
+    }
+
+    private boolean isAcrossSubHoldInProgress() {
+        return mHoldHandler != null;
+    }
+
+    private static boolean isConcurrentCallsPossible() {
+        return TelephonyManager.isConcurrentCallsPossible();
+    }
+
+    private static boolean isTelephonyConnection(Connection conn) {
+        return conn instanceof TelephonyConnection;
+    }
+
+    private static boolean isImsConference(Conference conf) {
+        return conf instanceof ImsConference;
+    }
+
+    private static boolean isTelephonyConferenceBase(Conference conn) {
+        return conn instanceof TelephonyConferenceBase;
+    }
+
+    /* Returns a pair of the active TelephonyConnection and PhoneAccountHandle for DSDA.
+     * Throws CallStateException when conference is not an ImsConference or
+     * when Connection is not a TelephonyConnection.
+     */
+    private Pair<TelephonyConnection, PhoneAccountHandle> getActiveDsdaConnectionPhoneAccountPair()
+            throws CallStateException {
+        //If non-DSDA use case, follow legacy behavior.
+        if (!isConcurrentCallsPossible()) {
+            return new Pair<>(null, null);
+        }
+        PhoneAccountHandle handle = null;
+        Connection activeConn = getActiveConnection();
+        Conference activeConf = getActiveConference();
+        if (activeConf != null) {
+            if (!isImsConference(activeConf)) {
+                throw new CallStateException("Not an instance of ImsConference.");
+            }
+            activeConn = ((ImsConference)activeConf).getConferenceHost();
+            handle = activeConf.getPhoneAccountHandle();
+            Log.d(this, "hold conference call.") ;
+        } else if (activeConn != null) {
+            handle = activeConn.getPhoneAccountHandle();
+        }
+        if (activeConn != null && !isTelephonyConnection(activeConn)) {
+            throw new CallStateException("Not an instance of TelephonyConnection.");
+        }
+        return new Pair<>((TelephonyConnection)activeConn, handle);
+    }
+
+    /* Returns connection or conference host connection corresponding to callId
+     * Throws CallStateException when conference is not an ImsConference or
+     * when Connection is not a TelephonyConnection
+     */
+    private Pair<TelephonyConnection, PhoneAccountHandle> getConnectionPhoneAccountPair(
+            String callId, String action) throws CallStateException {
+        Connection conn;
+        PhoneAccountHandle handle;
+        Conference conf = findConferenceForAction(callId, action);
+        if (!conf.equals(getNullConference())) {
+            // Operations on ImsConference act on the conference host. Send the host connection
+            // to hold handler to simplify handling conference use case
+            if (!isImsConference(conf)) {
+                throw new CallStateException("Not an instance of TelephonyConnection or" +
+                        "ImsConference");
+            }
+            conn = ((ImsConference)conf).getConferenceHost();
+            handle = conf.getPhoneAccountHandle();
+            Log.d(this, "action on conference call");
+        } else {
+            conn = findConnectionForAction(callId, action);
+            handle = conn.getPhoneAccountHandle();
+        }
+        if (!isTelephonyConnection(conn)) {
+            throw new CallStateException("Not an instance of TelephonyConnection or" +
+                    "ImsConference");
+        }
+        return new Pair<>((TelephonyConnection)conn, handle);
+    }
+
+    private void prepareForAcrossSubHold(TelephonyConnection telConn) {
+        mHoldHandler.addListener(mHoldListener);
+        telConn.disableContextBasedSwap(true);
+    }
+
+    /* Invoked when incoming call is accepted to disconnect dialing calls on the other sub */
+    public void maybeDisconnectDialingCallsOnOtherSubs
+            (@NonNull PhoneAccountHandle incomingHandle) {
+        Log.i(this, "maybeDisconnectCallsOnOtherSubs: check for calls not on %s", incomingHandle);
+        maybeDisconnectDialingCallsOnOtherSubs(getAllConnections(), incomingHandle);
+    }
+
+    private void maybeDisconnectDialingCallsOnOtherSubs(
+            @NonNull Collection<Connection>connections,
+            @NonNull PhoneAccountHandle incomingHandle) {
+        connections.stream()
+                .filter(c ->
+                        (c.getState() == Connection.STATE_DIALING)
+                                // Include any calls not on same sub as current connection.
+                                && !Objects.equals(c.getPhoneAccountHandle(), incomingHandle))
+                .forEach(c -> {
+                    if (c instanceof TelephonyConnection) {
+                        TelephonyConnection tc = (TelephonyConnection) c;
+                        if (!tc.shouldTreatAsEmergencyCall()) {
+                            Log.i(LOG_TAG, "maybeDisconnectDialingCallsOnOtherSubs: disconnect" +
+                                    " %s due to incoming call accepted on other sub.",
+                                    tc.getTelecomCallId());
+                            tc.hangup(android.telephony.DisconnectCause.LOCAL);
+                        }
+                    }
+                });
+    }
 }