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 可为以下类型的来电者提供协助:失聪者、听力障碍人士、语言障碍人士或需要语音以外服务的人。<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>了解详情</a>\n <br><br> - 系统会以信息转录的方式存储 RTT 通话\n <br> - 视频通话不支持 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);
+ }
+ }
+ });
+ }
}