Merge "Revert "Do not use unstable Android styable.CheckBoxPreference""
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 34c67a4..ba60d67 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -34,7 +34,6 @@
<protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
<protected-broadcast android:name="android.intent.action.DATA_STALL_DETECTED" />
<protected-broadcast android:name="android.intent.action.SIM_STATE_CHANGED" />
- <protected-broadcast android:name="android.telephony.action.NETWORK_SET_TIME" />
<protected-broadcast android:name="com.android.internal.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS" />
<protected-broadcast android:name="android.intent.action.ACTION_MDN_STATE_CHANGED" />
<protected-broadcast android:name="android.provider.Telephony.SPN_STRINGS_UPDATED" />
@@ -49,6 +48,7 @@
<protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
<protected-broadcast android:name="android.provider.Telephony.SMS_CB_RECEIVED" />
<protected-broadcast android:name="android.provider.action.SMS_EMERGENCY_CB_RECEIVED" />
+ <protected-broadcast android:name="android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED" />
<protected-broadcast android:name="android.provider.Telephony.SECRET_CODE" />
<protected-broadcast android:name= "com.android.internal.stk.command" />
<protected-broadcast android:name= "com.android.internal.stk.session_end" />
@@ -76,7 +76,7 @@
<protected-broadcast android:name= "com.android.imsconnection.DISCONNECTED" />
<protected-broadcast android:name= "com.android.intent.action.IMS_FEATURE_CHANGED" />
<protected-broadcast android:name= "com.android.intent.action.IMS_CONFIG_CHANGED" />
- <protected-broadcast android:name= "com.android.ims.REGISTRATION_ERROR" />
+ <protected-broadcast android:name= "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR" />
<protected-broadcast android:name= "com.android.phone.vvm.omtp.sms.REQUEST_SENT" />
<protected-broadcast android:name= "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT" />
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED" />
@@ -90,10 +90,14 @@
<protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.NETWORK_COUNTRY_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED" />
+ <protected-broadcast android:name= "android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
<!-- 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" />
+
<!-- Allows granting runtime permissions to telephony related components. -->
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
@@ -130,7 +134,7 @@
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE" />
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
- <uses-permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE" />
+ <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
@@ -209,6 +213,7 @@
ACTION_SIM_SLOT_STATUS_CHANGED broadcast to start activities
from the background. -->
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+ <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
<application android:name="PhoneApp"
android:persistent="true"
@@ -307,10 +312,19 @@
</intent-filter>
</activity>
+ <activity android:name="CdmaCallForwardOptions"
+ android:label="@string/labelCF"
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:theme="@style/DialerSettingsLight">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+
<activity android:name="GsmUmtsCallBarringOptions"
android:label="@string/labelCallBarring"
android:configChanges="orientation|screenSize|keyboardHidden"
- android:theme="@style/CallSettingsWithoutDividerTheme">
+ android:theme="@style/DialerSettingsLight">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -508,7 +522,7 @@
<receiver android:name="com.android.services.telephony.sip.SipIncomingCallReceiver">
<intent-filter>
- <action android:name="com.android.phone.SIP_INCOMING_CALL" />
+ <action android:name="android.net.sip.action.SIP_INCOMING_CALL" />
</intent-filter>
</receiver>
@@ -539,6 +553,12 @@
android:uiOptions="splitActionBarWhenNarrow">
</activity>
+ <service android:name="com.android.services.telephony.sip.components.TelephonySipService">
+ <intent-filter>
+ <action android:name="android.net.sip.action.START_SIP" />
+ </intent-filter>
+ </service>
+
<!-- End SIP -->
<activity android:name="MMIDialogActivity"
@@ -631,5 +651,33 @@
<action android:name="android.telephony.data.DataService" />
</intent-filter>
</service>
+
+ <provider
+ android:name="ServiceStateProvider"
+ android:authorities="service-state"
+ android:exported="true"
+ android:multiprocess="false"
+ android:singleUser="true"
+ android:writePermission="android.permission.MODIFY_PHONE_STATE"/>
+
+ <activity
+ android:name=".settings.RadioInfo"
+ android:label="@string/phone_info_label"
+ android:theme="@style/Theme.AppCompat.DayNight">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".settings.BandMode"
+ android:label="@string/band_mode_title"
+ android:theme="@style/Theme.AppCompat.DayNight">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.VOICE_LAUNCH" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/OWNERS b/OWNERS
index 849347f..3059d4d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,15 +1,15 @@
amitmahajan@google.com
breadley@google.com
fionaxu@google.com
-hallliu@google.com
jackyu@google.com
+hallliu@google.com
rgreenwalt@google.com
tgunn@google.com
-refuhoo@google.com
-mpq@google.com
jminjie@google.com
shuoq@google.com
-paulye@google.com
+refuhoo@google.com
nazaninb@google.com
sarahchin@google.com
dbright@google.com
+xiaotonj@google.com
+
diff --git a/apex/Android.bp b/apex/Android.bp
index 16bcb72..a7137d9 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -14,7 +14,7 @@
apex {
name: "com.android.telephony",
manifest: "apex_manifest.json",
- apps: ["StkLib"],
+ //apps: ["StkLib"],
defaults:["com.android.telephony-defaults"],
}
@@ -30,4 +30,4 @@
// This will use com.android.telephony.x509.pem (the cert) and
// com.android.telephony.pk8 (the private key)
certificate: "com.android.telephony",
-}
\ No newline at end of file
+}
diff --git a/res/layout/emergency_dialer.xml b/res/layout/emergency_dialer.xml
index d14a679..ab32c62 100644
--- a/res/layout/emergency_dialer.xml
+++ b/res/layout/emergency_dialer.xml
@@ -58,8 +58,6 @@
android:accessibilityPaneTitle="@string/pane_title_emergency_dialpad"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingLeft="36dp"
- android:paddingRight="36dp"
android:paddingBottom="@dimen/dialpad_bottom_padding"
android:visibility="visible">
<LinearLayout
@@ -73,6 +71,7 @@
android:id="@+id/emergency_action_group"
android:layout_height="64dp"
android:layout_width="match_parent"
+ android:layout_marginHorizontal="36dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="24dp">
@@ -155,8 +154,14 @@
</FrameLayout>
</com.android.phone.EmergencyActionGroup>
-
+ <Space
+ android:id="@+id/emergency_info_dialpad_spacer"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
<include layout="@layout/dialpad_view_unthemed"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
android:theme="?attr/dialpadTheme" />
</LinearLayout>
diff --git a/res/layout/sim_ndp.xml b/res/layout/sim_ndp.xml
index 5e3c472..5f03d7b 100644
--- a/res/layout/sim_ndp.xml
+++ b/res/layout/sim_ndp.xml
@@ -29,6 +29,7 @@
android:layout_centerInParent="true">
<TextView
+ android:id="@+id/perso_subtype_text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/res/values-af/config.xml b/res/values-af/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-af/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-am/config.xml b/res/values-am/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-am/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ar/config.xml b/res/values-ar/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ar/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-as/config.xml b/res/values-as/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-as/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-az/config.xml b/res/values-az/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-az/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-b+sr+Latn/config.xml b/res/values-b+sr+Latn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-b+sr+Latn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-be/config.xml b/res/values-be/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-be/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-bg/config.xml b/res/values-bg/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-bg/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-bn/config.xml b/res/values-bn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-bn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-bs/config.xml b/res/values-bs/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-bs/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ca/config.xml b/res/values-ca/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ca/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-cs/config.xml b/res/values-cs/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-cs/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-da/config.xml b/res/values-da/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-da/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-de/config.xml b/res/values-de/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-de/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-el/config.xml b/res/values-el/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-el/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rAU/config.xml b/res/values-en-rAU/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rAU/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rCA/config.xml b/res/values-en-rCA/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rCA/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rGB/config.xml b/res/values-en-rGB/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rGB/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rIN/config.xml b/res/values-en-rIN/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rIN/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rXC/config.xml b/res/values-en-rXC/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rXC/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-es-rUS/config.xml b/res/values-es-rUS/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-es-rUS/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-es/config.xml b/res/values-es/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-es/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-et/config.xml b/res/values-et/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-et/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-eu/config.xml b/res/values-eu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-eu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fa/config.xml b/res/values-fa/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fa/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fi/config.xml b/res/values-fi/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fi/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fr-rCA/config.xml b/res/values-fr-rCA/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fr-rCA/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fr/config.xml b/res/values-fr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-gl/config.xml b/res/values-gl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-gl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-gu/config.xml b/res/values-gu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-gu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-h500dp/dimens.xml b/res/values-h500dp/dimens.xml
index 2c7c797..d74f0a1 100644
--- a/res/values-h500dp/dimens.xml
+++ b/res/values-h500dp/dimens.xml
@@ -16,5 +16,5 @@
-->
<resources>
- <dimen name="dialpad_bottom_padding">36dp</dimen>
+ <dimen name="dialpad_bottom_padding">16dp</dimen>
</resources>
\ No newline at end of file
diff --git a/res/values-hi/config.xml b/res/values-hi/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hi/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-hr/config.xml b/res/values-hr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-hu/config.xml b/res/values-hu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-hy/config.xml b/res/values-hy/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hy/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-in/config.xml b/res/values-in/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-in/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-is/config.xml b/res/values-is/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-is/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-it/config.xml b/res/values-it/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-it/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-iw/config.xml b/res/values-iw/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-iw/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ja/config.xml b/res/values-ja/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ja/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ka/config.xml b/res/values-ka/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ka/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-kk/config.xml b/res/values-kk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-kk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-km/config.xml b/res/values-km/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-km/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-kn/config.xml b/res/values-kn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-kn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ko/config.xml b/res/values-ko/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ko/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ky/config.xml b/res/values-ky/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ky/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-lo/config.xml b/res/values-lo/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-lo/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-lt/config.xml b/res/values-lt/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-lt/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-lv/config.xml b/res/values-lv/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-lv/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-mk/config.xml b/res/values-mk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-mk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ml/config.xml b/res/values-ml/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ml/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-mn/config.xml b/res/values-mn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-mn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-mr/config.xml b/res/values-mr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-mr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ms/config.xml b/res/values-ms/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ms/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-my/config.xml b/res/values-my/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-my/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-nb/config.xml b/res/values-nb/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-nb/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ne/config.xml b/res/values-ne/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ne/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-nl/config.xml b/res/values-nl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-nl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-or/config.xml b/res/values-or/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-or/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pa/config.xml b/res/values-pa/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pa/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pl/config.xml b/res/values-pl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pt-rPT/config.xml b/res/values-pt-rPT/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pt-rPT/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pt/config.xml b/res/values-pt/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pt/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ro/config.xml b/res/values-ro/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ro/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ru/config.xml b/res/values-ru/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ru/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-si/config.xml b/res/values-si/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-si/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sk/config.xml b/res/values-sk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sl/config.xml b/res/values-sl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sq/config.xml b/res/values-sq/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sq/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sr/config.xml b/res/values-sr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sv/config.xml b/res/values-sv/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sv/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sw/config.xml b/res/values-sw/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sw/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ta/config.xml b/res/values-ta/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ta/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-te/config.xml b/res/values-te/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-te/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-th/config.xml b/res/values-th/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-th/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-tl/config.xml b/res/values-tl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-tl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-tr/config.xml b/res/values-tr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-tr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-uk/config.xml b/res/values-uk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-uk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ur/config.xml b/res/values-ur/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ur/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-uz/config.xml b/res/values-uz/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-uz/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-vi/config.xml b/res/values-vi/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-vi/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zh-rCN/config.xml b/res/values-zh-rCN/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zh-rCN/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zh-rHK/config.xml b/res/values-zh-rHK/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zh-rHK/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zh-rTW/config.xml b/res/values-zh-rTW/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zh-rTW/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zu/config.xml b/res/values-zu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 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.
- -->
-
-<!-- NOTE: Many variables that used to be in this file have been migrated to
- CarrierConfigManager.java. Please consider whether new variables belong
- there before adding to this file. Variables here should be more closely
- related to devices than to networks. -->
-
-<!-- Phone app resources that may need to be customized
- for different hardware or product builds. -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8d84baf..fca8acf 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -15,21 +15,6 @@
-->
<resources>
- <!-- Base attributes available to CheckBoxPreference. Copied from frameworks/base/core/res. -->
- <declare-styleable name="CheckBoxPreference">
- <!-- The summary for the Preference in a PreferenceActivity screen when the
- CheckBoxPreference is checked. If separate on/off summaries are not
- needed, the summary attribute can be used instead. -->
- <attr name="android:summaryOn" />
- <!-- The summary for the Preference in a PreferenceActivity screen when the
- CheckBoxPreference is unchecked. If separate on/off summaries are not
- needed, the summary attribute can be used instead. -->
- <attr name="android:summaryOff" />
- <!-- The state (true for on, or false for off) that causes dependents to be disabled. By default,
- dependents will be disabled when this is unchecked, so the value of this preference is false. -->
- <attr name="android:disableDependentsState" />
- </declare-styleable>
-
<declare-styleable name="EditPhoneNumberPreference">
<!-- The enable button text. -->
<attr name="enableButtonText" format="string" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 8c36b1a..7e71068 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -209,7 +209,7 @@
<!-- The package to handle visual voicemail if the default dialer or the package
CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING does not handle it -->
- <string name="system_visual_voicemail_client"></string>
+ <string name="system_visual_voicemail_client" translatable="false"/>
<!-- Flag to enable VVM3 visual voicemail. VVM3 is used by Verizon Wireless. -->
<bool name="vvm3_enabled">false</bool>
@@ -236,10 +236,10 @@
<bool name="config_support_rtt">false</bool>
<!-- String indicating the package name of the device ImsService implementation for MMTEL. -->
- <string name="config_ims_mmtel_package"></string>
+ <string name="config_ims_mmtel_package" translatable="false"/>
<!-- String indicating the package name of the device ImsService implementation for RCS. -->
- <string name="config_ims_rcs_package"></string>
+ <string name="config_ims_rcs_package" translatable="false"/>
<!-- The package name for the platform number verification supplier app. -->
<string name="platform_number_verification_package" translatable="false"></string>
@@ -257,6 +257,24 @@
audio stream which the remote party will be able to hear. -->
<bool name="config_support_telephony_audio_device">false</bool>
+ <!-- Whether the device supports dialing emergency RTT calls when there's no SIM card installed
+ -->
+ <bool name="config_support_simless_emergency_rtt">false</bool>
+
+ <!-- Array of countries that support sim-less emergency RTT calls. Values should be
+ ISO3166 country codes in lowercase. -->
+ <string-array name="config_simless_emergency_rtt_supported_countries" translatable="false">
+ <item>us</item>
+ </string-array>
+
+ <string-array translatable="false" name="config_volte_provision_error_on_publish_response">
+ <item>403 not authorized for presence</item>
+ </string-array>
+
+ <string-array translatable="false" name="config_rcs_provision_error_on_publish_response">
+ <item>404 not found</item>
+ </string-array>
+
<!-- The country list that shortcut view can be enabled. -->
<string-array name="config_countries_to_enable_shortcut_view" translatable="false">
</string-array>
@@ -267,4 +285,18 @@
slot. If true, telephony will always try to place the emergency call on the subscription
associated with default data first, instead of using the default voice configuration.-->
<bool name="config_gnss_supl_requires_default_data_for_emergency">false</bool>
+
+ <!-- Whether a device can have 5G connection in DSDS mode. It should be true by default, but
+ in some devices per modem limitation 5G network can't be connected if two or more SIMs
+ are active simultaneously. In that case, this value should be false. -->
+ <bool name="config_5g_connection_in_dsds_mode">true</bool>
+
+ <!-- Vibrator pattern to be used as the default for notifications
+ that specify DEFAULT_VIBRATE. -->
+ <integer-array name="config_defaultNotificationVibePattern">
+ <item>0</item>
+ <item>350</item>
+ <item>250</item>
+ <item>350</item>
+ </integer-array>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6ac95c3..b74b2ff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -273,6 +273,15 @@
<string name="sum_cfnrc_disabled">Off</string>
<!-- Error message displayed after failing to disable forwarding calls when the phone is unreachable -->
<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>
+
+ <!-- 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>
+ <string name="enable_cdma_cw">Turn on</string>
+ <string name="disable_cdma_cw">Cancel</string>
+ <string name="cdma_call_waiting_in_ims_on">CDMA Call Waiting under IMS On</string>
+ <string name="cdma_call_waiting_in_ims_off">CDMA Call Waiting under IMS Off</string>
<!-- Title of the progress dialog displayed while updating Call settings -->
<string name="updating_title">Call settings</string>
@@ -1447,6 +1456,17 @@
<string name="alert_dialog_no">No</string>
<!-- ECM: ECM exit dialog choice -->
<string name="alert_dialog_dismiss">Dismiss</string>
+ <!-- ECM: Notification body wihout data restriction hint -->
+ <string name="phone_in_ecm_call_notification_text_without_data_restriction_hint">The phone is in emergency callback mode</string>
+ <!-- ECM: Displays the time when ECM will end without data restriction hint, Example: "Until 10:45 AM" -->
+ <string name="phone_in_ecm_notification_complete_time_without_data_restriction_hint">Until <xliff:g id="completeTime">%s</xliff:g></string>
+ <!-- ECM: Dialog box message without data restriction hint for exiting from the notifications screen -->
+ <plurals name="alert_dialog_exit_ecm_without_data_restriction_hint">
+ <!-- number of minutes is one -->
+ <item quantity="one">The phone will be in emergency callback mode for <xliff:g id="count">%s</xliff:g> minute.\nDo you want to exit now?</item>
+ <!-- number of minutes is not equal to one -->
+ <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>
<!-- 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. -->
@@ -1843,8 +1863,6 @@
<string name="messageCallBarring">Enter password</string>
<!-- Call barring settings screen, section heading -->
<string name="call_barring_settings">Call barring settings</string>
- <!-- Call barring settings screen, deactivate all call barring settings -->
- <string name="call_barring_deactivate_all_no_password">Deactivate all call barring settings?</string>
<!-- In-call screen: error message shown when the user attempts to place a call, but the network
does not have enough resources (e.g. it is busy) and the call cannot be placed. -->
<string name="callFailed_NetworkBusy">Network is busy. Please try your call again later.</string>
@@ -2144,5 +2162,4 @@
<string name="carrier_provisioning">Carrier Provisioning Info</string>
<!-- Trigger Carrier Provisioning [CHAR LIMIT=NONE] -->
<string name="trigger_carrier_provisioning">Trigger Carrier Provisioning</string>
-
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d131bd8..e8a0bed 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -260,7 +260,7 @@
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
<item name="emergencyButtonBackgroundColor">#3cffffff</item>
- <item name="dialpadTheme">@style/Dialpad_DarkTransparent</item>
+ <item name="dialpadTheme">@style/Dialpad_DarkTransparent.Emergency</item>
</style>
<style name="EmergencyDialerThemeDark" parent="@style/EmergencyDialerTheme">
@@ -268,7 +268,15 @@
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="emergencyButtonBackgroundColor">#19414549</item>
<item name="android:colorControlHighlight">#40000000</item>
- <item name="dialpadTheme">@style/Dialpad_LightTransparent</item>
+ <item name="dialpadTheme">@style/Dialpad_LightTransparent.Emergency</item>
+ </style>
+
+ <style name="Dialpad_LightTransparent.Emergency">
+ <item name="dialpad_delete_padding">16dp</item>
+ </style>
+
+ <style name="Dialpad_DarkTransparent.Emergency">
+ <item name="dialpad_delete_padding">16dp</item>
</style>
<style name="EmergencyDialerAlertDialogTheme"
diff --git a/res/xml/cdma_call_privacy.xml b/res/xml/cdma_call_privacy.xml
index 1aeeefe..a16a504 100644
--- a/res/xml/cdma_call_privacy.xml
+++ b/res/xml/cdma_call_privacy.xml
@@ -7,4 +7,14 @@
android:title="@string/voice_privacy"
android:persistent="false"
android:summary="@string/voice_privacy_summary"/>
+
+ <PreferenceScreen
+ android:key="call_forwarding_key"
+ android:title="@string/labelCF"
+ android:persistent="false" />
+
+ <com.android.phone.CdmaCallWaitingPreference
+ android:key="call_waiting_key"
+ android:title="@string/labelCW"
+ android:persistent="false" />
</PreferenceScreen>
diff --git a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
index 3212c00..2dbd707 100644
--- a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
+++ b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
@@ -84,7 +84,7 @@
}
private boolean isRunningInSystemUser() {
- return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
+ return UserHandle.myUserId() == UserHandle.SYSTEM.getIdentifier();
}
private static void log(String msg) {
diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java
index ded16df..813ba51 100644
--- a/sip/src/com/android/services/telephony/sip/SipSettings.java
+++ b/sip/src/com/android/services/telephony/sip/SipSettings.java
@@ -42,6 +42,7 @@
import com.android.phone.R;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
@@ -238,9 +239,9 @@
}
private void processActiveProfilesFromSipService() {
- SipProfile[] activeList = {};
+ List<SipProfile> activeList = new ArrayList<>();
try {
- activeList = mSipManager.getListOfProfiles();
+ activeList = mSipManager.getProfiles();
} catch (SipException e) {
log("SipManager could not retrieve SIP profiles: " + e);
}
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index ff38754..828174e 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -29,12 +29,11 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import com.android.phone.PhoneGlobals;
-import com.android.phone.R;
-import com.android.server.sip.SipService;
import java.io.IOException;
import java.util.ArrayList;
@@ -46,6 +45,7 @@
"com.android.services.telephony.sip.incoming_call_intent";
static final String EXTRA_PHONE_ACCOUNT =
"com.android.services.telephony.sip.phone_account";
+ static final String PHONE_PACKAGE = "com.android.phone";
private SipUtil() {
}
@@ -53,9 +53,9 @@
public static boolean isVoipSupported(Context context) {
return SipManager.isVoipSupported(context) &&
context.getResources().getBoolean(
- com.android.internal.R.bool.config_built_in_sip_phone) &&
- context.getResources().getBoolean(
- com.android.internal.R.bool.config_voice_capable);
+ com.android.internal.R.bool.config_built_in_sip_phone)
+ && ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+ .isVoiceCapable();
}
static PendingIntent createIncomingCallPendingIntent(
@@ -190,7 +190,10 @@
// Migrate SIP database from DE->CE storage if the device has just upgraded.
possiblyMigrateSipDb(phoneGlobalsContext);
// Wait until boot complete to start SIP so that it has access to CE storage.
- SipService.start(phoneGlobalsContext);
+ Intent startSipIntent = new Intent();
+ startSipIntent.setAction(SipManager.ACTION_START_SIP);
+ startSipIntent.setPackage(PHONE_PACKAGE);
+ phoneGlobalsContext.startService(startSipIntent);
}
/**
diff --git a/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java b/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java
new file mode 100644
index 0000000..5b863b1
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.sip.components;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.sip.SipManager;
+import android.os.IBinder;
+
+import com.android.server.sip.SipService;
+
+/**
+ * This class represents telephony SIP service which handle start SIP service requests from
+ * Telephony.
+ */
+public class TelephonySipService extends Service {
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if ((intent != null)
+ && SipManager.ACTION_START_SIP.equals(intent.getAction())) {
+ SipService.start(this);
+ }
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/src/com/android/phone/CallBarringDeselectAllPreference.java b/src/com/android/phone/CallBarringDeselectAllPreference.java
index e9310f8..7191937 100644
--- a/src/com/android/phone/CallBarringDeselectAllPreference.java
+++ b/src/com/android/phone/CallBarringDeselectAllPreference.java
@@ -19,12 +19,9 @@
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.EditText;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.phone.settings.fdn.EditPinPreference;
/**
@@ -34,9 +31,6 @@
private static final String LOG_TAG = "CallBarringDeselectAllPreference";
private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
- private boolean mShowPassword;
- private Phone mPhone;
-
/**
* CallBarringDeselectAllPreference constructor.
*
@@ -49,28 +43,10 @@
@Override
protected void showDialog(Bundle state) {
- // Finds out if the password field should be shown or not.
- ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
- mShowPassword = !(imsPhone != null && imsPhone.isUtEnabled());
-
- // Selects dialog message depending on if the password field is shown or not.
- setDialogMessage(getContext().getString(mShowPassword
- ? R.string.messageCallBarring : R.string.call_barring_deactivate_all_no_password));
-
- if (DBG) {
- Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword);
- }
-
+ setDialogMessage(getContext().getString(R.string.messageCallBarring));
super.showDialog(state);
}
- void init(Phone phone) {
- if (DBG) {
- Log.d(LOG_TAG, "init: phoneId = " + phone.getPhoneId());
- }
- mPhone = phone;
- }
-
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
@@ -78,20 +54,7 @@
final EditText editText = (EditText) view.findViewById(android.R.id.edit);
if (editText != null) {
// Hide the input-text-line if the password is not shown.
- editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+ editText.setVisibility(View.VISIBLE);
}
}
-
- @Override
- protected boolean needInputMethod() {
- // Input method should only be displayed if the password-field is shown.
- return mShowPassword;
- }
-
- /**
- * Returns whether the password field is shown.
- */
- boolean isPasswordShown() {
- return mShowPassword;
- }
}
diff --git a/src/com/android/phone/CallBarringEditPreference.java b/src/com/android/phone/CallBarringEditPreference.java
index edff1e3..b8e3250 100644
--- a/src/com/android/phone/CallBarringEditPreference.java
+++ b/src/com/android/phone/CallBarringEditPreference.java
@@ -35,10 +35,10 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.phone.settings.fdn.EditPinPreference;
import java.lang.ref.WeakReference;
@@ -57,10 +57,7 @@
private CharSequence mDisableText;
private CharSequence mSummaryOn;
private CharSequence mSummaryOff;
- private CharSequence mDialogMessageEnabled;
- private CharSequence mDialogMessageDisabled;
private int mButtonClicked;
- private boolean mShowPassword;
private final MyHandler mHandler = new MyHandler(this);
private Phone mPhone;
private TimeConsumingPreferenceListener mTcpListener;
@@ -90,10 +87,6 @@
typedArray = context.obtainStyledAttributes(attrs,
R.styleable.CallBarringEditPreference, 0, R.style.EditPhoneNumberPreference);
mFacility = typedArray.getString(R.styleable.CallBarringEditPreference_facility);
- mDialogMessageEnabled = typedArray.getString(
- R.styleable.CallBarringEditPreference_dialogMessageEnabledNoPwd);
- mDialogMessageDisabled = typedArray.getString(
- R.styleable.CallBarringEditPreference_dialogMessageDisabledNoPwd);
typedArray.recycle();
}
@@ -116,7 +109,7 @@
if (!skipReading) {
// Query call barring status
mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
- MyHandler.MESSAGE_GET_CALL_BARRING), 0);
+ MyHandler.MESSAGE_GET_CALL_BARRING), CommandsInterface.SERVICE_CLASS_VOICE);
if (mTcpListener != null) {
mTcpListener.onStarted(this, true);
}
@@ -130,29 +123,8 @@
}
@Override
- protected boolean needInputMethod() {
- // Input method should only be displayed if the password-field is shown.
- return mShowPassword;
- }
-
- void setInputMethodNeeded(boolean needed) {
- mShowPassword = needed;
- }
-
- @Override
protected void showDialog(Bundle state) {
- setShowPassword();
- if (mShowPassword) {
- setDialogMessage(getContext().getString(R.string.messageCallBarring));
- } else {
- setDialogMessage(mIsActivated ? mDialogMessageEnabled : mDialogMessageDisabled);
- }
-
- if (DBG) {
- Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword
- + ", mIsActivated: " + mIsActivated);
- }
-
+ setDialogMessage(getContext().getString(R.string.messageCallBarring));
super.showDialog(state);
}
@@ -204,8 +176,7 @@
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
editText.setKeyListener(DigitsKeyListener.getInstance());
- // Hide the input-text-line if the password is not shown.
- editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+ editText.setVisibility(View.VISIBLE);
}
}
@@ -217,17 +188,14 @@
+ positiveResult);
}
if (mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
- String password = null;
- if (mShowPassword) {
- password = getEditText().getText().toString();
+ String password = getEditText().getText().toString();
- // Check if the password is valid.
- if (password == null || password.length() != PW_LENGTH) {
- Toast.makeText(getContext(),
- getContext().getString(R.string.call_barring_right_pwd_number),
- Toast.LENGTH_SHORT).show();
- return;
- }
+ // Check if the password is valid.
+ if (password == null || password.length() != PW_LENGTH) {
+ Toast.makeText(getContext(),
+ getContext().getString(R.string.call_barring_right_pwd_number),
+ Toast.LENGTH_SHORT).show();
+ return;
}
if (DBG) {
@@ -235,7 +203,8 @@
}
// Send set call barring message to RIL layer.
mPhone.setCallBarring(mFacility, !mIsActivated, password,
- mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING), 0);
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING),
+ CommandsInterface.SERVICE_CLASS_VOICE);
if (mTcpListener != null) {
mTcpListener.onStarted(this, false);
}
@@ -254,11 +223,6 @@
notifyDependencyChange(shouldDisableDependents());
}
- private void setShowPassword() {
- ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
- mShowPassword = !(imsPhone != null && imsPhone.isUtEnabled());
- }
-
@Override
public boolean shouldDisableDependents() {
return mIsActivated;
@@ -310,16 +274,6 @@
pref.mTcpListener.onFinished(pref, false);
} else {
pref.mTcpListener.onFinished(pref, true);
- ImsPhone imsPhone = pref.mPhone != null
- ? (ImsPhone) pref.mPhone.getImsPhone() : null;
- if (!pref.mShowPassword && (imsPhone == null || !imsPhone.isUtEnabled())) {
- // Re-enable password when rejected from NW and modem would perform CSFB
- pref.mShowPassword = true;
- if (DBG) {
- Log.d(LOG_TAG,
- "handleGetCallBarringResponse: mShowPassword changed for CSFB");
- }
- }
}
// Unsuccessful query for call barring.
@@ -374,7 +328,7 @@
"",
obtainMessage(MESSAGE_GET_CALL_BARRING, 0, MESSAGE_SET_CALL_BARRING,
ar.exception),
- 0);
+ CommandsInterface.SERVICE_CLASS_VOICE);
}
}
}
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 3f57cae..ef83ead 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -97,6 +97,7 @@
"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 Phone mPhone;
private ImsManager mImsMgr;
@@ -396,11 +397,10 @@
} else {
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
prefSet.removePreference(fdnButton);
-
- if (!carrierConfig.getBoolean(
- CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
- addPreferencesFromResource(R.xml.cdma_call_privacy);
- }
+ addPreferencesFromResource(R.xml.cdma_call_privacy);
+ CdmaVoicePrivacySwitchPreference buttonVoicePrivacy =
+ (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+ buttonVoicePrivacy.setPhone(mPhone);
} else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
prefSet.removePreference(fdnButton);
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 76f17b1..8e0b685 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -122,7 +122,27 @@
Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked + ", positiveResult=" + positiveResult);
// 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 (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+ if (isUnknownStatus() && this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+ int action = (mButtonClicked == DialogInterface.BUTTON_POSITIVE) ?
+ CommandsInterface.CF_ACTION_REGISTRATION :
+ CommandsInterface.CF_ACTION_DISABLE;
+ final String number = (action == CommandsInterface.CF_ACTION_DISABLE) ?
+ "" : getPhoneNumber();
+
+ Log.d(LOG_TAG, "reason=" + reason + ", action=" + action + ", number=" + number);
+
+ // Display no forwarding number while we're waiting for confirmation.
+ setSummaryOff("");
+
+ mPhone.setCallForwardingOption(action,
+ reason,
+ number,
+ mServiceClass,
+ 0,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
+ action,
+ MyHandler.MESSAGE_SET_CF));
+ } else if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
CommandsInterface.CF_ACTION_REGISTRATION :
CommandsInterface.CF_ACTION_DISABLE;
@@ -159,6 +179,7 @@
mPhone.setCallForwardingOption(action,
reason,
number,
+ mServiceClass,
time,
mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
action,
@@ -193,6 +214,7 @@
Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
}
+ setUnknownStatus(callForwardInfo.status == CommandsInterface.SS_STATUS_UNKNOWN);
setToggled(callForwardInfo.status == 1);
boolean displayVoicemailNumber = false;
if (TextUtils.isEmpty(callForwardInfo.number)) {
@@ -216,7 +238,7 @@
*/
void startCallForwardOptionsQuery() {
if (!mCallForwardByUssd) {
- mPhone.getCallForwardingOption(reason,
+ mPhone.getCallForwardingOption(reason, mServiceClass,
mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
// unused in this case
CommandsInterface.CF_ACTION_DISABLE,
@@ -342,6 +364,7 @@
AsyncResult ar = (AsyncResult) msg.obj;
callForwardInfo = null;
+ boolean summaryOff = false;
if (ar.exception != null) {
Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
if (ar.exception instanceof CommandException) {
@@ -359,9 +382,8 @@
mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
}
CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
- if (cfInfoArray.length == 0) {
+ if (cfInfoArray == null || cfInfoArray.length == 0) {
Log.d(LOG_TAG, "handleGetCFResponse: cfInfoArray.length==0");
- setEnabled(false);
mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
} else {
for (int i = 0, length = cfInfoArray.length; i < length; i++) {
@@ -372,10 +394,16 @@
CallForwardInfo info = cfInfoArray[i];
handleCallForwardResult(info);
+ summaryOff = (info.status == CommandsInterface.SS_STATUS_UNKNOWN);
+
+ if (ar.userObj instanceof Throwable) {
+ Log.d(LOG_TAG, "Skipped duplicated error dialog");
+ continue;
+ }
+
// Show an alert if we got a success response but
// with unexpected values.
- // Currently only handle the fail-to-disable case
- // since we haven't observed fail-to-enable.
+ // Handle the fail-to-disable case.
if (msg.arg2 == MESSAGE_SET_CF &&
msg.arg1 == CommandsInterface.CF_ACTION_DISABLE &&
info.status == 1) {
@@ -399,7 +427,21 @@
}
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setNeutralButton(R.string.close_dialog, null);
- builder.setTitle(getContext().getText(R.string.error_updating_title));
+ builder.setTitle(getContext()
+ .getText(R.string.error_updating_title));
+ builder.setMessage(s);
+ builder.setCancelable(true);
+ builder.create().show();
+ } else if (msg.arg2 == MESSAGE_SET_CF &&
+ msg.arg1 == CommandsInterface.CF_ACTION_REGISTRATION &&
+ info.status == 0) {
+ // Handle the fail-to-enable case.
+ CharSequence s = getContext()
+ .getText(R.string.registration_cf_forbidden);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setNeutralButton(R.string.close_dialog, null);
+ builder.setTitle(getContext()
+ .getText(R.string.error_updating_title));
builder.setMessage(s);
builder.setCancelable(true);
builder.create().show();
@@ -412,7 +454,15 @@
// Now whether or not we got a new number, reset our enabled
// summary text since it may have been replaced by an empty
// placeholder.
- updateSummaryText();
+ // for CDMA, doesn't display summary.
+ if (summaryOff) {
+ setSummaryOff("");
+ } else {
+ // Now whether or not we got a new number, reset our enabled
+ // summary text since it may have been replaced by an empty
+ // placeholder.
+ updateSummaryText();
+ }
}
private void handleSetCFResponse(Message msg) {
@@ -421,9 +471,19 @@
Log.d(LOG_TAG, "handleSetCFResponse: ar.exception=" + ar.exception);
// setEnabled(false);
}
+
+ if (ar.result != null) {
+ int arr = (int)ar.result;
+ if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+ Log.d(LOG_TAG, "handleSetCFResponse: no need to re get in CDMA");
+ mTcpListener.onFinished(CallForwardEditPreference.this, false);
+ return;
+ }
+ }
+
Log.d(LOG_TAG, "handleSetCFResponse: re get");
if (!mCallForwardByUssd) {
- mPhone.getCallForwardingOption(reason,
+ mPhone.getCallForwardingOption(reason, mServiceClass,
obtainMessage(MESSAGE_GET_CF, msg.arg1, MESSAGE_SET_CF, ar.exception));
} else {
mHandler.sendMessage(mHandler.obtainMessage(mHandler.MESSAGE_GET_CF_USSD,
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 0af1b23..23a9649 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -21,6 +21,7 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
+import android.media.AudioSystem;
import android.media.ToneGenerator;
import android.os.AsyncResult;
import android.os.Handler;
@@ -392,10 +393,10 @@
try {
int stream;
if (mBluetoothHeadset != null) {
- stream = isBluetoothAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO :
- AudioManager.STREAM_VOICE_CALL;
+ stream = isBluetoothAudioOn() ? AudioSystem.STREAM_BLUETOOTH_SCO :
+ AudioSystem.STREAM_VOICE_CALL;
} else {
- stream = AudioManager.STREAM_VOICE_CALL;
+ stream = AudioSystem.STREAM_VOICE_CALL;
}
toneGenerator = new ToneGenerator(stream, toneVolume);
// if (DBG) log("- created toneGenerator: " + toneGenerator);
@@ -616,7 +617,7 @@
for (int i = 0; i < subInfos.size(); i++) {
int subId = subInfos.get(i).getSubscriptionId();
if (!mPhoneStateListeners.containsKey(subId)) {
- CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener();
+ CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
mTelephonyManager.createForSubscriptionId(subId).listen(listener,
PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
| PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
@@ -768,8 +769,11 @@
};
private class CallNotifierPhoneStateListener extends PhoneStateListener {
- public CallNotifierPhoneStateListener() {
+ private final int mSubId;
+
+ CallNotifierPhoneStateListener(int subId) {
super();
+ this.mSubId = subId;
}
@Override
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 3812b2f..f181e9f 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2015, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -37,9 +38,11 @@
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.service.carrier.CarrierIdentifier;
import android.service.carrier.CarrierService;
@@ -47,6 +50,8 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
@@ -71,6 +76,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
/**
* CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
@@ -95,8 +101,12 @@
private PersistableBundle[] mOverrideConfigs;
// Service connection for binding to config app.
private CarrierServiceConnection[] mServiceConnection;
- // Whether we have sent config change bcast for each phone id.
+ // Whether we are bound to a service for each phone
+ private boolean[] mServiceBound;
+ // Whether we have sent config change broadcast for each phone id.
private boolean[] mHasSentConfigChange;
+ // Whether the broadcast was sent from EVENT_SYSTEM_UNLOCKED, to track rebroadcasts
+ private boolean[] mFromSystemUnlocked;
// SubscriptionInfoUpdater
private final SubscriptionInfoUpdater mSubscriptionInfoUpdater;
@@ -151,6 +161,12 @@
// SharedPreferences key for last known build fingerprint.
private static final String KEY_FINGERPRINT = "build_fingerprint";
+ // Argument for #dump that indicates we should also call the default and specified carrier
+ // service's #dump method. In multi-SIM devices, it's possible that carrier A is on SIM 1 and
+ // carrier B is on SIM 2, in which case we should not dump carrier B's service when carrier A
+ // requested the dump.
+ private static final String DUMP_ARG_REQUESTING_PACKAGE = "--requesting-package";
+
// Handler to process various events.
//
// For each phoneId, the event sequence should be:
@@ -172,42 +188,41 @@
@Override
public void handleMessage(Message msg) {
final int phoneId = msg.arg1;
- logWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+ logdWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
if (!SubscriptionManager.isValidPhoneId(phoneId)
&& msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
return;
}
switch (msg.what) {
- case EVENT_CLEAR_CONFIG:
- {
+ case EVENT_CLEAR_CONFIG: {
clearConfigForPhone(phoneId, true);
break;
}
- case EVENT_SYSTEM_UNLOCKED:
- {
+ case EVENT_SYSTEM_UNLOCKED: {
for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount();
++i) {
- // When user unlock device, we should only try to send broadcast again if we
- // have sent it before unlock. This will avoid we try to load carrier config
- // when SIM is still loading when unlock happens.
+ // When the user unlocks the device, send the broadcast again (with a
+ // rebroadcast extra) if we have sent it before unlock. This will avoid
+ // trying to load the carrier config when the SIM is still loading when the
+ // unlock happens.
if (mHasSentConfigChange[i]) {
- logWithLocalLog("System unlocked");
+ logdWithLocalLog("System unlocked");
+ mFromSystemUnlocked[i] = true;
updateConfigForPhoneId(i);
}
}
break;
}
- case EVENT_PACKAGE_CHANGED:
- {
+ case EVENT_PACKAGE_CHANGED: {
final String carrierPackageName = (String) msg.obj;
// Only update if there are cached config removed to avoid updating config for
// unrelated packages.
if (clearCachedConfigForPackage(carrierPackageName)) {
int numPhones = TelephonyManager.from(mContext).getActiveModemCount();
for (int i = 0; i < numPhones; ++i) {
- logWithLocalLog("Package changed: " + carrierPackageName
+ logdWithLocalLog("Package changed: " + carrierPackageName
+ ", phone=" + i);
updateConfigForPhoneId(i);
}
@@ -215,13 +230,12 @@
break;
}
- case EVENT_DO_FETCH_DEFAULT:
- {
+ case EVENT_DO_FETCH_DEFAULT: {
// Restore persistent override values.
PersistableBundle config = restoreConfigFromXml(
mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
if (config != null) {
- log("Loaded persistent override config from XML. package="
+ logd("Loaded persistent override config from XML. package="
+ mPlatformCarrierConfigPackage
+ " phoneId=" + phoneId);
mPersistentOverrideConfigs[phoneId] = config;
@@ -229,7 +243,7 @@
config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
if (config != null) {
- log(
+ logd(
"Loaded config from XML. package="
+ mPlatformCarrierConfigPackage
+ " phoneId="
@@ -259,13 +273,12 @@
break;
}
- case EVENT_CONNECTED_TO_DEFAULT:
- {
+ case EVENT_CONNECTED_TO_DEFAULT: {
removeMessages(EVENT_BIND_DEFAULT_TIMEOUT);
final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
// If new service connection has been created, unbind.
if (mServiceConnection[phoneId] != conn || conn.service == null) {
- mContext.unbindService(conn);
+ unbindIfBound(mContext, conn, phoneId);
break;
}
final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
@@ -274,7 +287,7 @@
new ResultReceiver(this) {
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
- mContext.unbindService(conn);
+ unbindIfBound(mContext, conn, phoneId);
// If new service connection has been created, this is stale.
if (mServiceConnection[phoneId] != conn) {
loge("Received response for stale request.");
@@ -290,7 +303,7 @@
PersistableBundle config =
resultData.getParcelable(KEY_CONFIG_BUNDLE);
saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
- carrierId, config);
+ carrierId, config);
mConfigFromDefaultApp[phoneId] = config;
sendMessage(
obtainMessage(
@@ -302,13 +315,13 @@
ICarrierService carrierService =
ICarrierService.Stub.asInterface(conn.service);
carrierService.getCarrierConfig(carrierId, resultReceiver);
- logWithLocalLog("fetch config for default app: "
+ logdWithLocalLog("Fetch config for default app: "
+ mPlatformCarrierConfigPackage
+ " carrierid: " + carrierId.toString());
} catch (RemoteException e) {
loge("Failed to get carrier config from default app: " +
mPlatformCarrierConfigPackage + " err: " + e.toString());
- mContext.unbindService(conn);
+ unbindIfBound(mContext, conn, phoneId);
break; // So we don't set a timeout.
}
sendMessageDelayed(
@@ -318,9 +331,8 @@
}
case EVENT_BIND_DEFAULT_TIMEOUT:
- case EVENT_FETCH_DEFAULT_TIMEOUT:
- {
- loge("bind/fetch time out from " + mPlatformCarrierConfigPackage);
+ case EVENT_FETCH_DEFAULT_TIMEOUT: {
+ loge("Bind/fetch time out from " + mPlatformCarrierConfigPackage);
removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT);
// If we attempted to bind to the app, but the service connection is null due to
// the race condition that clear config event happens before bind/fetch complete
@@ -328,15 +340,14 @@
if (mServiceConnection[phoneId] != null) {
// If a ResponseReceiver callback is in the queue when this happens, we will
// unbind twice and throw an exception.
- mContext.unbindService(mServiceConnection[phoneId]);
+ unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
broadcastConfigChangedIntent(phoneId);
}
notifySubscriptionInfoUpdater(phoneId);
break;
}
- case EVENT_FETCH_DEFAULT_DONE:
- {
+ case EVENT_FETCH_DEFAULT_DONE: {
// If we attempted to bind to the app, but the service connection is null, then
// config was cleared while we were waiting and we should not continue.
if (!msg.getData().getBoolean("loaded_from_xml", false)
@@ -345,7 +356,7 @@
}
final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
if (carrierPackageName != null) {
- log("Found carrier config app: " + carrierPackageName);
+ logd("Found carrier config app: " + carrierPackageName);
sendMessage(obtainMessage(EVENT_DO_FETCH_CARRIER, phoneId, -1));
} else {
notifySubscriptionInfoUpdater(phoneId);
@@ -353,13 +364,12 @@
break;
}
- case EVENT_DO_FETCH_CARRIER:
- {
+ case EVENT_DO_FETCH_CARRIER: {
final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
final PersistableBundle config =
restoreConfigFromXml(carrierPackageName, "", phoneId);
if (config != null) {
- log(
+ logd(
"Loaded config from XML. package="
+ carrierPackageName
+ " phoneId="
@@ -370,31 +380,27 @@
sendMessage(newMsg);
} else {
// No cached config, so fetch it from a carrier app.
- if (carrierPackageName != null
- && bindToConfigPackage(
- carrierPackageName,
- phoneId,
- EVENT_CONNECTED_TO_CARRIER)) {
+ if (carrierPackageName != null && bindToConfigPackage(carrierPackageName,
+ phoneId, EVENT_CONNECTED_TO_CARRIER)) {
sendMessageDelayed(
obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1),
BIND_TIMEOUT_MILLIS);
} else {
// Send broadcast if bind fails.
broadcastConfigChangedIntent(phoneId);
- loge("bind to carrier app: " + carrierPackageName + " fails");
+ loge("Bind to carrier app: " + carrierPackageName + " fails");
notifySubscriptionInfoUpdater(phoneId);
}
}
break;
}
- case EVENT_CONNECTED_TO_CARRIER:
- {
+ case EVENT_CONNECTED_TO_CARRIER: {
removeMessages(EVENT_BIND_CARRIER_TIMEOUT);
final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
// If new service connection has been created, unbind.
if (mServiceConnection[phoneId] != conn || conn.service == null) {
- mContext.unbindService(conn);
+ unbindIfBound(mContext, conn, phoneId);
break;
}
final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
@@ -403,7 +409,7 @@
new ResultReceiver(this) {
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
- mContext.unbindService(conn);
+ unbindIfBound(mContext, conn, phoneId);
// If new service connection has been created, this is stale.
if (mServiceConnection[phoneId] != conn) {
loge("Received response for stale request.");
@@ -433,12 +439,12 @@
ICarrierService carrierService =
ICarrierService.Stub.asInterface(conn.service);
carrierService.getCarrierConfig(carrierId, resultReceiver);
- logWithLocalLog("fetch config for carrier app: "
+ logdWithLocalLog("Fetch config for carrier app: "
+ getCarrierPackageForPhoneId(phoneId)
+ " carrierid: " + carrierId.toString());
} catch (RemoteException e) {
loge("Failed to get carrier config: " + e.toString());
- mContext.unbindService(conn);
+ unbindIfBound(mContext, conn, phoneId);
break; // So we don't set a timeout.
}
sendMessageDelayed(
@@ -448,9 +454,8 @@
}
case EVENT_BIND_CARRIER_TIMEOUT:
- case EVENT_FETCH_CARRIER_TIMEOUT:
- {
- loge("bind/fetch from carrier app timeout");
+ case EVENT_FETCH_CARRIER_TIMEOUT: {
+ loge("Bind/fetch from carrier app timeout");
removeMessages(EVENT_FETCH_CARRIER_TIMEOUT);
// If we attempted to bind to the app, but the service connection is null due to
// the race condition that clear config event happens before bind/fetch complete
@@ -458,14 +463,13 @@
if (mServiceConnection[phoneId] != null) {
// If a ResponseReceiver callback is in the queue when this happens, we will
// unbind twice and throw an exception.
- mContext.unbindService(mServiceConnection[phoneId]);
+ unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
broadcastConfigChangedIntent(phoneId);
}
notifySubscriptionInfoUpdater(phoneId);
break;
}
- case EVENT_FETCH_CARRIER_DONE:
- {
+ case EVENT_FETCH_CARRIER_DONE: {
// If we attempted to bind to the app, but the service connection is null, then
// config was cleared while we were waiting and we should not continue.
if (!msg.getData().getBoolean("loaded_from_xml", false)
@@ -476,13 +480,12 @@
break;
}
- case EVENT_CHECK_SYSTEM_UPDATE:
- {
+ case EVENT_CHECK_SYSTEM_UPDATE: {
SharedPreferences sharedPrefs =
PreferenceManager.getDefaultSharedPreferences(mContext);
final String lastFingerprint = sharedPrefs.getString(KEY_FINGERPRINT, null);
if (!Build.FINGERPRINT.equals(lastFingerprint)) {
- log(
+ logd(
"Build fingerprint changed. old: "
+ lastFingerprint
+ " new: "
@@ -537,10 +540,12 @@
mPersistentOverrideConfigs = new PersistableBundle[numPhones];
mOverrideConfigs = new PersistableBundle[numPhones];
mServiceConnection = new CarrierServiceConnection[numPhones];
+ mServiceBound = new boolean[numPhones];
mHasSentConfigChange = new boolean[numPhones];
+ mFromSystemUnlocked = new boolean[numPhones];
// Make this service available through ServiceManager.
ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
- log("CarrierConfigLoader has started");
+ logd("CarrierConfigLoader has started");
mSubscriptionInfoUpdater = PhoneFactory.getSubscriptionInfoUpdater();
mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
}
@@ -593,6 +598,10 @@
configToSend = mConfigFromDefaultApp[phoneId];
}
+ if (configToSend == null) {
+ configToSend = new PersistableBundle();
+ }
+
// mOverrideConfigs is for testing. And it will override current configs.
PersistableBundle config = mOverrideConfigs[phoneId];
if (config != null) {
@@ -612,7 +621,6 @@
private void broadcastConfigChangedIntent(int phoneId, boolean addSubIdExtra) {
Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND |
Intent.FLAG_RECEIVER_FOREGROUND);
if (addSubIdExtra) {
int simApplicationState = TelephonyManager.SIM_STATE_UNKNOWN;
@@ -622,6 +630,8 @@
.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) {
@@ -632,20 +642,33 @@
}
}
intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, phoneId);
- log("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
- mContext.sendBroadcast(intent);
+ intent.putExtra(CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK,
+ mFromSystemUnlocked[phoneId]);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (subIds != null && subIds.length > 0) {
+ logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId + ", subId=" + subIds[0]);
+ } else {
+ logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
+ }
mHasSentConfigChange[phoneId] = true;
+ mFromSystemUnlocked[phoneId] = false;
}
/** Binds to the default or carrier config app. */
private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) {
- logWithLocalLog("Binding to " + pkgName + " for phone " + phoneId);
+ logdWithLocalLog("Binding to " + pkgName + " for phone " + phoneId);
Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
carrierService.setPackage(pkgName);
- mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, eventId);
+ mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, pkgName, eventId);
try {
- return mContext.bindService(carrierService, mServiceConnection[phoneId],
- Context.BIND_AUTO_CREATE);
+ if (mContext.bindService(carrierService, mServiceConnection[phoneId],
+ Context.BIND_AUTO_CREATE)) {
+ mServiceBound[phoneId] = true;
+ return true;
+ } else {
+ return false;
+ }
} catch (SecurityException ex) {
return false;
}
@@ -745,9 +768,9 @@
*
* @param packageName the name of the package from which we fetched this bundle.
* @param extraString An extra string to be used in the XML file name.
- * @param phoneId the phone ID.
- * @param carrierId contains all carrier-identifying information.
- * @param config the bundle to be written. Null will be treated as an empty bundle.
+ * @param phoneId the phone ID.
+ * @param carrierId contains all carrier-identifying information.
+ * @param config the bundle to be written. Null will be treated as an empty bundle.
*/
private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
CarrierIdentifier carrierId, PersistableBundle config) {
@@ -778,7 +801,8 @@
return;
}
- logWithLocalLog("save config to xml, packagename: " + packageName + " phoneId: " + phoneId);
+ logdWithLocalLog(
+ "Save config to xml, packagename: " + packageName + " phoneId: " + phoneId);
FileOutputStream outFile = null;
try {
@@ -789,8 +813,7 @@
config.writeToStream(outFile);
outFile.flush();
outFile.close();
- }
- catch (IOException e) {
+ } catch (IOException e) {
loge(e.toString());
}
}
@@ -806,9 +829,9 @@
*
* @param packageName the name of the package from which we fetched this bundle.
* @param extraString An extra string to be used in the XML file name.
- * @param phoneId the phone ID.
+ * @param phoneId the phone ID.
* @return the bundle from the XML file. Returns null if there is no saved config, the saved
- * version does not match, or reading config fails.
+ * version does not match, or reading config fails.
*/
private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
int phoneId) {
@@ -848,13 +871,11 @@
}
inFile.close();
- }
- catch (FileNotFoundException e) {
+ } catch (FileNotFoundException e) {
// Missing file is normal occurrence that might occur with a new sim or when restoring
// an override file during boot and should not be treated as an error.
- if (file != null) log("File not found: " + file.getPath());
- }
- catch (IOException e) {
+ if (file != null) logd("File not found: " + file.getPath());
+ } catch (IOException e) {
loge(e.toString());
}
@@ -883,7 +904,7 @@
});
if (packageFiles == null || packageFiles.length < 1) return false;
for (File f : packageFiles) {
- log("deleting " + f.getName());
+ logd("Deleting " + f.getName());
f.delete();
}
return true;
@@ -891,7 +912,7 @@
/** Builds a canonical file name for a config file. */
private String getFilenameForConfig(@NonNull String packageName, @NonNull String extraString,
- @NonNull String iccid, int cid) {
+ @NonNull String iccid, int cid) {
// the same carrier should have a single copy of XML file named after carrier id.
// However, it's still possible that platform doesn't recognize the current sim carrier,
// we will use iccid + carrierid as the canonical file name. carrierid can also handle the
@@ -934,12 +955,14 @@
}
@Override
- public @NonNull PersistableBundle getConfigForSubId(int subId, String callingPackage) {
+ @NonNull
+ public PersistableBundle getConfigForSubId(int subId, String callingPackage) {
return getConfigForSubIdWithFeature(subId, callingPackage, null);
}
@Override
- public @NonNull PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
+ @NonNull
+ public PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
String callingFeatureId) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
callingFeatureId, "getCarrierConfig")) {
@@ -983,7 +1006,7 @@
//TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- log("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
+ logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
return;
}
overrideConfig(mOverrideConfigs, phoneId, overrides);
@@ -1022,7 +1045,7 @@
public void notifyConfigChangedForSubId(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
- log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
+ logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
return;
}
@@ -1044,7 +1067,7 @@
public void updateConfigForPhoneId(int phoneId, String simState) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, null);
- logWithLocalLog("update config for phoneId: " + phoneId + " simState: " + simState);
+ logdWithLocalLog("Update config for phoneId: " + phoneId + " simState: " + simState);
if (!SubscriptionManager.isValidPhoneId(phoneId)) {
return;
}
@@ -1072,44 +1095,78 @@
return mPlatformCarrierConfigPackage;
}
+ private void unbindIfBound(Context context, CarrierServiceConnection conn,
+ int phoneId) {
+ if (mServiceBound[phoneId]) {
+ mServiceBound[phoneId] = false;
+ context.unbindService(conn);
+ }
+ }
+
+ /**
+ * If {@code args} contains {@link #DUMP_ARG_REQUESTING_PACKAGE} and a following package name,
+ * we'll also call {@link IBinder#dump} on the default carrier service (if bound) and the
+ * specified carrier service (if bound). Typically, this is done for connectivity bug reports
+ * where we don't call {@code dumpsys activity service all-non-platform} because that contains
+ * too much info, but we still want to let carrier apps include their diagnostics.
+ */
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ IndentingPrintWriter indentPW = new IndentingPrintWriter(pw, " ");
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump carrierconfig from from pid="
+ indentPW.println("Permission Denial: can't dump carrierconfig from from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
- pw.println("CarrierConfigLoader: " + this);
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
- pw.println("Phone Id = " + i);
- // display default values in CarrierConfigManager
- printConfig(CarrierConfigManager.getDefaultConfig(), pw,
- "Default Values from CarrierConfigManager");
- pw.println("");
- // display ConfigFromDefaultApp
- printConfig(mConfigFromDefaultApp[i], pw, "mConfigFromDefaultApp");
- pw.println("");
- // display ConfigFromCarrierApp
- printConfig(mConfigFromCarrierApp[i], pw, "mConfigFromCarrierApp");
- pw.println("");
- printConfig(mPersistentOverrideConfigs[i], pw, "mPersistentOverrideConfigs");
- pw.println("");
- printConfig(mOverrideConfigs[i], pw, "mOverrideConfigs");
+ String requestingPackage = null;
+ int requestingPackageIndex = ArrayUtils.indexOf(args, DUMP_ARG_REQUESTING_PACKAGE);
+ if (requestingPackageIndex >= 0 && requestingPackageIndex < args.length - 1
+ && !TextUtils.isEmpty(args[requestingPackageIndex + 1])) {
+ requestingPackage = args[requestingPackageIndex + 1];
+ // Throws a SecurityException if the caller is impersonating another app in an effort to
+ // dump extra info (which may contain PII the caller doesn't have a right to).
+ enforceCallerIsSystemOrRequestingPackage(requestingPackage);
}
- pw.println("CarrierConfigLoadingLog=");
- mCarrierConfigLoadingLog.dump(fd, pw, args);
+ indentPW.println("CarrierConfigLoader: " + this);
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
+ indentPW.println("Phone Id = " + i);
+ // display default values in CarrierConfigManager
+ printConfig(CarrierConfigManager.getDefaultConfig(), indentPW,
+ "Default Values from CarrierConfigManager");
+ // display ConfigFromDefaultApp
+ printConfig(mConfigFromDefaultApp[i], indentPW, "mConfigFromDefaultApp");
+ // display ConfigFromCarrierApp
+ printConfig(mConfigFromCarrierApp[i], indentPW, "mConfigFromCarrierApp");
+ printConfig(mPersistentOverrideConfigs[i], indentPW, "mPersistentOverrideConfigs");
+ printConfig(mOverrideConfigs[i], indentPW, "mOverrideConfigs");
+ }
+
+ indentPW.println("CarrierConfigLoadingLog=");
+ mCarrierConfigLoadingLog.dump(fd, indentPW, args);
+
+ if (requestingPackage != null) {
+ logd("Including default and requesting package " + requestingPackage
+ + " carrier services in dump");
+ indentPW.println("");
+ indentPW.println("Connected services");
+ dumpCarrierServiceIfBound(fd, indentPW, "Default config package",
+ mPlatformCarrierConfigPackage, false /* considerCarrierPrivileges */);
+ dumpCarrierServiceIfBound(fd, indentPW, "Requesting package", requestingPackage,
+ true /* considerCarrierPrivileges */);
+ }
}
- private void printConfig(PersistableBundle configApp, PrintWriter pw, String name) {
- IndentingPrintWriter indentPW = new IndentingPrintWriter(pw, " ");
+ private void printConfig(PersistableBundle configApp, IndentingPrintWriter indentPW,
+ String name) {
+ indentPW.increaseIndent();
if (configApp == null) {
- indentPW.increaseIndent();
indentPW.println(name + " : null ");
+ indentPW.decreaseIndent();
+ indentPW.println("");
return;
}
- indentPW.increaseIndent();
indentPW.println(name + " : ");
List<String> sortedKeys = new ArrayList<String>(configApp.keySet());
Collections.sort(sortedKeys);
@@ -1125,27 +1182,162 @@
indentPW.println(key + " = " + configApp.get(key));
}
}
+ indentPW.decreaseIndent();
+ indentPW.decreaseIndent();
+ indentPW.decreaseIndent();
+ indentPW.println("");
+ }
+
+ /**
+ * Passes without problem when one of these conditions is true:
+ * - The caller is a privileged UID (e.g. for dumpstate.cpp generating a bug report, where the
+ * system knows the true caller plumbed in through the {@link android.os.BugreportManager} API).
+ * - The caller's UID matches the supplied package.
+ *
+ * @throws SecurityException if none of the above conditions are met.
+ */
+ private void enforceCallerIsSystemOrRequestingPackage(String requestingPackage)
+ throws SecurityException {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID || callingUid == Process.PHONE_UID) {
+ // Bug reports (dumpstate.cpp) run as SHELL, and let some other privileged UIDs through
+ // as well.
+ return;
+ }
+ // An app is trying to dump extra detail, block it if they aren't who they claim to be.
+ AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+ if (appOps == null) {
+ throw new SecurityException("No AppOps");
+ }
+ // Will throw a SecurityException if the UID and package don't match.
+ appOps.checkPackage(callingUid, requestingPackage);
+ }
+
+ /**
+ * Searches for one or more appropriate {@link CarrierService} instances to dump based on the
+ * current connections.
+ *
+ * @param targetPkgName the target package name to dump carrier services for
+ * @param considerCarrierPrivileges if true, allow a carrier service to be dumped if it shares
+ * carrier privileges with {@code targetPkgName};
+ * otherwise, only dump a carrier service if it is {@code
+ * targetPkgName}
+ */
+ private void dumpCarrierServiceIfBound(FileDescriptor fd, IndentingPrintWriter indentPW,
+ String prefix, String targetPkgName, boolean considerCarrierPrivileges) {
+ // Null package is possible if it's early in the boot process, there was a recent crash, we
+ // loaded the config from XML most recently, or a SIM slot is empty. Carrier apps with
+ // long-lived bindings should typically get dumped here regardless. Even if an app is being
+ // used for multiple phoneIds, we assume that it's smart enough to handle that on its own,
+ // and that in most cases we'd just be dumping duplicate information and bloating a report.
+ indentPW.increaseIndent();
+ indentPW.println(prefix + " : " + targetPkgName);
+ Set<String> dumpedPkgNames = new ArraySet<>(mServiceConnection.length);
+ for (CarrierServiceConnection connection : mServiceConnection) {
+ if (connection == null || !SubscriptionManager.isValidPhoneId(connection.phoneId)
+ || TextUtils.isEmpty(connection.pkgName)) {
+ continue;
+ }
+ final String servicePkgName = connection.pkgName;
+ // Note: we intentionally ignore system components here because we should NOT match the
+ // shell caller that's typically used for bug reports via non-BugreportManager triggers.
+ final boolean exactPackageMatch = TextUtils.equals(targetPkgName, servicePkgName);
+ final boolean carrierPrivilegesMatch =
+ considerCarrierPrivileges && hasCarrierPrivileges(targetPkgName,
+ connection.phoneId);
+ if (!exactPackageMatch && !carrierPrivilegesMatch) continue;
+ // Make sure this service is actually alive before trying to dump it. We don't pay
+ // attention to mServiceBound[connection.phoneId] because typically carrier apps will
+ // request long-lived bindings, and even if we unbind the app, it may still be alive due
+ // to CarrierServiceBindHelper. Pull it out as a reference so even if it gets set to
+ // null within the ServiceConnection during unbinding we can avoid an NPE.
+ final IBinder service = connection.service;
+ if (service == null || !service.isBinderAlive() || !service.pingBinder()) continue;
+ // We've got a live service. Last check is just to make sure we don't dump a package
+ // multiple times.
+ if (!dumpedPkgNames.add(servicePkgName)) continue;
+ if (!exactPackageMatch) {
+ logd(targetPkgName + " has carrier privileges on phoneId " + connection.phoneId
+ + ", service provided by " + servicePkgName);
+ indentPW.increaseIndent();
+ indentPW.println("Proxy : " + servicePkgName);
+ indentPW.decreaseIndent();
+ }
+ // Flush before we let the app output anything to ensure correct ordering of output.
+ // Internally, Binder#dump calls flush on its printer after finishing so we don't
+ // need to do anything after.
+ indentPW.flush();
+ try {
+ logd("Dumping " + servicePkgName);
+ // We don't need to give the carrier service any args.
+ connection.service.dump(fd, null /* args */);
+ logd("Done with " + servicePkgName);
+ } catch (RemoteException e) {
+ logd("RemoteException from " + servicePkgName, e);
+ indentPW.increaseIndent();
+ indentPW.println("RemoteException");
+ indentPW.increaseIndent();
+ e.printStackTrace(indentPW);
+ indentPW.decreaseIndent();
+ indentPW.decreaseIndent();
+ // We won't retry this package again because now it's in dumpedPkgNames.
+ }
+ indentPW.println("");
+ }
+ if (dumpedPkgNames.isEmpty()) {
+ indentPW.increaseIndent();
+ indentPW.println("Not bound");
+ indentPW.decreaseIndent();
+ indentPW.println("");
+ }
+ indentPW.decreaseIndent();
+ }
+
+ private boolean hasCarrierPrivileges(String pkgName, int phoneId) {
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (ArrayUtils.isEmpty(subIds)) {
+ return false;
+ }
+ return TelephonyManager.from(mContext).createForSubscriptionId(
+ subIds[0]).checkCarrierPrivilegesForPackage(pkgName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
}
private class CarrierServiceConnection implements ServiceConnection {
- int phoneId;
- int eventId;
+ final int phoneId;
+ final String pkgName;
+ final int eventId;
IBinder service;
- public CarrierServiceConnection(int phoneId, int eventId) {
+ CarrierServiceConnection(int phoneId, String pkgName, int eventId) {
this.phoneId = phoneId;
+ this.pkgName = pkgName;
this.eventId = eventId;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
- log("Connected to config app: " + name.flattenToString());
+ logd("Connected to config app: " + name.flattenToShortString());
this.service = service;
mHandler.sendMessage(mHandler.obtainMessage(eventId, phoneId, -1, this));
}
@Override
public void onServiceDisconnected(ComponentName name) {
+ logd("Disconnected from config app: " + name.flattenToShortString());
+ this.service = null;
+ }
+
+ @Override
+ public void onBindingDied(ComponentName name) {
+ logd("Binding died from config app: " + name.flattenToShortString());
+ this.service = null;
+ }
+
+ @Override
+ public void onNullBinding(ComponentName name) {
+ logd("Null binding from config app: " + name.flattenToShortString());
this.service = null;
}
}
@@ -1156,8 +1348,9 @@
String action = intent.getAction();
boolean replace = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
// If replace is true, only care ACTION_PACKAGE_REPLACED.
- if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action))
+ if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
return;
+ }
switch (action) {
case Intent.ACTION_BOOT_COMPLETED:
@@ -1179,11 +1372,15 @@
}
}
- private void log(String msg) {
+ private void logd(String msg) {
Log.d(LOG_TAG, msg);
}
- private void logWithLocalLog(String msg) {
+ private void logd(String msg, Throwable tr) {
+ Log.d(LOG_TAG, msg, tr);
+ }
+
+ private void logdWithLocalLog(String msg) {
Log.d(LOG_TAG, msg);
mCarrierConfigLoadingLog.log(msg);
}
diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java
new file mode 100644
index 0000000..a8d2e93
--- /dev/null
+++ b/src/com/android/phone/CdmaCallForwardOptions.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 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.ActionBar;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import java.util.ArrayList;
+
+public class CdmaCallForwardOptions extends TimeConsumingPreferenceActivity {
+ private static final String LOG_TAG = "CdmaCallForwardOptions";
+
+ private static final String NUM_PROJECTION[] = {
+ android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
+ };
+
+ 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 KEY_TOGGLE = "toggle";
+ private static final String KEY_STATUS = "status";
+ private static final String KEY_NUMBER = "number";
+ private static final String KEY_ENABLE = "enable";
+
+ private CallForwardEditPreference mButtonCFU;
+ private CallForwardEditPreference mButtonCFB;
+ private CallForwardEditPreference mButtonCFNRy;
+ private CallForwardEditPreference mButtonCFNRc;
+
+ private final ArrayList<CallForwardEditPreference> mPreferences =
+ new ArrayList<CallForwardEditPreference> ();
+ private int mInitIndex= 0;
+
+ private boolean mFirstResume;
+ private Bundle mIcicle;
+ private Phone mPhone;
+ private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private boolean mReplaceInvalidCFNumbers;
+ private boolean mCallForwardByUssd;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.callforward_options);
+
+ mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+ mSubscriptionInfoHelper.setActionBarTitle(
+ getActionBar(), getResources(), R.string.call_forwarding_settings_with_label);
+ mPhone = mSubscriptionInfoHelper.getPhone();
+
+ PersistableBundle b = null;
+ boolean supportCFNRc = true;
+ if (mSubscriptionInfoHelper.hasSubId()) {
+ b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+ mSubscriptionInfoHelper.getSubId());
+ } else {
+ b = PhoneGlobals.getInstance().getCarrierConfig();
+ }
+ if (b != null) {
+ mReplaceInvalidCFNumbers = b.getBoolean(
+ CarrierConfigManager.KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL);
+ mCallForwardByUssd = b.getBoolean(
+ CarrierConfigManager.KEY_USE_CALL_FORWARDING_USSD_BOOL);
+ supportCFNRc = b.getBoolean(
+ CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL);
+ }
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+ mButtonCFU = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFU_KEY);
+ mButtonCFB = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
+ mButtonCFNRy = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
+ mButtonCFNRc = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
+
+ mButtonCFU.setParentActivity(this, mButtonCFU.reason);
+ mButtonCFB.setParentActivity(this, mButtonCFB.reason);
+ mButtonCFNRy.setParentActivity(this, mButtonCFNRy.reason);
+ mButtonCFNRc.setParentActivity(this, mButtonCFNRc.reason);
+
+ mPreferences.add(mButtonCFU);
+ mPreferences.add(mButtonCFB);
+ mPreferences.add(mButtonCFNRy);
+
+ if (supportCFNRc) {
+ mPreferences.add(mButtonCFNRc);
+ } else {
+ // When CFNRc is not supported, mButtonCFNRc is grayed out from the menu.
+ // Default state for the preferences in this PreferenceScreen is disabled.
+ // Only preferences listed in the ArrayList mPreferences will be enabled.
+ // By not adding mButtonCFNRc to mPreferences it will be kept disabled.
+ Log.d(LOG_TAG, "onCreate: CFNRc is not supported, grey out the item.");
+ }
+
+ if (mCallForwardByUssd) {
+ //the call forwarding ussd command's behavior is similar to the call forwarding when
+ //unanswered,so only display the call forwarding when unanswered item.
+ prefSet.removePreference(mButtonCFU);
+ prefSet.removePreference(mButtonCFB);
+ prefSet.removePreference(mButtonCFNRc);
+ mPreferences.remove(mButtonCFU);
+ mPreferences.remove(mButtonCFB);
+ mPreferences.remove(mButtonCFNRc);
+ mButtonCFNRy.setDependency(null);
+ }
+
+ // we wait to do the initialization until onResume so that the
+ // TimeConsumingPreferenceActivity dialog can display as it
+ // relies on onResume / onPause to maintain its foreground state.
+
+ mFirstResume = true;
+ mIcicle = icicle;
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ // android.R.id.home will be triggered in onOptionsItemSelected()
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mFirstResume) {
+ if (mIcicle == null) {
+ Log.d(LOG_TAG, "start to init ");
+ CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+ pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+ pref.startCallForwardOptionsQuery();
+
+ } else {
+ mInitIndex = mPreferences.size();
+
+ for (CallForwardEditPreference pref : mPreferences) {
+ Bundle bundle = mIcicle.getParcelable(pref.getKey());
+ pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+ pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
+ CallForwardInfo cf = new CallForwardInfo();
+ cf.number = bundle.getString(KEY_NUMBER);
+ cf.status = bundle.getInt(KEY_STATUS);
+ pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+ pref.restoreCallForwardInfo(cf);
+ }
+ }
+ mFirstResume = false;
+ mIcicle = null;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ for (CallForwardEditPreference pref : mPreferences) {
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+ bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
+ if (pref.callForwardInfo != null) {
+ bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
+ bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
+ }
+ outState.putParcelable(pref.getKey(), bundle);
+ }
+ }
+
+ @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();
+ }
+
+ super.onFinished(preference, reading);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(LOG_TAG, "onActivityResult: done");
+ if (resultCode != RESULT_OK) {
+ Log.d(LOG_TAG, "onActivityResult: contact picker result not OK.");
+ return;
+ }
+ Cursor cursor = null;
+ try {
+ cursor = getContentResolver().query(data.getData(),
+ NUM_PROJECTION, null, null, null);
+ if ((cursor == null) || (!cursor.moveToFirst())) {
+ Log.d(LOG_TAG, "onActivityResult: bad contact data, no results found.");
+ return;
+ }
+
+ switch (requestCode) {
+ case CommandsInterface.CF_REASON_UNCONDITIONAL:
+ mButtonCFU.onPickActivityResult(cursor.getString(0));
+ break;
+ case CommandsInterface.CF_REASON_BUSY:
+ mButtonCFB.onPickActivityResult(cursor.getString(0));
+ break;
+ case CommandsInterface.CF_REASON_NO_REPLY:
+ mButtonCFNRy.onPickActivityResult(cursor.getString(0));
+ break;
+ case CommandsInterface.CF_REASON_NOT_REACHABLE:
+ mButtonCFNRc.onPickActivityResult(cursor.getString(0));
+ break;
+ default:
+ // TODO: may need exception here.
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ final int itemId = item.getItemId();
+ if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled()
+ CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper);
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/com/android/phone/CdmaCallOptions.java b/src/com/android/phone/CdmaCallOptions.java
index acfa496..2e310aa 100644
--- a/src/com/android/phone/CdmaCallOptions.java
+++ b/src/com/android/phone/CdmaCallOptions.java
@@ -19,20 +19,20 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.preference.Preference;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
-import android.preference.SwitchPreference;
import android.telephony.CarrierConfigManager;
import android.view.MenuItem;
import com.android.internal.telephony.PhoneConstants;
-public class CdmaCallOptions extends PreferenceActivity {
+public class CdmaCallOptions extends TimeConsumingPreferenceActivity {
private static final String LOG_TAG = "CdmaCallOptions";
private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
- private SwitchPreference mButtonVoicePrivacy;
+ private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+ private static final String CALL_WAITING_KEY = "call_waiting_key";
+ private CdmaVoicePrivacySwitchPreference mButtonVoicePrivacy;
@Override
protected void onCreate(Bundle icicle) {
@@ -44,7 +44,8 @@
subInfoHelper.setActionBarTitle(
getActionBar(), getResources(), R.string.labelCdmaMore_with_label);
- mButtonVoicePrivacy = (SwitchPreference) findPreference(BUTTON_VP_KEY);
+ mButtonVoicePrivacy = (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+ mButtonVoicePrivacy.setPhone(subInfoHelper.getPhone());
PersistableBundle carrierConfig;
if (subInfoHelper.hasSubId()) {
carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
@@ -55,8 +56,15 @@
if (subInfoHelper.getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA
|| carrierConfig.getBoolean(CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
// disable the entire screen
- getPreferenceScreen().setEnabled(false);
+ mButtonVoicePrivacy.setEnabled(false);
}
+
+ Preference callForwardingPref = getPreferenceScreen().findPreference(CALL_FORWARDING_KEY);
+ callForwardingPref.setIntent(subInfoHelper.getIntent(CdmaCallForwardOptions.class));
+
+ CdmaCallWaitingPreference callWaitingPref = (CdmaCallWaitingPreference)getPreferenceScreen()
+ .findPreference(CALL_WAITING_KEY);
+ callWaitingPref.init(this, subInfoHelper.getPhone());
}
@Override
@@ -76,5 +84,4 @@
}
return false;
}
-
}
diff --git a/src/com/android/phone/CdmaCallWaitingPreference.java b/src/com/android/phone/CdmaCallWaitingPreference.java
new file mode 100644
index 0000000..4cda7ba
--- /dev/null
+++ b/src/com/android/phone/CdmaCallWaitingPreference.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.util.AttributeSet;
+import android.util.Log;
+
+public class CdmaCallWaitingPreference extends Preference {
+ private static final String LOG_TAG = "CdmaCallWaitingPreference";
+ private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+ private int mButtonClicked;
+ private Context mContext;
+ private Phone mPhone;
+ private SubscriptionInfoHelper mSubscriptionInfoHelper;
+ private TimeConsumingPreferenceListener mTcpListener;
+ private MyHandler mHandler = new MyHandler();
+
+ public CdmaCallWaitingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContext = context;
+ }
+
+ public CdmaCallWaitingPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.preferenceStyle);
+ }
+
+ public CdmaCallWaitingPreference(Context context) {
+ this(context, null);
+ }
+
+ public void init(TimeConsumingPreferenceListener listener, Phone phone) {
+ mPhone = phone;
+ mTcpListener = listener;
+ Log.d(LOG_TAG, "phone id= " + mPhone.getPhoneId());
+ mPhone.getCallWaiting(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_WAITING,
+ MyHandler.MESSAGE_GET_CALL_WAITING, MyHandler.MESSAGE_GET_CALL_WAITING));
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(this, true);
+ }
+ }
+
+ @Override
+ public void onClick() {
+ super.onClick();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setTitle(mContext.getText(R.string.cdma_call_waiting));
+ builder.setMessage(mContext.getText(R.string.enable_cdma_call_waiting_setting));
+ builder.setPositiveButton(R.string.enable_cdma_cw, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mPhone.setCallWaiting(true,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+ }
+ }
+ });
+ builder.setNegativeButton(R.string.disable_cdma_cw, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mPhone.setCallWaiting(false,
+ mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+ if (mTcpListener != null) {
+ mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+ }
+ }
+ });
+ builder.create().show();
+ }
+
+ private class MyHandler extends Handler {
+ static final int MESSAGE_GET_CALL_WAITING = 0;
+ static final int MESSAGE_SET_CALL_WAITING = 1;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_GET_CALL_WAITING:
+ handleGetCallWaitingResponse(msg);
+ break;
+ case MESSAGE_SET_CALL_WAITING:
+ handleSetCallWaitingResponse(msg);
+ break;
+ }
+ }
+
+ private void handleGetCallWaitingResponse(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (mTcpListener != null) {
+ if (msg.arg2 == MESSAGE_SET_CALL_WAITING) {
+ mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+ } else {
+ mTcpListener.onFinished(CdmaCallWaitingPreference.this, true);
+ }
+ }
+
+ if (ar.exception instanceof CommandException) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallWaitingResponse: CommandException=" +
+ ar.exception);
+ }
+ if (mTcpListener != null) {
+ mTcpListener.onException(CdmaCallWaitingPreference.this,
+ (CommandException)ar.exception);
+ }
+ } else if (ar.userObj instanceof Throwable || ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception" + ar.exception);
+ }
+ if (mTcpListener != null) {
+ mTcpListener.onError(CdmaCallWaitingPreference.this,
+ TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+ }
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleGetCallWaitingResponse: CW state successfully queried.");
+ }
+ int[] cwArray = (int[])ar.result;
+ if (cwArray == null) {
+ if (mTcpListener != null) {
+ mTcpListener.onError(CdmaCallWaitingPreference.this,
+ TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+ }
+ return;
+ }
+
+ try {
+ if (cwArray[0] == CommandsInterface.SS_STATUS_UNKNOWN) {
+ setSummary("");
+ } else if(cwArray[0] == 1) {
+ setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_on));
+ } else if(cwArray[0] == 0) {
+ setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_off));
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ setSummary("");
+ Log.e(LOG_TAG, "handleGetCallWaitingResponse: improper result: err ="
+ + e.getMessage());
+ }
+ }
+ }
+
+ private void handleSetCallWaitingResponse(Message msg) {
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (ar.exception != null) {
+ if (DBG) {
+ Log.d(LOG_TAG, "handleSetCallWaitingResponse: ar.exception=" + ar.exception);
+ }
+ }
+
+ if (ar.result != null) {
+ int arr = (int)ar.result;
+ if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+ Log.d(LOG_TAG, "handleSetCallWaitingResponse: no need to re get in CDMA");
+ mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+ return;
+ }
+ }
+
+ if (DBG) Log.d(LOG_TAG, "handleSetCallWaitingResponse: re get");
+ mPhone.getCallWaiting(obtainMessage(MESSAGE_GET_CALL_WAITING,
+ MESSAGE_SET_CALL_WAITING, MESSAGE_SET_CALL_WAITING, ar.exception));
+ }
+ }
+}
diff --git a/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java b/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
index b79a3f0..0192c2a 100644
--- a/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
+++ b/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
@@ -102,4 +102,8 @@
phone.getEnhancedVoicePrivacy(obtainMessage(MESSAGE_GET_VP));
}
}
+
+ public void setPhone(Phone phone) {
+ this.phone = phone;
+ }
}
diff --git a/src/com/android/phone/CellInfoUtil.java b/src/com/android/phone/CellInfoUtil.java
index 8272029..f6d741f 100644
--- a/src/com/android/phone/CellInfoUtil.java
+++ b/src/com/android/phone/CellInfoUtil.java
@@ -33,6 +33,7 @@
import com.android.internal.telephony.OperatorInfo;
+import java.util.Collections;
import java.util.List;
/**
@@ -149,7 +150,8 @@
mcc,
mnc,
operatorInfo.getOperatorAlphaLong(),
- operatorInfo.getOperatorAlphaShort());
+ operatorInfo.getOperatorAlphaShort(),
+ Collections.emptyList());
CellInfoGsm ci = new CellInfoGsm();
ci.setCellIdentity(cig);
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 35af20d..505c284 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -16,6 +16,9 @@
package com.android.phone;
+import static android.view.View.LAYOUT_DIRECTION_LOCALE;
+import static android.view.View.TEXT_DIRECTION_LOCALE;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -37,6 +40,8 @@
import android.widget.ImageButton;
import android.widget.TextView;
+import com.android.internal.telephony.CommandsInterface;
+
public class EditPhoneNumberPreference extends EditTextPreference {
//allowed modes for this preference.
@@ -90,6 +95,7 @@
private String mPhoneNumber;
private boolean mChecked;
+ private boolean mIsUnknownStatus;
/**
* Interface for the dialog closed listener, related to
@@ -136,9 +142,9 @@
a.recycle();
//get the summary settings, use CheckBoxPreference as the standard.
- a = context.obtainStyledAttributes(attrs, R.styleable.CheckBoxPreference, 0, 0);
- mSummaryOn = a.getString(R.styleable.CheckBoxPreference_summaryOn);
- mSummaryOff = a.getString(R.styleable.CheckBoxPreference_summaryOff);
+ a = context.obtainStyledAttributes(attrs, android.R.styleable.CheckBoxPreference, 0, 0);
+ mSummaryOn = a.getString(android.R.styleable.CheckBoxPreference_summaryOn);
+ mSummaryOff = a.getString(android.R.styleable.CheckBoxPreference_summaryOff);
a.recycle();
}
@@ -209,7 +215,9 @@
}
}
editText.setText(BidiFormatter.getInstance().unicodeWrap(
- mPhoneNumber, TextDirectionHeuristics.LTR));
+ mPhoneNumber, TextDirectionHeuristics.LOCALE));
+ editText.setTextDirection(TEXT_DIRECTION_LOCALE);
+ editText.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
editText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
editText.setKeyListener(DialerKeyListener.getInstance());
editText.setOnFocusChangeListener(mDialogFocusChangeListener);
@@ -254,7 +262,13 @@
// displayed, since there is no need to hide the edittext
// field anymore.
if (mConfirmationMode == CM_ACTIVATION) {
- if (mChecked) {
+ if (mIsUnknownStatus) {
+ builder.setPositiveButton(mEnableText, this);
+ builder.setNeutralButton(mDisableText, this);
+ if (mPrefId == CommandsInterface.CF_REASON_ALL) {
+ builder.setPositiveButton(null, null);
+ }
+ } else if (mChecked) {
builder.setPositiveButton(mChangeNumberText, this);
builder.setNeutralButton(mDisableText, this);
} else {
@@ -310,7 +324,8 @@
@Override
public void onClick(DialogInterface dialog, int which) {
// The neutral button (button3) is always the toggle.
- if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)) {
+ if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)
+ && !mIsUnknownStatus) {
//flip the toggle if we are in the correct mode.
setToggled(!isToggled());
}
@@ -499,4 +514,12 @@
public void showPhoneNumberDialog() {
showDialog(null);
}
+
+ public void setUnknownStatus(boolean isUnknown) {
+ mIsUnknownStatus = isUnknown;
+ }
+
+ public boolean isUnknownStatus() {
+ return mIsUnknownStatus;
+ }
}
diff --git a/src/com/android/phone/EmergencyActionGroup.java b/src/com/android/phone/EmergencyActionGroup.java
index 53ec1eb..4961a69 100644
--- a/src/com/android/phone/EmergencyActionGroup.java
+++ b/src/com/android/phone/EmergencyActionGroup.java
@@ -159,7 +159,7 @@
if (v.getId() == R.id.action1 || v.getId() == R.id.action2 || v.getId() == R.id.action3) {
AccessibilityManager accessibilityMgr =
- (AccessibilityManager) mContext.getSystemService(
+ (AccessibilityManager) getContext().getSystemService(
Context.ACCESSIBILITY_SERVICE);
if (accessibilityMgr.isTouchExplorationEnabled()) {
getContext().startActivity(intent);
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index dcfa024..1210627 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -35,6 +35,7 @@
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.Phone;
@@ -300,8 +301,14 @@
return String.format(getResources().getQuantityText(
R.plurals.alert_dialog_not_avaialble_in_ecm, minutes).toString(), time);
case EXIT_ECM_DIALOG:
- return String.format(getResources().getQuantityText(R.plurals.alert_dialog_exit_ecm,
- minutes).toString(), time);
+ boolean shouldRestrictData = mPhone.getImsPhone() != null
+ && mPhone.getImsPhone().isInImsEcm();
+ return String.format(getResources().getQuantityText(
+ // During IMS ECM, data restriction hint should be removed.
+ shouldRestrictData
+ ? R.plurals.alert_dialog_exit_ecm_without_data_restriction_hint
+ : R.plurals.alert_dialog_exit_ecm,
+ minutes).toString(), time);
}
return null;
}
@@ -324,7 +331,8 @@
// Received exit Emergency Callback Mode notification close all dialogs
if (intent.getAction().equals(
TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
- if (intent.getBooleanExtra("phoneinECMState", false) == false) {
+ // Cancel if the sticky broadcast extra for whether or not we are in ECM is false.
+ if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
if (mAlertDialog != null)
mAlertDialog.dismiss();
if (mProgressDialog != null)
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index 41d83c4..6b247bd 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -30,7 +30,9 @@
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.Phone;
@@ -105,7 +107,8 @@
mPhone.unregisterForEcmTimerReset(mHandler);
// Cancel the notification and timer
- mNotificationManager.cancel(R.string.phone_in_ecm_notification_title);
+ mNotificationManager.cancelAsUser(null, R.string.phone_in_ecm_notification_title,
+ UserHandle.ALL);
mTimer.cancel();
}
}
@@ -119,7 +122,7 @@
// Stop the service when phone exits Emergency Callback Mode
if (intent.getAction().equals(
TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
- if (intent.getBooleanExtra("phoneinECMState", false) == false) {
+ if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
stopSelf();
}
}
@@ -194,7 +197,11 @@
// Format notification string
String text = null;
if(mInEmergencyCall) {
- text = getText(R.string.phone_in_ecm_call_notification_text).toString();
+ text = getText(
+ // During IMS ECM, data restriction hint should be removed.
+ (imsPhone != null && imsPhone.isInImsEcm())
+ ? R.string.phone_in_ecm_call_notification_text_without_data_restriction_hint
+ : R.string.phone_in_ecm_call_notification_text).toString();
} else {
// Calculate the time in ms when the notification will be finished.
long finishedCountMs = millisUntilFinished + System.currentTimeMillis();
@@ -205,14 +212,19 @@
String completeTime = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(
finishedCountMs);
- text = getResources().getString(R.string.phone_in_ecm_notification_complete_time,
+ text = getResources().getString(
+ // During IMS ECM, data restriction hint should be removed.
+ (imsPhone != null && imsPhone.isInImsEcm())
+ ? R.string.phone_in_ecm_notification_complete_time_without_data_restriction_hint
+ : R.string.phone_in_ecm_notification_complete_time,
completeTime);
}
builder.setContentText(text);
builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
// Show notification
- mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
+ mNotificationManager.notifyAsUser(null, R.string.phone_in_ecm_notification_title,
+ builder.build(), UserHandle.ALL);
}
/**
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 7531aca..183742e 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -40,12 +40,11 @@
import android.os.Bundle;
import android.os.PersistableBundle;
import android.provider.Settings;
-import android.telecom.ParcelableCallAnalytics;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -490,7 +489,7 @@
if (!TextUtils.isEmpty(phoneNumber)) {
if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
- placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
+ placeCall(phoneNumber, TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT,
mShortcutViewConfig.getPhoneInfo());
} else {
Log.d(LOG_TAG, "emergency number is empty");
@@ -720,8 +719,12 @@
isEmergencyNumber = true;
phoneToMakeCall = mShortcutViewConfig.getPhoneInfo();
} else {
- isEmergencyNumber = getSystemService(TelephonyManager.class)
- .isEmergencyNumber(mLastNumber);
+ try {
+ isEmergencyNumber = getSystemService(TelephonyManager.class)
+ .isEmergencyNumber(mLastNumber);
+ } catch (IllegalStateException ise) {
+ isEmergencyNumber = false;
+ }
}
if (isEmergencyNumber) {
@@ -734,7 +737,7 @@
return;
}
- placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
+ placeCall(mLastNumber, TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD,
phoneToMakeCall);
} else {
if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
@@ -1172,9 +1175,9 @@
private String callSourceToString(int callSource) {
switch (callSource) {
- case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD:
+ case TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD:
return "DialPad";
- case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT:
+ case TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT:
return "Shortcut";
default:
return "Unknown-" + callSource;
diff --git a/src/com/android/phone/EmergencyInfoGroup.java b/src/com/android/phone/EmergencyInfoGroup.java
index f5aca7f..a62edfd 100644
--- a/src/com/android/phone/EmergencyInfoGroup.java
+++ b/src/com/android/phone/EmergencyInfoGroup.java
@@ -209,7 +209,7 @@
public void onClick(View view) {
if (view.getId() == R.id.emergency_info_view) {
AccessibilityManager accessibilityMgr =
- (AccessibilityManager) mContext.getSystemService(
+ (AccessibilityManager) getContext().getSystemService(
Context.ACCESSIBILITY_SERVICE);
if (accessibilityMgr.isTouchExplorationEnabled()) {
if (mOnConfirmClickListener != null) {
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index d147ce4..bfa956c 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -184,7 +184,7 @@
public void onClick(View view) {
if (view.getId() == R.id.emergency_call_number_info_view) {
AccessibilityManager accessibilityMgr =
- (AccessibilityManager) mContext.getSystemService(
+ (AccessibilityManager) getContext().getSystemService(
Context.ACCESSIBILITY_SERVICE);
if (accessibilityMgr.isTouchExplorationEnabled()) {
// TalkBack itself includes a prompt to confirm click action implicitly,
diff --git a/src/com/android/phone/GsmUmtsCallBarringOptions.java b/src/com/android/phone/GsmUmtsCallBarringOptions.java
index 3c9cd84..30e9b5c 100644
--- a/src/com/android/phone/GsmUmtsCallBarringOptions.java
+++ b/src/com/android/phone/GsmUmtsCallBarringOptions.java
@@ -37,7 +37,6 @@
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.phone.settings.fdn.EditPinPreference;
import java.util.ArrayList;
@@ -75,7 +74,6 @@
private static final String DIALOG_PW_ENTRY_KEY = "dialog_pw_enter_key";
private static final String KEY_STATUS = "toggle";
private static final String PREFERENCE_ENABLED_KEY = "PREFERENCE_ENABLED";
- private static final String PREFERENCE_SHOW_PASSWORD_KEY = "PREFERENCE_SHOW_PASSWORD";
private static final String SAVED_BEFORE_LOAD_COMPLETED_KEY = "PROGRESS_SHOWING";
private CallBarringEditPreference mButtonBAOC;
@@ -141,16 +139,13 @@
return;
}
- String password = null;
- if (mButtonDisableAll.isPasswordShown()) {
- password = mButtonDisableAll.getText();
- // Validate the length of password first, before submitting it to the
- // RIL for CB disable.
- if (!validatePassword(password)) {
- mButtonDisableAll.setText("");
- displayMessage(R.string.call_barring_right_pwd_number);
- return;
- }
+ String password = mButtonDisableAll.getText();
+ // Validate the length of password first, before submitting it to the
+ // RIL for CB disable.
+ if (!validatePassword(password)) {
+ mButtonDisableAll.setText("");
+ displayMessage(R.string.call_barring_right_pwd_number);
+ return;
}
// Submit the disable all request
@@ -417,34 +412,18 @@
mPreferences.add(mButtonBAIC);
mPreferences.add(mButtonBAICr);
- // Find out if password is currently used.
- boolean usePassword = true;
- boolean useDisableaAll = true;
-
- ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
- if (imsPhone != null && imsPhone.isUtEnabled()) {
- usePassword = false;
- useDisableaAll = false;
- }
-
// 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 is unavailable when sim card is not ready or Ut is enabled.
- if (isSimReady && useDisableaAll) {
+ // Deactivate all option and Change password option are unavailable
+ // when sim card is not ready.
+ if (isSimReady) {
mButtonDisableAll.setEnabled(true);
- mButtonDisableAll.init(mPhone);
- } else {
- mButtonDisableAll.setEnabled(false);
- }
-
- // Change password option is unavailable when sim card is not ready or when the password is
- // not used.
- if (isSimReady && usePassword) {
mButtonChangePW.setEnabled(true);
} else {
+ mButtonDisableAll.setEnabled(false);
mButtonChangePW.setEnabled(false);
mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled);
}
@@ -472,8 +451,6 @@
pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS));
pref.init(this, true, mPhone);
pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()));
- pref.setInputMethodNeeded(bundle.getBoolean(PREFERENCE_SHOW_PASSWORD_KEY,
- pref.needInputMethod()));
}
}
mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY);
@@ -513,7 +490,6 @@
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_STATUS, pref.mIsActivated);
bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled());
- bundle.putBoolean(PREFERENCE_SHOW_PASSWORD_KEY, pref.needInputMethod());
outState.putParcelable(pref.getKey(), bundle);
}
outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState);
diff --git a/src/com/android/phone/IccNetworkDepersonalizationPanel.java b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
index 8bd10a2..7d854cd 100644
--- a/src/com/android/phone/IccNetworkDepersonalizationPanel.java
+++ b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
@@ -22,7 +22,10 @@
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.TelephonyManager;
import android.text.Editable;
import android.text.Spannable;
import android.text.TextUtils;
@@ -37,6 +40,8 @@
import android.widget.TextView;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
/**
* "SIM network unlock" PIN entry screen.
@@ -52,7 +57,8 @@
* Tracks whether there is an instance of the network depersonalization dialog showing or not.
* Ensures only a single instance of the dialog is visible.
*/
- private static boolean sShowingDialog = false;
+ private static boolean [] sShowingDialog =
+ new boolean[TelephonyManager.getDefault().getSimCount()];
//debug constants
private static final boolean DBG = false;
@@ -61,29 +67,48 @@
private static final int EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT = 100;
private Phone mPhone;
+ private int mPersoSubtype;
+ private static IccNetworkDepersonalizationPanel [] sNdpPanel =
+ new IccNetworkDepersonalizationPanel[TelephonyManager.getDefault().getSimCount()];
//UI elements
private EditText mPinEntry;
private LinearLayout mEntryPanel;
private LinearLayout mStatusPanel;
+ private TextView mPersoSubtypeText;
+ private PersoSubState mPersoSubState;
private TextView mStatusText;
private Button mUnlockButton;
private Button mDismissButton;
+ enum statusType {
+ ENTRY,
+ IN_PROGRESS,
+ ERROR,
+ SUCCESS
+ }
+
/**
* Shows the network depersonalization dialog, but only if it is not already visible.
*/
- public static void showDialog(Phone phone) {
- if (sShowingDialog) {
+ public static void showDialog(Phone phone, int subType) {
+ int phoneId = phone == null ? 0: phone.getPhoneId();
+ if (sShowingDialog[phoneId]) {
Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; skipped already shown.");
return;
}
Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; showing dialog.");
- sShowingDialog = true;
- IccNetworkDepersonalizationPanel ndpPanel =
- new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance(), phone);
- ndpPanel.show();
+ sShowingDialog[phoneId] = true;
+ sNdpPanel[phoneId] = new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance(),
+ phone, subType);
+ sNdpPanel[phoneId].show();
+ }
+
+ public static void dialogDismiss(int phoneId) {
+ if (sNdpPanel[phoneId] != null && sShowingDialog[phoneId]) {
+ sNdpPanel[phoneId].dismiss();
+ }
}
//private textwatcher to control text entry.
@@ -109,37 +134,41 @@
AsyncResult res = (AsyncResult) msg.obj;
if (res.exception != null) {
if (DBG) log("network depersonalization request failure.");
- indicateError();
+ displayStatus(statusType.ERROR.name());
postDelayed(new Runnable() {
- public void run() {
- hideAlert();
- mPinEntry.getText().clear();
- mPinEntry.requestFocus();
- }
- }, 3000);
+ public void run() {
+ hideAlert();
+ mPinEntry.getText().clear();
+ mPinEntry.requestFocus();
+ }
+ }, 3000);
} else {
if (DBG) log("network depersonalization success.");
- indicateSuccess();
+ displayStatus(statusType.SUCCESS.name());
postDelayed(new Runnable() {
- public void run() {
- dismiss();
- }
- }, 3000);
+ public void run() {
+ dismiss();
+ }
+ }, 3000);
}
}
}
};
+
//constructor
public IccNetworkDepersonalizationPanel(Context context) {
super(context);
mPhone = PhoneGlobals.getPhone();
+ mPersoSubtype = PersoSubState.PERSOSUBSTATE_SIM_NETWORK.ordinal();
}
//constructor
- public IccNetworkDepersonalizationPanel(Context context, Phone phone) {
+ public IccNetworkDepersonalizationPanel(Context context, Phone phone,
+ int subtype) {
super(context);
mPhone = phone == null ? PhoneGlobals.getPhone() : phone;
+ mPersoSubtype = subtype;
}
@Override
@@ -158,6 +187,8 @@
span.setSpan(mPinEntryWatcher, 0, text.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
mEntryPanel = (LinearLayout) findViewById(R.id.entry_panel);
+ mPersoSubtypeText = (TextView) findViewById(R.id.perso_subtype_text);
+ displayStatus(statusType.ENTRY.name());
mUnlockButton = (Button) findViewById(R.id.ndp_unlock);
mUnlockButton.setOnClickListener(mUnlockListener);
@@ -190,7 +221,8 @@
public void onStop() {
super.onStop();
Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; hiding dialog.");
- sShowingDialog = false;
+ int phoneId = mPhone == null ? 0 : mPhone.getPhoneId();
+ sShowingDialog[phoneId] = false;
}
//Mirrors IccPinUnlockPanel.onKeyDown().
@@ -210,29 +242,45 @@
return;
}
- if (DBG) log("requesting network depersonalization with code " + pin);
- mPhone.getIccCard().supplyNetworkDepersonalization(pin,
- Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
- indicateBusy();
+ log("Requesting De-Personalization for subtype " + mPersoSubtype);
+
+ try {
+ mPhone.getIccCard().supplySimDepersonalization(mPersoSubState,pin,
+ Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
+ } catch (NullPointerException ex) {
+ log("NullPointerException @supplySimDepersonalization" + ex);
+ }
+ displayStatus(statusType.IN_PROGRESS.name());
}
};
- private void indicateBusy() {
- mStatusText.setText(R.string.requesting_unlock);
- mEntryPanel.setVisibility(View.GONE);
- mStatusPanel.setVisibility(View.VISIBLE);
- }
+ private void displayStatus(String type) {
+ int label = 0;
- private void indicateError() {
- mStatusText.setText(R.string.unlock_failed);
- mEntryPanel.setVisibility(View.GONE);
- mStatusPanel.setVisibility(View.VISIBLE);
- }
+ mPersoSubState = PersoSubState.values()[mPersoSubtype];
+ log("displayStatus mPersoSubState: " +mPersoSubState.name() +"type: " +type);
- private void indicateSuccess() {
- mStatusText.setText(R.string.unlock_success);
- mEntryPanel.setVisibility(View.GONE);
- mStatusPanel.setVisibility(View.VISIBLE);
+ label = getContext().getResources().getIdentifier(mPersoSubState.name()
+ + "_" + type, "string", "android");
+
+ if (label == 0) {
+ log ("Unable to get the PersoSubType string");
+ return;
+ }
+
+ if(!PersoSubState.isPersoLocked(mPersoSubState)) {
+ log ("Unsupported Perso Subtype :" + mPersoSubState.name());
+ return;
+ }
+
+ if (type == statusType.ENTRY.name()) {
+ String displayText = getContext().getString(label);
+ mPersoSubtypeText.setText(displayText);
+ } else {
+ mStatusText.setText(label);
+ mEntryPanel.setVisibility(View.GONE);
+ mStatusPanel.setVisibility(View.VISIBLE);
+ }
}
private void hideAlert() {
@@ -241,13 +289,13 @@
}
View.OnClickListener mDismissListener = new View.OnClickListener() {
- public void onClick(View v) {
- if (DBG) log("mDismissListener: skipping depersonalization...");
- dismiss();
- }
- };
+ public void onClick(View v) {
+ if (DBG) log("mDismissListener: skipping depersonalization...");
+ dismiss();
+ }
+ };
private void log(String msg) {
- Log.v(TAG, "[IccNetworkDepersonalizationPanel] " + msg);
+ Log.d(TAG, "[IccNetworkDepersonalizationPanel] " + msg);
}
}
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 1a28afd..f390501 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -22,21 +22,26 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.telephony.SubscriptionManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsRcsController;
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import com.android.ims.ImsManager;
-import com.android.ims.RcsFeatureManager;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.services.telephony.rcs.RcsFeatureController;
+import com.android.services.telephony.rcs.TelephonyRcsService;
+import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
import java.util.List;
@@ -50,6 +55,7 @@
private static ImsRcsController sInstance;
private PhoneGlobals mApp;
+ private TelephonyRcsService mRcsService;
/**
* Initialize the singleton ImsRcsController instance.
@@ -74,16 +80,16 @@
}
/**
- * Register a IImsRegistrationCallback to receive IMS network registration state.
+ * Register a {@link RegistrationManager.RegistrationCallback} to receive IMS network
+ * registration state.
*/
@Override
- public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)
- throws RemoteException {
+ public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
enforceReadPrivilegedPermission("registerImsRegistrationCallback");
final long token = Binder.clearCallingIdentity();
try {
- getRcsFeatureManager(subId).registerImsRegistrationCallback(callback);
- } catch (com.android.ims.ImsException e) {
+ getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
+ } catch (ImsException e) {
Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage());
throw new ServiceSpecificException(e.getCode());
} finally {
@@ -92,14 +98,14 @@
}
/**
- * Removes an existing {@link RegistrationCallback}.
+ * Removes an existing {@link RegistrationManager.RegistrationCallback}.
*/
@Override
public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
final long token = Binder.clearCallingIdentity();
try {
- getRcsFeatureManager(subId).unregisterImsRegistrationCallback(callback);
+ getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
} catch (ServiceSpecificException e) {
Log.e(TAG, "unregisterImsRegistrationCallback: error=" + e.errorCode);
} finally {
@@ -115,7 +121,7 @@
enforceReadPrivilegedPermission("getImsRcsRegistrationState");
final long token = Binder.clearCallingIdentity();
try {
- getImsPhone(subId).getImsRcsRegistrationState(regState -> {
+ getRcsFeatureController(subId).getRegistrationState(regState -> {
try {
consumer.accept((regState == null)
? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
@@ -136,7 +142,7 @@
enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
final long token = Binder.clearCallingIdentity();
try {
- getImsPhone(subId).getImsRcsRegistrationTech(regTech -> {
+ getRcsFeatureController(subId).getRegistrationTech(regTech -> {
// Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
int regTechConverted = (regTech == null)
? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
@@ -161,13 +167,12 @@
* @param callback The ImsCapabilityCallback to be registered.
*/
@Override
- public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
- throws RemoteException {
+ public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
final long token = Binder.clearCallingIdentity();
try {
- getRcsFeatureManager(subId).registerRcsAvailabilityCallback(callback);
- } catch (com.android.ims.ImsException e) {
+ getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
+ } catch (ImsException e) {
Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
throw new ServiceSpecificException(e.getCode());
} finally {
@@ -186,9 +191,41 @@
enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
final long token = Binder.clearCallingIdentity();
try {
- getRcsFeatureManager(subId).unregisterRcsAvailabilityCallback(callback);
- } catch (com.android.ims.ImsException e) {
- Log.e(TAG, "unregisterRcsAvailabilityCallback: sudId=" + subId + "," + e.getMessage());
+ getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+ enforceReadPrivilegedPermission("registerUcePublishStateCallback");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+ UserCapabilityExchangeImpl.class);
+ if (uce == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ uce.registerPublishStateCallback(c);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+ enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+ UserCapabilityExchangeImpl.class);
+ if (uce == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ uce.unregisterUcePublishStateCallback(c);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -209,8 +246,8 @@
enforceReadPrivilegedPermission("isCapable");
final long token = Binder.clearCallingIdentity();
try {
- return getRcsFeatureManager(subId).isCapable(capability, radioTech);
- } catch (com.android.ims.ImsException e) {
+ return getRcsFeatureController(subId).isCapable(capability, radioTech);
+ } catch (ImsException e) {
Log.e(TAG, "isCapable: sudId=" + subId
+ ", capability=" + capability + ", " + e.getMessage());
return false;
@@ -233,8 +270,8 @@
enforceReadPrivilegedPermission("isAvailable");
final long token = Binder.clearCallingIdentity();
try {
- return getRcsFeatureManager(subId).isAvailable(capability);
- } catch (com.android.ims.ImsException e) {
+ return getRcsFeatureController(subId).isAvailable(capability);
+ } catch (ImsException e) {
Log.e(TAG, "isAvailable: sudId=" + subId
+ ", capability=" + capability + ", " + e.getMessage());
return false;
@@ -244,26 +281,71 @@
}
@Override
- public void requestCapabilities(int subId, List<Uri> contactNumbers,
- IRcsUceControllerCallback c) {
+ public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
+ List<Uri> contactNumbers, IRcsUceControllerCallback c) {
enforceReadPrivilegedPermission("requestCapabilities");
+ if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "The user has not enabled UCE for this subscription.");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+ UserCapabilityExchangeImpl.class);
+ if (uce == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ uce.requestCapabilities(contactNumbers, c);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public int getUcePublishState(int subId) {
enforceReadPrivilegedPermission("getUcePublishState");
- return -1;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+ UserCapabilityExchangeImpl.class);
+ if (uce == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support UCE.");
+ }
+ return uce.getUcePublishState();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
- public boolean isUceSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("isUceSettingEnabled");
- return false;
+ public boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, subId, callingPackage, callingFeatureId, "isUceSettingEnabled")) {
+ Log.w(TAG, "isUceSettingEnabled: READ_PHONE_STATE app op disabled when accessing "
+ + "isUceSettingEnabled");
+ return false;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return SubscriptionManager.getBooleanSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void setUceSettingEnabled(int subId, boolean isEnabled) {
enforceModifyPermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SubscriptionManager.setSubscriptionProperty(subId,
+ SubscriptionManager.IMS_RCS_UCE_ENABLED, (isEnabled ? "1" : "0"));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -317,26 +399,30 @@
* @return The RcsFeatureManager instance
* @throws ServiceSpecificException if getting RcsFeatureManager instance failed.
*/
- private RcsFeatureManager getRcsFeatureManager(int subId) {
+ private RcsFeatureController getRcsFeatureController(int subId) {
if (!ImsManager.isImsSupportedOnDevice(mApp)) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"IMS is not available on device.");
}
+ if (mRcsService == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS is not available on device.");
+ }
Phone phone = PhoneGlobals.getPhone(subId);
if (phone == null) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
"Invalid subscription Id: " + subId);
}
- ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
- if (imsPhone == null) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
- "Cannot find ImsPhone instance: " + subId);
+ int slotId = phone.getPhoneId();
+ RcsFeatureController c = mRcsService.getFeatureController(slotId);
+ if (c == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "The requested operation is not supported for subId " + subId);
}
- RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
- if (rcsFeatureManager == null) {
- throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
- "Cannot find RcsFeatureManager instance: " + subId);
- }
- return rcsFeatureManager;
+ return c;
+ }
+
+ void setRcsService(TelephonyRcsService rcsService) {
+ mRcsService = rcsService;
}
}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index fe55335..fccceec 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -470,7 +470,7 @@
private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
String dialerPackage = mContext.getSystemService(TelecomManager.class)
- .getDefaultDialerPackage(userHandle.getIdentifier());
+ .getDefaultDialerPackage(userHandle);
return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
.setPackage(dialerPackage);
}
diff --git a/src/com/android/phone/NumberVerificationManager.java b/src/com/android/phone/NumberVerificationManager.java
index 9ec16f8..2298d40 100644
--- a/src/com/android/phone/NumberVerificationManager.java
+++ b/src/com/android/phone/NumberVerificationManager.java
@@ -135,7 +135,7 @@
}
// make sure at least one phone is registered for voice
- if (phone.getServiceState().getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+ if (phone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
isAnyPhoneVoiceRegistered = true;
}
// make sure at least one phone has room for an incoming call.
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 9ecb2c5..df1f470 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -71,6 +71,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.phone.settings.SettingsConstants;
import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
+import com.android.services.telephony.rcs.TelephonyRcsService;
import com.android.services.telephony.sip.SipAccountRegistry;
import com.android.services.telephony.sip.SipUtil;
@@ -149,6 +150,7 @@
CallerInfoCache callerInfoCache;
NotificationMgr notificationMgr;
ImsResolver mImsResolver;
+ TelephonyRcsService mTelephonyRcsService;
public PhoneInterfaceManager phoneMgr;
public ImsRcsController imsRcsController;
CarrierConfigLoader configLoader;
@@ -203,6 +205,16 @@
private final SettingsObserver mSettingsObserver;
+ private static class EventSimStateChangedBag {
+ final int mPhoneId;
+ final String mIccStatus;
+
+ EventSimStateChangedBag(int phoneId, String iccStatus) {
+ mPhoneId = phoneId;
+ mIccStatus = iccStatus;
+ }
+ }
+
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -223,7 +235,8 @@
// they enter a valid SIM network PIN.
Log.i(LOG_TAG, "show sim depersonal panel");
Phone phone = (Phone) ((AsyncResult) msg.obj).userObj;
- IccNetworkDepersonalizationPanel.showDialog(phone);
+ int subType = (Integer)((AsyncResult)msg.obj).result;
+ IccNetworkDepersonalizationPanel.showDialog(phone, subType);
}
break;
@@ -251,8 +264,9 @@
// Marks the event where the SIM goes into ready state.
// Right now, this is only used for the PUK-unlocking
// process.
- if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)
- || msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) {
+ EventSimStateChangedBag bag = (EventSimStateChangedBag)msg.obj;
+ if (bag.mIccStatus == IccCardConstants.INTENT_VALUE_ICC_READY
+ || bag.mIccStatus == IccCardConstants.INTENT_VALUE_ICC_LOADED) {
// when the right event is triggered and there
// are UI objects in the foreground, we close
// them to display the lock panel.
@@ -264,6 +278,8 @@
mPUKEntryProgressDialog.dismiss();
mPUKEntryProgressDialog = null;
}
+ Log.i(LOG_TAG, "Dismissing depersonal panel");
+ IccNetworkDepersonalizationPanel.dialogDismiss(bag.mPhoneId);
}
break;
@@ -303,8 +319,8 @@
// Cache the "voice capable" flag.
// This flag currently comes from a resource (which is
// overrideable on a per-product basis):
- sVoiceCapable =
- getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
+ sVoiceCapable = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
+ .isVoiceCapable();
// ...but this might eventually become a PackageManager "system
// feature" instead, in which case we'd do something like:
// sVoiceCapable =
@@ -372,6 +388,13 @@
imsRcsController = ImsRcsController.init(this);
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+ mTelephonyRcsService = new TelephonyRcsService(this,
+ PhoneFactory.getPhones().length);
+ mTelephonyRcsService.initialize();
+ imsRcsController.setRcsService(mTelephonyRcsService);
+ }
+
configLoader = CarrierConfigLoader.init(this);
// Create the CallNotifier singleton, which handles
@@ -403,7 +426,7 @@
IntentFilter sipIntentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
sipIntentFilter.addAction(SipManager.ACTION_SIP_SERVICE_UP);
sipIntentFilter.addAction(SipManager.ACTION_SIP_CALL_OPTION_CHANGED);
- sipIntentFilter.addAction(SipManager.ACTION_SIP_REMOVE_PHONE);
+ sipIntentFilter.addAction(SipManager.ACTION_SIP_REMOVE_PROFILE);
registerReceiver(mSipReceiver, sipIntentFilter);
mCarrierVvmPackageInstalledReceiver.register(this);
@@ -640,14 +663,9 @@
PhoneUtils.unregisterIccStatus(mHandler, phoneId);
PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED, phoneId);
}
- if (mPUKEntryActivity != null) {
- // if an attempt to un-PUK-lock the device was made, while we're
- // receiving this state change notification, notify the handler.
- // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
- // been attempted.
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
- intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
- }
+ String iccStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
+ new EventSimStateChangedBag(phoneId, iccStatus)));
} 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.");
@@ -662,7 +680,8 @@
if (TelephonyCapabilities.supportsEcm(phoneInEcm)) {
Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp.");
// Start Emergency Callback Mode service
- if (intent.getBooleanExtra("phoneinECMState", false)) {
+ if (intent.getBooleanExtra(
+ TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
context.startService(new Intent(context,
EmergencyCallbackModeService.class));
} else {
@@ -708,7 +727,7 @@
} else if (action.equals(SipManager.ACTION_SIP_SERVICE_UP)
|| action.equals(SipManager.ACTION_SIP_CALL_OPTION_CHANGED)) {
sipAccountRegistry.setup(context);
- } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PHONE)) {
+ } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PROFILE)) {
if (DBG) {
Log.d(LOG_TAG, "SIP_REMOVE_PHONE "
+ intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
@@ -905,6 +924,12 @@
e.printStackTrace();
}
pw.decreaseIndent();
+ pw.println("RcsService:");
+ try {
+ if (mTelephonyRcsService != null) mTelephonyRcsService.dump(fd, pw, args);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
pw.decreaseIndent();
pw.println("------- End PhoneGlobals -------");
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 859131f..3372aae 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -35,7 +35,6 @@
import android.content.pm.ComponentInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.net.NetworkStats;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
@@ -46,8 +45,10 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -65,6 +66,7 @@
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
+import android.telephony.CallForwardingInfo;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
@@ -84,7 +86,6 @@
import android.telephony.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.RadioAccessSpecifier;
-import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
@@ -110,24 +111,27 @@
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import com.android.ims.ImsManager;
import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CarrierInfoManager;
import com.android.internal.telephony.CarrierResolver;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.DefaultPhoneNotifier;
import com.android.internal.telephony.HalVersion;
+import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.IIntegerConsumer;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.ITelephony;
@@ -173,6 +177,7 @@
import com.android.phone.vvm.RemoteVvmTaskManager;
import com.android.phone.vvm.VisualVoicemailSettingsUtil;
import com.android.phone.vvm.VisualVoicemailSmsFilterConfig;
+import com.android.telephony.Rlog;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -185,6 +190,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
+import java.util.function.Consumer;
/**
* Implementation of the ITelephony interface.
@@ -266,6 +272,20 @@
private static final int EVENT_SET_FORBIDDEN_PLMNS_DONE = 73;
private static final int CMD_ERASE_MODEM_CONFIG = 74;
private static final int EVENT_ERASE_MODEM_CONFIG_DONE = 75;
+ private static final int CMD_CHANGE_ICC_LOCK_PASSWORD = 76;
+ private static final int EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE = 77;
+ private static final int CMD_SET_ICC_LOCK_ENABLED = 78;
+ private static final int EVENT_SET_ICC_LOCK_ENABLED_DONE = 79;
+ private static final int CMD_SET_SYSTEM_SELECTION_CHANNELS = 80;
+ private static final int EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE = 81;
+ private static final int CMD_GET_CALL_FORWARDING = 83;
+ private static final int EVENT_GET_CALL_FORWARDING_DONE = 84;
+ private static final int CMD_SET_CALL_FORWARDING = 85;
+ private static final int EVENT_SET_CALL_FORWARDING_DONE = 86;
+ private static final int CMD_GET_CALL_WAITING = 87;
+ private static final int EVENT_GET_CALL_WAITING_DONE = 88;
+ private static final int CMD_SET_CALL_WAITING = 89;
+ private static final int EVENT_SET_CALL_WAITING_DONE = 90;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -800,6 +820,148 @@
getPhoneFromRequest(request).getAvailableNetworks(onCompleted);
break;
+ case CMD_GET_CALL_FORWARDING:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_CALL_FORWARDING_DONE, request);
+ int callForwardingReason = (Integer) request.argument;
+ getPhoneFromRequest(request).getCallForwardingOption(
+ callForwardingReason, onCompleted);
+ break;
+
+ case EVENT_GET_CALL_FORWARDING_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ CallForwardingInfo callForwardingInfo = null;
+ if (ar.exception == null && ar.result != null) {
+ CallForwardInfo[] callForwardInfos = (CallForwardInfo[]) ar.result;
+ for (CallForwardInfo callForwardInfo : callForwardInfos) {
+ // Service Class is a bit mask per 3gpp 27.007. Search for
+ // any service for voice call.
+ if ((callForwardInfo.serviceClass
+ & CommandsInterface.SERVICE_CLASS_VOICE) > 0) {
+ callForwardingInfo = new CallForwardingInfo(
+ callForwardInfo.serviceClass, callForwardInfo.reason,
+ callForwardInfo.number,
+ callForwardInfo.timeSeconds);
+ break;
+ }
+ }
+ // Didn't find a call forward info for voice call.
+ if (callForwardingInfo == null) {
+ callForwardingInfo = new CallForwardingInfo(
+ CallForwardingInfo.STATUS_UNKNOWN_ERROR,
+ 0 /* reason */, null /* number */, 0 /* timeout */);
+ }
+ } else {
+ if (ar.result == null) {
+ loge("EVENT_GET_CALL_FORWARDING_DONE: Empty response");
+ }
+ if (ar.exception != null) {
+ loge("EVENT_GET_CALL_FORWARDING_DONE: Exception: " + ar.exception);
+ }
+ int errorCode = CallForwardingInfo.STATUS_UNKNOWN_ERROR;
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.FDN_CHECK_FAILURE) {
+ errorCode = CallForwardingInfo.STATUS_FDN_CHECK_FAILURE;
+ } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ errorCode = CallForwardingInfo.STATUS_NOT_SUPPORTED;
+ }
+ }
+ callForwardingInfo = new CallForwardingInfo(
+ errorCode, 0 /* reason */, null /* number */, 0 /* timeout */);
+ }
+ request.result = callForwardingInfo;
+ notifyRequester(request);
+ break;
+
+ case CMD_SET_CALL_FORWARDING:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_SET_CALL_FORWARDING_DONE, request);
+ CallForwardingInfo callForwardingInfoToSet =
+ (CallForwardingInfo) request.argument;
+ getPhoneFromRequest(request).setCallForwardingOption(
+ callForwardingInfoToSet.getStatus(),
+ callForwardingInfoToSet.getReason(),
+ callForwardingInfoToSet.getNumber(),
+ callForwardingInfoToSet.getTimeoutSeconds(), onCompleted);
+ break;
+
+ case EVENT_SET_CALL_FORWARDING_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result = true;
+ } else {
+ request.result = false;
+ loge("setCallForwarding exception: " + ar.exception);
+ }
+ notifyRequester(request);
+ break;
+
+ case CMD_GET_CALL_WAITING:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_CALL_WAITING_DONE, request);
+ getPhoneFromRequest(request).getCallWaiting(onCompleted);
+ break;
+
+ case EVENT_GET_CALL_WAITING_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ int callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+ if (ar.exception == null && ar.result != null) {
+ int[] callForwardResults = (int[]) ar.result;
+ // Service Class is a bit mask per 3gpp 27.007.
+ // Search for any service for voice call.
+ if (callForwardResults.length > 1
+ && ((callForwardResults[1]
+ & CommandsInterface.SERVICE_CLASS_VOICE) > 0)) {
+ callForwardingStatus = callForwardResults[0] == 0
+ ? TelephonyManager.CALL_WAITING_STATUS_INACTIVE
+ : TelephonyManager.CALL_WAITING_STATUS_ACTIVE;
+ } else {
+ callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_INACTIVE;
+ }
+ } else {
+ if (ar.result == null) {
+ loge("EVENT_GET_CALL_WAITING_DONE: Empty response");
+ }
+ if (ar.exception != null) {
+ loge("EVENT_GET_CALL_WAITING_DONE: Exception: " + ar.exception);
+ }
+ if (ar.exception instanceof CommandException) {
+ CommandException.Error error =
+ ((CommandException) (ar.exception)).getCommandError();
+ if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+ callForwardingStatus =
+ TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED;
+ }
+ }
+ }
+ request.result = callForwardingStatus;
+ notifyRequester(request);
+ break;
+
+ case CMD_SET_CALL_WAITING:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_SET_CALL_WAITING_DONE, request);
+ boolean isEnable = (Boolean) request.argument;
+ getPhoneFromRequest(request).setCallWaiting(isEnable, onCompleted);
+ break;
+
+ case EVENT_SET_CALL_WAITING_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result = true;
+ } else {
+ request.result = false;
+ loge("setCallWaiting exception: " + ar.exception);
+ }
+ notifyRequester(request);
+ break;
+
case EVENT_PERFORM_NETWORK_SCAN_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
@@ -859,14 +1021,47 @@
onCompleted = obtainMessage(EVENT_GET_MODEM_ACTIVITY_INFO_DONE, request);
if (defaultPhone != null) {
defaultPhone.getModemActivityInfo(onCompleted, request.workSource);
+ } else {
+ ResultReceiver result = (ResultReceiver) request.argument;
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY,
+ new ModemActivityInfo(0, 0, 0, new int[0], 0));
+ result.send(0, bundle);
}
break;
case EVENT_GET_MODEM_ACTIVITY_INFO_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
+ ResultReceiver result = (ResultReceiver) request.argument;
+
+ ModemActivityInfo ret = new ModemActivityInfo(0, 0, 0, new int[0], 0);
if (ar.exception == null && ar.result != null) {
- request.result = ar.result;
+ // Update the last modem activity info and the result of the request.
+ ModemActivityInfo info = (ModemActivityInfo) ar.result;
+ if (isModemActivityInfoValid(info)) {
+ int[] mergedTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+ int[] txTimeMs = info.getTransmitTimeMillis();
+ int[] lastModemTxTimeMs = mLastModemActivityInfo
+ .getTransmitTimeMillis();
+ for (int i = 0; i < mergedTxTimeMs.length; i++) {
+ mergedTxTimeMs[i] = txTimeMs[i] + lastModemTxTimeMs[i];
+ }
+ mLastModemActivityInfo.setTimestamp(info.getTimestamp());
+ mLastModemActivityInfo.setSleepTimeMillis(info.getSleepTimeMillis()
+ + mLastModemActivityInfo.getSleepTimeMillis());
+ mLastModemActivityInfo.setIdleTimeMillis(info.getIdleTimeMillis()
+ + mLastModemActivityInfo.getIdleTimeMillis());
+ mLastModemActivityInfo.setTransmitTimeMillis(mergedTxTimeMs);
+ mLastModemActivityInfo.setReceiveTimeMillis(
+ info.getReceiveTimeMillis()
+ + mLastModemActivityInfo.getReceiveTimeMillis());
+ }
+ ret = new ModemActivityInfo(mLastModemActivityInfo.getTimestamp(),
+ mLastModemActivityInfo.getSleepTimeMillis(),
+ mLastModemActivityInfo.getIdleTimeMillis(),
+ mLastModemActivityInfo.getTransmitTimeMillis(),
+ mLastModemActivityInfo.getReceiveTimeMillis());
} else {
if (ar.result == null) {
loge("queryModemActivityInfo: Empty response");
@@ -877,10 +1072,9 @@
loge("queryModemActivityInfo: Unknown exception");
}
}
- // Result cannot be null. Return ModemActivityInfo with all fields set to 0.
- if (request.result == null) {
- request.result = new ModemActivityInfo(0, 0, 0, null, 0);
- }
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY, ret);
+ result.send(0, bundle);
notifyRequester(request);
break;
@@ -1178,6 +1372,23 @@
}
notifyRequester(request);
break;
+ case CMD_SET_SYSTEM_SELECTION_CHANNELS: {
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE, request);
+ Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> args =
+ (Pair<List<RadioAccessSpecifier>, Consumer<Boolean>>) request.argument;
+ request.phone.setSystemSelectionChannels(args.first, onCompleted);
+ break;
+ }
+ case EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> args =
+ (Pair<List<RadioAccessSpecifier>, Consumer<Boolean>>) request.argument;
+ args.second.accept(ar.exception == null);
+ notifyRequester(request);
+ break;
+ }
case EVENT_SET_FORBIDDEN_PLMNS_DONE:
ar = (AsyncResult) msg.obj;
request = (MainThreadRequest) ar.userObj;
@@ -1230,6 +1441,43 @@
case EVENT_ERASE_MODEM_CONFIG_DONE:
handleNullReturnEvent(msg, "eraseModemConfig");
break;
+
+ case CMD_CHANGE_ICC_LOCK_PASSWORD:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE, request);
+ Pair<String, String> changed = (Pair<String, String>) request.argument;
+ getPhoneFromRequest(request).getIccCard().changeIccLockPassword(
+ changed.first, changed.second, onCompleted);
+ break;
+ case EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS;
+ } else {
+ request.result = msg.arg1;
+ }
+ notifyRequester(request);
+ break;
+
+ case CMD_SET_ICC_LOCK_ENABLED:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_SET_ICC_LOCK_ENABLED_DONE, request);
+ Pair<Boolean, String> enabled = (Pair<Boolean, String>) request.argument;
+ getPhoneFromRequest(request).getIccCard().setIccLockEnabled(
+ enabled.first, enabled.second, onCompleted);
+ break;
+ case EVENT_SET_ICC_LOCK_ENABLED_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS;
+ } else {
+ request.result = msg.arg1;
+ }
+ notifyRequester(request);
+ break;
+
default:
Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
break;
@@ -1453,6 +1701,16 @@
}
}
+ private boolean isImsAvailableOnDevice() {
+ PackageManager pm = getDefaultPhone().getContext().getPackageManager();
+ if (pm == null) {
+ // For some reason package manger is not available.. This will fail internally anyway,
+ // so do not throw error and allow.
+ return true;
+ }
+ return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0);
+ }
+
public void dial(String number) {
dialForSubscriber(getPreferredVoiceSubscription(), number);
}
@@ -2005,6 +2263,7 @@
.setCallingPid(Binder.getCallingPid())
.setCallingUid(Binder.getCallingUid())
.setMethod("getCellLocation")
+ .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE)
.setMinSdkVersionForFine(Build.VERSION_CODES.Q)
.build());
switch (locationResult) {
@@ -2027,16 +2286,7 @@
}
@Override
- public String getNetworkCountryIsoForPhone(int phoneId, String callingPackage,
- String callingFeatureId) {
- if (!TextUtils.isEmpty(callingPackage)) {
- final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
- callingFeatureId, "getNetworkCountryIsoForPhone")) {
- return "";
- }
- }
-
+ public String getNetworkCountryIsoForPhone(int phoneId) {
// Reporting the correct network country is ambiguous when IWLAN could conflict with
// registered cell info, so return a NULL country instead.
final long identity = Binder.clearCallingIdentity();
@@ -2046,29 +2296,15 @@
phoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
}
final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
- // Todo: fix this when we can get the actual cellular network info when the device
- // is on IWLAN.
- if (TelephonyManager.NETWORK_TYPE_IWLAN
- == getVoiceNetworkTypeForSubscriber(subId, mApp.getPackageName(),
- null)) {
- return "";
- }
Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone != null) {
- ServiceStateTracker sst = phone.getServiceStateTracker();
- EmergencyNumberTracker emergencyNumberTracker = phone.getEmergencyNumberTracker();
- if (sst != null) {
- LocaleTracker lt = sst.getLocaleTracker();
- if (lt != null) {
- if (!TextUtils.isEmpty(lt.getCurrentCountry())) {
- return lt.getCurrentCountry();
- } else if (emergencyNumberTracker != null) {
- return emergencyNumberTracker.getEmergencyCountryIso();
- }
- }
- }
- }
- return "";
+ if (phone == null) return "";
+ ServiceStateTracker sst = phone.getServiceStateTracker();
+ if (sst == null) return "";
+ LocaleTracker lt = sst.getLocaleTracker();
+ if (lt == null) return "";
+ if (!TextUtils.isEmpty(lt.getCurrentCountry())) return lt.getCurrentCountry();
+ EmergencyNumberTracker ent = phone.getEmergencyNumberTracker();
+ return (ent == null) ? "" : ent.getEmergencyCountryIso();
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2440,6 +2676,17 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
+ /**
+ * Make sure the caller is system.
+ *
+ * @throws SecurityException if the caller is not system.
+ */
+ private void enforceSystemCaller() {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Caller must be system");
+ }
+ }
+
private void enforceActiveEmergencySessionPermission() {
mApp.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
@@ -2677,7 +2924,8 @@
*/
@Override
public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
- TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, "setVoiceMailNumber");
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ mApp, subId, "setVoiceMailNumber");
final long identity = Binder.clearCallingIdentity();
try {
@@ -2783,14 +3031,15 @@
}
@Override
- public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
- String number, int port, String text, PendingIntent sentIntent) {
+ public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
+ String callingAttributionTag, int subId, String number, int port, String text,
+ PendingIntent sentIntent) {
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
enforceVisualVoicemailPackage(callingPackage, subId);
enforceSendSmsPermission();
SmsController smsController = PhoneFactory.getSmsController();
- smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
- sentIntent);
+ smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
+ subId, number, port, text, sentIntent);
}
/**
@@ -2926,7 +3175,7 @@
TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
String defaultDialer = tm.getDefaultDialerPackage();
if (!TextUtils.equals(callingPackage, defaultDialer)) {
- TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
getDefaultSubscription(), "sendDialerSpecialCode");
}
@@ -2940,11 +3189,18 @@
@Override
public int getNetworkSelectionMode(int subId) {
- if (!isActiveSubscription(subId)) {
- return TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getNetworkSelectionMode");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (!isActiveSubscription(subId)) {
+ return TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
+ }
+ return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
-
- return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
}
@Override
@@ -2963,10 +3219,17 @@
return false;
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ * @param c The callback that will be used to send the result.
+ */
@Override
public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c)
throws RemoteException {
- enforceReadPrivilegedPermission("registerImsRegistrationCallback");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "registerImsRegistrationCallback");
+
if (!ImsManager.isImsSupportedOnDevice(mApp)) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"IMS not available on device.");
@@ -2983,9 +3246,15 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ * @param c The callback that will be used to send the result.
+ */
@Override
public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c) {
- enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "unregisterImsRegistrationCallback");
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
@@ -3041,7 +3310,8 @@
*/
@Override
public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) {
- enforceReadPrivilegedPermission("getImsMmTelRegistrationTransportType");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getImsMmTelRegistrationTransportType");
if (!ImsManager.isImsSupportedOnDevice(mApp)) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"IMS not available on device.");
@@ -3072,10 +3342,16 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ * @param c The callback that will be used to send the result.
+ */
@Override
public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c)
throws RemoteException {
- enforceReadPrivilegedPermission("registerMmTelCapabilityCallback");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "registerMmTelCapabilityCallback");
if (!ImsManager.isImsSupportedOnDevice(mApp)) {
throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
"IMS not available on device.");
@@ -3092,9 +3368,15 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ * @param c The callback that will be used to send the result.
+ */
@Override
public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) {
- enforceReadPrivilegedPermission("unregisterMmTelCapabilityCallback");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "unregisterMmTelCapabilityCallback");
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
@@ -3141,6 +3423,9 @@
Phone phone = getPhone(subId);
if (phone == null) return false;
return phone.isImsCapabilityAvailable(capability, regTech);
+ } catch (com.android.ims.ImsException e) {
+ Log.w(LOG_TAG, "IMS isAvailable - service unavailable: " + e.getMessage());
+ return false;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3184,9 +3469,15 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ */
@Override
public boolean isAdvancedCallingSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("enforceReadPrivilegedPermission");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isAdvancedCallingSettingEnabled");
+
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
final long token = Binder.clearCallingIdentity();
try {
@@ -3215,9 +3506,14 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ */
@Override
public boolean isVtSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("isVtSettingEnabled");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isVtSettingEnabled");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3244,9 +3540,14 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ */
@Override
public boolean isVoWiFiSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("isVoWiFiSettingEnabled");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isVoWiFiSettingEnabled");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3274,9 +3575,14 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ */
@Override
public boolean isVoWiFiRoamingSettingEnabled(int subId) {
- enforceReadPrivilegedPermission("isVoWiFiRoamingSettingEnabled");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isVoWiFiRoamingSettingEnabled");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3321,9 +3627,14 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ */
@Override
public int getVoWiFiModeSetting(int subId) {
- enforceReadPrivilegedPermission("getVoWiFiModeSetting");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getVoWiFiModeSetting");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3398,9 +3709,14 @@
}
}
+ /**
+ * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+ * @param subId The subscription to use to check the configuration.
+ */
@Override
public boolean isTtyOverVolteEnabled(int subId) {
- enforceReadPrivilegedPermission("isTtyOverVolteEnabled");
+ TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isTtyOverVolteEnabled");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3418,6 +3734,10 @@
enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback");
final long identity = Binder.clearCallingIdentity();
try {
+ if (!isImsAvailableOnDevice()) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
.addProvisioningCallbackForSubscription(callback, subId);
@@ -3449,6 +3769,91 @@
}
}
+
+ private void checkModifyPhoneStatePermission(int subId, String message) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+ message);
+ }
+
+ private boolean isImsProvisioningRequired(int subId, int capability,
+ boolean isMmtelCapability) {
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("phone instance null for subid " + subId);
+ return false;
+ }
+ if (isMmtelCapability) {
+ if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+ return false;
+ }
+ } else {
+ if (!doesRcsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void setRcsProvisioningStatusForCapability(int subId, int capability,
+ boolean isProvisioned) {
+ checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+ if (!isImsProvisioningRequired(subId, capability, false)) {
+ return;
+ }
+
+ // this capability requires provisioning, route to the correct API.
+ ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
+ switch (capability) {
+ case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE:
+ case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
+ ims.setEabProvisioned(isProvisioned);
+ break;
+ default: {
+ throw new IllegalArgumentException("Tried to set provisioning for "
+ + "rcs capability '" + capability + "', which does not require "
+ + "provisioning.");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ }
+
+
+ @Override
+ public boolean getRcsProvisioningStatusForCapability(int subId, int capability) {
+ enforceReadPrivilegedPermission("getRcsProvisioningStatusForCapability");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+ if (!isImsProvisioningRequired(subId, capability, false)) {
+ return true;
+ }
+
+ ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
+ switch (capability) {
+ case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE:
+ case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
+ return ims.isEabProvisionedOnDevice();
+
+ default: {
+ throw new IllegalArgumentException("Tried to get rcs provisioning for "
+ + "capability '" + capability + "', which does not require "
+ + "provisioning.");
+ }
+ }
+
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
boolean isProvisioned) {
@@ -3456,18 +3861,11 @@
&& tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
}
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
- "setProvisioningStatusForCapability");
+ checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- Phone phone = getPhone(subId);
- if (phone == null) {
- loge("setImsProvisioningStatusForCapability: phone instance null for subid "
- + subId);
- return;
- }
- if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+ if (!isImsProvisioningRequired(subId, capability, true)) {
return;
}
@@ -3505,8 +3903,9 @@
break;
}
default: {
- throw new IllegalArgumentException("Tried to set provisioning for capability '"
- + capability + "', which does not require provisioning.");
+ throw new IllegalArgumentException("Tried to set provisioning for "
+ + "MmTel capability '" + capability + "', which does not require "
+ + "provisioning. ");
}
}
@@ -3525,16 +3924,7 @@
final long identity = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
- Phone phone = getPhone(subId);
- if (phone == null) {
- loge("getImsProvisioningStatusForCapability: phone instance null for subid "
- + subId);
- // We will fail with "true" as the provisioning status because this is the default
- // if we do not require provisioning.
- return true;
- }
-
- if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+ if (!isImsProvisioningRequired(subId, capability, true)) {
return true;
}
@@ -3560,8 +3950,9 @@
return isMmTelCapabilityProvisionedInCache(subId, capability, tech);
}
default: {
- throw new IllegalArgumentException("Tried to get provisioning for capability '"
- + capability + "', which does not require provisioning.");
+ throw new IllegalArgumentException(
+ "Tried to get provisioning for MmTel capability '" + capability
+ + "', which does not require provisioning.");
}
}
@@ -3670,6 +4061,29 @@
return false;
}
+ private boolean doesRcsCapabilityRequireProvisioning(Context context, int subId,
+ int capability) {
+ CarrierConfigManager configManager = new CarrierConfigManager(context);
+ PersistableBundle c = configManager.getConfigForSubId(subId);
+
+ boolean requireRcsProvisioning = c.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, false);
+
+ // First check to make sure that the capability requires provisioning.
+ switch (capability) {
+ case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
+ // intentional fallthrough
+ case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE: {
+ if (requireRcsProvisioning) {
+ // OPTION or PRESENCE requires provisioning
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
@Override
public int getImsProvisioningInt(int subId, int key) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -3922,9 +4336,9 @@
@Override
public int getLteOnCdmaModeForSubscriber(int subId, String callingPackage,
String callingFeatureId) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mApp, subId, callingPackage, callingFeatureId,
- "getLteOnCdmaModeForSubscriber")) {
+ try {
+ enforceReadPrivilegedPermission("getLteOnCdmaModeForSubscriber");
+ } catch (SecurityException e) {
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
}
@@ -4482,6 +4896,27 @@
}
/**
+ * Toggle IMS disable and enable for the framework to reset it. See {@link #enableIms(int)} and
+ * {@link #disableIms(int)}.
+ * @param slotIndex device slot.
+ */
+ public void resetIms(int slotIndex) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ if (mImsResolver == null) {
+ // may happen if the does not support IMS.
+ return;
+ }
+ mImsResolver.disableIms(slotIndex);
+ mImsResolver.enableIms(slotIndex);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
* status updates, if not already enabled.
*/
@@ -4720,12 +5155,11 @@
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "setNetworkSelectionModeAutomatic");
- if (!isActiveSubscription(subId)) {
- return;
- }
-
final long identity = Binder.clearCallingIdentity();
try {
+ if (!isActiveSubscription(subId)) {
+ return;
+ }
if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId);
} finally {
@@ -4804,6 +5238,75 @@
}
/**
+ * Get the call forwarding info, given the call forwarding reason.
+ */
+ @Override
+ public CallForwardingInfo getCallForwarding(int subId, int callForwardingReason) {
+ enforceReadPrivilegedPermission("getCallForwarding");
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (DBG) {
+ log("getCallForwarding: subId " + subId
+ + " callForwardingReason" + callForwardingReason);
+ }
+ return (CallForwardingInfo) sendRequest(
+ CMD_GET_CALL_FORWARDING, callForwardingReason, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Sets the voice call forwarding info including status (enable/disable), call forwarding
+ * reason, the number to forward, and the timeout before the forwarding is attempted.
+ */
+ @Override
+ public boolean setCallForwarding(int subId, CallForwardingInfo callForwardingInfo) {
+ enforceModifyPermission();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (DBG) {
+ log("setCallForwarding: subId " + subId
+ + " callForwardingInfo" + callForwardingInfo);
+ }
+ return (Boolean) sendRequest(CMD_SET_CALL_FORWARDING, callForwardingInfo, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Get the call forwarding info, given the call forwarding reason.
+ */
+ @Override
+ public int getCallWaitingStatus(int subId) {
+ enforceReadPrivilegedPermission("getCallForwarding");
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (DBG) log("getCallWaitingStatus: subId " + subId);
+ return (Integer) sendRequest(CMD_GET_CALL_WAITING, null, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Sets the voice call forwarding info including status (enable/disable), call forwarding
+ * reason, the number to forward, and the timeout before the forwarding is attempted.
+ */
+ @Override
+ public boolean setCallWaitingStatus(int subId, boolean isEnable) {
+ enforceModifyPermission();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (DBG) log("setCallWaitingStatus: subId " + subId + " isEnable: " + isEnable);
+ return (Boolean) sendRequest(CMD_SET_CALL_WAITING, isEnable, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Starts a new network scan and returns the id of this scan.
*
* @param subId id of the subscription
@@ -4944,7 +5447,6 @@
/**
* Set the preferred network type.
- * Used for device configuration by some CDMA operators.
*
* @param networkType the preferred network type, defined in RILConstants.java.
* @return true on success; false on any failure.
@@ -4956,14 +5458,12 @@
final long identity = Binder.clearCallingIdentity();
try {
- if (DBG) log("setPreferredNetworkType: subId " + subId + " type " + networkType);
+ Settings.Global.putInt(mApp.getContentResolver(),
+ Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);
+
Boolean success = (Boolean) sendRequest(
CMD_SET_PREFERRED_NETWORK_TYPE, networkType, subId);
if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
- if (success) {
- Settings.Global.putInt(mApp.getContentResolver(),
- Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);
- }
return success;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -4971,6 +5471,49 @@
}
/**
+ * Get the allowed network types that store in the telephony provider.
+ *
+ * @param subId the id of the subscription.
+ * @return allowedNetworkTypes the allowed network types.
+ */
+ @Override
+ public long getAllowedNetworkTypes(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getAllowedNetworkTypes");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return SubscriptionManager.getLongSubscriptionProperty(
+ subId, SubscriptionManager.ALLOWED_NETWORK_TYPES, -1, mApp);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set the allowed network types.
+ *
+ * @param subId the id of the subscription.
+ * @param allowedNetworkTypes the allowed network types.
+ * @return true on success; false on any failure.
+ */
+ @Override
+ public boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setAllowedNetworkTypes");
+
+ SubscriptionManager.setSubscriptionProperty(subId,
+ SubscriptionManager.ALLOWED_NETWORK_TYPES,
+ String.valueOf(allowedNetworkTypes));
+
+ int preferredNetworkMode = Settings.Global.getInt(mApp.getContentResolver(),
+ Settings.Global.PREFERRED_NETWORK_MODE + subId,
+ RILConstants.PREFERRED_NETWORK_MODE);
+ return setPreferredNetworkType(subId, preferredNetworkMode);
+ }
+
+ /**
* Check whether DUN APN is required for tethering with subId.
*
* @param subId the id of the subscription to require tethering.
@@ -5021,6 +5564,34 @@
}
/**
+ * Enable or disable always reporting signal strength changes from radio.
+ *
+ * @param isEnable {@code true} for enabling; {@code false} for disabling.
+ */
+ @Override
+ public void setAlwaysReportSignalStrength(int subId, boolean isEnable) {
+ enforceModifyPermission();
+ enforceSystemCaller();
+
+ final long identity = Binder.clearCallingIdentity();
+ final Phone phone = getPhone(subId);
+ try {
+ if (phone != null) {
+ if (DBG) {
+ log("setAlwaysReportSignalStrength: subId=" + subId
+ + " isEnable=" + isEnable);
+ }
+ phone.setAlwaysReportSignalStrength(isEnable);
+ } else {
+ loge("setAlwaysReportSignalStrength: no phone found for subId="
+ + subId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Get the user enabled state of Mobile Data.
*
* TODO: remove and use isUserDataEnabled.
@@ -5074,24 +5645,15 @@
}
/**
- * Get whether mobile data is enabled.
+ * Checks if the device is capable of mobile data by considering whether whether the
+ * user has enabled mobile data, whether the carrier has enabled mobile data, and
+ * whether the network policy allows data connections.
*
- * Comparable to {@link #isUserDataEnabled(int)}, this considers all factors deciding
- * whether mobile data is actually enabled.
- *
- * Accepts either ACCESS_NETWORK_STATE, MODIFY_PHONE_STATE or carrier privileges.
- *
- * @return {@code true} if data is enabled else {@code false}
+ * @return {@code true} if the overall data connection is capable; {@code false} if not.
*/
@Override
public boolean isDataEnabled(int subId) {
- try {
- mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
- null);
- } catch (Exception e) {
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
- mApp, subId, "isDataEnabled");
- }
+ enforceReadPrivilegedPermission("isDataEnabled");
final long identity = Binder.clearCallingIdentity();
try {
@@ -5111,14 +5673,13 @@
}
}
- private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim,
+ private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, int uid,
Phone phone) {
//load access rules from carrier configs, and check those as well: b/139133814
SubscriptionController subController = SubscriptionController.getInstance();
if (privilegeFromSim == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
|| subController == null) return privilegeFromSim;
- int uid = Binder.getCallingUid();
PackageManager pkgMgr = phone.getContext().getPackageManager();
String[] packages = pkgMgr.getPackagesForUid(uid);
@@ -5172,11 +5733,12 @@
return getCarrierPrivilegeStatusFromCarrierConfigRules(
card.getCarrierPrivilegeStatusForCurrentTransaction(
- phone.getContext().getPackageManager()), phone);
+ phone.getContext().getPackageManager()), Binder.getCallingUid(), phone);
}
@Override
public int getCarrierPrivilegeStatusForUid(int subId, int uid) {
+ enforceReadPrivilegedPermission("getCarrierPrivilegeStatusForUid");
final Phone phone = getPhone(subId);
if (phone == null) {
loge("getCarrierPrivilegeStatusForUid: Invalid subId");
@@ -5189,8 +5751,8 @@
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
return getCarrierPrivilegeStatusFromCarrierConfigRules(
- profile.getCarrierPrivilegeStatusForUid(
- phone.getContext().getPackageManager(), uid), phone);
+ profile.getCarrierPrivilegeStatusForUid(
+ phone.getContext().getPackageManager(), uid), uid, phone);
}
@Override
@@ -5262,7 +5824,7 @@
PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
| PackageManager.GET_SIGNING_CERTIFICATES,
- UserHandle.USER_SYSTEM);
+ UserHandle.SYSTEM.getIdentifier());
}
for (int p = packages.size() - 1; p >= 0; p--) {
PackageInfo pkgInfo = packages.get(p);
@@ -5312,7 +5874,7 @@
@Override
public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag,
String number) {
- TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
subId, "setLine1NumberForDisplayForSubscriber");
final long identity = Binder.clearCallingIdentity();
@@ -5325,7 +5887,7 @@
final String subscriberId = phone.getSubscriberId();
if (DBG_MERGE) {
- Slog.d(LOG_TAG, "Setting line number for ICC=" + iccId + ", subscriberId="
+ Rlog.d(LOG_TAG, "Setting line number for ICC=" + iccId + ", subscriberId="
+ subscriberId + " to " + number);
}
@@ -5451,7 +6013,7 @@
final String numberKey = PREF_CARRIERS_NUMBER_PREFIX + iccId;
mergeNumber = (String) prefs.get(numberKey);
if (DBG_MERGE) {
- Slog.d(LOG_TAG, "Found line number " + mergeNumber
+ Rlog.d(LOG_TAG, "Found line number " + mergeNumber
+ " for active subscriber " + subscriberId);
}
if (!TextUtils.isEmpty(mergeNumber)) {
@@ -5485,7 +6047,7 @@
final String[] resultArray = result.toArray(new String[result.size()]);
Arrays.sort(resultArray);
if (DBG_MERGE) {
- Slog.d(LOG_TAG,
+ Rlog.d(LOG_TAG,
"Found subscribers " + Arrays.toString(resultArray) + " after merge");
}
return resultArray;
@@ -5540,7 +6102,7 @@
@Override
public boolean setOperatorBrandOverride(int subId, String brand) {
- TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
subId, "setOperatorBrandOverride");
final long identity = Binder.clearCallingIdentity();
@@ -5556,7 +6118,8 @@
public boolean setRoamingOverride(int subId, List<String> gsmRoamingList,
List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
List<String> cdmaNonRoamingList) {
- TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, "setRoamingOverride");
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ mApp, subId, "setRoamingOverride");
final long identity = Binder.clearCallingIdentity();
try {
@@ -5757,7 +6320,12 @@
public boolean isRttEnabled(int subscriptionId) {
final long identity = Binder.clearCallingIdentity();
try {
- return isRttSupported(subscriptionId);
+ boolean isRttSupported = isRttSupported(subscriptionId);
+ boolean isUserRttSettingOn = Settings.Secure.getInt(
+ mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+ boolean shouldIgnoreUserRttSetting = mApp.getCarrierConfigForSubId(subscriptionId)
+ .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
+ return isRttSupported && (isUserRttSettingOn || shouldIgnoreUserRttSetting);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6027,38 +6595,7 @@
final long identity = Binder.clearCallingIdentity();
try {
- ModemActivityInfo ret = null;
- synchronized (mLastModemActivityInfo) {
- ModemActivityInfo info = (ModemActivityInfo) sendRequest(
- CMD_GET_MODEM_ACTIVITY_INFO,
- null, workSource);
- if (isModemActivityInfoValid(info)) {
- int[] mergedTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
- int[] txTimeMs = info.getTransmitTimeMillis();
- int[] lastModemTxTimeMs = mLastModemActivityInfo.getTransmitTimeMillis();
- for (int i = 0; i < mergedTxTimeMs.length; i++) {
- mergedTxTimeMs[i] = txTimeMs[i] + lastModemTxTimeMs[i];
- }
- mLastModemActivityInfo.setTimestamp(info.getTimestamp());
- mLastModemActivityInfo.setSleepTimeMillis(info.getSleepTimeMillis()
- + mLastModemActivityInfo.getSleepTimeMillis());
- mLastModemActivityInfo.setIdleTimeMillis(
- info.getIdleTimeMillis() + mLastModemActivityInfo.getIdleTimeMillis());
- mLastModemActivityInfo.setTransmitTimeMillis(mergedTxTimeMs);
- mLastModemActivityInfo.setReceiveTimeMillis(
- info.getReceiveTimeMillis() + mLastModemActivityInfo
- .getReceiveTimeMillis());
- }
-
- ret = new ModemActivityInfo(mLastModemActivityInfo.getTimestamp(),
- mLastModemActivityInfo.getSleepTimeMillis(),
- mLastModemActivityInfo.getIdleTimeMillis(),
- mLastModemActivityInfo.getTransmitTimeMillis(),
- mLastModemActivityInfo.getReceiveTimeMillis());
- }
- Bundle bundle = new Bundle();
- bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY, ret);
- result.send(0, bundle);
+ sendRequestAsync(CMD_GET_MODEM_ACTIVITY_INFO, result, null, workSource);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -6589,33 +7126,6 @@
}
/**
- * Get aggregated video call data usage since boot.
- *
- * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
- * @return Snapshot of video call data usage
- * {@hide}
- */
- @Override
- public NetworkStats getVtDataUsage(int subId, boolean perUidStats) {
- mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_NETWORK_USAGE_HISTORY,
- null);
-
- final long identity = Binder.clearCallingIdentity();
- try {
- // NetworkStatsService keeps tracking the active network interface and identity. It
- // records the delta with the corresponding network identity.
- // We just return the total video call data usage snapshot since boot.
- Phone phone = getPhone(subId);
- if (phone != null) {
- return phone.getVtDataUsage(perUidStats);
- }
- return null;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
* Policy control of data connection. Usually used when data limit is passed.
* @param enabled True if enabling the data, otherwise disabling.
* @param subId Subscription index
@@ -6995,23 +7505,6 @@
}
}
- @Override
- public void setRadioIndicationUpdateMode(int subId, int filters, int mode) {
- enforceModifyPermission();
- final Phone phone = getPhone(subId);
- if (phone == null) {
- loge("setRadioIndicationUpdateMode fails with invalid subId: " + subId);
- return;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- phone.setRadioIndicationUpdateMode(filters, mode);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
/**
* A test API to reload the UICC profile.
*
@@ -7132,7 +7625,8 @@
@Override
public int getCdmaRoamingMode(int subId) {
- TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
mApp, subId, "getCdmaRoamingMode");
final long identity = Binder.clearCallingIdentity();
@@ -7300,7 +7794,7 @@
}
@Override
- public void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath) {
+ public void updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor) {
enforceActiveEmergencySessionPermission();
final long identity = Binder.clearCallingIdentity();
@@ -7308,7 +7802,24 @@
for (Phone phone: PhoneFactory.getPhones()) {
EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
if (tracker != null) {
- tracker.updateTestOtaEmergencyNumberDbFilePath(otaFilePath);
+ tracker.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void resetOtaEmergencyNumberDbFilePath() {
+ enforceActiveEmergencySessionPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (Phone phone: PhoneFactory.getPhones()) {
+ EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+ if (tracker != null) {
+ tracker.resetOtaEmergencyNumberDbFilePath();
}
}
} finally {
@@ -7558,6 +8069,15 @@
}
/**
+ * Get the current calling package name.
+ * @return the current calling package name
+ */
+ @Override
+ public String getCurrentPackageName() {
+ return mApp.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0];
+ }
+
+ /**
* Return whether data is enabled for certain APN type. This will tell if framework will accept
* corresponding network requests on a subId.
*
@@ -7565,7 +8085,7 @@
* 1) user data is turned on, or
* 2) APN is un-metered for this subscription, or
* 3) APN type is whitelisted. E.g. MMS is whitelisted if
- * {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+ * {@link TelephonyManager#setAlwaysAllowMmsData} is turned on.
*
* @return whether data is allowed for a apn type.
*
@@ -7606,6 +8126,39 @@
}
@Override
+ public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
+ int subscriptionId, IBooleanConsumer resultCallback) {
+ enforceModifyPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhone(subscriptionId);
+ if (phone == null) {
+ try {
+ if (resultCallback != null) {
+ resultCallback.accept(false);
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ return;
+ }
+ Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> argument =
+ Pair.create(specifiers, (x) -> {
+ try {
+ if (resultCallback != null) {
+ resultCallback.accept(x);
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ });
+ sendRequestAsync(CMD_SET_SYSTEM_SELECTION_CHANNELS, argument, phone, null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public boolean isMvnoMatched(int subId, int mvnoType, @NonNull String mvnoMatchData) {
IccRecords iccRecords = UiccController.getInstance().getIccRecords(
SubscriptionManager.getPhoneId(subId), UiccController.APP_FAM_3GPP);
@@ -7617,10 +8170,15 @@
}
@Override
- public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+ public void enqueueSmsPickResult(String callingPackage, String callingAttributionTag,
+ IIntegerConsumer pendingSubIdResult) {
+ if (callingPackage == null) {
+ callingPackage = getCurrentPackageName();
+ }
SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
(AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
- if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
+ if (!permissions.checkCallingCanSendSms(callingPackage, callingAttributionTag,
+ "Sending message")) {
throw new SecurityException("Requires SEND_SMS permission to perform this operation");
}
PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult);
@@ -7689,6 +8247,22 @@
}
}
+ @Override
+ public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
+ enforceModifyPermission();
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhone(subId);
+ if (phone == null) return false;
+
+ return phone.getDataEnabledSettings().setAlwaysAllowMmsData(alwaysAllow);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/**
* Updates whether conference event pacakge handling is enabled.
* @param isCepEnabled {@code true} if CEP handling is enabled (default), or {@code false}
@@ -7716,4 +8290,111 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ /**
+ * Notify that an RCS autoconfiguration XML file has been received for provisioning.
+ *
+ * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed.
+ * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+ * before being read.
+ */
+ @Override
+ public void notifyRcsAutoConfigurationReceived(int subId, @NonNull byte[] config, boolean
+ isCompressed) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "notifyRcsAutoConfigurationReceived");
+ try {
+ IImsConfig configBinder = getImsConfig(getSlotIndex(subId), ImsFeature.FEATURE_RCS);
+ if (configBinder == null) {
+ Rlog.e(LOG_TAG, "null result for getImsConfig");
+ } else {
+ configBinder.notifyRcsAutoConfigurationReceived(config, isCompressed);
+ }
+ } catch (RemoteException e) {
+ Rlog.e(LOG_TAG, "fail to getImsConfig " + e.getMessage());
+ }
+ }
+
+ @Override
+ public boolean isIccLockEnabled(int subId) {
+ enforceReadPrivilegedPermission("isIccLockEnabled");
+
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhone(subId);
+ if (phone != null && phone.getIccCard() != null) {
+ return phone.getIccCard().getIccLockEnabled();
+ } else {
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
+ * Set the ICC pin lock enabled or disabled..
+ *
+ * @return Integer.MAX_VALUE if enable/disable IccLock successfully. If failed it will return
+ * 0 or a positive integer as the attempts remaining value.
+ *
+ */
+ @Override
+ public int setIccLockEnabled(int subId, boolean enabled, String password) {
+ enforceModifyPermission();
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ return 0;
+ }
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int attemptsRemaining = (int) sendRequest(CMD_SET_ICC_LOCK_ENABLED,
+ new Pair<Boolean, String>(enabled, password), phone, null);
+ return attemptsRemaining;
+
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "setIccLockEnabled. Exception e =" + e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return 0;
+ }
+
+ /**
+ * Change the ICC password used in ICC pin lock.
+ *
+ * @return Integer.MAX_VALUE if enable/disable IccLock successfully. If failed it will return
+ * 0 or a positive integer as the attempts remaining value.
+ *
+ */
+ @Override
+ public int changeIccLockPassword(int subId, String oldPassword, String newPassword) {
+ enforceModifyPermission();
+
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ return 0;
+ }
+ // Now that all security checks passes, perform the operation as ourselves.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ int attemptsRemaining = (int) sendRequest(CMD_CHANGE_ICC_LOCK_PASSWORD,
+ new Pair<String, String>(oldPassword, newPassword), phone, null);
+ return attemptsRemaining;
+
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "changeIccLockPassword. Exception e =" + e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean canConnectTo5GInDsdsMode() {
+ return mApp.getResources().getBoolean(R.bool.config_5g_connection_in_dsds_mode);
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index c865f14..8eaa336 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -22,16 +22,22 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.VideoProfile;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Log;
@@ -53,7 +59,9 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyCapabilities;
import com.android.phone.settings.SuppServicesUiUtil;
+import com.android.telephony.Rlog;
+import java.io.IOException;
import java.util.List;
/**
@@ -79,6 +87,9 @@
/** Define for not a special CNAP string */
private static final int CNAP_SPECIAL_CASE_NO = -1;
+ /** Define for default vibrate pattern if res cannot be found */
+ private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
+
/**
* 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
@@ -87,7 +98,6 @@
private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
/** USSD information used to aggregate all USSD messages */
- private static AlertDialog sUssdDialog = null;
private static StringBuilder sUssdMsg = new StringBuilder();
private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT =
@@ -500,9 +510,59 @@
newDialog.getButton(DialogInterface.BUTTON_NEGATIVE)
.setTextColor(context.getResources().getColor(R.color.dialer_theme_color));
}
+
+ if (mmiCode.isNetworkInitiatedUssd()) {
+ playSound(context);
+ }
}
}
+ private static void playSound(Context context) {
+ AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ int callsRingerMode = audioManager.getRingerMode();
+
+ if (callsRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+ log("playSound : RINGER_MODE_NORMAL");
+ try {
+ Uri notificationUri = RingtoneManager.getDefaultUri(
+ RingtoneManager.TYPE_NOTIFICATION);
+ MediaPlayer mediaPlayer = new MediaPlayer();
+ mediaPlayer.setDataSource(context, notificationUri);
+ AudioAttributes aa = new AudioAttributes.Builder()
+ .setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION)
+ .build();
+ mediaPlayer.setAudioAttributes(aa);
+ mediaPlayer.setLooping(false);
+ mediaPlayer.prepare();
+ mediaPlayer.start();
+ } catch (IOException e) {
+ log("playSound exception : " + e);
+ }
+ } else if (callsRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ log("playSound : RINGER_MODE_VIBRATE");
+ Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+ // Use NotificationManagerService#DEFAULT_VIBRATE_PATTERN if
+ // R.array.config_defaultNotificationVibePattern is not defined.
+ long[] pattern = getLongArray(context.getResources(),
+ R.array.config_defaultNotificationVibePattern, DEFAULT_VIBRATE_PATTERN);
+ vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
+ }
+ }
+
+ private static long[] getLongArray(Resources r, int resid, long[] def) {
+ int[] ar = r.getIntArray(resid);
+ if (ar == null) {
+ return def;
+ }
+ final int len = ar.length;
+ long[] out = new long[len];
+ for (int i = 0; i < len; i++) {
+ out[i] = ar[i];
+ }
+ return out;
+ }
+
/**
* It displays the message dialog for user about the mmi code result message.
*
@@ -524,39 +584,36 @@
// displaying system alert dialog on the screen instead of
// using another activity to display the message. This
// places the message at the forefront of the UI.
+ AlertDialog ussdDialog = new AlertDialog.Builder(context, THEME)
+ .setPositiveButton(R.string.ok, null)
+ .setCancelable(true)
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ sUssdMsg.setLength(0);
+ }
+ })
+ .create();
- if (sUssdDialog == null) {
- sUssdDialog = new AlertDialog.Builder(context, THEME)
- .setPositiveButton(R.string.ok, null)
- .setCancelable(true)
- .setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- sUssdMsg.setLength(0);
- }
- })
- .create();
+ ussdDialog.getWindow().setType(windowType);
+ ussdDialog.getWindow().addFlags(
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- sUssdDialog.getWindow().setType(windowType);
- sUssdDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- }
if (sUssdMsg.length() != 0) {
- sUssdMsg
- .insert(0, "\n")
+ sUssdMsg.insert(0, "\n")
.insert(0, app.getResources().getString(R.string.ussd_dialog_sep))
.insert(0, "\n");
}
if (phone != null && phone.getCarrierName() != null) {
- sUssdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title,
+ ussdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title,
phone.getCarrierName()));
} else {
- sUssdDialog
+ ussdDialog
.setTitle(app.getResources().getString(R.string.default_carrier_mmi_msg_title));
}
sUssdMsg.insert(0, text);
- sUssdDialog.setMessage(sUssdMsg.toString());
- sUssdDialog.show();
+ ussdDialog.setMessage(sUssdMsg.toString());
+ ussdDialog.show();
}
/**
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
new file mode 100644
index 0000000..a7d27d5
--- /dev/null
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2019 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.provider.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
+import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
+import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.net.Uri;
+import android.os.Parcel;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The class to provide base facility to access ServiceState related content,
+ * which is stored in a SQLite database.
+ */
+public class ServiceStateProvider extends ContentProvider {
+ private static final String TAG = "ServiceStateProvider";
+
+ public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * The current service state.
+ *
+ * This is the entire {@link ServiceState} object in byte array.
+ *
+ * @hide
+ */
+ public static final String SERVICE_STATE = "service_state";
+
+ /**
+ * An integer value indicating the current data service state.
+ * <p>
+ * Valid values: {@link ServiceState#STATE_IN_SERVICE},
+ * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
+ * {@link ServiceState#STATE_POWER_OFF}.
+ * <p>
+ * This is the same as {@link ServiceState#getDataRegState()}.
+ * @hide
+ */
+ public static final String DATA_REG_STATE = "data_reg_state";
+
+ /**
+ * An integer value indicating the current voice roaming type.
+ * <p>
+ * This is the same as {@link ServiceState#getVoiceRoamingType()}.
+ * @hide
+ */
+ public static final String VOICE_ROAMING_TYPE = "voice_roaming_type";
+
+ /**
+ * An integer value indicating the current data roaming type.
+ * <p>
+ * This is the same as {@link ServiceState#getDataRoamingType()}.
+ * @hide
+ */
+ public static final String DATA_ROAMING_TYPE = "data_roaming_type";
+
+ /**
+ * The current registered voice network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
+ * @hide
+ */
+ public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
+
+ /**
+ * The current registered operator name in short alphanumeric format.
+ * <p>
+ * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
+ * network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
+ * @hide
+ */
+ public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
+
+ /**
+ * The current registered operator numeric id.
+ * <p>
+ * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
+ * network code.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorNumeric()}.
+ */
+ public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
+
+ /**
+ * The current registered data network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
+ * @hide
+ */
+ public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
+
+ /**
+ * The current registered data network operator name in short alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
+ * @hide
+ */
+ public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
+
+ /**
+ * The current registered data network operator numeric id.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorNumeric()}.
+ * @hide
+ */
+ public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
+
+ /**
+ * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}.
+ * @hide
+ */
+ public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology";
+
+ /**
+ * This is the same as {@link ServiceState#getRilDataRadioTechnology()}.
+ * @hide
+ */
+ public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology";
+
+ /**
+ * This is the same as {@link ServiceState#getCssIndicator()}.
+ * @hide
+ */
+ public static final String CSS_INDICATOR = "css_indicator";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaNetworkId()}.
+ * @hide
+ */
+ public static final String NETWORK_ID = "network_id";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaSystemId()}.
+ * @hide
+ */
+ public static final String SYSTEM_ID = "system_id";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}.
+ * @hide
+ */
+ public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}.
+ * @hide
+ */
+ public static final String CDMA_DEFAULT_ROAMING_INDICATOR =
+ "cdma_default_roaming_indicator";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaEriIconIndex()}.
+ * @hide
+ */
+ public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaEriIconMode()}.
+ * @hide
+ */
+ public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode";
+
+ /**
+ * This is the same as {@link ServiceState#isEmergencyOnly()}.
+ * @hide
+ */
+ public static final String IS_EMERGENCY_ONLY = "is_emergency_only";
+
+ /**
+ * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}.
+ * @hide
+ */
+ public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
+ "is_data_roaming_from_registration";
+
+ /**
+ * This is the same as {@link ServiceState#isUsingCarrierAggregation()}.
+ * @hide
+ */
+ public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
+
+ /**
+ * The current registered raw data network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}.
+ * @hide
+ */
+ public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw";
+
+ /**
+ * The current registered raw data network operator name in short alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}.
+ * @hide
+ */
+ public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw";
+
+ private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
+ private static final String[] sColumns = {
+ VOICE_REG_STATE,
+ DATA_REG_STATE,
+ VOICE_ROAMING_TYPE,
+ DATA_ROAMING_TYPE,
+ VOICE_OPERATOR_ALPHA_LONG,
+ VOICE_OPERATOR_ALPHA_SHORT,
+ VOICE_OPERATOR_NUMERIC,
+ DATA_OPERATOR_ALPHA_LONG,
+ DATA_OPERATOR_ALPHA_SHORT,
+ DATA_OPERATOR_NUMERIC,
+ IS_MANUAL_NETWORK_SELECTION,
+ RIL_VOICE_RADIO_TECHNOLOGY,
+ RIL_DATA_RADIO_TECHNOLOGY,
+ CSS_INDICATOR,
+ NETWORK_ID,
+ SYSTEM_ID,
+ CDMA_ROAMING_INDICATOR,
+ CDMA_DEFAULT_ROAMING_INDICATOR,
+ CDMA_ERI_ICON_INDEX,
+ CDMA_ERI_ICON_MODE,
+ IS_EMERGENCY_ONLY,
+ IS_USING_CARRIER_AGGREGATION,
+ OPERATOR_ALPHA_LONG_RAW,
+ OPERATOR_ALPHA_SHORT_RAW,
+ };
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ /**
+ * Returns the {@link ServiceState} information on specified subscription.
+ *
+ * @param subId whose subscriber id is returned
+ * @return the {@link ServiceState} information on specified subscription.
+ */
+ @VisibleForTesting
+ public ServiceState getServiceState(int subId) {
+ return mServiceStates.get(subId);
+ }
+
+ /**
+ * Returns the system's default subscription id.
+ *
+ * @return the "system" default subscription id.
+ */
+ @VisibleForTesting
+ public int getDefaultSubId() {
+ return SubscriptionManager.getDefaultSubscriptionId();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ if (isPathPrefixMatch(uri, CONTENT_URI)) {
+ // Parse the subId
+ int subId = 0;
+ try {
+ subId = Integer.parseInt(uri.getLastPathSegment());
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "insert: no subId provided in uri");
+ throw e;
+ }
+ Log.d(TAG, "subId=" + subId);
+
+ // handle DEFAULT_SUBSCRIPTION_ID
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = getDefaultSubId();
+ }
+
+ final Parcel p = Parcel.obtain();
+ final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE);
+ p.unmarshall(rawBytes, 0, rawBytes.length);
+ p.setDataPosition(0);
+
+ // create the new service state
+ final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p);
+
+ // notify listeners
+ // if ss is null (e.g. first service state update) we will notify for all fields
+ ServiceState ss = getServiceState(subId);
+ notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
+ notifyChangeForSubId(getContext(), ss, newSS, subId);
+
+ // store the new service state
+ mServiceStates.put(subId, newSS);
+ return uri;
+ }
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new RuntimeException("Not supported");
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ if (!isPathPrefixMatch(uri, CONTENT_URI)) {
+ throw new IllegalArgumentException("Invalid URI: " + uri);
+ } else {
+ // Parse the subId
+ int subId = 0;
+ try {
+ subId = Integer.parseInt(uri.getLastPathSegment());
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "query: no subId provided in uri, using default.");
+ subId = getDefaultSubId();
+ }
+ Log.d(TAG, "subId=" + subId);
+
+ // handle DEFAULT_SUBSCRIPTION_ID
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = getDefaultSubId();
+ }
+
+ // Get the service state
+ ServiceState ss = getServiceState(subId);
+ if (ss == null) {
+ Log.d(TAG, "returning null");
+ return null;
+ }
+
+ // Build the result
+ final int voice_reg_state = ss.getState();
+ final int data_reg_state = ss.getDataRegistrationState();
+ final int voice_roaming_type = ss.getVoiceRoamingType();
+ final int data_roaming_type = ss.getDataRoamingType();
+ final String voice_operator_alpha_long = ss.getOperatorAlphaLong();
+ final String voice_operator_alpha_short = ss.getOperatorAlphaShort();
+ final String voice_operator_numeric = ss.getOperatorNumeric();
+ final String data_operator_alpha_long = ss.getOperatorAlphaLong();
+ final String data_operator_alpha_short = ss.getOperatorAlphaShort();
+ final String data_operator_numeric = ss.getOperatorNumeric();
+ final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
+ final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
+ final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
+ final int css_indicator = ss.getCssIndicator();
+ final int network_id = ss.getCdmaNetworkId();
+ final int system_id = ss.getCdmaSystemId();
+ final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
+ final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
+ final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
+ final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
+ final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
+ final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
+ final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw();
+ final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw();
+
+ return buildSingleRowResult(projection, sColumns, new Object[] {
+ voice_reg_state,
+ data_reg_state,
+ voice_roaming_type,
+ data_roaming_type,
+ voice_operator_alpha_long,
+ voice_operator_alpha_short,
+ voice_operator_numeric,
+ data_operator_alpha_long,
+ data_operator_alpha_short,
+ data_operator_numeric,
+ is_manual_network_selection,
+ ril_voice_radio_technology,
+ ril_data_radio_technology,
+ css_indicator,
+ network_id,
+ system_id,
+ cdma_roaming_indicator,
+ cdma_default_roaming_indicator,
+ cdma_eri_icon_index,
+ cdma_eri_icon_mode,
+ is_emergency_only,
+ is_using_carrier_aggregation,
+ operator_alpha_long_raw,
+ operator_alpha_short_raw,
+ });
+ }
+ }
+
+ private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
+ Object[] data) {
+ if (projection == null) {
+ projection = availableColumns;
+ }
+ final MatrixCursor c = new MatrixCursor(projection, 1);
+ final RowBuilder row = c.newRow();
+ for (int i = 0; i < c.getColumnCount(); i++) {
+ final String columnName = c.getColumnName(i);
+ boolean found = false;
+ for (int j = 0; j < availableColumns.length; j++) {
+ if (availableColumns[j].equals(columnName)) {
+ row.add(data[j]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException("Invalid column " + projection[i]);
+ }
+ }
+ return c;
+ }
+
+ /**
+ * Notify interested apps that certain fields of the ServiceState have changed.
+ *
+ * Apps which want to wake when specific fields change can use
+ * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit
+ * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
+ *
+ * We will only notify for certain fields. This is an intentional change from the behavior of
+ * the broadcast. Listeners will be notified when the voice or data registration state or
+ * roaming type changes.
+ */
+ @VisibleForTesting
+ public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
+ ServiceState newSS, int subId) {
+ final boolean firstUpdate = (oldSS == null) ? true : false;
+
+ // for every field, if the field has changed values, notify via the provider
+ if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
+ /* observer= */ null, /* syncToNetwork= */ false);
+ }
+ if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
+ }
+ if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
+ }
+ if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(
+ getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
+ }
+ }
+
+ private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getState() != newSS.getState();
+ }
+
+ private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
+ }
+
+ private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
+ }
+
+ private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
+ return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
+ }
+
+ /**
+ * Notify interested apps that the ServiceState has changed.
+ *
+ * Apps which want to wake when any field in the ServiceState has changed can use
+ * JobScheduler's TriggerContentUri. This replaces the waking functionality of the implicit
+ * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
+ *
+ * We will only notify for certain fields. This is an intentional change from the behavior of
+ * the broadcast. Listeners will only be notified when the voice/data registration state or
+ * roaming type changes.
+ */
+ @VisibleForTesting
+ public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
+ int subId) {
+ // if the voice or data registration or roaming state field has changed values, notify via
+ // the provider.
+ // If oldSS is null and newSS is not (e.g. first update of service state) this will also
+ // notify
+ if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
+ || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) {
+ context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
+ }
+ }
+
+ /**
+ * Test if this is a path prefix match against the given Uri. Verifies that
+ * scheme, authority, and atomic path segments match.
+ *
+ * Copied from frameworks/base/core/java/android/net/Uri.java
+ */
+ private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
+ if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
+ if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
+
+ List<String> segA = uriA.getPathSegments();
+ List<String> segB = uriB.getPathSegments();
+
+ final int size = segB.size();
+ if (segA.size() < size) return false;
+
+ for (int i = 0; i < size; i++) {
+ if (!Objects.equals(segA.get(i), segB.get(i))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
+ *
+ * @param state the ServiceState to convert into ContentValues
+ * @return the convertedContentValues instance
+ * @hide
+ */
+ public static ContentValues getContentValuesForServiceState(ServiceState state) {
+ ContentValues values = new ContentValues();
+ final Parcel p = Parcel.obtain();
+ state.writeToParcel(p, 0);
+ // Turn the parcel to byte array. Safe to do this because the content values were never
+ // written into a persistent storage. ServiceStateProvider keeps values in the memory.
+ values.put(SERVICE_STATE, p.marshall());
+ return values;
+ }
+}
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index 8c5ae6d..3b5fe21 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -187,6 +187,11 @@
@Override
public void onError(Preference preference, int error) {
if (DBG) dumpState();
+ if (!preference.isEnabled()) {
+ Log.i(LOG_TAG, "onError, skipped duplicated error popup");
+ return;
+ }
+
Log.i(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
if (mIsForeground) {
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index c9aa630..37212cf 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -19,11 +19,13 @@
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
@@ -38,6 +40,11 @@
import com.android.phone.PhoneGlobals;
import com.android.phone.R;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
public class AccessibilitySettingsFragment extends PreferenceFragment {
private static final String LOG_TAG = AccessibilitySettingsFragment.class.getSimpleName();
private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
@@ -47,6 +54,8 @@
private static final String BUTTON_RTT_KEY = "button_rtt_key";
private static final String RTT_INFO_PREF = "button_rtt_more_information_key";
+ private static final int WFC_QUERY_TIMEOUT_MILLIS = 20;
+
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
/**
* Disable the TTY setting when in/out of a call (and if carrier doesn't
@@ -64,12 +73,9 @@
// support multi sim configuration.
TelephonyManager telephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- final boolean isVolteTtySupported = getVolteTtySupported();
- final boolean isVolteCurrentlyEnabled =
- ImsManager.isVolteEnabledByPlatform(mContext);
- pref.setEnabled((isVolteTtySupported && isVolteCurrentlyEnabled &&
- !isVideoCallOrConferenceInProgress()) ||
- (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE));
+ final boolean isVolteTtySupported = isVolteTtySupportedInAnySlot();
+ pref.setEnabled((isVolteTtySupported && !isVideoCallOrConferenceInProgress())
+ || (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE));
}
}
};
@@ -112,10 +118,13 @@
}
if (shouldShowRttSetting()) {
- // TODO: this is going to be a on/off switch for now. Ask UX about how to integrate
- // this settings with TTY
- if (TelephonyManager.getDefault().isNetworkRoaming(
- SubscriptionManager.getDefaultVoiceSubscriptionId())) {
+ TelephonyManager tm =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ boolean isRoaming = tm.isNetworkRoaming(
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+
+ boolean shouldDisableBecauseRoamingOffWfc = isRoaming && !isOnWfc();
+ if (shouldDisableBecauseRoamingOffWfc) {
mButtonRtt.setSummary(TextUtils.concat(getText(R.string.rtt_mode_summary), "\n",
getText(R.string.no_rtt_when_roaming)));
}
@@ -166,17 +175,55 @@
int rttMode = mButtonRtt.isChecked() ? 1 : 0;
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.RTT_CALLING_MODE,
rttMode);
+ // Update RTT config with IMS Manager if the always-on carrier config isn't set to true.
+ CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
+ Context.CARRIER_CONFIG_SERVICE);
+ for (int subId : SubscriptionController.getInstance().getActiveSubIdList(true)) {
+ if (!configManager.getConfigForSubId(subId).getBoolean(
+ CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false)) {
+ int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+ ImsManager imsManager = ImsManager.getInstance(getContext(), phoneId);
+ imsManager.setRttEnabled(mButtonRtt.isChecked());
+ }
+ }
return true;
}
return false;
}
- private boolean getVolteTtySupported() {
+ private boolean isVolteTtySupportedInAnySlot() {
+ final Phone[] phones = PhoneFactory.getPhones();
+ if (phones == null) {
+ if (DBG) Log.d(LOG_TAG, "isVolteTtySupportedInAnySlot: No phones found.");
+ return false;
+ }
+
CarrierConfigManager configManager =
(CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- return configManager.getConfig().getBoolean(
- CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+ for (Phone phone : phones) {
+ // Check if this phone supports VoLTE.
+ ImsManager imsManager = ImsManager.getInstance(mContext, phone.getPhoneId());
+ boolean volteEnabled = false;
+ if (imsManager != null) {
+ volteEnabled = imsManager.isVolteEnabledByPlatform();
+ }
+
+ // Check if this phone suports VoLTE TTY.
+ boolean volteTtySupported = false;
+ PersistableBundle carrierConfig = configManager.getConfigForSubId(phone.getSubId());
+ if (carrierConfig != null) {
+ volteTtySupported = carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+ }
+
+ if (volteEnabled && volteTtySupported) {
+ // VoLTE TTY is supported on this phone that also suports VoLTE.
+ return true;
+ }
+ }
+ // VoLTE TTY was not supported on any phone that also supports VoLTE.
+ return false;
}
private boolean isVideoCallOrConferenceInProgress() {
@@ -194,6 +241,21 @@
return false;
}
+ private boolean isOnWfc() {
+ LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
+ Executor executor = Executors.newSingleThreadExecutor();
+ mContext.getSystemService(android.telephony.ims.ImsManager.class)
+ .getImsMmTelManager(SubscriptionManager.getDefaultSubscriptionId())
+ .getRegistrationTransportType(executor, result::offer);
+ try {
+ Integer transportType = result.poll(WFC_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ return transportType != null
+ && transportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
private boolean shouldShowRttSetting() {
// Go through all the subs -- if we want to display the RTT setting for any of them, do
// display it.
diff --git a/src/com/android/phone/settings/CallForwardInfoUtil.java b/src/com/android/phone/settings/CallForwardInfoUtil.java
index 1983fab..b963df8 100644
--- a/src/com/android/phone/settings/CallForwardInfoUtil.java
+++ b/src/com/android/phone/settings/CallForwardInfoUtil.java
@@ -82,6 +82,7 @@
phone.setCallForwardingOption(commandInterfaceCfAction,
info.reason,
info.number,
+ info.serviceClass,
info.timeSeconds,
message);
}
@@ -93,10 +94,12 @@
*/
public static CallForwardInfo getCallForwardInfo(CallForwardInfo[] infos, int reason) {
CallForwardInfo info = null;
- for (int i = 0 ; i < infos.length; i++) {
- if (isServiceClassVoice(infos[i])) {
- info = infos[i];
- break;
+ if (infos != null) {
+ for (int i = 0 ; i < infos.length; i++) {
+ if (isServiceClassVoice(infos[i])) {
+ info = infos[i];
+ break;
+ }
}
}
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 77f1135..d0951e4 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -38,6 +38,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -465,12 +466,12 @@
mQueuedWork = new ThreadPoolExecutor(1, 1, RUNNABLE_TIMEOUT_MS, TimeUnit.MICROSECONDS,
new LinkedBlockingDeque<Runnable>());
- mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mPhone = PhoneFactory.getDefaultPhone();
+ mTelephonyManager = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE))
+ .createForSubscriptionId(mPhone.getSubId());
- mImsManager = ImsManager.getInstance(getApplicationContext(),
- SubscriptionManager.getDefaultVoicePhoneId());
+ mImsManager = ImsManager.getInstance(getApplicationContext(), mPhone.getPhoneId());
sPhoneIndexLabels = getPhoneIndexLabels(mTelephonyManager);
@@ -1449,15 +1450,25 @@
OnCheckedChangeListener mRadioPowerOnChangeListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- log("toggle radio power: currently " + (isRadioOn() ? "on" : "off"));
- mPhone.setRadioPower(isChecked);
+ // TODO: b/145681511. Within current design, radio power on all of the phones need
+ // to be controlled at the same time.
+ Phone[] phones = PhoneFactory.getPhones();
+ if (phones == null) {
+ return;
+ }
+ log("toggle radio power: phone*" + phones.length + " " + (isRadioOn() ? "on" : "off"));
+ for (int phoneIndex = 0; phoneIndex < phones.length; phoneIndex++) {
+ if (phones[phoneIndex] != null) {
+ phones[phoneIndex].setRadioPower(isChecked);
+ }
+ }
}
};
private boolean isImsVolteProvisioned() {
- if (mPhone != null && mImsManager != null) {
- return mImsManager.isVolteEnabledByPlatform(mPhone.getContext())
- && mImsManager.isVolteProvisionedOnDevice(mPhone.getContext());
+ if (mImsManager != null) {
+ return mImsManager.isVolteEnabledByPlatform()
+ && mImsManager.isVolteProvisionedOnDevice();
}
return false;
}
@@ -1470,9 +1481,9 @@
};
private boolean isImsVtProvisioned() {
- if (mPhone != null && mImsManager != null) {
- return mImsManager.isVtEnabledByPlatform(mPhone.getContext())
- && mImsManager.isVtProvisionedOnDevice(mPhone.getContext());
+ if (mImsManager != null) {
+ return mImsManager.isVtEnabledByPlatform()
+ && mImsManager.isVtProvisionedOnDevice();
}
return false;
}
@@ -1485,9 +1496,9 @@
};
private boolean isImsWfcProvisioned() {
- if (mPhone != null && mImsManager != null) {
- return mImsManager.isWfcEnabledByPlatform(mPhone.getContext())
- && mImsManager.isWfcProvisionedOnDevice(mPhone.getContext());
+ if (mImsManager != null) {
+ return mImsManager.isWfcEnabledByPlatform()
+ && mImsManager.isWfcProvisionedOnDevice();
}
return false;
}
@@ -1529,13 +1540,14 @@
return provisioned;
}
- private static boolean isEabEnabledByPlatform(Context context) {
- if (context != null) {
+ private boolean isEabEnabledByPlatform() {
+ if (mPhone != null) {
CarrierConfigManager configManager = (CarrierConfigManager)
- context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager != null && configManager.getConfig().getBoolean(
- CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL)) {
- return true;
+ mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+ if (b != null) {
+ return b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+ false);
}
}
return false;
@@ -1552,25 +1564,25 @@
mImsVolteProvisionedSwitch.setChecked(isImsVolteProvisioned());
mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
mImsVolteProvisionedSwitch.setEnabled(!IS_USER_BUILD
- && mImsManager.isVolteEnabledByPlatform(mPhone.getContext()));
+ && mImsManager.isVolteEnabledByPlatform());
mImsVtProvisionedSwitch.setOnCheckedChangeListener(null);
mImsVtProvisionedSwitch.setChecked(isImsVtProvisioned());
mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
mImsVtProvisionedSwitch.setEnabled(!IS_USER_BUILD
- && mImsManager.isVtEnabledByPlatform(mPhone.getContext()));
+ && mImsManager.isVtEnabledByPlatform());
mImsWfcProvisionedSwitch.setOnCheckedChangeListener(null);
mImsWfcProvisionedSwitch.setChecked(isImsWfcProvisioned());
mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
mImsWfcProvisionedSwitch.setEnabled(!IS_USER_BUILD
- && mImsManager.isWfcEnabledByPlatform(mPhone.getContext()));
+ && mImsManager.isWfcEnabledByPlatform());
mEabProvisionedSwitch.setOnCheckedChangeListener(null);
mEabProvisionedSwitch.setChecked(isEabProvisioned());
mEabProvisionedSwitch.setOnCheckedChangeListener(mEabCheckedChangeListener);
mEabProvisionedSwitch.setEnabled(!IS_USER_BUILD
- && isEabEnabledByPlatform(mPhone.getContext()));
+ && isEabEnabledByPlatform());
}
OnClickListener mDnsCheckButtonHandler = new OnClickListener() {
@@ -1718,7 +1730,7 @@
mQueuedWork.execute(new Runnable() {
public void run() {
mTelephonyManager.setOpportunisticNetworkState(state);
- mCbrsDataSwitch.setChecked(getCbrsDataState());
+ mHandler.post(() -> mCbrsDataSwitch.setChecked(getCbrsDataState()));
}
});
}
diff --git a/src/com/android/phone/settings/SuppServicesUiUtil.java b/src/com/android/phone/settings/SuppServicesUiUtil.java
index 4e9841f..4f1a79f 100644
--- a/src/com/android/phone/settings/SuppServicesUiUtil.java
+++ b/src/com/android/phone/settings/SuppServicesUiUtil.java
@@ -84,7 +84,7 @@
.create();
}
- private static String makeMessage(Context context, String preferenceKey, Phone phone) {
+ public static String makeMessage(Context context, String preferenceKey, Phone phone) {
String message = "";
int simSlot = (phone.getPhoneId() == 0) ? 1 : 2;
String suppServiceName = getSuppServiceName(context, preferenceKey);
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index e18dc93..66b1af9 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -43,6 +43,7 @@
import android.widget.Toast;
import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.util.NotificationChannelController;
@@ -733,6 +734,7 @@
for (int i = 0; i < mForwardingReadResults.length; i++) {
mPhone.getCallForwardingOption(
VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS[i],
+ CommandsInterface.SERVICE_CLASS_VOICE,
mGetOptionComplete.obtainMessage(EVENT_FORWARDING_GET_COMPLETED, i, 0));
}
showDialogIfForeground(VoicemailDialogUtil.VM_FWD_READING_DIALOG);
diff --git a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
index 92baa97..8b17cfb 100644
--- a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
@@ -64,7 +64,8 @@
resolveIntent();
- authenticatePin2();
+ // Starts PIN2 authentication only for the first time.
+ if (icicle == null) authenticatePin2();
getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.delete_fdn_contact_screen);
diff --git a/src/com/android/services/telephony/CdmaConference.java b/src/com/android/services/telephony/CdmaConference.java
old mode 100755
new mode 100644
index 693fd16..7458195
--- a/src/com/android/services/telephony/CdmaConference.java
+++ b/src/com/android/services/telephony/CdmaConference.java
@@ -17,6 +17,7 @@
package com.android.services.telephony;
import android.content.Context;
+import android.net.Uri;
import android.os.PersistableBundle;
import android.telecom.Connection;
import android.telecom.PhoneAccountHandle;
@@ -73,6 +74,22 @@
}
@Override
+ public void onAddConferenceParticipants(List<Uri> participants) {
+ Log.e(this, new Exception(), "Adding Conference Participants not supported " +
+ " for CDMA conference call.");
+ }
+
+ @Override
+ public void onAnswer(int videoState) {
+ Log.e(this, new Exception(), "Answer not supported for CDMA conference call.");
+ }
+
+ @Override
+ public void onReject() {
+ Log.e(this, new Exception(), "Reject not supported for CDMA conference call.");
+ }
+
+ @Override
public void onHold() {
Log.e(this, new Exception(), "Hold not supported for CDMA conference call.");
}
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 9afcd0a..8523a5f 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -97,7 +97,7 @@
return;
}
- if (!mCdmaConnections.isEmpty() && connection.isOutgoing()) {
+ if (!mCdmaConnections.isEmpty() && connection.isOutgoingCall()) {
// There already exists a connection, so this will probably result in a conference once
// it is added. For outgoing connections which are added while another connection
// exists, we mark them as "dialing" for a set amount of time to give the user time to
@@ -183,7 +183,7 @@
isNewlyCreated = true;
}
- if (newConnection.isOutgoing()) {
+ if (newConnection.isOutgoingCall()) {
// Only an outgoing call can be merged with an ongoing call.
mConference.updateCapabilities(Connection.CAPABILITY_MERGE_CONFERENCE);
} else {
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index bd015e3..90e7663 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -81,9 +81,9 @@
Connection connection,
EmergencyTonePlayer emergencyTonePlayer,
boolean allowMute,
- boolean isOutgoing,
+ int callDirection,
String telecomCallId) {
- super(connection, telecomCallId, isOutgoing);
+ super(connection, telecomCallId, callDirection);
mEmergencyTonePlayer = emergencyTonePlayer;
mAllowMute = allowMute;
mIsCallWaiting = connection != null && connection.getState() == Call.State.WAITING;
@@ -151,7 +151,7 @@
@Override
public TelephonyConnection cloneConnection() {
CdmaConnection cdmaConnection = new CdmaConnection(getOriginalConnection(),
- mEmergencyTonePlayer, mAllowMute, mIsOutgoing, getTelecomCallId());
+ mEmergencyTonePlayer, mAllowMute, getCallDirection(), getTelecomCallId());
return cdmaConnection;
}
@@ -202,10 +202,6 @@
}
}
- boolean isOutgoing() {
- return mIsOutgoing;
- }
-
boolean isCallWaiting() {
return mIsCallWaiting;
}
@@ -288,9 +284,8 @@
private boolean isEmergency() {
Phone phone = getPhone();
- return phone != null &&
- PhoneNumberUtils.isLocalEmergencyNumber(
- phone.getContext(), getAddress().getSchemeSpecificPart());
+ return phone != null && getAddress() != null && PhoneNumberUtils.isLocalEmergencyNumber(
+ phone.getContext(), getAddress().getSchemeSpecificPart());
}
/**
@@ -306,7 +301,7 @@
private void handleCdmaConnectionTimeReset() {
boolean isImsCall = getOriginalConnection() instanceof ImsPhoneConnection;
- if (!isImsCall && !mIsConnectionTimeReset && mIsOutgoing
+ if (!isImsCall && !mIsConnectionTimeReset && isOutgoingCall()
&& getOriginalConnection() != null
&& getOriginalConnection().getState() == Call.State.ACTIVE
&& getOriginalConnection().getDurationMillis() > 0) {
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index ed92e7f..b7ecd48 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -19,13 +19,13 @@
import android.net.Uri;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
-import android.telephony.Rlog;
import android.telephony.SubscriptionInfo;
import android.text.TextUtils;
import com.android.ims.internal.ConferenceParticipant;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.telephony.Rlog;
/**
* Represents a participant in a conference call.
@@ -215,7 +215,7 @@
sb.append(" connectTime:");
sb.append(getConnectTimeMillis());
sb.append(" connectElapsedTime:");
- sb.append(getConnectElapsedTimeMillis());
+ sb.append(getConnectionStartElapsedRealtimeMillis());
sb.append("]");
return sb.toString();
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 9e32f00..fd645a8 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.media.ToneGenerator;
import android.os.PersistableBundle;
+import android.provider.Settings;
import android.telecom.DisconnectCause;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
@@ -347,6 +348,9 @@
case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
resourceId = R.string.callFailed_data_limit_reached;
break;
+ case android.telephony.DisconnectCause.WIFI_LOST:
+ resourceId = R.string.callFailed_wifi_lost;
+ break;
case android.telephony.DisconnectCause.ALREADY_DIALING:
resourceId = R.string.callFailed_already_dialing;
break;
@@ -360,7 +364,12 @@
resourceId = R.string.callFailed_too_many_calls;
break;
case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
- resourceId = R.string.incall_error_power_off;
+ int airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0);
+ resourceId = R.string.incall_error_call_failed;
+ if (airplaneMode != 0) {
+ resourceId = R.string.incall_error_power_off;
+ }
break;
case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
resourceId = R.string.callFailed_otasp_provisioning_in_process;
@@ -753,7 +762,12 @@
resourceId = R.string.callFailed_too_many_calls;
break;
case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
- resourceId = R.string.incall_error_power_off;
+ int airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0);
+ resourceId = R.string.incall_error_call_failed;
+ if (airplaneMode != 0) {
+ resourceId = R.string.incall_error_power_off;
+ }
break;
case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
resourceId = R.string.callFailed_otasp_provisioning_in_process;
@@ -847,17 +861,18 @@
case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
return ToneGenerator.TONE_SUP_ERROR;
+ case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
+ // Do not play any tones if disconnected because of a successful merge.
+ return ToneGenerator.TONE_UNKNOWN;
+
case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
case android.telephony.DisconnectCause.LOCAL:
case android.telephony.DisconnectCause.NORMAL:
case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED:
case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
- return ToneGenerator.TONE_PROP_PROMPT;
-
- case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
- // Do not play any tones if disconnected because of a successful merge.
+ case android.telephony.DisconnectCause.WIFI_LOST:
default:
- return ToneGenerator.TONE_UNKNOWN;
+ return ToneGenerator.TONE_PROP_PROMPT;
}
}
}
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index 47434c0..769ec22 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -22,8 +22,8 @@
* Manages a single phone call handled by GSM.
*/
final class GsmConnection extends TelephonyConnection {
- GsmConnection(Connection connection, String telecomCallId, boolean isOutgoing) {
- super(connection, telecomCallId, isOutgoing);
+ GsmConnection(Connection connection, String telecomCallId, int callDirection) {
+ super(connection, telecomCallId, callDirection);
}
/**
@@ -35,9 +35,7 @@
*/
@Override
public TelephonyConnection cloneConnection() {
- GsmConnection gsmConnection = new GsmConnection(getOriginalConnection(),
- getTelecomCallId(), mIsOutgoing);
- return gsmConnection;
+ return new GsmConnection(getOriginalConnection(), getTelecomCallId(), getCallDirection());
}
/** {@inheritDoc} */
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4c3f9c9..f7fcf64 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -20,7 +20,6 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.PersistableBundle;
import android.telecom.Connection;
import android.telecom.Connection.VideoProvider;
import android.telecom.DisconnectCause;
@@ -28,9 +27,7 @@
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
-import android.telephony.CarrierConfigManager;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
import android.util.Pair;
import com.android.ims.internal.ConferenceParticipant;
@@ -39,9 +36,9 @@
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
-import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.Arrays;
@@ -79,6 +76,126 @@
}
/**
+ * Abstracts out carrier configuration items specific to the conference.
+ */
+ public static class CarrierConfiguration {
+ /**
+ * Builds and instance of {@link CarrierConfiguration}.
+ */
+ public static class Builder {
+ private boolean mIsMaximumConferenceSizeEnforced = false;
+ private int mMaximumConferenceSize = 5;
+ private boolean mShouldLocalDisconnectEmptyConference = false;
+ private boolean mIsHoldAllowed = false;
+
+ /**
+ * Sets whether the maximum size of the conference is enforced.
+ * @param isMaximumConferenceSizeEnforced {@code true} if conference size enforced.
+ * @return builder instance.
+ */
+ public Builder setIsMaximumConferenceSizeEnforced(
+ boolean isMaximumConferenceSizeEnforced) {
+ mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
+ return this;
+ }
+
+ /**
+ * Sets the maximum size of an IMS conference.
+ * @param maximumConferenceSize Max conference size.
+ * @return builder instance.
+ */
+ public Builder setMaximumConferenceSize(int maximumConferenceSize) {
+ mMaximumConferenceSize = maximumConferenceSize;
+ return this;
+ }
+
+ /**
+ * Sets whether an empty conference should be locally disconnected.
+ * @param shouldLocalDisconnectEmptyConference {@code true} if conference should be
+ * locally disconnected if empty.
+ * @return builder instance.
+ */
+ public Builder setShouldLocalDisconnectEmptyConference(
+ boolean shouldLocalDisconnectEmptyConference) {
+ mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
+ return this;
+ }
+
+ /**
+ * Sets whether holding the conference is allowed.
+ * @param isHoldAllowed {@code true} if holding is allowed.
+ * @return builder instance.
+ */
+ public Builder setIsHoldAllowed(boolean isHoldAllowed) {
+ mIsHoldAllowed = isHoldAllowed;
+ return this;
+ }
+
+ /**
+ * Build instance of {@link CarrierConfiguration}.
+ * @return carrier config instance.
+ */
+ public ImsConference.CarrierConfiguration build() {
+ return new ImsConference.CarrierConfiguration(mIsMaximumConferenceSizeEnforced,
+ mMaximumConferenceSize, mShouldLocalDisconnectEmptyConference,
+ mIsHoldAllowed);
+ }
+ }
+
+ private CarrierConfiguration(boolean isMaximumConferenceSizeEnforced,
+ int maximumConferenceSize, boolean shouldLocalDisconnectEmptyConference,
+ boolean isHoldAllowed) {
+ mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
+ mMaximumConferenceSize = maximumConferenceSize;
+ mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
+ mIsHoldAllowed = isHoldAllowed;
+ }
+
+ private boolean mIsMaximumConferenceSizeEnforced;
+
+ private int mMaximumConferenceSize;
+
+ private boolean mShouldLocalDisconnectEmptyConference;
+
+ private boolean mIsHoldAllowed;
+
+ /**
+ * Determines whether the {@link ImsConference} should enforce a size limit based on
+ * {@link #getMaximumConferenceSize()}.
+ * {@code true} if maximum size limit should be enforced, {@code false} otherwise.
+ */
+ public boolean isMaximumConferenceSizeEnforced() {
+ return mIsMaximumConferenceSizeEnforced;
+ }
+
+ /**
+ * Determines the maximum number of participants (not including the host) in a conference
+ * which is enforced when {@link #isMaximumConferenceSizeEnforced()} is {@code true}.
+ */
+ public int getMaximumConferenceSize() {
+ return mMaximumConferenceSize;
+ }
+
+ /**
+ * Determines whether this {@link ImsConference} should locally disconnect itself when the
+ * number of participants in the conference drops to zero.
+ * {@code true} if empty conference should be locally disconnected, {@code false}
+ * otherwise.
+ */
+ public boolean shouldLocalDisconnectEmptyConference() {
+ return mShouldLocalDisconnectEmptyConference;
+ }
+
+ /**
+ * Determines whether holding the conference is permitted or not.
+ * {@code true} if hold is permitted, {@code false} otherwise.
+ */
+ public boolean isHoldAllowed() {
+ return mIsHoldAllowed;
+ }
+ }
+
+ /**
* Listener used to respond to changes to the underlying radio connection for the conference
* host connection. Used to respond to SRVCC changes.
*/
@@ -199,6 +316,18 @@
TelephonyConnection telephonyConnection = (TelephonyConnection) c;
handleConferenceParticipantsUpdate(telephonyConnection, participants);
}
+
+ /**
+ * Handles request to play a ringback tone.
+ *
+ * @param c The connection.
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ @Override
+ public void onRingbackRequested(android.telecom.Connection c, boolean ringback) {
+ Log.d(this, "onRingbackRequested ringback %s", ringback ? "Y" : "N");
+ setRingbackRequested(ringback);
+ }
};
/**
@@ -224,6 +353,11 @@
private TelecomAccountRegistry mTelecomAccountRegistry;
/**
+ * The participant with which Adhoc Conference call is getting formed.
+ */
+ private List<Uri> mParticipants;
+
+ /**
* The known conference participant connections. The HashMap is keyed by a Pair containing
* the handle and endpoint Uris.
* Access to the hashmap is protected by the {@link #mUpdateSyncRoot}.
@@ -243,6 +377,7 @@
private boolean mIsHoldable;
private boolean mCouldManageConference;
private FeatureFlagProxy mFeatureFlagProxy;
+ private final CarrierConfiguration mCarrierConfig;
private boolean mIsEmulatingSinglePartyCall = false;
private boolean mIsUsingSimCallManager = false;
@@ -284,29 +419,30 @@
public ImsConference(TelecomAccountRegistry telecomAccountRegistry,
TelephonyConnectionServiceProxy telephonyConnectionService,
TelephonyConnection conferenceHost, PhoneAccountHandle phoneAccountHandle,
- FeatureFlagProxy featureFlagProxy) {
+ FeatureFlagProxy featureFlagProxy, CarrierConfiguration carrierConfig) {
super(phoneAccountHandle);
mTelecomAccountRegistry = telecomAccountRegistry;
mFeatureFlagProxy = featureFlagProxy;
+ mCarrierConfig = carrierConfig;
// Specify the connection time of the conference to be the connection time of the original
// connection.
long connectTime = conferenceHost.getOriginalConnection().getConnectTime();
long connectElapsedTime = conferenceHost.getOriginalConnection().getConnectTimeReal();
setConnectionTime(connectTime);
- setConnectionStartElapsedRealTime(connectElapsedTime);
+ setConnectionStartElapsedRealtimeMillis(connectElapsedTime);
// Set the connectTime in the connection as well.
conferenceHost.setConnectTimeMillis(connectTime);
- conferenceHost.setConnectionStartElapsedRealTime(connectElapsedTime);
+ conferenceHost.setConnectionStartElapsedRealtimeMillis(connectElapsedTime);
mTelephonyConnectionService = telephonyConnectionService;
setConferenceHost(conferenceHost);
int capabilities = Connection.CAPABILITY_MUTE |
Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN;
- if (canHoldImsCalls()) {
+ if (mCarrierConfig.isHoldAllowed()) {
capabilities |= Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD;
mIsHoldable = true;
}
@@ -357,6 +493,10 @@
Connection.CAPABILITY_CAN_PAUSE_VIDEO,
mConferenceHost.getVideoPauseSupported() && isVideoCapable());
+ conferenceCapabilities = changeBitmask(conferenceCapabilities,
+ Connection.CAPABILITY_ADD_PARTICIPANT,
+ (capabilities & Connection.CAPABILITY_ADD_PARTICIPANT) != 0);
+
return conferenceCapabilities;
}
@@ -383,6 +523,10 @@
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
+ conferenceProperties = changeBitmask(conferenceProperties,
+ Connection.PROPERTY_IS_ADHOC_CONFERENCE,
+ (properties & Connection.PROPERTY_IS_ADHOC_CONFERENCE) != 0);
+
return conferenceProperties;
}
@@ -422,6 +566,26 @@
return VideoProfile.STATE_AUDIO_ONLY;
}
+ public Connection getConferenceHost() {
+ return mConferenceHost;
+ }
+
+ /**
+ * @return The address's to which this Connection is currently communicating.
+ */
+ public final List<Uri> getParticipants() {
+ return mParticipants;
+ }
+
+ /**
+ * Sets the value of the {@link #getParticipants()}.
+ *
+ * @param address The new address's.
+ */
+ public final void setParticipants(List<Uri> address) {
+ mParticipants = address;
+ }
+
/**
* Invoked when the Conference and all its {@link Connection}s should be disconnected.
* <p>
@@ -446,6 +610,8 @@
} catch (CallStateException e) {
Log.e(this, e, "Exception thrown trying to hangup conference");
}
+ } else {
+ Log.w(this, "onDisconnect - null call");
}
}
@@ -481,6 +647,41 @@
}
/**
+ * Supports adding participants to an existing conference call
+ *
+ * @param participants that are pulled to existing conference call
+ */
+ @Override
+ public void onAddConferenceParticipants(List<Uri> participants) {
+ if (mConferenceHost == null) {
+ return;
+ }
+ mConferenceHost.performAddConferenceParticipants(participants);
+ }
+
+ /**
+ * Invoked when the conference is answered.
+ */
+ @Override
+ public void onAnswer(int videoState) {
+ if (mConferenceHost == null) {
+ return;
+ }
+ mConferenceHost.performAnswer(videoState);
+ }
+
+ /**
+ * Invoked when the conference is rejected.
+ */
+ @Override
+ public void onReject() {
+ if (mConferenceHost == null) {
+ return;
+ }
+ mConferenceHost.performReject(android.telecom.Call.REJECT_REASON_DECLINED);
+ }
+
+ /**
* Invoked when the conference should be put on hold.
*/
@Override
@@ -678,7 +879,8 @@
setAddress(mConferenceHost.getAddress(), mConferenceHost.getAddressPresentation());
setCallerDisplayName(mConferenceHost.getCallerDisplayName(),
mConferenceHost.getCallerDisplayNamePresentation());
- setConnectionStartElapsedRealTime(mConferenceHost.getConnectElapsedTimeMillis());
+ setConnectionStartElapsedRealtimeMillis(
+ mConferenceHost.getConnectionStartElapsedRealtimeMillis());
setConnectionTime(mConferenceHost.getConnectTimeMillis());
}
@@ -838,7 +1040,8 @@
+ "newParticipantcount=%d", oldParticipantCount, newParticipantCount);
// If the single party call emulation fature flag is enabled, we can potentially treat
// the conference as a single party call when there is just one participant.
- if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation()) {
+ if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation() &&
+ !mConferenceHost.isAdhocConferenceCall()) {
if (oldParticipantCount != 1 && newParticipantCount == 1) {
// If number of participants goes to 1, emulate a single party call.
startEmulatingSinglePartyCall();
@@ -853,6 +1056,14 @@
if (newParticipantsAdded || oldParticipantsRemoved) {
updateManageConference();
}
+
+ // If the conference is empty and we're supposed to do a local disconnect, do so now.
+ if (mCarrierConfig.shouldLocalDisconnectEmptyConference()
+ && oldParticipantCount > 0 && newParticipantCount == 0) {
+ Log.i(this, "handleConferenceParticipantsUpdate: empty conference; "
+ + "local disconnect.");
+ onDisconnect();
+ }
}
}
@@ -895,7 +1106,8 @@
Log.d(this,
"stopEmulatingSinglePartyCall: restored lone participant connect time");
loneParticipant.setConnectTimeMillis(getConnectionTime());
- loneParticipant.setConnectionStartElapsedRealTime(getConnectionStartElapsedRealTime());
+ loneParticipant.setConnectionStartElapsedRealtimeMillis(
+ getConnectionStartElapsedRealtimeMillis());
}
// Tell Telecom its a conference again.
@@ -935,8 +1147,10 @@
setAddress(entry.getAddress(), entry.getAddressPresentation());
setCallerDisplayName(entry.getCallerDisplayName(),
entry.getCallerDisplayNamePresentation());
- setConnectionStartElapsedRealTime(entry.getConnectElapsedTimeMillis());
+ setConnectionStartElapsedRealtimeMillis(
+ entry.getConnectionStartElapsedRealtimeMillis());
setConnectionTime(entry.getConnectTimeMillis());
+ setCallDirection(entry.getCallDirection());
mLoneParticipantIdentity = new Pair<>(entry.getUserEntity(), entry.getEndpoint());
// Remove the participant from Telecom. It'll get picked up in a future CEP update
@@ -979,10 +1193,11 @@
!isConferenceHost() /* isRemotelyHosted */);
if (participant.getConnectTime() == 0) {
connection.setConnectTimeMillis(parent.getConnectTimeMillis());
- connection.setConnectionStartElapsedRealTime(parent.getConnectElapsedTimeMillis());
+ connection.setConnectionStartElapsedRealtimeMillis(
+ parent.getConnectionStartElapsedRealtimeMillis());
} else {
connection.setConnectTimeMillis(participant.getConnectTime());
- connection.setConnectionStartElapsedRealTime(participant.getConnectElapsedTime());
+ connection.setConnectionStartElapsedRealtimeMillis(participant.getConnectElapsedTime());
}
// Indicate whether this is an MT or MO call to Telecom; the participant has the cached
// data from the time of merge.
@@ -1140,7 +1355,7 @@
if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
Log.i(this,"handleOriginalConnectionChange : SRVCC to GSM");
GsmConnection c = new GsmConnection(originalConnection, getTelecomCallId(),
- mConferenceHost.isOutgoingCall());
+ mConferenceHost.getCallDirection());
// This is a newly created conference connection as a result of SRVCC
c.setConferenceSupported(true);
c.setTelephonyConnectionProperties(
@@ -1148,7 +1363,8 @@
c.updateState();
// Copy the connect time from the conferenceHost
c.setConnectTimeMillis(mConferenceHost.getConnectTimeMillis());
- c.setConnectionStartElapsedRealTime(mConferenceHost.getConnectElapsedTimeMillis());
+ c.setConnectionStartElapsedRealtimeMillis(
+ mConferenceHost.getConnectionStartElapsedRealtimeMillis());
mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c);
mTelephonyConnectionService.addConnectionToConferenceController(c);
} // CDMA case not applicable for SRVCC
@@ -1173,11 +1389,13 @@
switch (state) {
case Connection.STATE_INITIALIZING:
case Connection.STATE_NEW:
- case Connection.STATE_RINGING:
// No-op -- not applicable.
break;
+ case Connection.STATE_RINGING:
+ setConferenceOnRinging();
+ break;
case Connection.STATE_DIALING:
- setDialing();
+ setConferenceOnDialing();
break;
case Connection.STATE_DISCONNECTED:
DisconnectCause disconnectCause;
@@ -1198,10 +1416,10 @@
destroyTelephonyConference();
break;
case Connection.STATE_ACTIVE:
- setActive();
+ setConferenceOnActive();
break;
case Connection.STATE_HOLDING:
- setOnHold();
+ setConferenceOnHold();
break;
}
}
@@ -1258,51 +1476,6 @@
return sb.toString();
}
- private boolean canHoldImsCalls() {
- PersistableBundle b = getCarrierConfig();
- // Return true if the CarrierConfig is unavailable
- return b == null || b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
- }
-
- private PersistableBundle getCarrierConfig() {
- if (mConferenceHost == null) {
- return null;
- }
-
- Phone phone = mConferenceHost.getPhone();
- if (phone == null) {
- return null;
- }
- return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
- }
-
- /**
- * @return {@code true} if the carrier associated with the conference requires that the maximum
- * size of the conference is enforced, {@code false} otherwise.
- */
- public boolean isMaximumConferenceSizeEnforced() {
- PersistableBundle b = getCarrierConfig();
- // Return false if the CarrierConfig is unavailable
- return b != null && b.getBoolean(
- CarrierConfigManager.KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL);
- }
-
- /**
- * @return The maximum size of a conference call where
- * {@link #isMaximumConferenceSizeEnforced()} is true.
- */
- public int getMaximumConferenceSize() {
- PersistableBundle b = getCarrierConfig();
-
- // If there is no carrier config its really a problem, but we'll still define a sane limit
- // of 5 so that we can still make a conference.
- if (b == null) {
- Log.w(this, "getMaximumConferenceSize - failed to get conference size");
- return 5;
- }
- return b.getInt(CarrierConfigManager.KEY_IMS_CONFERENCE_SIZE_LIMIT_INT);
- }
-
/**
* @return The number of participants in the conference.
*/
@@ -1315,8 +1488,8 @@
* participants in the conference has reached the limit, {@code false} otherwise.
*/
public boolean isFullConference() {
- return isMaximumConferenceSizeEnforced()
- && getNumberOfParticipants() >= getMaximumConferenceSize();
+ return mCarrierConfig.isMaximumConferenceSizeEnforced()
+ && getNumberOfParticipants() >= mCarrierConfig.getMaximumConferenceSize();
}
/**
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 1e10055..6d3d4c2 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -16,13 +16,17 @@
package com.android.services.telephony;
+import android.content.Context;
+import android.os.PersistableBundle;
import android.telecom.Conference;
import android.telecom.Conferenceable;
import android.telecom.Connection;
import android.telecom.ConnectionService;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
-import android.telephony.Rlog;
+import android.telephony.CarrierConfigManager;
+
+import com.android.telephony.Rlog;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -52,8 +56,19 @@
Log.v(ImsConferenceController.class, "onDestroyed: %s", conference);
}
+ if (conference instanceof ImsConference) {
+ // Ims Conference call ended, so UE may now have the ability to initiate
+ // an Adhoc Conference call. Hence, try enabling adhoc conference capability
+ mTelecomAccountRegistry.refreshAdhocConference(true);
+ }
mImsConferences.remove(conference);
}
+
+ @Override
+ public void onStateChanged(Conference conference, int oldState, int newState) {
+ Log.v(this, "onStateChanged: Conference = " + conference);
+ recalculateConferenceable();
+ }
};
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -101,10 +116,11 @@
private final ArrayList<TelephonyConnection> mTelephonyConnections = new ArrayList<>();
/**
- * List of known {@link ImsConference}s. Realistically there will only ever be a single
- * concurrent IMS conference.
+ * List of known {@link ImsConference}s. There can be upto maximum two Ims conference calls.
+ * One conference call can be a host conference call and another conference call formed as a
+ * result of accepting incoming conference call.
*/
- private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(1);
+ private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(2);
private TelecomAccountRegistry mTelecomAccountRegistry;
@@ -122,6 +138,17 @@
mFeatureFlagProxy = featureFlagProxy;
}
+ void addConference(ImsConference conference) {
+ if (mImsConferences.contains(conference)) {
+ // Adding a duplicate realistically shouldn't happen.
+ Log.w(this, "addConference - conference already tracked; conference=%s", conference);
+ return;
+ }
+ mImsConferences.add(conference);
+ conference.addTelephonyConferenceListener(mConferenceListener);
+ recalculateConferenceable();
+ }
+
/**
* Adds a new connection to the IMS conference controller.
*
@@ -250,6 +277,11 @@
continue;
}
+ // Since UE cannot host two conference calls, remove the ability to initiate
+ // another conference call as there already exists a conference call, which
+ // is hosted on this device.
+ mTelecomAccountRegistry.refreshAdhocConference(false);
+
switch (conference.getState()) {
case Connection.STATE_ACTIVE:
//fall through
@@ -360,6 +392,10 @@
Log.v(this, "Start new ImsConference - connection: %s", connection);
}
+ if (connection.isAdhocConferenceCall()) {
+ Log.w(this, "start new ImsConference - control should never come here");
+ return;
+ }
// Make a clone of the connection which will become the Ims conference host connection.
// This is necessary since the Connection Service does not support removing a connection
// from Telecom. Instead we create a new instance and remove the old one from telecom.
@@ -371,6 +407,7 @@
PhoneAccountHandle phoneAccountHandle = null;
// Attempt to determine the phone account associated with the conference host connection.
+ ImsConference.CarrierConfiguration carrierConfig = null;
if (connection.getPhone() != null &&
connection.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
Phone imsPhone = connection.getPhone();
@@ -378,11 +415,13 @@
// base GSM or CDMA phone, not on the ImsPhone itself).
phoneAccountHandle =
PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+ carrierConfig = getCarrierConfig(imsPhone);
}
ImsConference conference = new ImsConference(mTelecomAccountRegistry, mConnectionService,
- conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy);
+ conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy, carrierConfig);
conference.setState(conferenceHostConnection.getState());
+ conference.setCallDirection(conferenceHostConnection.getCallDirection());
conference.addTelephonyConferenceListener(mConferenceListener);
conference.updateConferenceParticipantsAfterCreation();
mConnectionService.addConference(conference);
@@ -401,4 +440,33 @@
// conferenceable connections for the conference to show merge calls option.
recalculateConferenceable();
}
+
+ public static ImsConference.CarrierConfiguration getCarrierConfig(Phone phone) {
+ ImsConference.CarrierConfiguration.Builder config =
+ new ImsConference.CarrierConfiguration.Builder();
+ if (phone == null) {
+ return config.build();
+ }
+
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (cfgManager != null) {
+ PersistableBundle bundle = cfgManager.getConfigForSubId(phone.getSubId());
+ boolean isMaximumConferenceSizeEnforced = bundle.getBoolean(
+ CarrierConfigManager.KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL);
+ int maximumConferenceSize = bundle.getInt(
+ CarrierConfigManager.KEY_IMS_CONFERENCE_SIZE_LIMIT_INT);
+ boolean isHoldAllowed = bundle.getBoolean(
+ CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
+ boolean shouldLocalDisconnectOnEmptyConference = bundle.getBoolean(
+ CarrierConfigManager.KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL);
+
+ config.setIsMaximumConferenceSizeEnforced(isMaximumConferenceSizeEnforced)
+ .setMaximumConferenceSize(maximumConferenceSize)
+ .setIsHoldAllowed(isHoldAllowed)
+ .setShouldLocalDisconnectEmptyConference(
+ shouldLocalDisconnectOnEmptyConference);
+ }
+ return config.build();
+ }
}
diff --git a/src/com/android/services/telephony/MmiCodeUtil.java b/src/com/android/services/telephony/MmiCodeUtil.java
new file mode 100644
index 0000000..d208ec3
--- /dev/null
+++ b/src/com/android/services/telephony/MmiCodeUtil.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 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.services.telephony;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class MmiCodeUtil {
+ //***** Constants
+
+ // Supp Service codes from TS 22.030 Annex B
+
+ //Called line presentation
+ static final String SC_CLIP = "30";
+ static final String SC_CLIR = "31";
+
+ // Call Forwarding
+ static final String SC_CFU = "21";
+ static final String SC_CFB = "67";
+ static final String SC_CFNRy = "61";
+ static final String SC_CFNR = "62";
+
+ static final String SC_CF_All = "002";
+ static final String SC_CF_All_Conditional = "004";
+
+ // Call Waiting
+ static final String SC_WAIT = "43";
+
+ // Call Barring
+ static final String SC_BAOC = "33";
+ static final String SC_BAOIC = "331";
+ static final String SC_BAOICxH = "332";
+ static final String SC_BAIC = "35";
+ static final String SC_BAICr = "351";
+
+ static final String SC_BA_ALL = "330";
+ static final String SC_BA_MO = "333";
+ static final String SC_BA_MT = "353";
+
+ // Supp Service Password registration
+ static final String SC_PWD = "03";
+
+ // PIN/PIN2/PUK/PUK2
+ static final String SC_PIN = "04";
+ static final String SC_PIN2 = "042";
+ static final String SC_PUK = "05";
+ static final String SC_PUK2 = "052";
+
+ // See TS 22.030 6.5.2 "Structure of the MMI"
+
+ static Pattern sPatternSuppService = Pattern.compile(
+ "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+/* 1 2 3 4 5 6 7 8 9 10 11 12
+
+ 1 = Full string up to and including #
+ 2 = action (activation/interrogation/registration/erasure)
+ 3 = service code
+ 5 = SIA
+ 7 = SIB
+ 9 = SIC
+ 10 = dialing number
+*/
+
+ static final int MATCH_GROUP_SERVICE_CODE = 3;
+
+ public static final String BUTTON_CLIR_KEY = "button_clir_key";
+ public static final String BUTTON_CW_KEY = "button_cw_key";
+ public static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+ public static final String CALL_BARRING_KEY = "call_barring_key";
+
+ //***** Public Class methods
+ public static String getMmiServiceCode(String dialString) {
+ Matcher m;
+ String ret = null;
+
+ m = sPatternSuppService.matcher(dialString);
+
+ if (m.matches()) {
+ ret = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
+ }
+
+ return ret;
+ }
+
+ private static String makeEmptyNull(String s) {
+ if (s != null && s.length() == 0) return null;
+
+ return s;
+ }
+
+ static boolean isServiceCodeCallForwarding(String sc) {
+ return sc != null &&
+ (sc.equals(SC_CFU)
+ || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
+ || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
+ || sc.equals(SC_CF_All_Conditional));
+ }
+
+ static boolean isServiceCodeCallBarring(String sc) {
+ return sc != null &&
+ (sc.equals(SC_BAOC)
+ || sc.equals(SC_BAOIC) || sc.equals(SC_BAOICxH)
+ || sc.equals(SC_BAIC) || sc.equals(SC_BAICr)
+ || sc.equals(SC_BA_ALL) || sc.equals(SC_BA_MO)
+ || sc.equals(SC_BA_MT));
+ }
+
+ static boolean isPinPukCommand(String sc) {
+ return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2)
+ || sc.equals(SC_PUK) || sc.equals(SC_PUK2));
+ }
+
+ public static String getSuppServiceKey(String dialString) {
+ String sc = getMmiServiceCode(dialString);
+ if (sc != null && sc.equals(SC_CLIP)) {
+ return "";
+ } else if (sc != null && sc.equals(SC_CLIR)) {
+ return BUTTON_CLIR_KEY;
+ } else if (isServiceCodeCallForwarding(sc)) {
+ return CALL_FORWARDING_KEY;
+ } else if (isServiceCodeCallBarring(sc)) {
+ return CALL_BARRING_KEY;
+ } else if (sc != null && sc.equals(SC_PWD)) {
+ return "";
+ } else if (sc != null && sc.equals(SC_WAIT)) {
+ return BUTTON_CW_KEY;
+ } else if (isPinPukCommand(sc)) {
+ return "";
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 2dfeaed..d2dfab5 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -25,7 +25,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
import android.text.TextUtils;
import com.android.internal.telephony.Call;
@@ -288,7 +288,11 @@
}
} else {
TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
- tm.addNewIncomingCall(handle, extras);
+ if (connection.isMultiparty()) {
+ tm.addNewIncomingConference(handle, extras);
+ } else {
+ tm.addNewIncomingCall(handle, extras);
+ }
}
}
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 116c61d..25ac220 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -77,7 +77,8 @@
* RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely
* serialized, and runs on the main looper.)
*/
- public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback) {
+ public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
+ boolean forEmergencyCall, Phone phoneForEmergencyCall) {
setupListeners();
mCallback = callback;
mInProgressListeners.clear();
@@ -89,16 +90,17 @@
}
mInProgressListeners.add(mListeners.get(i));
- mListeners.get(i).waitForRadioOn(phone, this);
+ mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall,
+ forEmergencyCall && phone == phoneForEmergencyCall);
}
- powerOnRadio();
+ powerOnRadio(forEmergencyCall, phoneForEmergencyCall);
}
/**
* Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
* get an onServiceStateChanged() callback when the radio successfully comes up.
*/
- private void powerOnRadio() {
+ private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall) {
// If airplane mode is on, we turn it off the same way that the Settings activity turns it
// off.
@@ -112,7 +114,7 @@
for (Phone phone : PhoneFactory.getPhones()) {
Log.d(this, "powerOnRadio, enabling Radio");
- phone.setRadioPower(true);
+ phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall, false);
}
// Post the broadcast intend for change in airplane mode
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 43269b4..93e1e3c 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -70,7 +70,10 @@
Phone phone = (Phone) args.arg1;
RadioOnStateListener.Callback callback =
(RadioOnStateListener.Callback) args.arg2;
- startSequenceInternal(phone, callback);
+ boolean forEmergencyCall = (boolean) args.arg3;
+ boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4;
+ startSequenceInternal(phone, callback, forEmergencyCall,
+ isSelectedPhoneForEmergencyCall);
} finally {
args.recycle();
}
@@ -97,6 +100,10 @@
private Callback mCallback; // The callback to notify upon completion.
private Phone mPhone; // The phone that will attempt to place the call.
+ private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
+ // Whether this phone is selected to place emergency call. Can be true only if
+ // mForEmergencyCall is true.
+ private boolean mSelectedPhoneForEmergencyCall;
private int mNumRetriesSoFar;
/**
@@ -113,7 +120,8 @@
* RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely
* serialized, and runs only on the handler thread.)
*/
- public void waitForRadioOn(Phone phone, Callback callback) {
+ public void waitForRadioOn(Phone phone, Callback callback,
+ boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
Log.d(this, "waitForRadioOn: Phone " + phone.getPhoneId());
if (mPhone != null) {
@@ -124,6 +132,8 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = phone;
args.arg2 = callback;
+ args.arg3 = forEmergencyCall;
+ args.arg4 = isSelectedPhoneForEmergencyCall;
mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
}
@@ -132,7 +142,8 @@
*
* @see #waitForRadioOn
*/
- private void startSequenceInternal(Phone phone, Callback callback) {
+ private void startSequenceInternal(Phone phone, Callback callback,
+ boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId());
// First of all, clean up any state left over from a prior RadioOn call sequence. This
@@ -142,6 +153,8 @@
mPhone = phone;
mCallback = callback;
+ mForEmergencyCall = forEmergencyCall;
+ mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall;
registerForServiceStateChanged();
// Register for RADIO_OFF to handle cases where emergency call is dialed before
@@ -159,6 +172,7 @@
* call with {@link Callback#isOkToCall}
*/
private void onServiceStateChanged(ServiceState state) {
+ if (mPhone == null) return;
Log.d(this, "onServiceStateChanged(), new state = %s, Phone = %s", state,
mPhone.getPhoneId());
@@ -182,6 +196,7 @@
}
private void onRadioOn() {
+ if (mPhone == null) return;
ServiceState state = mPhone.getServiceState();
Log.d(this, "onRadioOn, state = %s, Phone = %s", state,
mPhone.getPhoneId());
@@ -203,6 +218,7 @@
* Handles the retry timer expiring.
*/
private void onRetryTimeout() {
+ if (mPhone == null) return;
int serviceState = mPhone.getServiceState().getState();
Log.d(this, "onRetryTimeout(): phone state = %s, service state = %d, retries = %d.",
mPhone.getState(), serviceState, mNumRetriesSoFar);
@@ -231,7 +247,8 @@
cleanup();
} else {
Log.d(this, "Trying (again) to turn on the radio.");
- mPhone.setRadioPower(true);
+ mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
+ false);
startRetryTimer();
}
}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 5a1e6a6..c38923f 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,16 +32,17 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Telephony;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
-import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -49,22 +50,29 @@
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsRcsManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import com.android.ims.ImsManager;
+import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.LocaleTracker;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionController;
import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.telephony.Rlog;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
+import java.util.function.Predicate;
/**
* Owns all data we have registered with Telecom including handling dynamic addition and
@@ -79,6 +87,26 @@
private final static int DEFAULT_SIM_ICON = R.drawable.ic_multi_sim;
private final static String GROUP_PREFIX = "group_";
+ private static final int REGISTER_START_DELAY_MS = 1 * 1000; // 1 second
+ private static final int REGISTER_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
+
+ /**
+ * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has not yet been
+ * registered.
+ */
+ private static final int LISTENER_STATE_UNREGISTERED = 0;
+
+ /**
+ * Indicates the first {@link SubscriptionManager.OnSubscriptionsChangedListener} registration
+ * attempt failed and we are performing backoff registration.
+ */
+ private static final int LISTENER_STATE_PERFORMING_BACKOFF = 2;
+
+ /**
+ * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has been registered.
+ */
+ private static final int LISTENER_STATE_REGISTERED = 3;
+
final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
private final Phone mPhone;
private PhoneAccount mAccount;
@@ -86,9 +114,11 @@
private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
private boolean mIsEmergency;
private boolean mIsRttCapable;
+ private boolean mIsAdhocConfCapable;
private boolean mIsEmergencyPreferred;
private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
private ImsMmTelManager.CapabilityCallback mMmtelCapabilityCallback;
+ private RegistrationManager.RegistrationCallback mImsRegistrationCallback;
private ImsMmTelManager mMmTelManager;
private final boolean mIsDummy;
private boolean mIsVideoCapable;
@@ -106,6 +136,7 @@
mPhone = phone;
mIsEmergency = isEmergency;
mIsDummy = isDummy;
+ mIsAdhocConfCapable = mPhone.isImsRegistered();
mAccount = registerPstnPhoneAccount(isEmergency, isDummy);
Log.i(this, "Registered phoneAccount: %s with handle: %s",
mAccount, mAccount.getAccountHandle());
@@ -138,15 +169,38 @@
updateRttCapability();
}
};
-
registerMmTelCapabilityCallback();
+
+ mImsRegistrationCallback = new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ updateAdhocConfCapability(true);
+ }
+
+ @Override
+ public void onRegistering(int imsRadioTech) {
+ updateAdhocConfCapability(false);
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo imsReasonInfo) {
+ updateAdhocConfCapability(false);
+ }
+ };
+ registerImsRegistrationCallback();
}
void teardown() {
mIncomingCallNotifier.teardown();
mPhoneCapabilitiesNotifier.teardown();
- if (mMmTelManager != null && mMmtelCapabilityCallback != null) {
- mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
+ if (mMmTelManager != null) {
+ if (mMmtelCapabilityCallback != null) {
+ mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
+ }
+
+ if (mImsRegistrationCallback != null) {
+ mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+ }
}
}
@@ -171,6 +225,25 @@
}
}
+ private void registerImsRegistrationCallback() {
+ if (mMmTelManager == null || mImsRegistrationCallback == null) {
+ return;
+ }
+
+ try {
+ mMmTelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
+ mImsRegistrationCallback);
+ } catch (ImsException e) {
+ Log.w(this, "registerImsRegistrationCallback: registration failed, no ImsService"
+ + " available. Exception: " + e.getMessage());
+ return;
+ } catch (IllegalArgumentException e) {
+ Log.w(this, "registerImsRegistrationCallback: registration failed, invalid"
+ + " subscription, Exception" + e.getMessage());
+ return;
+ }
+ }
+
/**
* Trigger re-registration of this account.
*/
@@ -226,6 +299,7 @@
// slotId from the subId or the phoneId in all instances.
SubscriptionInfo record =
mSubscriptionManager.getActiveSubscriptionInfo(subId);
+ TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
if (isEmergency) {
label = mContext.getResources().getString(R.string.sim_label_emergency_calls);
@@ -234,7 +308,7 @@
} else if (mTelephonyManager.getPhoneCount() == 1) {
// For single-SIM devices, we show the label and description as whatever the name of
// the network is.
- description = label = mTelephonyManager.getNetworkOperatorName();
+ description = label = tm.getNetworkOperatorName();
} else {
CharSequence subDisplayName = null;
@@ -321,6 +395,12 @@
extras.putAll(getPhoneAccountExtras());
}
+ if (mIsAdhocConfCapable && isCarrierAdhocConferenceCallSupported()) {
+ capabilities |= PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING;
+ } else {
+ capabilities &= ~PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING;
+ }
+
final boolean isHandoverFromSupported = mContext.getResources().getBoolean(
R.bool.config_support_handover_from);
if (isHandoverFromSupported && !isEmergency) {
@@ -486,16 +566,31 @@
}
/**
- * Determines from carrier configuration whether RCS presence indication for video calls is
- * supported.
+ * Determines from carrier configuration and user setting whether RCS presence indication
+ * for video calls is supported.
*
* @return {@code true} if RCS presence indication for video calls is supported.
*/
private boolean isCarrierVideoPresenceSupported() {
PersistableBundle b =
PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
- return b != null &&
- b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ boolean carrierConfigEnabled = b != null
+ && b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ return carrierConfigEnabled && isUserContactDiscoverySettingEnabled();
+ }
+
+ /**
+ * @return true if the user has enabled contact discovery for the subscription associated
+ * with this account entry, false otherwise.
+ */
+ private boolean isUserContactDiscoverySettingEnabled() {
+ try {
+ ImsRcsManager manager = mImsManager.getImsRcsManager(mPhone.getSubId());
+ return manager.getUceAdapter().isUceSettingEnabled();
+ } catch (Exception e) {
+ Log.w(LOG_TAG, "isUserContactDiscoverySettingEnabled caught exception: " + e);
+ return false;
+ }
}
/**
@@ -511,6 +606,19 @@
}
/**
+ * Determines from carrier config whether adhoc conference calling is supported.
+ *
+ * @return {@code true} if adhoc conference calling is supported, {@code false} otherwise.
+ */
+ private boolean isCarrierAdhocConferenceCallSupported() {
+ PersistableBundle b =
+ PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+ return b != null &&
+ b.getBoolean(CarrierConfigManager.KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL);
+ }
+
+
+ /**
* Determines from carrier config whether merging calls is supported.
*
* @return {@code true} if merging calls is supported, {@code false} otherwise.
@@ -664,6 +772,45 @@
}
}
+ public void updateAdhocConfCapability(boolean isAdhocConfCapable) {
+ synchronized (mAccountsLock) {
+ if (!mAccounts.contains(this)) {
+ // Account has already been torn down, don't try to register it again.
+ // This handles the case where teardown has already happened, and we got a Ims
+ // registartion update that lost the race for the mAccountsLock. In such a
+ // scenario by the time we get here, the original phone account could have been
+ // torn down.
+ return;
+ }
+
+ if (isAdhocConfCapable != mIsAdhocConfCapable) {
+ Log.i(this, "updateAdhocConfCapability - changed, new value: "
+ + isAdhocConfCapable);
+ mIsAdhocConfCapable = isAdhocConfCapable;
+ mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+ }
+ }
+ }
+
+ public void updateVideoPresenceCapability() {
+ synchronized (mAccountsLock) {
+ if (!mAccounts.contains(this)) {
+ // Account has already been torn down, don't try to register it again.
+ // This handles the case where teardown has already happened, and we got a Ims
+ // registration update that lost the race for the mAccountsLock. In such a
+ // scenario by the time we get here, the original phone account could have been
+ // torn down.
+ return;
+ }
+ boolean isVideoPresenceSupported = isCarrierVideoPresenceSupported();
+ if (mIsVideoPresenceSupported != isVideoPresenceSupported) {
+ Log.i(this, "updateVideoPresenceCapability for subId=" + mPhone.getSubId()
+ + ", new value= " + isVideoPresenceSupported);
+ mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+ }
+ }
+ }
+
public void updateRttCapability() {
boolean isRttEnabled = isRttCurrentlySupported();
if (isRttEnabled != mIsRttCapable) {
@@ -686,14 +833,51 @@
* device.
*/
private boolean isRttCurrentlySupported() {
+ // First check the emergency case -- if it's supported and turned on,
+ // we want to present RTT as available on the emergency-only phone account
+ if (mIsEmergency) {
+ // First check whether the device supports it
+ boolean devicesSupportsRtt =
+ mContext.getResources().getBoolean(R.bool.config_support_rtt);
+ boolean deviceSupportsEmergencyRtt = mContext.getResources().getBoolean(
+ R.bool.config_support_simless_emergency_rtt);
+ if (!(deviceSupportsEmergencyRtt && devicesSupportsRtt)) {
+ Log.i(this, "isRttCurrentlySupported -- emergency acct and no device support");
+ return false;
+ }
+ // Next check whether we're in or near a country that supports it
+ String country =
+ mPhone.getServiceStateTracker().getLocaleTracker()
+ .getCurrentCountry().toLowerCase();
+ String[] supportedCountries = mContext.getResources().getStringArray(
+ R.array.config_simless_emergency_rtt_supported_countries);
+ if (supportedCountries == null || Arrays.stream(supportedCountries).noneMatch(
+ Predicate.isEqual(country))) {
+ Log.i(this, "isRttCurrentlySupported -- emergency acct and"
+ + " not supported in this country: " + country);
+ return false;
+ }
+
+ return true;
+ }
+
boolean hasVoiceAvailability = isImsVoiceAvailable();
boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr
.isRttEnabled(mPhone.getSubId());
boolean isRoaming = mTelephonyManager.isNetworkRoaming(mPhone.getSubId());
+ boolean isOnWfc = mPhone.getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
- return hasVoiceAvailability && isRttSupported && !isRoaming;
+ boolean shouldDisableBecauseRoamingOffWfc = isRoaming && !isOnWfc;
+ Log.i(this, "isRttCurrentlySupported -- regular acct,"
+ + " hasVoiceAvailability: " + hasVoiceAvailability + "\n"
+ + " isRttSupported: " + isRttSupported + "\n"
+ + " isRoaming: " + isRoaming + "\n"
+ + " isOnWfc: " + isOnWfc + "\n");
+
+ return hasVoiceAvailability && isRttSupported && !shouldDisableBecauseRoamingOffWfc;
}
/**
@@ -790,18 +974,46 @@
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
- // Any time the SubscriptionInfo changes...rerun the setup
- Log.i(this, "onSubscriptionsChanged - update accounts");
+ if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+ mRegisterSubscriptionListenerBackoff.stop();
+ mHandlerThread.quitSafely();
+ }
+ mSubscriptionListenerState = LISTENER_STATE_REGISTERED;
+
+ // Any time the SubscriptionInfo changes rerun the setup
+ Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - update accounts");
tearDownAccounts();
setupAccounts();
}
+
+ @Override
+ public void onAddListenerFailed() {
+ // Woe! Failed to add the listener!
+ Log.w(this, "TelecomAccountRegistry: onAddListenerFailed - failed to register "
+ + "OnSubscriptionsChangedListener");
+
+ // Even though registering the listener failed, we will still try to setup the phone
+ // accounts now; the phone instances should already be present and ready, so even if
+ // telephony registry is poking along we can still try to setup the phone account.
+ tearDownAccounts();
+ setupAccounts();
+
+ if (mSubscriptionListenerState == LISTENER_STATE_UNREGISTERED) {
+ // Initial registration attempt failed; start exponential backoff.
+ mSubscriptionListenerState = LISTENER_STATE_PERFORMING_BACKOFF;
+ mRegisterSubscriptionListenerBackoff.start();
+ } else {
+ // We're already doing exponential backoff and a registration failed.
+ mRegisterSubscriptionListenerBackoff.notifyFailed();
+ }
+ }
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- Log.i(this, "User changed, re-registering phone accounts.");
+ Log.i(this, "TelecomAccountRegistry: User changed, re-registering phone accounts.");
int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
UserHandle currentUserHandle = UserHandle.of(userHandleId);
@@ -860,23 +1072,47 @@
private static TelecomAccountRegistry sInstance;
private final Context mContext;
private final TelecomManager mTelecomManager;
+ private final android.telephony.ims.ImsManager mImsManager;
private final TelephonyManager mTelephonyManager;
private final SubscriptionManager mSubscriptionManager;
private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
private final Object mAccountsLock = new Object();
+ private int mSubscriptionListenerState = LISTENER_STATE_UNREGISTERED;
private int mServiceState = ServiceState.STATE_POWER_OFF;
private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mIsPrimaryUser = true;
+ private ExponentialBackoff mRegisterSubscriptionListenerBackoff;
+ private final HandlerThread mHandlerThread = new HandlerThread("TelecomAccountRegistry");
// TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
// pattern; redesign. This was added to fix a late release bug.
private TelephonyConnectionService mTelephonyConnectionService;
+ // Used to register subscription changed listener when initial attempts fail.
+ private Runnable mRegisterOnSubscriptionsChangedListenerRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+ Log.i(this, "TelecomAccountRegistry: performing delayed register.");
+ SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
+ mOnSubscriptionsChangedListener);
+ }
+ }
+ };
+
TelecomAccountRegistry(Context context) {
mContext = context;
mTelecomManager = context.getSystemService(TelecomManager.class);
+ mImsManager = context.getSystemService(android.telephony.ims.ImsManager.class);
mTelephonyManager = TelephonyManager.from(context);
mSubscriptionManager = SubscriptionManager.from(context);
+ mHandlerThread.start();
+ mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
+ REGISTER_START_DELAY_MS,
+ REGISTER_MAXIMUM_DELAY_MS,
+ 2, /* multiplier */
+ mHandlerThread.getLooper(),
+ mRegisterOnSubscriptionsChangedListenerRunnable);
}
/**
@@ -1048,6 +1284,21 @@
return null;
}
+ public void refreshAdhocConference(boolean isEnableAdhocConf) {
+ synchronized (mAccountsLock) {
+ Log.v(this, "refreshAdhocConference isEnable = " + isEnableAdhocConf);
+ for (AccountEntry entry : mAccounts) {
+ boolean hasAdhocConfCapability = entry.mAccount.hasCapabilities(
+ PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING);
+ if (!isEnableAdhocConf && hasAdhocConfCapability) {
+ entry.updateAdhocConfCapability(isEnableAdhocConf);
+ } else if (isEnableAdhocConf && !hasAdhocConfCapability) {
+ entry.updateAdhocConfCapability(entry.mPhone.isImsRegistered());
+ }
+ }
+ }
+ }
+
/**
* Returns whethere a the subscription associated with a {@link PhoneAccountHandle} is using a
* sim call manager.
@@ -1077,6 +1328,7 @@
// Register for SubscriptionInfo list changes which is guaranteed
// to invoke onSubscriptionsChanged the first time.
+ Log.i(this, "TelecomAccountRegistry: setupOnBoot - register subscription listener");
SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
@@ -1094,9 +1346,14 @@
//We also need to listen for locale changes
//(e.g. system language changed -> SIM card name changed)
- mContext.registerReceiver(mLocaleChangeReceiver,
- new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+ IntentFilter localeChangeFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ localeChangeFilter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+ mContext.registerReceiver(mLocaleChangeReceiver, localeChangeFilter);
+ registerContentObservers();
+ }
+
+ private void registerContentObservers() {
// Listen to the RTT system setting so that we update it when the user flips it.
ContentObserver rttUiSettingObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@@ -1113,6 +1370,23 @@
Uri rttSettingUri = Settings.Secure.getUriFor(Settings.Secure.RTT_CALLING_MODE);
mContext.getContentResolver().registerContentObserver(
rttSettingUri, false, rttUiSettingObserver);
+
+ // Listen to the changes to the user's Contacts Discovery Setting.
+ ContentObserver contactDiscoveryObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mAccountsLock) {
+ for (AccountEntry account : mAccounts) {
+ account.updateVideoPresenceCapability();
+ }
+ }
+ }
+ };
+ Uri contactDiscUri = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+ Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
+ mContext.getContentResolver().registerContentObserver(
+ contactDiscUri, true /*notifyForDescendants*/, contactDiscoveryObserver);
}
/**
@@ -1214,30 +1488,6 @@
// Clean up any PhoneAccounts that are no longer relevant
cleanupPhoneAccounts();
-
- // At some point, the phone account ID was switched from the subId to the iccId.
- // If there is a default account, check if this is the case, and upgrade the default account
- // from using the subId to iccId if so.
- PhoneAccountHandle defaultPhoneAccount =
- mTelecomManager.getUserSelectedOutgoingPhoneAccount();
- ComponentName telephonyComponentName =
- new ComponentName(mContext, TelephonyConnectionService.class);
-
- if (defaultPhoneAccount != null &&
- telephonyComponentName.equals(defaultPhoneAccount.getComponentName()) &&
- !hasAccountEntryForPhoneAccount(defaultPhoneAccount)) {
-
- String phoneAccountId = defaultPhoneAccount.getId();
- if (!TextUtils.isEmpty(phoneAccountId) && TextUtils.isDigitsOnly(phoneAccountId)) {
- PhoneAccountHandle upgradedPhoneAccount =
- PhoneUtils.makePstnPhoneAccountHandle(
- PhoneGlobals.getPhone(Integer.parseInt(phoneAccountId)));
-
- if (hasAccountEntryForPhoneAccount(upgradedPhoneAccount)) {
- mTelecomManager.setUserSelectedOutgoingPhoneAccount(upgradedPhoneAccount);
- }
- }
- }
}
private void tearDownAccounts() {
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index afbe89d..7e4693f 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -16,6 +16,7 @@
package com.android.services.telephony;
+import android.net.Uri;
import android.telecom.Connection;
import android.telecom.PhoneAccountHandle;
@@ -93,6 +94,22 @@
}
@Override
+ public void onAnswer(int videoState) {
+ Log.e(this, new Exception(), "Answer not supported for GSM conference call.");
+ }
+
+ @Override
+ public void onReject() {
+ Log.e(this, new Exception(), "Reject not supported for GSM conference call.");
+ }
+
+ @Override
+ public void onAddConferenceParticipants(List<Uri> participants) {
+ Log.e(this, new Exception(), "Adding Conference Participants not supported " +
+ " for GSM conference call.");
+ }
+
+ @Override
public void onMerge(Connection connection) {
try {
Phone phone = ((TelephonyConnection) connection).getPhone();
diff --git a/src/com/android/services/telephony/TelephonyConferenceBase.java b/src/com/android/services/telephony/TelephonyConferenceBase.java
index 5e7ecf6..1c81fb9 100644
--- a/src/com/android/services/telephony/TelephonyConferenceBase.java
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -48,6 +48,14 @@
public void onConferenceMembershipChanged(Connection connection) {}
/**
+ * Listener called when there conference call state changes.
+ * @param conference The conference.
+ * @param oldState previous state of conference call.
+ * @param newState new state of conference call.
+ */
+ public void onStateChanged(Conference conference, int oldState, int newState) {}
+
+ /**
* Listener called when a conference is destroyed.
* @param conference The conference.
*/
@@ -129,6 +137,54 @@
}
/**
+ * Sets state to be on hold.
+ */
+ public final void setConferenceOnHold() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_HOLDING) {
+ return;
+ }
+ setOnHold();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
+ * Sets state to be dialing.
+ */
+ public final void setConferenceOnDialing() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_DIALING) {
+ return;
+ }
+ setDialing();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
+ * Sets state to be ringing.
+ */
+ public final void setConferenceOnRinging() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_RINGING) {
+ return;
+ }
+ setRinging();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
+ * Sets state to be active.
+ */
+ public final void setConferenceOnActive() {
+ int oldState = getState();
+ if (oldState == Connection.STATE_ACTIVE) {
+ return;
+ }
+ setActive();
+ notifyStateChanged(oldState, getState());
+ }
+
+ /**
* Updates RIL voice radio technology used for current conference after its creation.
*/
public void updateCallRadioTechAfterCreation() {
@@ -188,4 +244,12 @@
listener.onDestroyed(this);
}
}
+
+ private void notifyStateChanged(int oldState, int newState) {
+ if (oldState != newState) {
+ for (TelephonyConferenceListener listener : mListeners) {
+ listener.onStateChanged(this, oldState, newState);
+ }
+ }
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
old mode 100644
new mode 100755
index 14f0cb8..19afe24
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -40,7 +40,6 @@
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.SubscriptionManager;
@@ -51,6 +50,7 @@
import android.util.Pair;
import com.android.ims.ImsCall;
+import com.android.ims.ImsException;
import com.android.ims.internal.ConferenceParticipant;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
@@ -63,12 +63,14 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.phone.ImsUtil;
import com.android.phone.PhoneGlobals;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.telephony.Rlog;
import java.util.ArrayList;
import java.util.Arrays;
@@ -113,6 +115,13 @@
private static final int MSG_SET_CALL_RADIO_TECH = 18;
private static final int MSG_ON_CONNECTION_EVENT = 19;
private static final int MSG_REDIAL_CONNECTION_CHANGED = 20;
+ private static final int MSG_REJECT = 21;
+
+ private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
+ private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
+
+ private List<Uri> mParticipants;
+ private boolean mIsAdhocConferenceCall;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -163,7 +172,9 @@
"not foreground connection, skipping");
return;
}
- setRingbackRequested((Boolean) ((AsyncResult) msg.obj).result);
+ boolean ringback = (Boolean) ((AsyncResult) msg.obj).result;
+ setRingbackRequested(ringback);
+ notifyRingbackRequested(ringback);
break;
case MSG_DISCONNECT:
updateState();
@@ -268,6 +279,10 @@
int cause = (int) msg.obj;
hangup(cause);
break;
+ case MSG_REJECT:
+ int rejectReason = (int) msg.obj;
+ reject(rejectReason);
+ break;
case MSG_SET_CALL_RADIO_TECH:
int vrat = (int) msg.obj;
@@ -463,6 +478,7 @@
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) {}
}
private final PostDialListener mPostDialListener = new PostDialListener() {
@@ -756,11 +772,6 @@
private boolean mIsCdmaVoicePrivacyEnabled;
/**
- * Indicates whether this call is an outgoing call.
- */
- protected final boolean mIsOutgoing;
-
- /**
* Indicates whether the connection can be held. This filed combined with the state of the
* connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the
* connection.
@@ -794,8 +805,8 @@
new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
- String callId, boolean isOutgoingCall) {
- mIsOutgoing = isOutgoingCall;
+ String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
+ setCallDirection(callDirection);
setTelecomCallId(callId);
if (originalConnection != null) {
setOriginalConnection(originalConnection);
@@ -859,6 +870,11 @@
}
@Override
+ public void onAddConferenceParticipants(List<Uri> participants) {
+ performAddConferenceParticipants(participants);
+ }
+
+ @Override
public void onAbort() {
Log.v(this, "onAbort");
mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
@@ -876,14 +892,7 @@
@Override
public void onAnswer(int videoState) {
- Log.v(this, "onAnswer");
- if (isValidRingingCall() && getPhone() != null) {
- try {
- getPhone().acceptCall(videoState);
- } catch (CallStateException e) {
- Log.e(this, e, "Failed to accept call.");
- }
- }
+ performAnswer(videoState);
}
@Override
@@ -927,15 +936,76 @@
@Override
public void onReject() {
- Log.v(this, "onReject");
+ performReject(android.telecom.Call.REJECT_REASON_DECLINED);
+ }
+
+ @Override
+ public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
+ performReject(rejectReason);
+ }
+
+ public void performReject(int rejectReason) {
+ Log.v(this, "performReject");
if (isValidRingingCall()) {
- mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
+ mHandler.obtainMessage(MSG_REJECT, rejectReason)
.sendToTarget();
}
super.onReject();
}
@Override
+ public void onTransfer(Uri number, boolean isConfirmationRequired) {
+ Log.v(this, "onTransfer");
+ if (mOriginalConnection != null) {
+ if (number == null) {
+ Log.w(this, "call transfer uri is null");
+ return;
+ }
+ String scheme = number.getScheme();
+ String transferNumber = "";
+ String uriString = number.getSchemeSpecificPart();
+ if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
+ if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
+ Log.w(this, "onTransfer, number scheme is not of type tel instead: "
+ + scheme);
+ return;
+ }
+ if (PhoneNumberUtils.isUriNumber(uriString)) {
+ Log.w(this, "Invalid transfer address. Not a legal PSTN number.");
+ return;
+ }
+ transferNumber = PhoneNumberUtils.convertAndStrip(uriString);
+ if (TextUtils.isEmpty(transferNumber)) {
+ Log.w(this, "Empty transfer number obtained from uri");
+ return;
+ }
+ } else {
+ Log.w(this, "Cannot transfer to voicemail uri");
+ return;
+ }
+
+ try {
+ mOriginalConnection.transfer(transferNumber, isConfirmationRequired);
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to transfer call.");
+ }
+ }
+ }
+
+ @Override
+ public void onTransfer(Connection otherConnection) {
+ Log.v(this, "onConsultativeTransfer");
+ if (mOriginalConnection != null && (otherConnection instanceof TelephonyConnection)) {
+ try {
+ mOriginalConnection.consultativeTransfer(
+ ((TelephonyConnection) otherConnection).getOriginalConnection());
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to transfer call.");
+ }
+ }
+ }
+
+ @Override
public void onPostDialContinue(boolean proceed) {
Log.v(this, "onPostDialContinue, proceed: " + proceed);
if (mOriginalConnection != null) {
@@ -1001,6 +1071,17 @@
originalConnection.sendRttModifyResponse(textStream);
}
+ public void performAnswer(int videoState) {
+ Log.v(this, "performAnswer");
+ if (isValidRingingCall() && getPhone() != null) {
+ try {
+ getPhone().acceptCall(videoState);
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to accept call.");
+ }
+ }
+ }
+
public void performHold() {
Log.v(this, "performHold");
// TODO: Can dialing calls be put on hold as well since they take up the
@@ -1096,6 +1177,29 @@
}
}
+ private String[] getAddConferenceParticipants(List<Uri> participants) {
+ String[] addConfParticipants = new String[participants.size()];
+ int i = 0;
+ for (Uri participant : participants) {
+ addConfParticipants[i] = participant.getSchemeSpecificPart();
+ i++;
+ }
+ return addConfParticipants;
+ }
+
+ public void performAddConferenceParticipants(List<Uri> participants) {
+ Log.v(this, "performAddConferenceParticipants");
+ if (mOriginalConnection.getCall() instanceof ImsPhoneCall) {
+ ImsPhoneCall imsPhoneCall = (ImsPhoneCall)mOriginalConnection.getCall();
+ try {
+ imsPhoneCall.getImsCall().inviteParticipants(
+ getAddConferenceParticipants(participants));
+ } catch(ImsException e) {
+ Log.e(this, e, "failed to add conference participants");
+ }
+ }
+ }
+
/**
* Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
* capabilities.
@@ -1130,6 +1234,12 @@
newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
isImsConnection() && canDeflectImsCalls());
+ newCapabilities = applyAddParticipantCapabilities(newCapabilities);
+ newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER_CONSULTATIVE,
+ isImsConnection() && canConsultativeTransfer());
+ newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER,
+ isImsConnection() && canTransferToNumber());
+
if (getConnectionCapabilities() != newCapabilities) {
setConnectionCapabilities(newCapabilities);
notifyConnectionCapabilitiesChanged(newCapabilities);
@@ -1162,11 +1272,13 @@
isExternalConnection());
newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
mIsCdmaVoicePrivacyEnabled);
- newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
+ newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING,
mIsUsingAssistedDialing);
newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
isNetworkIdentifiedEmergencyCall());
+ newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE,
+ isAdhocConferenceCall());
if (getConnectionProperties() != newProperties) {
setTelephonyConnectionProperties(newProperties);
@@ -1186,6 +1298,9 @@
if (isShowingOriginalDialString()
&& mOriginalConnection.getOrigDialString() != null) {
address = getAddressFromNumber(mOriginalConnection.getOrigDialString());
+ } else if (isNeededToFormatIncomingNumberForJp()) {
+ address = getAddressFromNumber(
+ formatIncomingNumberForJp(mOriginalConnection.getAddress()));
} else {
address = getAddressFromNumber(mOriginalConnection.getAddress());
}
@@ -1255,6 +1370,7 @@
setTelephonyVideoState(mOriginalConnection.getVideoState());
setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
+ setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference());
setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
setAudioQuality(mOriginalConnection.getAudioQuality());
@@ -1272,6 +1388,8 @@
if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
mTreatAsEmergencyCall = true;
}
+ // Propagate VERSTAT for IMS calls.
+ setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus());
if (isImsConnection()) {
mWasImsConnection = true;
@@ -1511,6 +1629,39 @@
return true;
}
+ /**
+ * @return The address's to which this Connection is currently communicating.
+ */
+ public final @Nullable List<Uri> getParticipants() {
+ return mParticipants;
+ }
+
+ /**
+ * Sets the value of the {@link #getParticipants()} property.
+ *
+ * @param address The participant address's.
+ */
+ public final void setParticipants(@Nullable List<Uri> address) {
+ mParticipants = address;
+ }
+
+ /**
+ * @return true if connection is adhocConference call else false.
+ */
+ public final boolean isAdhocConferenceCall() {
+ return mIsAdhocConferenceCall;
+ }
+
+ /**
+ * Sets the value of the {@link #isAdhocConferenceCall()} property.
+ *
+ * @param isAdhocConferenceCall represents if the call is adhoc conference call or not.
+ */
+ public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) {
+ mIsAdhocConferenceCall = isAdhocConferenceCall;
+ updateConnectionProperties();
+ }
+
private boolean canHoldImsCalls() {
PersistableBundle b = getCarrierConfig();
// Return true if the CarrierConfig is unavailable
@@ -1520,6 +1671,75 @@
|| !VideoProfile.isVideo(getVideoState()));
}
+ private boolean isConferenceHosted() {
+ boolean isHosted = false;
+ if (getTelephonyConnectionService() != null) {
+ for (Conference current : getTelephonyConnectionService().getAllConferences()) {
+ if (current instanceof ImsConference) {
+ ImsConference other = (ImsConference) current;
+ if (getState() == current.getState()) {
+ continue;
+ }
+ if (other.isConferenceHost()) {
+ isHosted = true;
+ break;
+ }
+ }
+ }
+ }
+ return isHosted;
+ }
+
+ private boolean isAddParticipantCapable() {
+ // not add participant capable for non ims phones
+ if (getPhone() == null || getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
+ return false;
+ }
+
+ if (!getCarrierConfig()
+ .getBoolean(CarrierConfigManager.KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL)) {
+ return false;
+ }
+
+ boolean isCapable = !mTreatAsEmergencyCall && (mConnectionState == Call.State.ACTIVE ||
+ mConnectionState == Call.State.HOLDING);
+
+ // add participant capable if current connection is a host connection or
+ // if conference is not hosted on the device
+ isCapable = isCapable && ((mOriginalConnection != null &&
+ mOriginalConnection.isConferenceHost()) ||
+ !isConferenceHosted());
+
+ /**
+ * For individual IMS calls, if the extra for remote conference support is
+ * - indicated, then consider the same for add participant capability
+ * - not indicated, then the add participant capability is same as before.
+ */
+ if (isCapable && (mOriginalConnection != null) && !mIsMultiParty) {
+ isCapable = mOriginalConnectionExtras.getBoolean(
+ ImsCallProfile.EXTRA_CONFERENCE_AVAIL, isCapable);
+ }
+ return isCapable;
+ }
+
+ /**
+ * Applies the add participant capabilities to the {@code CallCapabilities} bit-mask.
+ *
+ * @param callCapabilities The {@code CallCapabilities} bit-mask.
+ * @return The capabilities with the add participant capabilities applied.
+ */
+ private int applyAddParticipantCapabilities(int callCapabilities) {
+ int currentCapabilities = callCapabilities;
+ if (isAddParticipantCapable()) {
+ currentCapabilities = changeBitmask(currentCapabilities,
+ Connection.CAPABILITY_ADD_PARTICIPANT, true);
+ } else {
+ currentCapabilities = changeBitmask(currentCapabilities,
+ Connection.CAPABILITY_ADD_PARTICIPANT, false);
+ }
+ return currentCapabilities;
+ }
+
@VisibleForTesting
public PersistableBundle getCarrierConfig() {
Phone phone = getPhone();
@@ -1540,6 +1760,50 @@
return false;
}
+ private boolean isCallTransferSupported() {
+ PersistableBundle b = getCarrierConfig();
+ // Return false if the CarrierConfig is unavailable
+ if (b != null) {
+ return b.getBoolean(CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
+ }
+ return false;
+ }
+
+ private boolean canTransfer(TelephonyConnection c) {
+ com.android.internal.telephony.Connection connection = c.getOriginalConnection();
+ return (connection != null && !connection.isMultiparty()
+ && (c.getState() == STATE_ACTIVE || c.getState() == STATE_HOLDING));
+ }
+
+ private boolean canTransferToNumber() {
+ if (!isCallTransferSupported()) {
+ return false;
+ }
+ return canTransfer(this);
+ }
+
+ private boolean canConsultativeTransfer() {
+ if (!isCallTransferSupported()) {
+ return false;
+ }
+ if (!canTransfer(this)) {
+ return false;
+ }
+ boolean canConsultativeTransfer = false;
+ if (getTelephonyConnectionService() != null) {
+ for (Connection current : getTelephonyConnectionService().getAllConnections()) {
+ if (current != this && current instanceof TelephonyConnection) {
+ TelephonyConnection other = (TelephonyConnection) current;
+ if (canTransfer(other)) {
+ canConsultativeTransfer = true;
+ break;
+ }
+ }
+ }
+ }
+ return canConsultativeTransfer;
+ }
+
/**
* Determines if the device will respect the value of the
* {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
@@ -1628,6 +1892,46 @@
}
}
+ protected void reject(@android.telecom.Call.RejectReason int rejectReason) {
+ if (mOriginalConnection != null) {
+ mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED;
+ try {
+ // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
+ // connection.hangup(). Without this change, the party originating the call
+ // will not get sent to voicemail if the user opts to reject the call.
+ if (isValidRingingCall()) {
+ Call call = getCall();
+ if (call != null) {
+ call.hangup(rejectReason);
+ } else {
+ Log.w(this, "Attempting to hangup a connection without backing call.");
+ }
+ } else {
+ // We still prefer to call connection.hangup() for non-ringing calls
+ // in order to support hanging-up specific calls within a conference call.
+ // If we invoked call.hangup() while in a conference, we would end up
+ // hanging up the entire conference call instead of the specific connection.
+ mOriginalConnection.hangup();
+ }
+ } catch (CallStateException e) {
+ Log.e(this, e, "Call to Connection.hangup failed with exception");
+ }
+ } else {
+ if (getState() == STATE_DISCONNECTED) {
+ Log.i(this, "hangup called on an already disconnected call!");
+ close();
+ } else {
+ // There are a few cases where mOriginalConnection has not been set yet. For
+ // example, when the radio has to be turned on to make an emergency call,
+ // mOriginalConnection could not be set for many seconds.
+ setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.LOCAL,
+ "Local Disconnect before connection established."));
+ close();
+ }
+ }
+ }
+
com.android.internal.telephony.Connection getOriginalConnection() {
return mOriginalConnection;
}
@@ -1742,6 +2046,12 @@
// Ensure extras are propagated to Telecom.
putTelephonyExtras(mOriginalConnectionExtras);
+ // If extras contain Conference support information,
+ // then ensure capabilities are updated.
+ if (mOriginalConnectionExtras.containsKey(
+ ImsCallProfile.EXTRA_CONFERENCE_AVAIL)) {
+ updateConnectionCapabilities();
+ }
} else {
Log.d(this, "Extras update not required");
}
@@ -2119,8 +2429,8 @@
/**
* @return {@code true} if this is an outgoing call, {@code false} otherwise.
*/
- boolean isOutgoingCall() {
- return mIsOutgoing;
+ public boolean isOutgoingCall() {
+ return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING;
}
/**
@@ -2530,6 +2840,8 @@
}
sb.append(" confSupported:");
sb.append(mIsConferenceSupported ? "Y" : "N");
+ sb.append(" isAdhocConf:");
+ sb.append(isAdhocConferenceCall() ? "Y" : "N");
sb.append("]");
return sb.toString();
}
@@ -2654,8 +2966,13 @@
* <p>
* Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are
* notified.
+ * @param videoState The new video state. Valid values:
+ * {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
*/
- public void setTelephonyVideoState(@VideoProfile.VideoState int videoState) {
+ public void setTelephonyVideoState(int videoState) {
setVideoState(videoState);
notifyVideoStateChanged(videoState);
}
@@ -2846,15 +3163,29 @@
/**
* Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection.
- * @param videoState The new video state.
+ * @param videoState The new video state. Valid values:
+ * {@link VideoProfile#STATE_AUDIO_ONLY},
+ * {@link VideoProfile#STATE_BIDIRECTIONAL},
+ * {@link VideoProfile#STATE_TX_ENABLED},
+ * {@link VideoProfile#STATE_RX_ENABLED}.
*/
- private void notifyVideoStateChanged(@VideoProfile.VideoState int videoState) {
+ private void notifyVideoStateChanged(int videoState) {
for (TelephonyConnectionListener listener : mTelephonyListeners) {
listener.onVideoStateChanged(this, videoState);
}
}
/**
+ * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not.
+ * @param ringback Whether the ringback tone is to be played
+ */
+ private void notifyRingbackRequested(boolean ringback) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onRingbackRequested(this, ringback);
+ }
+ }
+
+ /**
* Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
* connection.
* @param videoProvider The new video provider.
@@ -2875,4 +3206,29 @@
listener.onStatusHintsChanged(this, statusHints);
}
}
+
+ /**
+ * Whether the incoming call number should be formatted to national number for Japan.
+ * @return {@code true} should be convert to the national format, {@code false} otherwise.
+ */
+ private boolean isNeededToFormatIncomingNumberForJp() {
+ if (mOriginalConnection.isIncoming()
+ && !TextUtils.isEmpty(mOriginalConnection.getAddress())
+ && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) {
+ PersistableBundle b = getCarrierConfig();
+ return b != null && b.getBoolean(
+ CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL);
+ }
+ return false;
+ }
+
+ /**
+ * Format the incoming call number to national number for Japan.
+ * @param number
+ * @return the formatted phone number (e.g, "+819012345678" -> "09012345678")
+ */
+ private String formatIncomingNumberForJp(String number) {
+ return PhoneNumberUtils.stripSeparators(
+ PhoneNumberUtils.formatNumber(number, JAPAN_ISO_COUNTRY_CODE));
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 2aba682..bd10aea 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -17,10 +17,13 @@
package com.android.services.telephony;
import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
@@ -46,6 +49,7 @@
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.Pair;
+import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -65,12 +69,14 @@
import com.android.phone.MMIDialogActivity;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
+import com.android.phone.settings.SuppServicesUiUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -246,12 +252,20 @@
@Override
public boolean isCurrentEmergencyNumber(String number) {
- return mTelephonyManager.isEmergencyNumber(number);
+ try {
+ return mTelephonyManager.isEmergencyNumber(number);
+ } catch (IllegalStateException ise) {
+ return false;
+ }
}
@Override
public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList() {
- return mTelephonyManager.getEmergencyNumberList();
+ try {
+ return mTelephonyManager.getEmergencyNumberList();
+ } catch (IllegalStateException ise) {
+ return new HashMap<>();
+ }
}
}
@@ -421,6 +435,14 @@
}
/**
+ * Overrides radioOnHelper for testing.
+ */
+ @VisibleForTesting
+ public void setRadioOnHelper(RadioOnHelper radioOnHelper) {
+ mRadioOnHelper = radioOnHelper;
+ }
+
+ /**
* Overrides PhoneSwitcher dependencies for testing.
*/
@VisibleForTesting
@@ -516,6 +538,138 @@
return super.onUnbind(intent);
}
+ private Conference placeOutgoingConference(ConnectionRequest request,
+ Connection resultConnection, Phone phone) {
+ if (resultConnection instanceof TelephonyConnection) {
+ return placeOutgoingConference((TelephonyConnection) resultConnection, phone, request);
+ }
+ return null;
+ }
+
+ private Conference placeOutgoingConference(TelephonyConnection conferenceHostConnection,
+ Phone phone, ConnectionRequest request) {
+ 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());
+ } catch (CallStateException e) {
+ Log.e(this, e, "placeOutgoingConference, phone.startConference exception: " + e);
+ handleCallStateException(e, conferenceHostConnection, phone);
+ return null;
+ }
+
+ if (originalConnection == null) {
+ Log.d(this, "placeOutgoingConference, phone.startConference returned null");
+ conferenceHostConnection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUTGOING_FAILURE,
+ "conferenceHostConnection is null",
+ phone.getPhoneId()));
+ conferenceHostConnection.clearOriginalConnection();
+ conferenceHostConnection.destroy();
+ } else {
+ conferenceHostConnection.setOriginalConnection(originalConnection);
+ }
+
+ return prepareConference(conferenceHostConnection, request.getAccountHandle());
+ }
+
+ Conference prepareConference(Connection conn, PhoneAccountHandle phoneAccountHandle) {
+ if (!(conn instanceof TelephonyConnection)) {
+ Log.w(this, "prepareConference returning NULL conference");
+ return null;
+ }
+
+ TelephonyConnection connection = (TelephonyConnection)conn;
+
+ ImsConference conference = new ImsConference(TelecomAccountRegistry.getInstance(this),
+ mTelephonyConnectionServiceProxy, connection,
+ phoneAccountHandle, () -> true,
+ ImsConferenceController.getCarrierConfig(connection.getPhone()));
+ mImsConferenceController.addConference(conference);
+ conference.setVideoState(connection,
+ connection.getVideoState());
+ conference.setVideoProvider(connection,
+ connection.getVideoProvider());
+ conference.setStatusHints(connection.getStatusHints());
+ conference.setAddress(connection.getAddress(),
+ connection.getAddressPresentation());
+ conference.setCallerDisplayName(connection.getCallerDisplayName(),
+ connection.getCallerDisplayNamePresentation());
+ conference.setParticipants(connection.getParticipants());
+ return conference;
+ }
+
+ @Override
+ public @Nullable Conference onCreateIncomingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull final ConnectionRequest request) {
+ Log.i(this, "onCreateIncomingConference, request: " + request);
+ Connection connection = onCreateIncomingConnection(connectionManagerPhoneAccount, request);
+ Log.d(this, "onCreateIncomingConference, connection: %s", connection);
+ if (connection == null) {
+ Log.i(this, "onCreateIncomingConference, implementation returned null connection.");
+ return Conference.createFailedConference(
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
+ request.getAccountHandle());
+ }
+
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+ false /* isEmergencyCall*/, null /* not an emergency call */);
+ if (phone == null) {
+ Log.d(this, "onCreateIncomingConference, phone is null");
+ return Conference.createFailedConference(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUT_OF_SERVICE,
+ "Phone is null"),
+ request.getAccountHandle());
+ }
+
+ return prepareConference(connection, request.getAccountHandle());
+ }
+
+ @Override
+ public @Nullable Conference onCreateOutgoingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @NonNull final ConnectionRequest request) {
+ Log.i(this, "onCreateOutgoingConference, request: " + request);
+ Connection connection = onCreateOutgoingConnection(connectionManagerPhoneAccount, request);
+ Log.d(this, "onCreateOutgoingConference, connection: %s", connection);
+ if (connection == null) {
+ Log.i(this, "onCreateOutgoingConference, implementation returned null connection.");
+ return Conference.createFailedConference(
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
+ request.getAccountHandle());
+ }
+
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+ false /* isEmergencyCall*/, null /* not an emergency call */);
+ if (phone == null) {
+ Log.d(this, "onCreateOutgoingConference, phone is null");
+ return Conference.createFailedConference(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.OUT_OF_SERVICE,
+ "Phone is null"),
+ request.getAccountHandle());
+ }
+
+ return placeOutgoingConference(request, connection, phone);
+ }
+
+ private String[] getParticipantsToDial(List<Uri> participants) {
+ String[] participantsToDial = new String[participants.size()];
+ int i = 0;
+ for (Uri participant : participants) {
+ participantsToDial[i] = participant.getSchemeSpecificPart();
+ i++;
+ }
+ return participantsToDial;
+ }
+
@Override
public Connection onCreateOutgoingConnection(
PhoneAccountHandle connectionManagerPhoneAccount,
@@ -523,7 +677,9 @@
Log.i(this, "onCreateOutgoingConnection, request: " + request);
Uri handle = request.getAddress();
- if (handle == null) {
+ boolean isAdhocConference = request.isAdhocConferenceCall();
+
+ if (!isAdhocConference && handle == null) {
Log.d(this, "onCreateOutgoingConnection, handle is null");
return Connection.createFailedConnection(
mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -626,19 +782,21 @@
}
final String numberToDial = number;
-
final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
|| isRadioPowerDownOnBluetooth();
+ // Get the right phone object from the account data passed in.
+ final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
+ /* Note: when not an emergency, handle can be null for unknown callers */
+ handle == null ? null : handle.getSchemeSpecificPart());
+
if (needToTurnOnRadio) {
final Uri resultHandle = handle;
- // By default, Connection based on the default Phone, since we need to return to Telecom
- // now.
- final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
+ final int originalPhoneType = phone.getPhoneType();
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
- isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
+ isEmergencyNumber, resultHandle, phone);
if (mRadioOnHelper == null) {
mRadioOnHelper = new RadioOnHelper(this);
}
@@ -646,7 +804,7 @@
@Override
public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
handleOnComplete(isRadioReady, isEmergencyNumber, resultConnection, request,
- numberToDial, resultHandle, originalPhoneType);
+ numberToDial, resultHandle, originalPhoneType, phone);
}
@Override
@@ -675,7 +833,7 @@
|| serviceState == ServiceState.STATE_IN_SERVICE;
}
}
- });
+ }, isEmergencyNumber && !isTestEmergencyNumber, phone);
// Return the still unconnected GsmConnection and wait for the Radios to boot before
// connecting it to the underlying Phone.
return resultConnection;
@@ -691,14 +849,18 @@
"Add call restricted due to ongoing video call"));
}
- // Get the right phone object from the account data passed in.
- final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
- /* Note: when not an emergency, handle can be null for unknown callers */
- handle == null ? null : handle.getSchemeSpecificPart());
if (!isEmergencyNumber) {
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
false, handle, phone);
- return placeOutgoingConnection(request, resultConnection, phone);
+ if (isAdhocConference) {
+ if (resultConnection instanceof TelephonyConnection) {
+ TelephonyConnection conn = (TelephonyConnection)resultConnection;
+ conn.setParticipants(request.getParticipants());
+ }
+ return resultConnection;
+ } else {
+ return placeOutgoingConnection(request, resultConnection, phone);
+ }
} else {
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
true, handle, phone);
@@ -763,18 +925,21 @@
*/
private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
Connection originalConnection, ConnectionRequest request, String numberToDial,
- Uri handle, int originalPhoneType) {
+ Uri handle, int originalPhoneType, Phone phone) {
// Make sure the Call has not already been canceled by the user.
if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
+ "placement.");
+ if (isEmergencyNumber) {
+ // If call is already canceled by the user, notify modem to exit emergency call
+ // mode by sending radio on with forEmergencyCall=false.
+ for (Phone curPhone : mPhoneFactoryProxy.getPhones()) {
+ curPhone.setRadioPower(true, false, false, true);
+ }
+ }
return;
}
- // Get the right phone object since the radio has been turned on successfully.
if (isRadioReady) {
- final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
- /* Note: when not an emergency, handle can be null for unknown callers */
- handle == null ? null : handle.getSchemeSpecificPart());
if (!isEmergencyNumber) {
adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
handle, originalPhoneType, false);
@@ -896,7 +1061,7 @@
int dataNetType = phone.getServiceState().getDataNetworkType();
if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
- state = phone.getServiceState().getDataRegState();
+ state = phone.getServiceState().getDataRegistrationState();
}
}
@@ -982,7 +1147,7 @@
final TelephonyConnection connection =
createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
- request.getTelecomCallId());
+ request.getTelecomCallId(), request.isAdhocConferenceCall());
if (connection == null) {
return Connection.createFailedConnection(
mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -995,6 +1160,8 @@
connection.setTelephonyVideoState(request.getVideoState());
connection.setRttTextStream(request.getRttTextStream());
connection.setTtyEnabled(isTtyModeEnabled);
+ connection.setIsAdhocConferenceCall(request.isAdhocConferenceCall());
+ connection.setParticipants(request.getParticipants());
return connection;
}
@@ -1026,11 +1193,26 @@
Call call = phone.getRingingCall();
if (!call.getState().isRinging()) {
Log.i(this, "onCreateIncomingConnection, no ringing call");
- return Connection.createFailedConnection(
+ Connection connection = Connection.createFailedConnection(
mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.INCOMING_MISSED,
"Found no ringing call",
phone.getPhoneId()));
+ Bundle extras = request.getExtras();
+
+ long time = extras.getLong(TelecomManager.EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS);
+ if (time != 0) {
+ Log.i(this, "onCreateIncomingConnection. Set connect time info.");
+ connection.setConnectTimeMillis(time);
+ }
+
+ Uri address = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
+ if (address != null) {
+ Log.i(this, "onCreateIncomingConnection. Set caller id info.");
+ connection.setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
+ }
+
+ return connection;
}
com.android.internal.telephony.Connection originalConnection =
@@ -1043,7 +1225,8 @@
TelephonyConnection connection =
createConnectionFor(phone, originalConnection, false /* isOutgoing */,
- request.getAccountHandle(), request.getTelecomCallId());
+ request.getAccountHandle(), request.getTelecomCallId(),
+ request.isAdhocConferenceCall());
handleIncomingRtt(request, originalConnection);
if (connection == null) {
return Connection.createCanceledConnection();
@@ -1155,6 +1338,29 @@
connection.close();
}
+ /**
+ * Called by the {@link ConnectionService} when a newly created {@link Conference} has been
+ * added to the {@link ConnectionService} and sent to Telecom. Here it is safe to send
+ * connection events.
+ *
+ * @param conference the {@link Conference}.
+ */
+ @Override
+ public void onCreateConferenceComplete(Conference conference) {
+ if (conference instanceof ImsConference) {
+ ImsConference imsConference = (ImsConference)conference;
+ TelephonyConnection telephonyConnection =
+ (TelephonyConnection)(imsConference.getConferenceHost());
+ maybeSendInternationalCallEvent(telephonyConnection);
+ }
+ }
+
+ public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ Log.i(this, "onCreateIncomingConferenceFailed, request: " + request);
+ onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request);
+ }
+
@Override
public void triggerConferenceRecalculate() {
if (mTelephonyConferenceController.shouldRecalculate()) {
@@ -1449,7 +1655,16 @@
private void placeOutgoingConnection(
TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
- String number = connection.getAddress().getSchemeSpecificPart();
+
+ String number = (connection.getAddress() != null)
+ ? connection.getAddress().getSchemeSpecificPart()
+ : "";
+
+ if (showDataDialog(phone, number)) {
+ connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ android.telephony.DisconnectCause.DIALED_MMI, "UT is not available"));
+ return;
+ }
com.android.internal.telephony.Connection originalConnection = null;
try {
@@ -1471,6 +1686,12 @@
.OUTGOING_EMERGENCY_CALL_PLACED);
}
}
+ for (Conference c : getAllConferences()) {
+ if (c.getState() != Connection.STATE_DISCONNECTED
+ && c instanceof Conference) {
+ ((Conference) c).onDisconnect();
+ }
+ }
} else if (!isVideoCallHoldAllowed(phone)) {
// If we do not support holding ongoing video call for an outgoing
// emergency call, disconnect the ongoing video call.
@@ -1496,41 +1717,15 @@
}
} catch (CallStateException e) {
Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
- int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
- switch (e.getError()) {
- case CallStateException.ERROR_OUT_OF_SERVICE:
- cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
- break;
- case CallStateException.ERROR_POWER_OFF:
- cause = android.telephony.DisconnectCause.POWER_OFF;
- break;
- case CallStateException.ERROR_ALREADY_DIALING:
- cause = android.telephony.DisconnectCause.ALREADY_DIALING;
- break;
- case CallStateException.ERROR_CALL_RINGING:
- cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
- break;
- case CallStateException.ERROR_CALLING_DISABLED:
- cause = android.telephony.DisconnectCause.CALLING_DISABLED;
- break;
- case CallStateException.ERROR_TOO_MANY_CALLS:
- cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
- break;
- case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
- cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
- break;
- }
- connection.setTelephonyConnectionDisconnected(
- mDisconnectCauseFactory.toTelecomDisconnectCause(cause, e.getMessage(),
- phone.getPhoneId()));
- connection.close();
+ handleCallStateException(e, connection, phone);
return;
}
if (originalConnection == null) {
int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
// On GSM phones, null connection means that we dialed an MMI code
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
+ phone.isUtEnabled()) {
Log.d(this, "dialed MMI code");
int subId = phone.getSubId();
Log.d(this, "subId: "+subId);
@@ -1539,7 +1734,7 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ SubscriptionManager.putSubscriptionIdExtra(intent, subId);
}
startActivity(intent);
}
@@ -1562,7 +1757,7 @@
return true;
}
return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
- CarrierConfigManager.KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
+ CarrierConfigManager.KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
}
private boolean shouldHoldForEmergencyCall(Phone phone) {
@@ -1577,24 +1772,71 @@
CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
}
+ private void handleCallStateException(CallStateException e, TelephonyConnection connection,
+ Phone phone) {
+ int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
+ switch (e.getError()) {
+ case CallStateException.ERROR_OUT_OF_SERVICE:
+ cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
+ break;
+ case CallStateException.ERROR_POWER_OFF:
+ cause = android.telephony.DisconnectCause.POWER_OFF;
+ break;
+ case CallStateException.ERROR_ALREADY_DIALING:
+ cause = android.telephony.DisconnectCause.ALREADY_DIALING;
+ break;
+ case CallStateException.ERROR_CALL_RINGING:
+ cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
+ break;
+ case CallStateException.ERROR_CALLING_DISABLED:
+ cause = android.telephony.DisconnectCause.CALLING_DISABLED;
+ break;
+ case CallStateException.ERROR_TOO_MANY_CALLS:
+ cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
+ break;
+ case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
+ cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
+ break;
+ }
+ connection.setTelephonyConnectionDisconnected(
+ DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
+ phone.getPhoneId()));
+ connection.close();
+ }
+
private TelephonyConnection createConnectionFor(
Phone phone,
com.android.internal.telephony.Connection originalConnection,
boolean isOutgoing,
PhoneAccountHandle phoneAccountHandle,
String telecomCallId) {
+ return createConnectionFor(phone, originalConnection, isOutgoing, phoneAccountHandle,
+ telecomCallId, false);
+ }
+
+ private TelephonyConnection createConnectionFor(
+ Phone phone,
+ com.android.internal.telephony.Connection originalConnection,
+ boolean isOutgoing,
+ PhoneAccountHandle phoneAccountHandle,
+ String telecomCallId,
+ boolean isAdhocConference) {
TelephonyConnection returnConnection = null;
int phoneType = phone.getPhoneType();
+ int callDirection = isOutgoing ? android.telecom.Call.Details.DIRECTION_OUTGOING
+ : android.telecom.Call.Details.DIRECTION_INCOMING;
if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
- returnConnection = new GsmConnection(originalConnection, telecomCallId, isOutgoing);
+ returnConnection = new GsmConnection(originalConnection, telecomCallId, callDirection);
} else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
boolean allowsMute = allowsMute(phone);
returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,
- allowsMute, isOutgoing, telecomCallId);
+ allowsMute, callDirection, telecomCallId);
}
if (returnConnection != null) {
- // Listen to Telephony specific callbacks from the connection
- returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+ if (!isAdhocConference) {
+ // Listen to Telephony specific callbacks from the connection
+ returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+ }
returnConnection.setVideoPauseSupported(
TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
phoneAccountHandle));
@@ -1942,30 +2184,24 @@
return 1;
}
// sort by number of RadioAccessFamily Capabilities.
- int compare = Integer.bitCount(o1.capabilities) -
- Integer.bitCount(o2.capabilities);
+ int compare = RadioAccessFamily.compare(o1.capabilities, o2.capabilities);
if (compare == 0) {
- // Sort by highest RAF Capability if the number is the same.
- compare = RadioAccessFamily.getHighestRafCapability(o1.capabilities) -
- RadioAccessFamily.getHighestRafCapability(o2.capabilities);
- if (compare == 0) {
- if (firstOccupiedSlot != null) {
- // If the RAF capability is the same, choose based on whether or
- // not any of the slots are occupied with a SIM card (if both
- // are, always choose the first).
- if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
- return 1;
- } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
- return -1;
- }
- } else {
- // No slots have SIMs detected in them, so weight the default
- // Phone Id greater than the others.
- if (o1.slotId == defaultPhoneId) {
- return 1;
- } else if (o2.slotId == defaultPhoneId) {
- return -1;
- }
+ if (firstOccupiedSlot != null) {
+ // If the RAF capability is the same, choose based on whether or
+ // not any of the slots are occupied with a SIM card (if both
+ // are, always choose the first).
+ if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
+ return 1;
+ } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
+ return -1;
+ }
+ } else {
+ // No slots have SIMs detected in them, so weight the default
+ // Phone Id greater than the others.
+ if (o1.slotId == defaultPhoneId) {
+ return 1;
+ } else if (o2.slotId == defaultPhoneId) {
+ return -1;
}
}
}
@@ -2134,6 +2370,78 @@
}
}
+ private boolean showDataDialog(Phone phone, String number) {
+ boolean ret = false;
+ final Context context = getApplicationContext();
+ String suppKey = MmiCodeUtil.getSuppServiceKey(number);
+ if (suppKey != null) {
+ boolean clirOverUtPrecautions = false;
+ boolean cfOverUtPrecautions = false;
+ boolean cbOverUtPrecautions = false;
+ boolean cwOverUtPrecautions = false;
+
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (cfgManager != null) {
+ clirOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALLER_ID_OVER_UT_WARNING_BOOL);
+ cfOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL);
+ cbOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALL_BARRING_OVER_UT_WARNING_BOOL);
+ cwOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+ .getBoolean(CarrierConfigManager.KEY_CALL_WAITING_OVER_UT_WARNING_BOOL);
+ }
+
+ boolean isSsOverUtPrecautions = SuppServicesUiUtil
+ .isSsOverUtPrecautions(context, phone);
+ if (isSsOverUtPrecautions) {
+ boolean showDialog = false;
+ if (suppKey == MmiCodeUtil.BUTTON_CLIR_KEY && clirOverUtPrecautions) {
+ showDialog = true;
+ } else if (suppKey == MmiCodeUtil.CALL_FORWARDING_KEY && cfOverUtPrecautions) {
+ showDialog = true;
+ } else if (suppKey == MmiCodeUtil.CALL_BARRING_KEY && cbOverUtPrecautions) {
+ showDialog = true;
+ } else if (suppKey == MmiCodeUtil.BUTTON_CW_KEY && cwOverUtPrecautions) {
+ showDialog = true;
+ }
+
+ if (showDialog) {
+ Log.d(this, "Creating UT Data enable dialog");
+ String message = SuppServicesUiUtil.makeMessage(context, suppKey, phone);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ DialogInterface.OnClickListener networkSettingsClickListener =
+ new Dialog.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ ComponentName mobileNetworkSettingsComponent
+ = new ComponentName(
+ context.getString(
+ R.string.mobile_network_settings_package),
+ context.getString(
+ R.string.mobile_network_settings_class));
+ intent.setComponent(mobileNetworkSettingsComponent);
+ context.startActivity(intent);
+ }
+ };
+ Dialog dialog = builder.setMessage(message)
+ .setNeutralButton(context.getResources().getString(
+ R.string.settings_label),
+ networkSettingsClickListener)
+ .setPositiveButton(context.getResources().getString(
+ R.string.supp_service_over_ut_precautions_dialog_dismiss), null)
+ .create();
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ ret = true;
+ }
+ }
+ }
+ return ret;
+ }
+
/**
* Adds a {@link Conference} to the telephony ConnectionService and registers a listener for
* changes to the conference. Should be used instead of {@link #addConference(Conference)}.
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
new file mode 100644
index 0000000..fcfe312
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.rcs;
+
+import android.annotation.AnyThread;
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.IFeatureConnector;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Contains the RCS feature implementations that are associated with this slot's RcsFeature.
+ */
+@AnyThread
+public class RcsFeatureController {
+ private static final String LOG_TAG = "RcsFeatureController";
+
+ /**
+ * Interface used by RCS features that need to listen for when the associated service has been
+ * connected.
+ */
+ public interface Feature {
+ /**
+ * The RcsFeature has been connected to the framework and is ready.
+ */
+ void onRcsConnected(RcsFeatureManager manager);
+
+ /**
+ * The framework has lost the binding to the RcsFeature or it is in the process of changing.
+ */
+ void onRcsDisconnected();
+
+ /**
+ * The subscription associated with the slot this controller is bound to has changed or its
+ * carrier configuration has changed.
+ */
+ void onAssociatedSubscriptionUpdated(int subId);
+
+ /**
+ * Called when the feature should be destroyed.
+ */
+ void onDestroy();
+ }
+
+ /**
+ * Used to inject FeatureConnector instances for testing.
+ */
+ @VisibleForTesting
+ public interface FeatureConnectorFactory<T extends IFeatureConnector> {
+ /**
+ * @return a {@link FeatureConnector} associated for the given {@link IFeatureConnector}
+ * and slot id.
+ */
+ FeatureConnector<T> create(Context context, int slotId,
+ FeatureConnector.Listener<T> listener, Executor executor, String tag);
+ }
+
+ /**
+ * Used to inject ImsRegistrationCallbackHelper instances for testing.
+ */
+ @VisibleForTesting
+ public interface RegistrationHelperFactory {
+ /**
+ * @return an {@link ImsRegistrationCallbackHelper}, which helps manage IMS registration
+ * state.
+ */
+ ImsRegistrationCallbackHelper create(
+ ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor);
+ }
+
+ private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory = FeatureConnector::new;
+ private RegistrationHelperFactory mRegistrationHelperFactory =
+ ImsRegistrationCallbackHelper::new;
+
+ private final Map<Class<?>, Feature> mFeatures = new ArrayMap<>();
+ private final Context mContext;
+ private final ImsRegistrationCallbackHelper mImsRcsRegistrationHelper;
+ private final int mSlotId;
+ private final Object mLock = new Object();
+ private FeatureConnector<RcsFeatureManager> mFeatureConnector;
+ private RcsFeatureManager mFeatureManager;
+
+ private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
+ new FeatureConnector.Listener<RcsFeatureManager>() {
+ @Override
+ public RcsFeatureManager getFeatureManager() {
+ return new RcsFeatureManager(mContext, mSlotId);
+ }
+
+ @Override
+ public void connectionReady(RcsFeatureManager manager)
+ throws com.android.ims.ImsException {
+ if (manager == null) {
+ logw("connectionReady returned null RcsFeatureManager");
+ return;
+ }
+ try {
+ // May throw ImsException if for some reason the connection to the
+ // ImsService is gone.
+ updateConnectionStatus(manager);
+ setupConnectionToService(manager);
+ } catch (ImsException e) {
+ updateConnectionStatus(null /*manager*/);
+ // Use deprecated Exception for compatibility.
+ throw new com.android.ims.ImsException(e.getMessage(),
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+ }
+ }
+
+ @Override
+ public void connectionUnavailable() {
+ // Call before disabling connection to manager.
+ removeConnectionToService();
+ updateConnectionStatus(null /*manager*/);
+ }
+ };
+
+ private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mRcsRegistrationUpdate = new
+ ImsRegistrationCallbackHelper.ImsRegistrationUpdate() {
+ @Override
+ public void handleImsRegistered(int imsRadioTech) {
+ }
+
+ @Override
+ public void handleImsRegistering(int imsRadioTech) {
+ }
+
+ @Override
+ public void handleImsUnregistered(ImsReasonInfo imsReasonInfo) {
+ }
+
+ @Override
+ public void handleImsSubscriberAssociatedUriChanged(Uri[] uris) {
+ }
+ };
+
+ public RcsFeatureController(Context context, int slotId) {
+ mContext = context;
+ mSlotId = slotId;
+ mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
+ mContext.getMainExecutor());
+ }
+
+ /**
+ * Should only be used to inject registration helpers for testing.
+ */
+ @VisibleForTesting
+ public RcsFeatureController(Context context, int slotId, RegistrationHelperFactory f) {
+ mContext = context;
+ mSlotId = slotId;
+ mRegistrationHelperFactory = f;
+ mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
+ mContext.getMainExecutor());
+ }
+
+ /**
+ * This method should be called after constructing an instance of this class to start the
+ * connection process to the associated RcsFeature.
+ */
+ public void connect() {
+ synchronized (mLock) {
+ if (mFeatureConnector != null) return;
+ mFeatureConnector = mFeatureFactory.create(mContext, mSlotId, mFeatureConnectorListener,
+ mContext.getMainExecutor(), LOG_TAG);
+ mFeatureConnector.connect();
+ }
+ }
+
+ /**
+ * Adds a {@link Feature} to be tracked by this FeatureController.
+ */
+ public <T extends Feature> void addFeature(T connector, Class<T> clazz) {
+ synchronized (mLock) {
+ mFeatures.put(clazz, connector);
+ }
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ connector.onRcsConnected(manager);
+ } else {
+ connector.onRcsDisconnected();
+ }
+ }
+
+ /**
+ * @return The RCS feature implementation tracked by this controller.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T getFeature(Class<T> clazz) {
+ synchronized (mLock) {
+ return (T) mFeatures.get(clazz);
+ }
+ }
+
+ /**
+ * Removes the feature associated with this class.
+ */
+ public <T> void removeFeature(Class<T> clazz) {
+ synchronized (mLock) {
+ RcsFeatureController.Feature feature = mFeatures.remove(clazz);
+ feature.onDestroy();
+ }
+ }
+
+ /**
+ * @return true if this controller has features it is actively tracking.
+ */
+ public boolean hasActiveFeatures() {
+ synchronized (mLock) {
+ return mFeatures.size() > 0;
+ }
+ }
+
+ /**
+ * Update the subscription associated with this controller.
+ */
+ public void updateAssociatedSubscription(int newSubId) {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ try {
+ manager.updateCapabilities();
+ } catch (ImsException e) {
+ Log.w(LOG_TAG, "associatedSubscriptionChanged failed:" + e);
+ }
+ }
+ synchronized (mLock) {
+ for (Feature c : mFeatures.values()) {
+ c.onAssociatedSubscriptionUpdated(newSubId);
+ }
+ }
+ }
+
+ /**
+ * Call before this controller is destroyed to tear down associated features.
+ */
+ public void destroy() {
+ synchronized (mLock) {
+ Log.i(LOG_TAG, "destroy: slotId=" + mSlotId);
+ if (mFeatureConnector != null) {
+ mFeatureConnector.disconnect();
+ }
+ for (Feature c : mFeatures.values()) {
+ c.onRcsDisconnected();
+ c.onDestroy();
+ }
+ mFeatures.clear();
+ }
+ }
+
+ @VisibleForTesting
+ public void setFeatureConnectorFactory(FeatureConnectorFactory factory) {
+ mFeatureFactory = factory;
+ }
+
+ /**
+ * Add a {@link RegistrationManager.RegistrationCallback} callback that gets called when IMS
+ * registration has changed for a specific subscription.
+ */
+ public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)
+ throws ImsException {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager == null) {
+ throw new ImsException("Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ manager.registerImsRegistrationCallback(subId, callback);
+ }
+
+ /**
+ * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback
+ * that is associated with a specific subscription.
+ */
+ public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ manager.unregisterImsRegistrationCallback(subId, callback);
+ }
+ }
+
+ /**
+ * Register an {@link ImsRcsManager.AvailabilityCallback} with the associated RcsFeature,
+ * which will provide availability updates.
+ */
+ public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
+ throws ImsException {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager == null) {
+ throw new ImsException("Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ manager.registerRcsAvailabilityCallback(subId, callback);
+ }
+
+ /**
+ * Remove a registered {@link ImsRcsManager.AvailabilityCallback} from the RcsFeature.
+ */
+ public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ manager.unregisterRcsAvailabilityCallback(subId, callback);
+ }
+ }
+
+ /**
+ * Query for the specific capability.
+ */
+ public boolean isCapable(int capability, int radioTech)
+ throws android.telephony.ims.ImsException {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager == null) {
+ throw new ImsException("Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ return manager.isCapable(capability, radioTech);
+ }
+
+ /**
+ * Query the availability of an IMS RCS capability.
+ */
+ public boolean isAvailable(int capability) throws android.telephony.ims.ImsException {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager == null) {
+ throw new ImsException("Service is not available",
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ }
+ return manager.isAvailable(capability);
+ }
+
+ /**
+ * Get the IMS RCS registration technology for this Phone.
+ */
+ public void getRegistrationTech(Consumer<Integer> callback) {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ manager.getImsRegistrationTech(callback);
+ }
+ callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+ }
+
+ /**
+ * Retrieve the current RCS registration state.
+ */
+ public void getRegistrationState(Consumer<Integer> callback) {
+ callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
+ }
+
+ private void setupConnectionToService(RcsFeatureManager manager) throws ImsException {
+ // Open persistent listener connection, sends RcsFeature#onFeatureReady.
+ manager.openConnection();
+ manager.updateCapabilities();
+ manager.registerImsRegistrationCallback(mImsRcsRegistrationHelper.getCallbackBinder());
+ }
+
+ private void removeConnectionToService() {
+ RcsFeatureManager manager = getFeatureManager();
+ if (manager != null) {
+ manager.unregisterImsRegistrationCallback(
+ mImsRcsRegistrationHelper.getCallbackBinder());
+ // Remove persistent listener connection.
+ manager.releaseConnection();
+ }
+ mImsRcsRegistrationHelper.reset();
+ }
+
+ private void updateConnectionStatus(RcsFeatureManager manager) {
+ synchronized (mLock) {
+ mFeatureManager = manager;
+ if (mFeatureManager != null) {
+ for (Feature c : mFeatures.values()) {
+ c.onRcsConnected(manager);
+ }
+ } else {
+ for (Feature c : mFeatures.values()) {
+ c.onRcsDisconnected();
+ }
+ }
+ }
+ }
+
+ private RcsFeatureManager getFeatureManager() {
+ synchronized (mLock) {
+ return mFeatureManager;
+ }
+ }
+
+ /**
+ * Dump this controller's instance information for usage in dumpsys.
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.print("slotId=");
+ pw.println(mSlotId);
+ pw.print("RegistrationState=");
+ pw.println(mImsRcsRegistrationHelper.getImsRegistrationState());
+ pw.print("connected=");
+ synchronized (mLock) {
+ pw.println(mFeatureManager != null);
+ }
+ }
+
+ private void logw(String log) {
+ Log.w(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
+ private StringBuilder getLogPrefix() {
+ StringBuilder sb = new StringBuilder("[");
+ sb.append(mSlotId);
+ sb.append("] ");
+ return sb;
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
new file mode 100644
index 0000000..69d8f82
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.rcs;
+
+import android.annotation.AnyThread;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Singleton service setup to manage RCS related services that the platform provides such as User
+ * Capability Exchange.
+ */
+@AnyThread
+public class TelephonyRcsService {
+
+ private static final String LOG_TAG = "TelephonyRcsService";
+
+ /**
+ * Used to inject RcsFeatureController and UserCapabilityExchangeImpl instances for testing.
+ */
+ @VisibleForTesting
+ public interface FeatureFactory {
+ /**
+ * @return an {@link RcsFeatureController} assoicated with the slot specified.
+ */
+ RcsFeatureController createController(Context context, int slotId);
+
+ /**
+ * @return an instance of {@link UserCapabilityExchangeImpl} associated with the slot
+ * specified.
+ */
+ UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
+ int subId);
+ }
+
+ private FeatureFactory mFeatureFactory = new FeatureFactory() {
+ @Override
+ public RcsFeatureController createController(Context context, int slotId) {
+ return new RcsFeatureController(context, slotId);
+ }
+
+ @Override
+ public UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
+ int subId) {
+ return new UserCapabilityExchangeImpl(context, slotId, subId);
+ }
+ };
+
+ // Notifies this service that there has been a change in available slots.
+ private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
+
+ private final Context mContext;
+ private final Object mLock = new Object();
+ private int mNumSlots;
+
+ // Maps slot ID -> RcsFeatureController.
+ private SparseArray<RcsFeatureController> mFeatureControllers;
+
+ private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+ Bundle bundle = intent.getExtras();
+ if (bundle == null) {
+ return;
+ }
+ int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
+ SubscriptionManager.INVALID_PHONE_INDEX);
+ int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ updateFeatureControllerSubscription(slotId, subId);
+ }
+ }
+ };
+
+ private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
+ switch (msg.what) {
+ case HANDLER_MSIM_CONFIGURATION_CHANGE: {
+ AsyncResult result = (AsyncResult) msg.obj;
+ Integer numSlots = (Integer) result.result;
+ if (numSlots == null) {
+ Log.w(LOG_TAG, "msim config change with null num slots.");
+ break;
+ }
+ updateFeatureControllerSize(numSlots);
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+ });
+
+ public TelephonyRcsService(Context context, int numSlots) {
+ mContext = context;
+ mNumSlots = numSlots;
+ mFeatureControllers = new SparseArray<>(numSlots);
+ }
+
+ /**
+ * @return the {@link RcsFeatureController} associated with the given slot.
+ */
+ public RcsFeatureController getFeatureController(int slotId) {
+ synchronized (mLock) {
+ return mFeatureControllers.get(slotId);
+ }
+ }
+
+ /**
+ * Called after instance creation to initialize internal structures as well as register for
+ * system callbacks.
+ */
+ public void initialize() {
+ updateFeatureControllerSize(mNumSlots);
+
+ PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
+ HANDLER_MSIM_CONFIGURATION_CHANGE, null);
+ mContext.registerReceiver(mCarrierConfigChangedReceiver, new IntentFilter(
+ CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+ }
+
+ @VisibleForTesting
+ public void setFeatureFactory(FeatureFactory f) {
+ mFeatureFactory = f;
+ }
+
+ /**
+ * Update the number of {@link RcsFeatureController}s that are created based on the number of
+ * active slots on the device.
+ */
+ @VisibleForTesting
+ public void updateFeatureControllerSize(int newNumSlots) {
+ synchronized (mLock) {
+ int oldNumSlots = mFeatureControllers.size();
+ if (oldNumSlots == newNumSlots) {
+ return;
+ }
+ Log.i(LOG_TAG, "updateFeatureControllers: oldSlots=" + oldNumSlots + ", newNumSlots="
+ + newNumSlots);
+ mNumSlots = newNumSlots;
+ if (oldNumSlots < newNumSlots) {
+ for (int i = oldNumSlots; i < newNumSlots; i++) {
+ RcsFeatureController c = constructFeatureController(i);
+ // Do not add feature controllers for inactive subscriptions
+ if (c.hasActiveFeatures()) {
+ mFeatureControllers.put(i, c);
+ }
+ }
+ } else {
+ for (int i = (oldNumSlots - 1); i > (newNumSlots - 1); i--) {
+ RcsFeatureController c = mFeatureControllers.get(i);
+ if (c != null) {
+ mFeatureControllers.remove(i);
+ c.destroy();
+ }
+ }
+ }
+ }
+ }
+
+ private void updateFeatureControllerSubscription(int slotId, int newSubId) {
+ synchronized (mLock) {
+ RcsFeatureController f = mFeatureControllers.get(slotId);
+ Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId + " newSubId="
+ + newSubId + ", existing feature=" + (f != null));
+ if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+ if (f == null) {
+ // A controller doesn't exist for this slot yet.
+ f = mFeatureFactory.createController(mContext, slotId);
+ updateSupportedFeatures(f, slotId, newSubId);
+ if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
+ } else {
+ updateSupportedFeatures(f, slotId, newSubId);
+ // Do not keep an empty container around.
+ if (!f.hasActiveFeatures()) {
+ f.destroy();
+ mFeatureControllers.remove(slotId);
+ }
+ }
+ }
+ if (f != null) f.updateAssociatedSubscription(newSubId);
+ }
+ }
+
+ private RcsFeatureController constructFeatureController(int slotId) {
+ RcsFeatureController c = mFeatureFactory.createController(mContext, slotId);
+ int subId = getSubscriptionFromSlot(slotId);
+ updateSupportedFeatures(c, slotId, subId);
+ return c;
+ }
+
+ private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
+ if (doesSubscriptionSupportPresence(subId)) {
+ if (c.getFeature(UserCapabilityExchangeImpl.class) == null) {
+ c.addFeature(mFeatureFactory.createUserCapabilityExchange(mContext, slotId, subId),
+ UserCapabilityExchangeImpl.class);
+ }
+ } else {
+ if (c.getFeature(UserCapabilityExchangeImpl.class) != null) {
+ c.removeFeature(UserCapabilityExchangeImpl.class);
+ }
+ }
+ // Only start the connection procedure if we have active features.
+ if (c.hasActiveFeatures()) c.connect();
+ }
+
+ private boolean doesSubscriptionSupportPresence(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
+ CarrierConfigManager carrierConfigManager =
+ mContext.getSystemService(CarrierConfigManager.class);
+ if (carrierConfigManager == null) return false;
+ boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
+ CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+ supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
+ CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
+ return supportsUce;
+ }
+
+
+ private int getSubscriptionFromSlot(int slotId) {
+ SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
+ if (manager == null) {
+ Log.w(LOG_TAG, "Couldn't find SubscriptionManager for slotId=" + slotId);
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ int[] subIds = manager.getSubscriptionIds(slotId);
+ if (subIds != null && subIds.length > 0) {
+ return subIds[0];
+ }
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ /**
+ * Dump this instance into a readable format for dumpsys usage.
+ */
+ public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ pw.println("RcsFeatureControllers:");
+ pw.increaseIndent();
+ synchronized (mLock) {
+ for (int i = 0; i < mNumSlots; i++) {
+ RcsFeatureController f = mFeatureControllers.get(i);
+ if (f == null) continue;
+ pw.increaseIndent();
+ f.dump(fd, printWriter, args);
+ pw.decreaseIndent();
+ }
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
new file mode 100644
index 0000000..9b0a303
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.rcs;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.RcsCapabilityExchange;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.util.Log;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
+import com.android.ims.ResultCode;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.R;
+import com.android.service.ims.presence.ContactCapabilityResponse;
+import com.android.service.ims.presence.PresenceBase;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresencePublisher;
+import com.android.service.ims.presence.PresenceSubscriber;
+import com.android.service.ims.presence.SubscribePublisher;
+
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * Implements User Capability Exchange using Presence.
+ */
+public class UserCapabilityExchangeImpl implements RcsFeatureController.Feature, SubscribePublisher,
+ PresencePublisher {
+
+ private static final String LOG_TAG = "RcsUceImpl";
+
+ private final int mSlotId;
+ private volatile int mSubId;
+ private volatile boolean mImsContentChangedCallbackRegistered = false;
+ // The result of requesting publish
+ private volatile int mPublishState = PresenceBase.PUBLISH_STATE_NOT_PUBLISHED;
+ // The network type which IMS registers on
+ private volatile int mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+ // The MMTel capabilities of this subscription Id
+ private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
+ private final Object mCapabilitiesLock = new Object();
+
+ private final Context mContext;
+ private final UceImplHandler mUceImplHandler;
+ private RcsFeatureManager mRcsFeatureManager;
+
+ private final PresencePublication mPresencePublication;
+ private final PresenceSubscriber mPresenceSubscriber;
+
+ // The task Ids of updating capabilities
+ private final Set<Integer> mRequestingPublishTaskIds = new HashSet<>();
+
+ // The callbacks to notify publish state changed.
+ private final RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
+
+ private final ConcurrentHashMap<Integer, IRcsUceControllerCallback> mPendingCapabilityRequests =
+ new ConcurrentHashMap<>();
+
+ UserCapabilityExchangeImpl(Context context, int slotId, int subId) {
+ mSlotId = slotId;
+ mSubId = subId;
+ logi("created");
+
+ mContext = context;
+ mPublishStateCallbacks = new RemoteCallbackList<>();
+
+ HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
+ handlerThread.start();
+ mUceImplHandler = new UceImplHandler(this, handlerThread.getLooper());
+
+ String[] volteError = context.getResources().getStringArray(
+ R.array.config_volte_provision_error_on_publish_response);
+ String[] rcsError = context.getResources().getStringArray(
+ R.array.config_rcs_provision_error_on_publish_response);
+
+ // Initialize PresencePublication
+ mPresencePublication = new PresencePublication(null /*PresencePublisher*/, context,
+ volteError, rcsError);
+ // Initialize PresenceSubscriber
+ mPresenceSubscriber = new PresenceSubscriber(null /*SubscribePublisher*/, context,
+ volteError, rcsError);
+
+ onAssociatedSubscriptionUpdated(mSubId);
+ registerReceivers();
+ }
+
+ @VisibleForTesting
+ UserCapabilityExchangeImpl(Context context, int slotId, int subId, Looper looper,
+ PresencePublication presencePublication, PresenceSubscriber presenceSubscriber,
+ RemoteCallbackList<IRcsUcePublishStateCallback> publishStateCallbacks) {
+ mSlotId = slotId;
+ mSubId = subId;
+ mContext = context;
+ mPublishStateCallbacks = publishStateCallbacks;
+ mUceImplHandler = new UceImplHandler(this, looper);
+ mPresencePublication = presencePublication;
+ mPresenceSubscriber = presenceSubscriber;
+ onAssociatedSubscriptionUpdated(mSubId);
+ registerReceivers();
+ }
+
+ // Runs on main thread.
+ @Override
+ public void onRcsConnected(RcsFeatureManager rcsFeatureManager) {
+ logi("onRcsConnected");
+ mRcsFeatureManager = rcsFeatureManager;
+ mRcsFeatureManager.addFeatureListenerCallback(mRcsFeatureCallback);
+
+ mPresencePublication.updatePresencePublisher(this);
+ mPresenceSubscriber.updatePresenceSubscriber(this);
+ }
+
+ // Runs on main thread.
+ @Override
+ public void onRcsDisconnected() {
+ logi("onRcsDisconnected");
+ mPresencePublication.removePresencePublisher();
+ mPresenceSubscriber.removePresenceSubscriber();
+
+ if (mRcsFeatureManager != null) {
+ mRcsFeatureManager.releaseConnection();
+ mRcsFeatureManager = null;
+ }
+ }
+
+ // Runs on main thread.
+ @Override
+ public void onAssociatedSubscriptionUpdated(int subId) {
+ logi("onAssociatedSubscriptionUpdated: new subId=" + subId);
+
+ // Listen to the IMS content changed with new subId.
+ mUceImplHandler.registerImsContentChangedReceiver(subId);
+
+ mSubId = subId;
+ mPresencePublication.handleAssociatedSubscriptionChanged(subId);
+ mPresenceSubscriber.handleAssociatedSubscriptionChanged(subId);
+ }
+
+ /**
+ * Should be called before destroying this instance.
+ * This instance is not usable after this method is called.
+ */
+ // Called on main thread.
+ public void onDestroy() {
+ logi("onDestroy");
+ mUceImplHandler.getLooper().quit();
+ unregisterReceivers();
+ unregisterImsProvisionCallback(mSubId);
+ onRcsDisconnected();
+ }
+
+ /**
+ * @return the UCE Publish state.
+ */
+ // May happen on a Binder thread, PresencePublication locks to get result.
+ public int getUcePublishState() {
+ int publishState = mPresencePublication.getPublishState();
+ return toUcePublishState(publishState);
+ }
+
+ @VisibleForTesting
+ public UceImplHandler getHandler() {
+ return mUceImplHandler;
+ }
+
+ /**
+ * Register receiver to receive UCE publish state changed.
+ */
+ public void registerPublishStateCallback(IRcsUcePublishStateCallback c) {
+ synchronized (mPublishStateCallbacks) {
+ mPublishStateCallbacks.register(c);
+ }
+ }
+
+ /**
+ * Unregister UCE publish state callback.
+ */
+ public void unregisterUcePublishStateCallback(IRcsUcePublishStateCallback c) {
+ synchronized (mPublishStateCallbacks) {
+ mPublishStateCallbacks.unregister(c);
+ }
+ }
+
+ private void clearPublishStateCallbacks() {
+ synchronized (mPublishStateCallbacks) {
+ logi("clearPublishStateCallbacks");
+ final int lastIndex = mPublishStateCallbacks.getRegisteredCallbackCount() - 1;
+ for (int index = lastIndex; index >= 0; index--) {
+ IRcsUcePublishStateCallback callback =
+ mPublishStateCallbacks.getRegisteredCallbackItem(index);
+ mPublishStateCallbacks.unregister(callback);
+ }
+ }
+ }
+
+ private void notifyPublishStateChanged(@PresenceBase.PresencePublishState int state) {
+ int result = toUcePublishState(state);
+ mPublishStateCallbacks.broadcast(c -> {
+ try {
+ c.onPublishStateChanged(result);
+ } catch (RemoteException e) {
+ logw("notifyPublishStateChanged error: " + e);
+ }
+ });
+ }
+
+ /**
+ * Perform a capabilities request and call {@link IRcsUceControllerCallback} with the result.
+ */
+ // May happen on a Binder thread, PresenceSubscriber locks when requesting Capabilities.
+ public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c) {
+ List<String> numbers = contactNumbers.stream()
+ .map(UserCapabilityExchangeImpl::getNumberFromUri).collect(Collectors.toList());
+ int taskId = mPresenceSubscriber.requestCapability(numbers,
+ new ContactCapabilityResponse() {
+ @Override
+ public void onSuccess(int reqId) {
+ logi("onSuccess called for reqId:" + reqId);
+ }
+
+ @Override
+ public void onError(int reqId, int resultCode) {
+ IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
+ try {
+ if (c != null) {
+ c.onError(toUceError(resultCode));
+ } else {
+ logw("onError called for unknown reqId:" + reqId);
+ }
+ } catch (RemoteException e) {
+ logi("Calling back to dead service");
+ }
+ }
+
+ @Override
+ public void onFinish(int reqId) {
+ logi("onFinish called for reqId:" + reqId);
+ }
+
+ @Override
+ public void onTimeout(int reqId) {
+ IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
+ try {
+ if (c != null) {
+ c.onError(RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
+ } else {
+ logw("onTimeout called for unknown reqId:" + reqId);
+ }
+ } catch (RemoteException e) {
+ logi("Calling back to dead service");
+ }
+ }
+
+ @Override
+ public void onCapabilitiesUpdated(int reqId,
+ List<RcsContactUceCapability> contactCapabilities,
+ boolean updateLastTimestamp) {
+ IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
+ try {
+ if (c != null) {
+ c.onCapabilitiesReceived(contactCapabilities);
+ } else {
+ logw("onCapabilitiesUpdated, unknown reqId:" + reqId);
+ }
+ } catch (RemoteException e) {
+ logw("onCapabilitiesUpdated on dead service");
+ }
+ }
+ });
+ if (taskId < 0) {
+ try {
+ c.onError(toUceError(taskId));
+ return;
+ } catch (RemoteException e) {
+ logi("Calling back to dead service");
+ }
+ }
+ mPendingCapabilityRequests.put(taskId, c);
+ }
+
+ /**
+ * The feature callback is to receive the request and update from RcsPresExchangeImplBase
+ */
+ @VisibleForTesting
+ public RcsFeatureCallbacks mRcsFeatureCallback = new RcsFeatureCallbacks() {
+ public void onCommandUpdate(int commandCode, int operationToken) {
+ logi("onCommandUpdate: code=" + commandCode + ", token=" + operationToken);
+ onCommandUpdateForPublishRequest(commandCode, operationToken);
+ }
+
+ /** See {@link RcsPresenceExchangeImplBase#onNetworkResponse(int, String, int)} */
+ public void onNetworkResponse(int responseCode, String reason, int operationToken) {
+ logi("onNetworkResponse: code=" + responseCode + ", reason=" + reason
+ + ", operationToken=" + operationToken);
+ onNetworkResponseForPublishRequest(responseCode, reason, operationToken);
+ }
+
+ /** See {@link RcsPresenceExchangeImplBase#onCapabilityRequestResponse(List, int)} */
+ public void onCapabilityRequestResponsePresence(List<RcsContactUceCapability> infos,
+ int operationToken) {
+
+ }
+
+ /** See {@link RcsPresenceExchangeImplBase#onNotifyUpdateCapabilites(int)} */
+ public void onNotifyUpdateCapabilities(int publishTriggerType) {
+ logi("onNotifyUpdateCapabilities: type=" + publishTriggerType);
+ mUceImplHandler.notifyUpdateCapabilities(publishTriggerType);
+ }
+
+ /** See {@link RcsPresenceExchangeImplBase#onUnpublish()} */
+ public void onUnpublish() {
+ logi("onUnpublish");
+ mUceImplHandler.unpublish();
+ }
+ };
+
+ private static class UceImplHandler extends Handler {
+ private static final int EVENT_REGISTER_IMS_CHANGED_RECEIVER = 1;
+ private static final int EVENT_NOTIFY_UPDATE_CAPABILITIES = 2;
+ private static final int EVENT_UNPUBLISH = 3;
+
+ private static final int REGISTER_IMS_CHANGED_DELAY = 10000; //10 seconds
+
+ private final WeakReference<UserCapabilityExchangeImpl> mUceImplRef;
+
+ UceImplHandler(UserCapabilityExchangeImpl uceImpl, Looper looper) {
+ super(looper);
+ mUceImplRef = new WeakReference(uceImpl);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ UserCapabilityExchangeImpl uceImpl = mUceImplRef.get();
+ if (uceImpl == null) {
+ return;
+ }
+ switch (msg.what) {
+ case EVENT_REGISTER_IMS_CHANGED_RECEIVER:
+ int subId = msg.arg1;
+ uceImpl.registerImsContentChangedReceiverInternal(subId);
+ break;
+ case EVENT_NOTIFY_UPDATE_CAPABILITIES:
+ int publishTriggerType = msg.arg1;
+ uceImpl.onNotifyUpdateCapabilities(publishTriggerType);
+ break;
+ case EVENT_UNPUBLISH:
+ uceImpl.updatePublisherState(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED);
+ break;
+ default:
+ Log.w(LOG_TAG, "handleMessage: error=" + msg.what);
+ break;
+ }
+ }
+
+ private void retryRegisteringImsContentChangedReceiver(int subId) {
+ sendRegisteringImsContentChangedMessage(subId, REGISTER_IMS_CHANGED_DELAY);
+ }
+
+ private void registerImsContentChangedReceiver(int subId) {
+ sendRegisteringImsContentChangedMessage(subId, 0);
+ }
+
+ private void sendRegisteringImsContentChangedMessage(int subId, int delay) {
+ if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+ removeRegisteringImsContentChangedReceiver();
+ Message message = obtainMessage(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
+ message.arg1 = subId;
+ sendMessageDelayed(message, delay);
+ }
+
+ private void removeRegisteringImsContentChangedReceiver() {
+ removeMessages(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
+ }
+
+ private void notifyUpdateCapabilities(int publishTriggerType) {
+ Message message = obtainMessage(EVENT_NOTIFY_UPDATE_CAPABILITIES);
+ message.arg1 = publishTriggerType;
+ sendMessage(message);
+ }
+
+ private void unpublish() {
+ sendEmptyMessage(EVENT_UNPUBLISH);
+ }
+ }
+
+ private void onNotifyUpdateCapabilities(int publishTriggerType) {
+ mPresencePublication.onStackPublishRequested(publishTriggerType);
+ }
+
+ @Override
+ public @PresenceBase.PresencePublishState int getPublisherState() {
+ return mPublishState;
+ }
+
+ @Override
+ public int requestPublication(RcsContactUceCapability capabilities, String contactUri,
+ int taskId) {
+ if (mRcsFeatureManager == null) {
+ logw("requestPublication error: RcsFeatureManager is null.");
+ return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+ }
+
+ logi("requestPublication: taskId=" + taskId);
+ addPublishRequestTaskId(taskId);
+
+ try {
+ mRcsFeatureManager.requestPublication(capabilities, taskId);
+ } catch (Exception ex) {
+ logw("requestPublication error: " + ex.getMessage());
+ removePublishRequestTaskId(taskId);
+ return ResultCode.PUBLISH_GENERIC_FAILURE;
+ }
+ return ResultCode.SUCCESS;
+ }
+
+ /*
+ * Handle the callback method RcsFeatureCallbacks#onCommandUpdate(int, int)
+ */
+ private void onCommandUpdateForPublishRequest(int commandCode, int operationToken) {
+ if (!isPublishRequestExisted(operationToken)) {
+ return;
+ }
+ int resultCode = ResultCode.SUCCESS;
+ if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
+ logw("Command is failed: taskId=" + operationToken + ", code=" + commandCode);
+ removePublishRequestTaskId(operationToken);
+ resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
+ }
+ mPresencePublication.onCommandStatusUpdated(operationToken, operationToken, resultCode);
+ }
+
+ /*
+ * Handle the callback method RcsFeatureCallbacks#onNetworkResponse(int, String, int)
+ */
+ private void onNetworkResponseForPublishRequest(int responseCode, String reason,
+ int operationToken) {
+ if (!isPublishRequestExisted(operationToken)) {
+ return;
+ }
+ removePublishRequestTaskId(operationToken);
+ mPresencePublication.onSipResponse(operationToken, responseCode, reason);
+ }
+
+ @Override
+ public int requestCapability(String[] formatedContacts, int taskId) {
+ return 0;
+ }
+
+ @Override
+ public int requestAvailability(String formattedContact, int taskId) {
+ return 0;
+ }
+
+ @Override
+ public int getStackStatusForCapabilityRequest() {
+ return 0;
+ }
+
+ @Override
+ public void updatePublisherState(@PresenceBase.PresencePublishState int publishState) {
+ logi("updatePublisherState: from " + mPublishState + " to " + publishState);
+ mPublishState = publishState;
+ notifyPublishStateChanged(publishState);
+ }
+
+ private void addPublishRequestTaskId(int taskId) {
+ synchronized (mRequestingPublishTaskIds) {
+ mRequestingPublishTaskIds.add(taskId);
+ }
+ }
+
+ private void removePublishRequestTaskId(int taskId) {
+ synchronized (mRequestingPublishTaskIds) {
+ mRequestingPublishTaskIds.remove(taskId);
+ }
+ }
+
+ private boolean isPublishRequestExisted(Integer taskId) {
+ synchronized (mRequestingPublishTaskIds) {
+ return mRequestingPublishTaskIds.contains(taskId);
+ }
+ }
+
+ private static String getNumberFromUri(Uri uri) {
+ String number = uri.getSchemeSpecificPart();
+ String[] numberParts = number.split("[@;:]");
+
+ if (numberParts.length == 0) {
+ return null;
+ }
+ return numberParts[0];
+ }
+
+ private static int toUcePublishState(int publishState) {
+ switch (publishState) {
+ case PresenceBase.PUBLISH_STATE_200_OK:
+ return RcsUceAdapter.PUBLISH_STATE_OK;
+ case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
+ return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED;
+ case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
+ return RcsUceAdapter.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
+ case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
+ return RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
+ case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
+ return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
+ case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
+ return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+ default:
+ return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+ }
+ }
+
+ private static int toUceError(int resultCode) {
+ switch (resultCode) {
+ case ResultCode.SUBSCRIBE_NOT_REGISTERED:
+ return RcsUceAdapter.ERROR_NOT_REGISTERED;
+ case ResultCode.SUBSCRIBE_REQUEST_TIMEOUT:
+ return RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
+ case ResultCode.SUBSCRIBE_FORBIDDEN:
+ return RcsUceAdapter.ERROR_FORBIDDEN;
+ case ResultCode.SUBSCRIBE_NOT_FOUND:
+ return RcsUceAdapter.ERROR_NOT_FOUND;
+ case ResultCode.SUBSCRIBE_TOO_LARGE:
+ return RcsUceAdapter.ERROR_REQUEST_TOO_LARGE;
+ case ResultCode.SUBSCRIBE_INSUFFICIENT_MEMORY:
+ return RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
+ case ResultCode.SUBSCRIBE_LOST_NETWORK:
+ return RcsUceAdapter.ERROR_LOST_NETWORK;
+ case ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE:
+ return RcsUceAdapter.ERROR_ALREADY_IN_QUEUE;
+ default:
+ return RcsUceAdapter.ERROR_GENERIC_FAILURE;
+ }
+ }
+
+ /*
+ * Register receivers for updating capabilities
+ */
+ private void registerReceivers() {
+ IntentFilter filter = new IntentFilter(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
+ filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+
+ ContentResolver resolver = mContext.getContentResolver();
+ if (resolver != null) {
+ // Register mobile data content changed.
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.MOBILE_DATA), false,
+ mMobileDataObserver);
+
+ // Register SIM info content changed.
+ resolver.registerContentObserver(Telephony.SimInfo.CONTENT_URI, false,
+ mSimInfoContentObserver);
+ }
+ }
+
+ private void unregisterReceivers() {
+ mContext.unregisterReceiver(mReceiver);
+ ContentResolver resolver = mContext.getContentResolver();
+ if (resolver != null) {
+ resolver.unregisterContentObserver(mMobileDataObserver);
+ resolver.unregisterContentObserver(mSimInfoContentObserver);
+ }
+ }
+
+ /**
+ * Register IMS and provision content changed.
+ *
+ * Call the UceImplHandler#registerImsContentChangedReceiver instead of
+ * calling this method directly.
+ */
+ private void registerImsContentChangedReceiverInternal(int subId) {
+ mUceImplHandler.removeRegisteringImsContentChangedReceiver();
+ try {
+ final int originalSubId = mSubId;
+ if ((originalSubId == subId) && (mImsContentChangedCallbackRegistered)) {
+ logi("registerImsContentChangedReceiverInternal: already registered. skip");
+ return;
+ }
+ // Unregister original IMS and Provision callback
+ unregisterImsProvisionCallback(originalSubId);
+ // Register new IMS and Provision callback
+ registerImsProvisionCallback(subId);
+ } catch (ImsException e) {
+ logw("registerImsContentChangedReceiverInternal error: " + e);
+ mUceImplHandler.retryRegisteringImsContentChangedReceiver(subId);
+ }
+ }
+
+ private void unregisterImsProvisionCallback(int subId) {
+ if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+ // Unregister IMS callback
+ ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
+ if (imsMmtelManager != null) {
+ try {
+ imsMmtelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+ imsMmtelManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
+ } catch (RuntimeException e) {
+ logw("unregister IMS callback error: " + e.getMessage());
+ }
+ }
+
+ // Unregister provision changed callback
+ ProvisioningManager provisioningManager =
+ ProvisioningManager.createForSubscriptionId(subId);
+ try {
+ provisioningManager.unregisterProvisioningChangedCallback(mProvisioningChangedCallback);
+ } catch (RuntimeException e) {
+ logw("unregister provisioning callback error: " + e.getMessage());
+ }
+
+ // Remove all publish state callbacks
+ clearPublishStateCallbacks();
+
+ mImsContentChangedCallbackRegistered = false;
+ }
+
+ private void registerImsProvisionCallback(int subId) throws ImsException {
+ if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+ // Register IMS callback
+ ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
+ if (imsMmtelManager != null) {
+ imsMmtelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
+ mImsRegistrationCallback);
+ imsMmtelManager.registerMmTelCapabilityCallback(mContext.getMainExecutor(),
+ mCapabilityCallback);
+ }
+ // Register provision changed callback
+ ProvisioningManager provisioningManager =
+ ProvisioningManager.createForSubscriptionId(subId);
+ provisioningManager.registerProvisioningChangedCallback(mContext.getMainExecutor(),
+ mProvisioningChangedCallback);
+
+ mImsContentChangedCallbackRegistered = true;
+ logi("registerImsProvisionCallback");
+ }
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) return;
+ switch (intent.getAction()) {
+ case TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED:
+ int preferredMode = intent.getIntExtra(
+ TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
+ logi("TTY preferred mode changed: " + preferredMode);
+ mPresencePublication.onTtyPreferredModeChanged(preferredMode);
+ break;
+
+ case Intent.ACTION_AIRPLANE_MODE_CHANGED:
+ boolean airplaneMode = intent.getBooleanExtra("state", false);
+ logi("Airplane mode changed: " + airplaneMode);
+ mPresencePublication.onAirplaneModeChanged(airplaneMode);
+ break;
+ }
+ }
+ };
+
+ private ContentObserver mMobileDataObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ boolean isEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.MOBILE_DATA, 1) == 1;
+ logi("Mobile data changed: enabled=" + isEnabled);
+ mPresencePublication.onMobileDataChanged(isEnabled);
+ }
+ };
+
+ private ContentObserver mSimInfoContentObserver = new ContentObserver(
+ new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ if (mSubId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+
+ ImsMmTelManager ims = getImsMmTelManager(mSubId);
+ if (ims == null) return;
+
+ try {
+ boolean isEnabled = ims.isVtSettingEnabled();
+ logi("SimInfo changed: VT setting=" + isEnabled);
+ mPresencePublication.onVtEnabled(isEnabled);
+ } catch (RuntimeException e) {
+ logw("SimInfo changed error: " + e);
+ }
+ }
+ };
+
+ private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(int imsTransportType) {
+ logi("onRegistered: type=" + imsTransportType);
+ mNetworkRegistrationType = imsTransportType;
+ mPresencePublication.onImsConnected();
+
+ // Also trigger PresencePublication#onFeatureCapabilityChanged method
+ MmTelFeature.MmTelCapabilities capabilities = null;
+ synchronized (mCapabilitiesLock) {
+ capabilities = mMmTelCapabilities;
+ }
+ mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo info) {
+ logi("onUnregistered");
+ mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+
+ // Also trigger PresencePublication#onFeatureCapabilityChanged method
+ MmTelFeature.MmTelCapabilities capabilities = null;
+ synchronized (mCapabilitiesLock) {
+ capabilities = mMmTelCapabilities;
+ }
+ mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
+ mPresencePublication.onImsDisconnected();
+ }
+ };
+
+ private ImsMmTelManager.CapabilityCallback mCapabilityCallback =
+ new ImsMmTelManager.CapabilityCallback() {
+ @Override
+ public void onCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities capabilities) {
+ synchronized (mCapabilitiesLock) {
+ mMmTelCapabilities = capabilities;
+ }
+ mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
+ }
+ };
+
+ private ProvisioningManager.Callback mProvisioningChangedCallback =
+ new ProvisioningManager.Callback() {
+ @Override
+ public void onProvisioningIntChanged(int item, int value) {
+ logi("onProvisioningIntChanged: item=" + item);
+ switch (item) {
+ case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
+ case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
+ case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
+ mPresencePublication.handleProvisioningChanged();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ private ImsMmTelManager getImsMmTelManager(int subId) {
+ try {
+ ImsManager imsManager = (ImsManager) mContext.getSystemService(
+ Context.TELEPHONY_IMS_SERVICE);
+ return (imsManager == null) ? null : imsManager.getImsMmTelManager(subId);
+ } catch (IllegalArgumentException e) {
+ logw("getImsMmTelManager error: " + e.getMessage());
+ return null;
+ }
+ }
+
+ private void logi(String log) {
+ Log.i(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
+ private void logw(String log) {
+ Log.w(LOG_TAG, getLogPrefix().append(log).toString());
+ }
+
+ private StringBuilder getLogPrefix() {
+ StringBuilder builder = new StringBuilder("[");
+ builder.append(mSlotId);
+ builder.append("->");
+ builder.append(mSubId);
+ builder.append("] ");
+ return builder;
+ }
+}
diff --git a/testapps/Android.mk b/testapps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/testapps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/testapps/EmbmsServiceTestApp/Android.bp b/testapps/EmbmsServiceTestApp/Android.bp
new file mode 100644
index 0000000..e4a54cb
--- /dev/null
+++ b/testapps/EmbmsServiceTestApp/Android.bp
@@ -0,0 +1,11 @@
+// Build the Sample Embms Services
+android_app {
+ name: "EmbmsTestService",
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ privileged: true,
+ // Uncomment the following line to build the EmbmsTestService
+ // into the userdebug build:
+ // LOCAL_MODULE_TAGS := debug
+}
diff --git a/testapps/EmbmsServiceTestApp/Android.mk b/testapps/EmbmsServiceTestApp/Android.mk
deleted file mode 100644
index 29b8112..0000000
--- a/testapps/EmbmsServiceTestApp/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Sample Embms Services
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := EmbmsTestService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-# Uncomment the following line to build the EmbmsTestService into the userdebug build.
-# LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/EmbmsTestDownloadApp/Android.bp b/testapps/EmbmsTestDownloadApp/Android.bp
new file mode 100644
index 0000000..63f4e83
--- /dev/null
+++ b/testapps/EmbmsTestDownloadApp/Android.bp
@@ -0,0 +1,12 @@
+src_dirs = ["src"]
+res_dirs = ["res"]
+android_test {
+ name: "EmbmsTestDownloadApp",
+ static_libs: [
+ "androidx.recyclerview_recyclerview",
+ "androidx.legacy_legacy-support-v4",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/testapps/EmbmsTestDownloadApp/Android.mk b/testapps/EmbmsTestDownloadApp/Android.mk
deleted file mode 100644
index bd53d79..0000000
--- a/testapps/EmbmsTestDownloadApp/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Sample Embms Download frontend
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.recyclerview_recyclerview \
- androidx.legacy_legacy-support-v4
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := EmbmsTestDownloadApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/EmbmsTestStreamingApp/Android.bp b/testapps/EmbmsTestStreamingApp/Android.bp
new file mode 100644
index 0000000..814c5ca
--- /dev/null
+++ b/testapps/EmbmsTestStreamingApp/Android.bp
@@ -0,0 +1,7 @@
+android_test {
+ name: "EmbmsTestStreamingApp",
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ //LOCAL_MODULE_TAGS := debug
+}
diff --git a/testapps/EmbmsTestStreamingApp/Android.mk b/testapps/EmbmsTestStreamingApp/Android.mk
deleted file mode 100644
index f574990..0000000
--- a/testapps/EmbmsTestStreamingApp/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Sample Embms Streaming frontend
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := EmbmsTestStreamingApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-#LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/ImsTestService/Android.bp b/testapps/ImsTestService/Android.bp
new file mode 100644
index 0000000..a0b4edb
--- /dev/null
+++ b/testapps/ImsTestService/Android.bp
@@ -0,0 +1,13 @@
+android_app {
+ name: "ImsTestApp",
+ static_libs: [
+ "androidx.legacy_legacy-support-v4",
+ "androidx.appcompat_appcompat",
+ "androidx.recyclerview_recyclerview",
+ "androidx.cardview_cardview",
+ ],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ privileged: true,
+}
diff --git a/testapps/ImsTestService/Android.mk b/testapps/ImsTestService/Android.mk
deleted file mode 100644
index 2869c86..0000000
--- a/testapps/ImsTestService/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.legacy_legacy-support-v4 \
- androidx.appcompat_appcompat \
- androidx.recyclerview_recyclerview \
- androidx.cardview_cardview
-
-LOCAL_USE_AAPT2 := true
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := ImsTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/SmsManagerTestApp/Android.bp b/testapps/SmsManagerTestApp/Android.bp
new file mode 100644
index 0000000..5333eab
--- /dev/null
+++ b/testapps/SmsManagerTestApp/Android.bp
@@ -0,0 +1,5 @@
+android_app {
+ name: "SmsManagerTestApp",
+ srcs: ["src/**/*.java"],
+ sdk_version: "current",
+}
diff --git a/testapps/SmsManagerTestApp/Android.mk b/testapps/SmsManagerTestApp/Android.mk
deleted file mode 100644
index 307366b..0000000
--- a/testapps/SmsManagerTestApp/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := SmsManagerTestApp
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyManagerTestApp/Android.bp b/testapps/TelephonyManagerTestApp/Android.bp
new file mode 100644
index 0000000..8a37c99
--- /dev/null
+++ b/testapps/TelephonyManagerTestApp/Android.bp
@@ -0,0 +1,7 @@
+android_test {
+ name: "TelephonyManagerTestApp",
+ srcs: ["src/**/*.java"],
+ javacflags: ["-parameters"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/testapps/TelephonyManagerTestApp/Android.mk b/testapps/TelephonyManagerTestApp/Android.mk
deleted file mode 100644
index 290b261..0000000
--- a/testapps/TelephonyManagerTestApp/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_JAVACFLAGS := -parameters
-
-LOCAL_PACKAGE_NAME := TelephonyManagerTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
index 097c90a..ccb5639 100644
--- a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
@@ -19,12 +19,19 @@
import android.content.Context;
import android.telephony.NumberVerificationCallback;
import android.telephony.PhoneNumberRange;
+import android.telephony.RadioAccessSpecifier;
+import android.text.TextUtils;
import android.widget.Toast;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.stream.Collectors;
class ParameterParser {
private static ParameterParser sInstance;
@@ -42,6 +49,9 @@
put(PhoneNumberRange.class, ParameterParser::parsePhoneNumberRange);
put(Executor.class, s -> parseExecutor(s));
put(NumberVerificationCallback.class, s -> parseNumberVerificationCallback(s));
+ put(Consumer.class, s -> parseConsumer(s));
+ put(List.class, s -> parseList(s));
+ put(RadioAccessSpecifier.class, s -> parseRadioAccessSpecifier(s));
}};
private ParameterParser(Context context) {
@@ -49,6 +59,7 @@
}
Object executeParser(Class type, String input) {
+ if ("null".equals(input)) return null;
return mParsers.getOrDefault(type, s -> null).apply(input);
}
@@ -64,6 +75,57 @@
return mContext.getMainExecutor();
}
+ private Consumer parseConsumer(String input) {
+ return o -> Toast.makeText(mContext, "Consumer: " + String.valueOf(o), Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ /**
+ * input format: (ran)/(band1)+(band2)+.../(chan1)+(chan2)+...
+ * @return
+ */
+ private RadioAccessSpecifier parseRadioAccessSpecifier(String input) {
+ String[] parts = input.split("/");
+ int ran = Integer.parseInt(parts[0]);
+ String[] bandStrings = parts[1].split("\\+");
+ int[] bands = new int[bandStrings.length];
+ String[] chanStrings = parts[2].split("\\+");
+ int[] chans = new int[chanStrings.length];
+
+ for (int i = 0; i < bands.length; i++) {
+ bands[i] = Integer.parseInt(bandStrings[i]);
+ }
+
+ for (int i = 0; i < chans.length; i++) {
+ chans[i] = Integer.parseInt(chanStrings[i]);
+ }
+ return new RadioAccessSpecifier(ran, bands, chans);
+ }
+
+ private List parseList(String input) {
+ if (TextUtils.isEmpty(input)) {
+ return Collections.emptyList();
+ }
+ String[] components = input.split(" ");
+ String className = components[0];
+ Class c;
+ try {
+ c = mContext.getClassLoader().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ Toast.makeText(mContext, "Invalid class " + className,
+ Toast.LENGTH_SHORT).show();
+ return null;
+ }
+ if (!mParsers.containsKey(c)) {
+ Toast.makeText(mContext, "Cannot parse " + className,
+ Toast.LENGTH_SHORT).show();
+ return null;
+ }
+ return Arrays.stream(components).skip(1)
+ .map(mParsers.get(c))
+ .collect(Collectors.toList());
+ }
+
private NumberVerificationCallback parseNumberVerificationCallback(String input) {
return new NumberVerificationCallback() {
@Override
diff --git a/testapps/TelephonyRegistryTestApp/Android.bp b/testapps/TelephonyRegistryTestApp/Android.bp
new file mode 100644
index 0000000..fec5286
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/Android.bp
@@ -0,0 +1,7 @@
+android_test {
+ name: "TelephonyRegistryTestApp",
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+ //LOCAL_MODULE_TAGS := debug
+}
diff --git a/testapps/TelephonyRegistryTestApp/Android.mk b/testapps/TelephonyRegistryTestApp/Android.mk
deleted file mode 100644
index 8c0d286..0000000
--- a/testapps/TelephonyRegistryTestApp/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := TelephonyRegistryTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-#LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_PACKAGE)
diff --git a/tests/Android.bp b/tests/Android.bp
index 22b40b5..7ed234e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -25,6 +25,7 @@
"telephony-common",
"android.test.base",
"ims-common",
+ "android.test.mock",
],
platform_apis: true,
certificate: "platform",
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index 01267d8..86c5402 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -16,6 +16,8 @@
package com.android;
+import static org.mockito.Mockito.spy;
+
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
@@ -34,7 +36,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mContext = new TestContext();
+ mContext = spy(new TestContext());
// Set up the looper if it does not exist on the test thread.
if (Looper.myLooper() == null) {
Looper.prepare();
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 776ec6a..b1c6923 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -20,6 +20,7 @@
import static org.mockito.Mockito.doReturn;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -29,17 +30,21 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsManager;
import android.test.mock.MockContext;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.Executor;
+
public class TestContext extends MockContext {
@Mock CarrierConfigManager mMockCarrierConfigManager;
@Mock TelecomManager mMockTelecomManager;
@Mock TelephonyManager mMockTelephonyManager;
@Mock SubscriptionManager mMockSubscriptionManager;
+ @Mock ImsManager mMockImsManager;
private PersistableBundle mCarrierConfig = new PersistableBundle();
@@ -49,6 +54,12 @@
}
@Override
+ public Executor getMainExecutor() {
+ // Just run on current thread
+ return Runnable::run;
+ }
+
+ @Override
public Context getApplicationContext() {
return this;
}
@@ -81,6 +92,11 @@
}
@Override
+ public ContentResolver getContentResolver() {
+ return null;
+ }
+
+ @Override
public Object getSystemService(String name) {
switch (name) {
case (Context.CARRIER_CONFIG_SERVICE) : {
@@ -95,6 +111,9 @@
case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
return mMockSubscriptionManager;
}
+ case(Context.TELEPHONY_IMS_SERVICE) : {
+ return mMockImsManager;
+ }
}
return null;
}
diff --git a/tests/src/com/android/phone/NumberVerificationManagerTest.java b/tests/src/com/android/phone/NumberVerificationManagerTest.java
index d476ba5..f7914ab 100644
--- a/tests/src/com/android/phone/NumberVerificationManagerTest.java
+++ b/tests/src/com/android/phone/NumberVerificationManagerTest.java
@@ -57,7 +57,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
ServiceState ss = mock(ServiceState.class);
- when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ when(ss.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
when(mPhone1.getServiceState()).thenReturn(ss);
when(mPhone1.getForegroundCall()).thenReturn(mForegroundCall);
when(mPhone1.getRingingCall()).thenReturn(mRingingCall);
@@ -107,7 +107,7 @@
@Test
public void testNoPhoneInServiceFailure() throws Exception {
ServiceState ss = mock(ServiceState.class);
- when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
+ when(ss.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
when(mPhone1.getServiceState()).thenReturn(ss);
when(mPhone2.getServiceState()).thenReturn(ss);
NumberVerificationManager manager =
@@ -138,7 +138,7 @@
@Test
public void testVerificationWorksWithOnePhoneInService() throws Exception {
ServiceState ss = mock(ServiceState.class);
- when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
+ when(ss.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
when(mPhone1.getServiceState()).thenReturn(ss);
NumberVerificationManager manager =
new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
diff --git a/tests/src/com/android/phone/PhoneGlobalsTest.java b/tests/src/com/android/phone/PhoneGlobalsTest.java
new file mode 100644
index 0000000..d45a890
--- /dev/null
+++ b/tests/src/com/android/phone/PhoneGlobalsTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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 org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+
+@RunWith(AndroidJUnit4.class)
+public class PhoneGlobalsTest extends TelephonyTestBase {
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testGetImsResources() throws Exception {
+ // Do not use test context here, we are testing that overlaying for different locales works
+ // correctly
+ Context realContext = InstrumentationRegistry.getTargetContext();
+ String defaultImsMmtelPackage = getResourcesForLocale(realContext, Locale.US).getString(
+ R.string.config_ims_mmtel_package);
+ String defaultImsMmtelPackageUk = getResourcesForLocale(realContext, Locale.UK).getString(
+ R.string.config_ims_mmtel_package);
+ assertEquals("locales changed IMS package configuration!", defaultImsMmtelPackage,
+ defaultImsMmtelPackageUk);
+ }
+
+ private Resources getResourcesForLocale(Context context, Locale locale) {
+ Configuration config = new Configuration();
+ config.setToDefaults();
+ config.setLocale(locale);
+ Context localeContext = context.createConfigurationContext(config);
+ return localeContext.getResources();
+ }
+}
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
new file mode 100644
index 0000000..574c0c9
--- /dev/null
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2019 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.provider.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for simple queries of ServiceStateProvider.
+ *
+ * Build, install and run the tests by running the commands below:
+ * runtest --path <dir or file>
+ * runtest --path <dir or file> --test-method <testMethodName>
+ * e.g.)
+ * runtest --path tests/src/com/android/phone/ServiceStateProviderTest.java \
+ * --test-method testGetServiceState
+ */
+public class ServiceStateProviderTest {
+ private static final String TAG = "ServiceStateProviderTest";
+
+ private Context mContext;
+ private MockContentResolver mContentResolver;
+ private ServiceState mTestServiceState;
+ private ServiceState mTestServiceStateForSubId1;
+
+ private final String[] mTestProjection =
+ {
+ ServiceStateTable.VOICE_REG_STATE,
+ ServiceStateTable.DATA_REG_STATE,
+ ServiceStateTable.VOICE_OPERATOR_ALPHA_LONG,
+ ServiceStateTable.VOICE_OPERATOR_ALPHA_SHORT,
+ ServiceStateTable.VOICE_OPERATOR_NUMERIC,
+ ServiceStateTable.DATA_OPERATOR_ALPHA_LONG,
+ ServiceStateTable.DATA_OPERATOR_ALPHA_SHORT,
+ ServiceStateTable.DATA_OPERATOR_NUMERIC,
+ ServiceStateTable.IS_MANUAL_NETWORK_SELECTION,
+ ServiceStateTable.RIL_VOICE_RADIO_TECHNOLOGY,
+ ServiceStateTable.RIL_DATA_RADIO_TECHNOLOGY,
+ ServiceStateTable.CSS_INDICATOR,
+ ServiceStateTable.NETWORK_ID,
+ ServiceStateTable.SYSTEM_ID,
+ ServiceStateTable.CDMA_ROAMING_INDICATOR,
+ ServiceStateTable.CDMA_DEFAULT_ROAMING_INDICATOR,
+ ServiceStateTable.CDMA_ERI_ICON_INDEX,
+ ServiceStateTable.CDMA_ERI_ICON_MODE,
+ ServiceStateTable.IS_EMERGENCY_ONLY,
+ ServiceStateTable.IS_USING_CARRIER_AGGREGATION,
+ ServiceStateTable.OPERATOR_ALPHA_LONG_RAW,
+ ServiceStateTable.OPERATOR_ALPHA_SHORT_RAW,
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = mock(Context.class);
+ mContentResolver = new MockContentResolver() {
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ throw new RuntimeException("notifyChange!");
+ }
+ };
+ doReturn(mContentResolver).when(mContext).getContentResolver();
+
+ mTestServiceState = new ServiceState();
+ mTestServiceState.setStateOutOfService();
+ mTestServiceStateForSubId1 = new ServiceState();
+ mTestServiceStateForSubId1.setStateOff();
+
+ // Mock out the actual phone state
+ ServiceStateProvider provider = new ServiceStateProvider() {
+ @Override
+ public ServiceState getServiceState(int subId) {
+ if (subId == 1) {
+ return mTestServiceStateForSubId1;
+ } else {
+ return mTestServiceState;
+ }
+ }
+
+ @Override
+ public int getDefaultSubId() {
+ return 0;
+ }
+ };
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = "service-state";
+ provider.attachInfoForTesting(mContext, providerInfo);
+ mContentResolver.addProvider("service-state", provider);
+ }
+
+ @Test
+ @SmallTest
+ public void testQueryServiceStateWithNoSubId() {
+ // Verify that when calling query with no subId in the uri the default ServiceState is
+ // returned.
+ // In this case the subId is set to 0 and the expected service state is
+ // mTestServiceState.
+ verifyServiceStateForSubId(ServiceStateTable.CONTENT_URI, mTestServiceState);
+ }
+
+ @Test
+ @SmallTest
+ public void testGetServiceStateWithDefaultSubId() {
+ // Verify that when calling with the DEFAULT_SUBSCRIPTION_ID the correct ServiceState is
+ // returned
+ // In this case the subId is set to 0 and the expected service state is
+ // mTestServiceState.
+ verifyServiceStateForSubId(
+ getUriForSubscriptionId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+ mTestServiceState);
+ }
+
+ /**
+ * Test querying the service state for a given subId
+ */
+ @Test
+ @SmallTest
+ public void testGetServiceStateForSubId() {
+ // Verify that when calling with a specific subId the correct ServiceState is returned
+ // In this case the subId is set to 1 and the expected service state is
+ // mTestServiceStateForSubId1
+ verifyServiceStateForSubId(getUriForSubscriptionId(1), mTestServiceStateForSubId1);
+ }
+
+ private void verifyServiceStateForSubId(Uri uri, ServiceState ss) {
+ Cursor cursor = mContentResolver.query(uri, mTestProjection, "",
+ null, null);
+ assertNotNull(cursor);
+ cursor.moveToFirst();
+
+ final int voiceRegState = ss.getState();
+ final int dataRegState = ss.getDataRegistrationState();
+ final String voiceOperatorAlphaLong = ss.getOperatorAlphaLong();
+ final String voiceOperatorAlphaShort = ss.getOperatorAlphaShort();
+ final String voiceOperatorNumeric = ss.getOperatorNumeric();
+ final String dataOperatorAlphaLong = ss.getOperatorAlphaLong();
+ final String dataOperatorAlphaShort = ss.getOperatorAlphaShort();
+ final String dataOperatorNumeric = ss.getOperatorNumeric();
+ final int isManualNetworkSelection = (ss.getIsManualSelection()) ? 1 : 0;
+ final int rilVoiceRadioTechnology = ss.getRilVoiceRadioTechnology();
+ final int rilDataRadioTechnology = ss.getRilDataRadioTechnology();
+ final int cssIndicator = ss.getCssIndicator();
+ final int networkId = ss.getCdmaNetworkId();
+ final int systemId = ss.getCdmaSystemId();
+ final int cdmaRoamingIndicator = ss.getCdmaRoamingIndicator();
+ final int cdmaDefaultRoamingIndicator = ss.getCdmaDefaultRoamingIndicator();
+ final int cdmaEriIconIndex = ss.getCdmaEriIconIndex();
+ final int cdmaEriIconMode = ss.getCdmaEriIconMode();
+ final int isEmergencyOnly = (ss.isEmergencyOnly()) ? 1 : 0;
+ final int isUsingCarrierAggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
+ final String operatorAlphaLongRaw = ss.getOperatorAlphaLongRaw();
+ final String operatorAlphaShortRaw = ss.getOperatorAlphaShortRaw();
+
+ assertEquals(voiceRegState, cursor.getInt(0));
+ assertEquals(dataRegState, cursor.getInt(1));
+ assertEquals(voiceOperatorAlphaLong, cursor.getString(2));
+ assertEquals(voiceOperatorAlphaShort, cursor.getString(3));
+ assertEquals(voiceOperatorNumeric, cursor.getString(4));
+ assertEquals(dataOperatorAlphaLong, cursor.getString(5));
+ assertEquals(dataOperatorAlphaShort, cursor.getString(6));
+ assertEquals(dataOperatorNumeric, cursor.getString(7));
+ assertEquals(isManualNetworkSelection, cursor.getInt(8));
+ assertEquals(rilVoiceRadioTechnology, cursor.getInt(9));
+ assertEquals(rilDataRadioTechnology, cursor.getInt(10));
+ assertEquals(cssIndicator, cursor.getInt(11));
+ assertEquals(networkId, cursor.getInt(12));
+ assertEquals(systemId, cursor.getInt(13));
+ assertEquals(cdmaRoamingIndicator, cursor.getInt(14));
+ assertEquals(cdmaDefaultRoamingIndicator, cursor.getInt(15));
+ assertEquals(cdmaEriIconIndex, cursor.getInt(16));
+ assertEquals(cdmaEriIconMode, cursor.getInt(17));
+ assertEquals(isEmergencyOnly, cursor.getInt(18));
+ assertEquals(isUsingCarrierAggregation, cursor.getInt(19));
+ assertEquals(operatorAlphaLongRaw, cursor.getString(20));
+ assertEquals(operatorAlphaShortRaw, cursor.getString(21));
+ }
+
+ /**
+ * Test that we don't notify for certain field changes. (e.g. we don't notify when the NetworkId
+ * or SystemId change) This is an intentional behavior change from the broadcast.
+ */
+ @Test
+ @SmallTest
+ public void testNoNotify() {
+ int subId = 0;
+
+ ServiceState oldSS = new ServiceState();
+ oldSS.setStateOutOfService();
+ oldSS.setCdmaSystemAndNetworkId(1, 1);
+
+ ServiceState newSS = new ServiceState();
+ newSS.setStateOutOfService();
+ newSS.setCdmaSystemAndNetworkId(0, 0);
+
+ // Test that notifyChange is not called for these fields
+ boolean notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertFalse(notifyChangeWasCalled);
+ }
+
+ @Test
+ @SmallTest
+ public void testNotifyChanged() {
+ int subId = 0;
+
+ ServiceState oldSS = new ServiceState();
+ oldSS.setStateOutOfService();
+ oldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
+ ServiceState copyOfOldSS = new ServiceState();
+ copyOfOldSS.setStateOutOfService();
+ copyOfOldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
+ ServiceState newSS = new ServiceState();
+ newSS.setStateOutOfService();
+ newSS.setVoiceRegState(ServiceState.STATE_POWER_OFF);
+
+ // Test that notifyChange is not called with no change in notifyChangeForSubIdAndField
+ boolean notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, copyOfOldSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertFalse(notifyChangeWasCalled);
+
+ // Test that notifyChange is not called with no change in notifyChangeForSubId
+ notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, copyOfOldSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertFalse(notifyChangeWasCalled);
+
+ // Test that notifyChange is called by notifyChangeForSubIdAndField when the voice_reg_state
+ // changes
+ notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertTrue(notifyChangeWasCalled);
+
+ // Test that notifyChange is called by notifyChangeForSubId when the voice_reg_state changes
+ notifyChangeWasCalled = false;
+ try {
+ ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, newSS, subId);
+ } catch (RuntimeException e) {
+ final String message = e.getMessage();
+ if (message != null && message.equals("notifyChange!")) {
+ notifyChangeWasCalled = true;
+ }
+ }
+ assertTrue(notifyChangeWasCalled);
+ }
+}
diff --git a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
new file mode 100644
index 0000000..7f9efdc
--- /dev/null
+++ b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 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.services.telephony;
+
+import static android.media.ToneGenerator.TONE_PROP_PROMPT;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+
+import android.telephony.DisconnectCause;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DisconnectCauseUtilTest {
+ /**
+ * Verifies that a call drop due to loss of WIFI results in a disconnect cause of error and that
+ * the label, description and tone are all present.
+ */
+ @Test
+ public void testDropDueToWifiLoss() {
+ android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause(
+ DisconnectCause.WIFI_LOST);
+ assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode());
+ assertEquals(TONE_PROP_PROMPT, tcCause.getTone());
+ assertNotNull(tcCause.getDescription());
+ assertNotNull(tcCause.getReason());
+ }
+}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 7251402..b84aca1 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -43,6 +43,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.Collections;
public class ImsConferenceTest {
@Mock
@@ -73,14 +74,15 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
"A",
Uri.parse("sip:6505551212@testims.com"),
Connection.STATE_ACTIVE,
- Call.Details.DIRECTION_INCOMING);
+ Call.Details.DIRECTION_OUTGOING);
ConferenceParticipant participant2 = new ConferenceParticipant(
Uri.parse("tel:6505551213"),
"A",
@@ -98,6 +100,8 @@
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
Arrays.asList(participant1));
assertEquals(0, imsConference.getNumberOfParticipants());
+ // Ensure the call direction is set correctly during emulation
+ assertEquals(Call.Details.DIRECTION_OUTGOING, imsConference.getCallDirection());
reset(mMockTelephonyConnectionServiceProxy);
// Back to 2!
@@ -120,7 +124,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
// Start off with 3 participants.
ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -183,7 +188,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
// Start off with 3 participants.
ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -240,7 +246,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
// Setup the initial conference state with 2 participants.
ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -298,7 +305,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
final boolean[] isConferenceState = new boolean[1];
TelephonyConferenceBase.TelephonyConferenceListener conferenceListener =
@@ -354,7 +362,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -379,7 +388,8 @@
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -410,13 +420,14 @@
@Test
@SmallTest
- public void testNormalConference() {
+ public void testNormalConference() throws Exception {
when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
.thenReturn(false);
ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
mMockTelephonyConnectionServiceProxy, mConferenceHost,
- null /* phoneAccountHandle */, () -> false /* featureFlagProxy */);
+ null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder().build());
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -438,5 +449,45 @@
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
Arrays.asList(participant1));
assertEquals(1, imsConference.getNumberOfParticipants());
+
+ // Drop to 0 participants; should not hangup the conf now
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall, never()).hangup();
+ }
+
+ @Test
+ @SmallTest
+ public void testLocalDisconnectOnEmptyConference() throws Exception {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+ new ImsConference.CarrierConfiguration.Builder()
+ .setShouldLocalDisconnectEmptyConference(true)
+ .build());
+
+ ConferenceParticipant participant1 = new ConferenceParticipant(
+ Uri.parse("tel:6505551212"),
+ "A",
+ Uri.parse("sip:6505551212@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ ConferenceParticipant participant2 = new ConferenceParticipant(
+ Uri.parse("tel:6505551213"),
+ "A",
+ Uri.parse("sip:6505551213@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+
+ // Drop to 0 participants; should have a hangup request.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ verify(mConferenceHost.mMockCall).hangup();
}
}
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index afdfab5..d55a2fa 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -69,9 +69,11 @@
@Override
@After
public void tearDown() throws Exception {
+ mListener.setTimeBetweenRetriesMillis(5000);
+ mListener.setMaxNumRetries(5);
+ mListener.getHandler().removeCallbacksAndMessages(null);
// Wait for the queue to clear...
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /*ms timeout*/);
- mListener.getHandler().removeCallbacksAndMessages(null);
mListener = null;
super.tearDown();
}
@@ -83,7 +85,7 @@
@SmallTest
public void testRegisterForCallback() {
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
@@ -105,10 +107,11 @@
public void testPhoneChangeState_OkToCallTrue() {
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(true);
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -132,7 +135,7 @@
when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
when(mMockPhone.getServiceState()).thenReturn(state);
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -161,10 +164,32 @@
// Wait for the timer to expire and check state manually in onRetryTimeout
mMockPhone.mCi = mMockCi;
- mListener.waitForRadioOn(mMockPhone, mCallback);
+ mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
verify(mCallback).onComplete(eq(mListener), eq(false));
- verify(mMockPhone, times(2)).setRadioPower(eq(true));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true),
+ eq(false), eq(false), eq(false));
+ }
+
+ @Test
+ @SmallTest
+ public void testTimeout_RetryFailure_ForEmergency() {
+ ServiceState state = new ServiceState();
+ state.setState(ServiceState.STATE_POWER_OFF);
+ when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+ when(mMockPhone.getServiceState()).thenReturn(state);
+ when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
+ mListener.setTimeBetweenRetriesMillis(0/*ms*/);
+ mListener.setMaxNumRetries(2);
+
+ // Wait for the timer to expire and check state manually in onRetryTimeout
+ mMockPhone.mCi = mMockCi;
+ mListener.waitForRadioOn(mMockPhone, mCallback, true, true);
+ waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
+
+ verify(mCallback).onComplete(eq(mListener), eq(false));
+ verify(mMockPhone, times(2)).setRadioPower(eq(true),
+ eq(true), eq(true), eq(false));
}
}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 6e11e51..2060e6f 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,7 +16,10 @@
package com.android.services.telephony;
+import static com.android.internal.telephony.RILConstants.GSM_PHONE;
+
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -26,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -58,6 +62,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
@@ -93,6 +98,7 @@
private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
+ private android.telecom.Connection mConnection;
@Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
@Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
@@ -106,6 +112,8 @@
@Mock Handler mMockHandler;
@Mock EmergencyNumberTracker mEmergencyNumberTracker;
@Mock PhoneSwitcher mPhoneSwitcher;
+ @Mock RadioOnHelper mRadioOnHelper;
+ @Mock ServiceStateTracker mSST;
private static class TestTelephonyConnectionService extends TelephonyConnectionService {
@@ -135,6 +143,8 @@
doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+ when(mPhoneNumberUtilsProxy.convertToEmergencyNumber(any(), anyString()))
+ .thenAnswer(invocation -> invocation.getArgument(1));
mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
HandlerThread mockHandlerThread = mock(HandlerThread.class);
@@ -143,6 +153,7 @@
doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
mTestConnectionService.setHandlerFactory(mHandlerFactory);
mTestConnectionService.setDeviceState(mDeviceState);
+ mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
.toTelecomDisconnectCause(anyInt(), any());
doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
@@ -941,6 +952,64 @@
}
/**
+ * Test that the TelephonyConnectionService successfully turns radio on before placing the
+ * emergency call.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmerge_exitingApm_disconnected() {
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+ Phone testPhone = setupConnectionServiceInApm();
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ eq(testPhone));
+
+ assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ when(mSST.isRadioOn()).thenReturn(true);
+ assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+ mConnection.setDisconnected(null);
+ callback.getValue().onComplete(null, true);
+ for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+ verify(phone).setRadioPower(true, false, false, true);
+ }
+ }
+
+ /**
+ * Test that the TelephonyConnectionService successfully turns radio on before placing the
+ * emergency call.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_exitingApm_placeCall() {
+ when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+ Phone testPhone = setupConnectionServiceInApm();
+
+ ArgumentCaptor<RadioOnStateListener.Callback> callback =
+ ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+ verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+ eq(testPhone));
+
+ assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+ when(mSST.isRadioOn()).thenReturn(true);
+ assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+ callback.getValue().onComplete(null, true);
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ try {
+ doAnswer(invocation -> null).when(mContext).startActivity(any());
+ delayDialRunnable.run();
+ verify(testPhone).dial(anyString(), any());
+ } catch (CallStateException e) {
+ // This shouldn't happen
+ fail();
+ }
+ }
+
+ /**
* Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
* supports control-plane fallback.
*/
@@ -1115,9 +1184,49 @@
doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
- android.telecom.Connection testConnection = mTestConnectionService
- .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
- assertNotNull("test connection was not set up correctly.", testConnection);
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(
+ PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+ assertNotNull("test connection was not set up correctly.", mConnection);
+
+ return testPhone0;
+ }
+
+
+ /**
+ * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number in airplane mode.
+ * @return the Phone associated with slot 0.
+ */
+ private Phone setupConnectionServiceInApm() {
+ ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+ .setAddress(TEST_ADDRESS)
+ .build();
+ Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+ false /*isEmergencyOnly*/);
+ Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+ false /*isEmergencyOnly*/);
+ doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
+ doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
+ List<Phone> phones = new ArrayList<>(2);
+ doReturn(false).when(testPhone0).isRadioOn();
+ doReturn(false).when(testPhone1).isRadioOn();
+ phones.add(testPhone0);
+ phones.add(testPhone1);
+ setPhones(phones);
+ setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+ setupDeviceConfig(testPhone0, testPhone1, 0);
+ doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
+ TEST_ADDRESS.getSchemeSpecificPart());
+ HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
+ List<EmergencyNumber> numbers = new ArrayList<>();
+ numbers.add(setupEmergencyNumber(TEST_ADDRESS));
+ emergencyNumbers.put(0 /*subId*/, numbers);
+ doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
+ doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+ mConnection = mTestConnectionService.onCreateOutgoingConnection(
+ PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+ assertNotNull("test connection was not set up correctly.", mConnection);
return testPhone0;
}
@@ -1163,6 +1272,7 @@
when(phone.getPhoneId()).thenReturn(phoneId);
when(phone.getDefaultPhone()).thenReturn(phone);
when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
+ when(phone.getServiceStateTracker()).thenReturn(mSST);
when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
return phone;
}
@@ -1193,6 +1303,7 @@
private void setPhones(List<Phone> phones) {
when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
+ when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phones.get(0));
}
private void setPhonesDialConnection(Phone phone, Connection c) {
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 5b31c0f..c36c405 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.when;
import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -72,8 +73,13 @@
return mMockRadioConnection;
}
+ @Override
+ protected Call getCall() {
+ return mMockCall;
+ }
+
public TestTelephonyConnection() {
- super(null, null, false);
+ super(null, null, android.telecom.Call.Details.DIRECTION_INCOMING);
MockitoAnnotations.initMocks(this);
mMockPhone = mock(Phone.class);
@@ -100,6 +106,11 @@
when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
when(mMockCall.getPhone()).thenReturn(mMockPhone);
+ try {
+ doNothing().when(mMockCall).hangup();
+ } catch (CallStateException e) {
+ e.printStackTrace();
+ }
}
@Override
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
new file mode 100644
index 0000000..fbb270d
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.ims.FeatureConnector;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsFeatureControllerTest extends TelephonyTestBase {
+
+ private static final ImsReasonInfo REASON_DISCONNECTED = new ImsReasonInfo(
+ ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 0, "test");
+
+ @Mock RcsFeatureManager mFeatureManager;
+ @Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
+ @Mock ImsRegistrationCallbackHelper.ImsRegistrationUpdate mRegistrationCallback;
+ @Mock FeatureConnector<RcsFeatureManager> mFeatureConnector;
+ @Mock RcsFeatureController.Feature mMockFeature;
+ @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mConnectorListener;
+
+ private RcsFeatureController.RegistrationHelperFactory mRegistrationFactory =
+ new RcsFeatureController.RegistrationHelperFactory() {
+ @Override
+ public ImsRegistrationCallbackHelper create(
+ ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor) {
+ // Run on current thread for testing.
+ return new ImsRegistrationCallbackHelper(mRegistrationCallback, Runnable::run);
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testRcsFeatureManagerConnectDisconnect() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+ verify(mMockFeature).onRcsDisconnected();
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+
+ verify(mFeatureManager).updateCapabilities();
+ verify(mFeatureManager).registerImsRegistrationCallback(any());
+ verify(mMockFeature).onRcsConnected(mFeatureManager);
+
+ // Disconnect
+ mConnectorListener.getValue().connectionUnavailable();
+
+ verify(mFeatureManager).unregisterImsRegistrationCallback(any());
+ verify(mMockFeature, times(2)).onRcsDisconnected();
+ }
+
+ @Test
+ public void testFeatureManagerConnectedAddRemoveFeature() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+ controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+ verify(mMockFeature).onRcsConnected(mFeatureManager);
+ assertEquals(mMockFeature, controller.getFeature(RcsFeatureController.Feature.class));
+
+ controller.removeFeature(RcsFeatureController.Feature.class);
+ verify(mMockFeature).onDestroy();
+ assertNull(controller.getFeature(RcsFeatureController.Feature.class));
+ }
+
+ @Test
+ public void testFeatureManagerConnectedRegister() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
+ IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+
+ try {
+ controller.registerImsRegistrationCallback(0 /*subId*/, regCb);
+ controller.registerRcsAvailabilityCallback(0 /*subId*/, capCb);
+ controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+ controller.getRegistrationTech(integer -> {
+ });
+ verify(mFeatureManager).registerImsRegistrationCallback(0, regCb);
+ verify(mFeatureManager).registerRcsAvailabilityCallback(0, capCb);
+ verify(mFeatureManager).isCapable(
+ RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ verify(mFeatureManager).isAvailable(
+ RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+ verify(mFeatureManager).getImsRegistrationTech(any());
+ } catch (ImsException e) {
+ fail("ImsException not expected.");
+ }
+
+ controller.unregisterImsRegistrationCallback(0, regCb);
+ controller.unregisterRcsAvailabilityCallback(0, capCb);
+ verify(mFeatureManager).unregisterImsRegistrationCallback(0, regCb);
+ verify(mFeatureManager).unregisterRcsAvailabilityCallback(0, capCb);
+ }
+
+ @Test
+ public void testFeatureManagerConnectedHelper() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+ ArgumentCaptor<IImsRegistrationCallback> captor =
+ ArgumentCaptor.forClass(IImsRegistrationCallback.class);
+ verify(mFeatureManager).registerImsRegistrationCallback(captor.capture());
+ assertNotNull(captor.getValue());
+
+ captor.getValue().onDeregistered(REASON_DISCONNECTED);
+ controller.getRegistrationState(result -> {
+ assertNotNull(result);
+ assertEquals(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED, result.intValue());
+ });
+ verify(mRegistrationCallback).handleImsUnregistered(REASON_DISCONNECTED);
+
+ captor.getValue().onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ controller.getRegistrationState(result -> {
+ assertNotNull(result);
+ assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERING, result.intValue());
+ });
+ verify(mRegistrationCallback).handleImsRegistering(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+ captor.getValue().onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ controller.getRegistrationState(result -> {
+ assertNotNull(result);
+ assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERED, result.intValue());
+ });
+ verify(mRegistrationCallback).handleImsRegistered(
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ }
+
+ @Test
+ public void testFeatureManagerDisconnectedAddFeature() {
+ RcsFeatureController controller = createFeatureController();
+ // Disconnect the RcsFeatureManager
+ mConnectorListener.getValue().connectionUnavailable();
+ controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+ verify(mMockFeature).onRcsDisconnected();
+ }
+
+ @Test
+ public void testFeatureManagerDisconnectedException() {
+ RcsFeatureController controller = createFeatureController();
+ IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
+ IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
+ // Disconnect the RcsFeatureManager
+ mConnectorListener.getValue().connectionUnavailable();
+
+ try {
+ controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);
+ fail("ImsException expected for IMS registration.");
+ } catch (ImsException e) {
+ //expected
+ }
+ try {
+ controller.registerRcsAvailabilityCallback(0 /*subId*/, null /*callback*/);
+ fail("ImsException expected for availability");
+ } catch (ImsException e) {
+ //expected
+ }
+ try {
+ controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+ fail("ImsException expected for capability check");
+ } catch (ImsException e) {
+ //expected
+ }
+ try {
+ controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+ fail("ImsException expected for availability check");
+ } catch (ImsException e) {
+ //expected
+ }
+ controller.getRegistrationTech(integer -> {
+ assertNotNull(integer);
+ assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, integer.intValue());
+ });
+ controller.unregisterImsRegistrationCallback(0, regCb);
+ controller.unregisterRcsAvailabilityCallback(0, capCb);
+ verify(mFeatureManager, never()).unregisterImsRegistrationCallback(0, regCb);
+ verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(0, capCb);
+ }
+
+ @Test
+ public void testChangeSubId() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+ verify(mFeatureManager).updateCapabilities();
+ controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+ controller.updateAssociatedSubscription(1 /*new sub id*/);
+
+ verify(mFeatureManager, times(2)).updateCapabilities();
+ verify(mMockFeature).onAssociatedSubscriptionUpdated(1 /*new sub id*/);
+ }
+
+ @Test
+ public void testDestroy() throws Exception {
+ RcsFeatureController controller = createFeatureController();
+ // Connect the RcsFeatureManager
+ mConnectorListener.getValue().connectionReady(mFeatureManager);
+ controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+ controller.destroy();
+
+ verify(mFeatureConnector).disconnect();
+ verify(mMockFeature).onRcsDisconnected();
+ verify(mMockFeature).onDestroy();
+ assertNull(controller.getFeature(RcsFeatureController.Feature.class));
+ }
+
+ private RcsFeatureController createFeatureController() {
+ RcsFeatureController controller = new RcsFeatureController(mContext, 0 /*slotId*/,
+ mRegistrationFactory);
+ controller.setFeatureConnectorFactory(mFeatureFactory);
+ doReturn(mFeatureConnector).when(mFeatureFactory).create(any(), anyInt(),
+ mConnectorListener.capture(), any(), any());
+ controller.connect();
+ assertNotNull(mConnectorListener.getValue());
+ return controller;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
new file mode 100644
index 0000000..cfb68b7
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.rcs;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.ims.FeatureConnector;
+import com.android.ims.RcsFeatureManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class TelephonyRcsServiceTest extends TelephonyTestBase {
+
+ @Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+ @Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
+ @Mock UserCapabilityExchangeImpl mMockUceSlot0;
+ @Mock UserCapabilityExchangeImpl mMockUceSlot1;
+ @Mock RcsFeatureController.RegistrationHelperFactory mRegistrationFactory;
+ @Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureConnectorFactory;
+ @Mock FeatureConnector<RcsFeatureManager> mFeatureConnector;
+
+ private RcsFeatureController mFeatureControllerSlot0;
+ private RcsFeatureController mFeatureControllerSlot1;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ doReturn(mFeatureConnector).when(mFeatureConnectorFactory).create(any(), anyInt(),
+ any(), any(), any());
+ mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/);
+ mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/);
+ doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0));
+ doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1));
+ doReturn(mMockUceSlot0).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(0),
+ anyInt());
+ doReturn(mMockUceSlot1).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(1),
+ anyInt());
+ //set up default slot-> sub ID mappings.
+ setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
+ setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testUserCapabilityExchangePresenceConnected() {
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).connect();
+ }
+
+ @Test
+ public void testUserCapabilityExchangeOptionsConnected() {
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, true /*isEnabled*/);
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).connect();
+ }
+
+ @Test
+ public void testNoFeaturesEnabled() {
+ createRcsService(1 /*numSlots*/);
+ // No carrier config set for UCE.
+ verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0, never()).connect();
+ }
+
+ @Test
+ public void testNoFeaturesEnabledCarrierConfigChanged() {
+ createRcsService(1 /*numSlots*/);
+ // No carrier config set for UCE.
+
+ sendCarrierConfigChanged(0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0, never()).connect();
+ verify(mFeatureControllerSlot0, never()).updateAssociatedSubscription(anyInt());
+ }
+
+
+ @Test
+ public void testSlotUpdates() {
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ TelephonyRcsService service = createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).connect();
+
+ // there should be no changes if the new num slots = old num
+ service.updateFeatureControllerSize(1 /*newNumSlots*/);
+ verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0, times(1)).connect();
+
+ // Add a new slot.
+ verify(mFeatureControllerSlot1, never()).addFeature(mMockUceSlot1,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot1, never()).connect();
+ service.updateFeatureControllerSize(2 /*newNumSlots*/);
+ // This shouldn't have changed for slot 0.
+ verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0, times(1)).connect();
+ verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot1, times(1)).connect();
+
+ // Remove a slot.
+ verify(mFeatureControllerSlot0, never()).destroy();
+ verify(mFeatureControllerSlot1, never()).destroy();
+ service.updateFeatureControllerSize(1 /*newNumSlots*/);
+ // addFeature/connect shouldn't have been called again
+ verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0, times(1)).connect();
+ verify(mFeatureControllerSlot1, times(1)).addFeature(mMockUceSlot1,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot1, times(1)).connect();
+ // Verify destroy is only called for slot 1.
+ verify(mFeatureControllerSlot0, never()).destroy();
+ verify(mFeatureControllerSlot1, times(1)).destroy();
+ }
+
+ @Test
+ public void testCarrierConfigUpdate() {
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ createRcsService(2 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).connect();
+ verify(mFeatureControllerSlot1).connect();
+
+
+ // Send carrier config update for each slot.
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ verify(mFeatureControllerSlot1, never()).updateAssociatedSubscription(1);
+ sendCarrierConfigChanged(1 /*slotId*/, 2 /*subId*/);
+ verify(mFeatureControllerSlot0, never()).updateAssociatedSubscription(2);
+ verify(mFeatureControllerSlot1, times(1)).updateAssociatedSubscription(2);
+ }
+
+ @Test
+ public void testCarrierConfigUpdateUceToNoUce() {
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).connect();
+
+
+ // Send carrier config update for each slot.
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*isEnabled*/);
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0).removeFeature(UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ }
+
+ @Test
+ public void testCarrierConfigUpdateNoUceToUce() {
+ createRcsService(1 /*numSlots*/);
+ verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
+ UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0, never()).connect();
+
+
+ // Send carrier config update for each slot.
+ setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+ sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+ verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+ verify(mFeatureControllerSlot0).connect();
+ verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+ }
+
+ private void sendCarrierConfigChanged(int slotId, int subId) {
+ Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, slotId);
+ intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+ mReceiverCaptor.getValue().onReceive(mContext, intent);
+ }
+
+ private void setCarrierConfig(String key, boolean value) {
+ PersistableBundle bundle = mContext.getCarrierConfig();
+ bundle.putBoolean(key, value);
+ }
+
+ private void setSlotToSubIdMapping(int slotId, int loadedSubId) {
+ SubscriptionManager m = mContext.getSystemService(SubscriptionManager.class);
+ int [] subIds = new int[1];
+ subIds[0] = loadedSubId;
+ doReturn(subIds).when(m).getSubscriptionIds(eq(slotId));
+ }
+
+ private TelephonyRcsService createRcsService(int numSlots) {
+ TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots);
+ service.setFeatureFactory(mFeatureFactory);
+ service.initialize();
+ verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
+ return service;
+ }
+
+ private RcsFeatureController createFeatureController(int slotId) {
+ // Create a spy instead of a mock because TelephonyRcsService relies on state provided by
+ // RcsFeatureController.
+ RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId,
+ mRegistrationFactory));
+ controller.setFeatureConnectorFactory(mFeatureConnectorFactory);
+ return controller;
+ }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java b/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
new file mode 100644
index 0000000..2457d28
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2020 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.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.RemoteCallbackList;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.stub.RcsCapabilityExchange;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
+import com.android.ims.ResultCode;
+import com.android.service.ims.presence.PresenceBase;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresencePublisher;
+import com.android.service.ims.presence.PresenceSubscriber;
+import com.android.service.ims.presence.SubscribePublisher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class UserCapabilityExchangeImplTest extends TelephonyTestBase {
+
+ private int mSlotId = 0;
+ private int mSubId = 1;
+ private int mUpdatedSubId = 2;
+
+ @Captor ArgumentCaptor<IRcsUcePublishStateCallback> mPublishStateCallbacksCaptor;
+
+ @Mock PresencePublication mPresencePublication;
+ @Mock PresenceSubscriber mPresenceSubscriber;
+ @Mock RcsFeatureManager mRcsFeatureManager;
+ @Mock ImsMmTelManager mImsMmTelManager;
+ @Mock RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
+
+ private Looper mLooper;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ ImsManager imsManager =
+ (ImsManager) mContext.getSystemService(Context.TELEPHONY_IMS_SERVICE);
+ when(imsManager.getImsMmTelManager(mSubId)).thenReturn(mImsMmTelManager);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+
+ if (mLooper != null) {
+ mLooper.quit();
+ mLooper = null;
+ }
+ }
+
+ @Test
+ public void testServiceConnected() throws Exception {
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsConnected(mRcsFeatureManager);
+
+ verify(mRcsFeatureManager).addFeatureListenerCallback(any(RcsFeatureCallbacks.class));
+ verify(mPresencePublication).updatePresencePublisher(any(PresencePublisher.class));
+ verify(mPresenceSubscriber).updatePresenceSubscriber(any(SubscribePublisher.class));
+ }
+
+ @Test
+ public void testServiceDisconnected() throws Exception {
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsDisconnected();
+
+ verify(mPresencePublication).removePresencePublisher();
+ verify(mPresenceSubscriber).removePresenceSubscriber();
+ }
+
+ @Test
+ public void testSubscriptionUpdated() throws Exception {
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onAssociatedSubscriptionUpdated(mUpdatedSubId);
+
+ verify(mImsMmTelManager).registerImsRegistrationCallback(any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager).registerMmTelCapabilityCallback(any(Executor.class),
+ any(ImsMmTelManager.CapabilityCallback.class));
+ verify(mPresencePublication).handleAssociatedSubscriptionChanged(mUpdatedSubId);
+ verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(mUpdatedSubId);
+ }
+
+ @Test
+ public void testUcePublishStateRetrieval() throws Exception {
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.getUcePublishState();
+
+ verify(mPresencePublication).getPublishState();
+ }
+
+ @Test
+ public void testRegisterPublishStateCallbacks() throws Exception {
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.registerPublishStateCallback(any(IRcsUcePublishStateCallback.class));
+ verify(mPublishStateCallbacks).register(mPublishStateCallbacksCaptor.capture());
+ }
+
+ @Test
+ public void testOnNotifyUpdateCapabilities() throws Exception {
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsConnected(mRcsFeatureManager);
+
+ int triggerType = RcsPresenceExchangeImplBase.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN;
+ uceImpl.mRcsFeatureCallback.onNotifyUpdateCapabilities(triggerType);
+ waitForMs(1000);
+
+ verify(mPresencePublication).onStackPublishRequested(triggerType);
+ }
+
+ @Test
+ public void testRequestPublicationWithSuccessfulResponse() throws Exception {
+ int taskId = 1;
+ int sipResponse = 200;
+ Uri contact = Uri.fromParts("sip", "test", null);
+ RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(contact);
+ RcsContactUceCapability capability = builder.build();
+
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsConnected(mRcsFeatureManager);
+
+ doAnswer(invocation -> {
+ uceImpl.mRcsFeatureCallback.onCommandUpdate(RcsCapabilityExchange.COMMAND_CODE_SUCCESS,
+ taskId);
+ uceImpl.mRcsFeatureCallback.onNetworkResponse(sipResponse, null, taskId);
+ return null;
+ }).when(mRcsFeatureManager).requestPublication(capability, taskId);
+
+ // Request publication
+ int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
+
+ assertEquals(ResultCode.SUCCESS, result);
+ verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId, ResultCode.SUCCESS);
+ verify(mPresencePublication).onSipResponse(taskId, sipResponse, null);
+ }
+
+ @Test
+ public void testRequestPublicationWithFailedResponse() throws Exception {
+ int taskId = 1;
+ Uri contact = Uri.fromParts("sip", "test", null);
+ RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(contact);
+ RcsContactUceCapability capability = builder.build();
+
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsConnected(mRcsFeatureManager);
+
+ doAnswer(invocation -> {
+ uceImpl.mRcsFeatureCallback.onCommandUpdate(
+ RcsCapabilityExchange.COMMAND_CODE_GENERIC_FAILURE, taskId);
+ return null;
+ }).when(mRcsFeatureManager).requestPublication(capability, taskId);
+
+ // Request publication
+ int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
+
+ assertEquals(ResultCode.SUCCESS, result);
+ verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId,
+ ResultCode.PUBLISH_GENERIC_FAILURE);
+ }
+
+ @Test
+ public void testUpdatePublisherState() throws Exception {
+ IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+ doAnswer(invocation -> {
+ callback.onPublishStateChanged(anyInt());
+ return null;
+ }).when(mPublishStateCallbacks).broadcast(any());
+
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsConnected(mRcsFeatureManager);
+ uceImpl.registerPublishStateCallback(callback);
+ uceImpl.updatePublisherState(PresenceBase.PUBLISH_STATE_200_OK);
+
+ assertEquals(PresenceBase.PUBLISH_STATE_200_OK, uceImpl.getPublisherState());
+ verify(callback).onPublishStateChanged(anyInt());
+ }
+
+ @Test
+ public void testUnpublish() throws Exception {
+ IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+ doAnswer(invocation -> {
+ callback.onPublishStateChanged(anyInt());
+ return null;
+ }).when(mPublishStateCallbacks).broadcast(any());
+
+ UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+ uceImpl.onRcsConnected(mRcsFeatureManager);
+ uceImpl.mRcsFeatureCallback.onUnpublish();
+ waitForMs(1000);
+
+ assertEquals(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED, uceImpl.getPublisherState());
+ verify(callback).onPublishStateChanged(anyInt());
+ }
+
+ private UserCapabilityExchangeImpl createUserCapabilityExchangeImpl() throws Exception {
+ HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
+ handlerThread.start();
+ mLooper = handlerThread.getLooper();
+ UserCapabilityExchangeImpl uceImpl = new UserCapabilityExchangeImpl(mContext, mSlotId,
+ mSubId, mLooper, mPresencePublication, mPresenceSubscriber,
+ mPublishStateCallbacks);
+ verify(mPresencePublication).handleAssociatedSubscriptionChanged(1);
+ verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(1);
+ waitForHandlerAction(uceImpl.getHandler(), 1000);
+ verify(mImsMmTelManager, atLeast(1)).registerImsRegistrationCallback(
+ any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+ verify(mContext).registerReceiver(any(), any());
+ return uceImpl;
+ }
+}