Merge "IMS: Avoid to set CLIR after click cancel Fixes: 131474029 Change-Id: I55aa18685746145fc478a5b03d8b887cfbc1281d"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..76e910f
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,82 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// 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.
+
+// Build the Phone app which includes the emergency dialer. See Contacts
+// for the 'other' dialer.
+
+android_app {
+ name: "TeleService",
+
+ libs: [
+ "telephony-common",
+ "voip-common",
+ "ims-common",
+ "org.apache.http.legacy",
+ "libprotobuf-java-lite",
+ "unsupportedappusage",
+ ],
+
+ static_libs: [
+ "androidx.appcompat_appcompat",
+ "androidx.preference_preference",
+ "androidx.recyclerview_recyclerview",
+ "androidx.legacy_legacy-preference-v14",
+ "guava",
+ "volley",
+ "android-support-annotations",
+ "com.android.phone.common-lib",
+ ],
+
+ srcs: [
+ ":framework-telephony-stack-shared-srcs",
+ "src/**/*.java",
+ "sip/src/**/*.java",
+ "ecc/proto/**/*.proto",
+ "src/com/android/phone/EventLogTags.logtags",
+ ],
+
+ jarjar_rules: ":jarjar-rules-shared",
+
+ resource_dirs: [
+ "res",
+ "sip/res",
+ ],
+
+ asset_dirs: [
+ "assets",
+ "ecc/output",
+ ],
+
+ aaptflags: [
+ "--extra-packages com.android.services.telephony.sip",
+ ],
+
+ platform_apis: true,
+
+ certificate: "platform",
+ privileged: true,
+
+ optimize: {
+ proguard_flags_files: [
+ "proguard.flags",
+ "sip/proguard.flags",
+ ],
+ },
+
+ defaults: ["SettingsLibDefaults"],
+
+ proto: {
+ type: "lite",
+ },
+}
\ No newline at end of file
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 04c37e6..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,57 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Phone app which includes the emergency dialer. See Contacts
-# for the 'other' dialer.
-include $(CLEAR_VARS)
-
-phone_common_dir := ../../apps/PhoneCommon
-
-src_dirs := src $(phone_common_dir)/src sip/src
-res_dirs := res $(phone_common_dir)/res sip/res
-asset_dirs := assets ecc/output
-
-LOCAL_JAVA_LIBRARIES := \
- telephony-common \
- voip-common \
- ims-common \
- org.apache.http.legacy \
- libprotobuf-java-lite
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.appcompat_appcompat \
- androidx.preference_preference \
- androidx.recyclerview_recyclerview \
- androidx.legacy_legacy-preference-v14
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- guava \
- volley \
- android-support-annotations
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_SRC_FILES += $(call all-proto-files-under, ecc/proto)
-LOCAL_SRC_FILES += \
- src/com/android/phone/EventLogTags.logtags
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-LOCAL_ASSET_DIR := $(addprefix $(LOCAL_PATH)/, $(asset_dirs))
-LOCAL_USE_AAPT2 := true
-
-LOCAL_AAPT_FLAGS := \
- --extra-packages com.android.phone.common \
- --extra-packages com.android.services.telephony.sip
-
-LOCAL_PACKAGE_NAME := TeleService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags sip/proguard.flags
-
-include frameworks/base/packages/SettingsLib/common.mk
-
-include $(BUILD_PACKAGE)
-
-# Build the test package
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c4c33ca..1d32288 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -36,7 +36,6 @@
<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.intent.action.NETWORK_SET_TIME" />
- <protected-broadcast android:name="android.intent.action.NETWORK_SET_TIMEZONE" />
<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" />
@@ -50,7 +49,7 @@
<protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
<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.Telephony.SMS_EMERGENCY_CB_RECEIVED" />
+ <protected-broadcast android:name="android.provider.action.SMS_EMERGENCY_CB_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" />
@@ -62,6 +61,7 @@
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
<protected-broadcast android:name= "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED" />
+ <protected-broadcast android:name= "com.android.internal.telephony.ACTION_REPORT_RADIO_BUG" />
<protected-broadcast android:name= "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
<protected-broadcast android:name= "com.android.intent.isim_refresh" />
<protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
@@ -95,6 +95,9 @@
<!-- For Vendor Debugging in Telephony -->
<protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
+ <!-- Allows granting runtime permissions to telephony related components. -->
+ <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
+
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
@@ -138,6 +141,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
<uses-permission android:name="android.permission.BROADCAST_SMS"/>
<uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"/>
@@ -157,7 +161,9 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
- <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
+ <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
<uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
<uses-permission android:name="android.permission.DUMP" />
@@ -168,7 +174,8 @@
<!-- BIND_CARRIER_MESSAGING_SERVICE has been deprecated in favor of BIND_CARRIER_SERVICES. -->
<uses-permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE" />
<uses-permission android:name="android.permission.BIND_EUICC_SERVICE" />
- <uses-permission android:name="com.android.permission.BIND_TELEPHONY_NETWORK_SERVICE" />
+ <uses-permission android:name="android.permission.BIND_TELEPHONY_NETWORK_SERVICE" />
+ <uses-permission android:name="android.permission.BIND_CELL_BROADCAST_SERVICE" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
@@ -454,6 +461,21 @@
</intent-filter>
</activity>
+ <!--
+ Handler for EuiccManager's public action intents. These are public and do not require
+ any special permissions to start, although the calling package name should be
+ whitelisted by the underlying eUICC service implementation (i.e. the LPA).
+ -->
+ <activity android:name=".euicc.EuiccPublicActionUiDispatcherActivity"
+ android:theme="@android:style/Theme.NoDisplay">
+ <!-- Max out priority to ensure nobody else will handle these intents. -->
+ <intent-filter android:priority="1000">
+ <action android:name=
+ "android.telephony.euicc.action.START_EUICC_ACTIVATION" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name="EmergencyCallbackModeExitDialog"
android:excludeFromRecents="true"
android:label="@string/ecm_exit_dialog"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1321ace..22845e0 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -48,3 +48,5 @@
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.services.telephony.common_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/telephony-common.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/ims-common.jar)
diff --git a/OWNERS b/OWNERS
index 1ef8344..5be6fe5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,3 +11,4 @@
shuoq@google.com
paulye@google.com
nazaninb@google.com
+sarahchin@google.com
\ No newline at end of file
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..e75dcb0
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "TeleServiceTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/apex/Android.bp b/apex/Android.bp
new file mode 100644
index 0000000..86ebe3a
--- /dev/null
+++ b/apex/Android.bp
@@ -0,0 +1,28 @@
+apex {
+ name: "com.android.telephony",
+
+ manifest: "apex_manifest.json",
+
+ // optional. if unspecified, a default one is auto-generated
+ androidManifest: "AndroidManifest.xml",
+
+ java_libs: ["telephony-common", "ims-common"],
+ //apps: ["TeleService", "StkLib", "ONSLib"],
+ apps: ["StkLib"],
+
+ key: "com.android.telephony.key",
+ certificate: ":com.android.telephony.certificate",
+}
+
+apex_key {
+ name: "com.android.telephony.key",
+ public_key: "com.android.telephony.avbpubkey",
+ private_key: "com.android.telephony.pem",
+}
+
+android_app_certificate {
+ name: "com.android.telephony.certificate",
+ // This will use com.android.telephony.x509.pem (the cert) and
+ // com.android.telephony.pk8 (the private key)
+ certificate: "com.android.telephony",
+}
diff --git a/apex/AndroidManifest.xml b/apex/AndroidManifest.xml
new file mode 100644
index 0000000..a32ab33
--- /dev/null
+++ b/apex/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.telephony">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false" />
+ <!-- Setting maxSdk to lock the module to R. minSdk is auto-set by build system -->
+ <uses-sdk
+ android:maxSdkVersion="30"
+ android:targetSdkVersion="30"
+ />
+</manifest>
\ No newline at end of file
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
new file mode 100644
index 0000000..493d6fd
--- /dev/null
+++ b/apex/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.telephony",
+ "version": 1
+}
\ No newline at end of file
diff --git a/apex/com.android.telephony.avbpubkey b/apex/com.android.telephony.avbpubkey
new file mode 100644
index 0000000..cba14427
--- /dev/null
+++ b/apex/com.android.telephony.avbpubkey
Binary files differ
diff --git a/apex/com.android.telephony.pem b/apex/com.android.telephony.pem
new file mode 100644
index 0000000..19d8c05
--- /dev/null
+++ b/apex/com.android.telephony.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAvEOU0eLO65uykCUYoLBXxjsIB7pCIXahtyIMYiGuUl6tnYb2
+gjPxsD9fNrqgdT+WgzHlfMPr4GqkT7prSI4/y9NglnFk2nAKZLQwiwtLOmm54Cnp
+OqqQZ9+g4MvA2rsrmstJ/Pdu4t+9Bv9bZHYLEPCW4LV1v5S2jY7ie/C+SbZm8yFz
+X9RgTX7loH7oSNDqvVMRUeyrlH3gRHxPMQxIMfLMro1NCfkA4q8QnwPpM2+YEBf0
+kktXkvbfJUB0Ywo6BCVAoqxkghNQQTLBuBDFnho13gQSMOLu8C62pzdAyPm/n5Yv
+aqEdmmGkBsy2qiHTiRuiWWl9aGDhd0U6e+hYK4OM3ISDQiTr5OuraIKt8EH7jo8H
+Zb/WqJFygOq5RqXmAvbtq+eBAgLlUNAl5F9RwLU6zPTREsSGG/g1fqUSD4ZS2qHt
+d9aBdSepvL4xDCg9BDTFMt4Xjc83hk+v7nIIPB4cayA9s2kgjXwjZhiMOJuoNMmr
+Wu2GP9zw37WmUF5jXKgICVtoq//qofA3KA+YZ8NcmPRmeBzCEks5fCAZN64AIh5l
+vi8pPv+s8lbhGui/dkgJAGoMWKwukMZZDxTFfgbPU74yIWUpoiXGEv+0WPsOyxKe
+HEC0uXL6h6HtTaxTTyk9OJp1Z12ih2CdHmoK8aHeL1ND+YXxko4rcVyJGvcCAwEA
+AQKCAgAve1gAa5TGhZlOk5yXgoJQw7itwkNFj1cQCi1XXqlJqwlcrppjklhFnGYW
+NpGboLMEWcRIaF2IVz0jwp+mSAI9FQ3KQ8Us9o8YgP2xQ+LwU5QGvmtNWZFcMWGw
+8JKuWACSZO0/OOSlocpaBnN1bfQyjItxFzMgY5B7OWtT2Q8VJkTm0yv3Img7g2LE
+zN0m6FIb9+RpZiiMJgc3HqcWdsOKnXMUHx0sA7o+cereQC1DxMR0YqZTc18d+Nek
+3JG+HOku/HpFzPb4e4iFORj0pH5C9tdZLGuuGr4qo417tQrI5Xx38nf5C7OcqT2m
+XWFAnZFthiO3K0zu02hqHXK3p4z81aerzOAFHQP9Dvw0LrEevhMS7POwnoBMWIYT
+j2oJAKf84AIt+URNxRMcOH4Vs8133ZJxesotYzEdZBN4KBukCC+MzLtkJ3C3vLSu
+PPlwYfS0T/wMLBlQRfGM2pZbuldc4Th+VuE8lvDIvmZKxUXdAh/OkTJRt95SSpFQ
+PZy9NauagIaDyE08z3vgiYUH/498piibXiv4ox9puyfDI30W7VlZA9NBV7UBhkcV
+vxmop3U4KJdI0dcwB/8oQao/eM8Bk8TOI58Ux/gM+7tLM+pzeIPlKsZ3GWjZYyZi
+Fds7JdprR2AljUoKXAGA3G2IejhiB6VSivR8IRLwdWZSAaM9iQKCAQEA512QfnYR
+MDjYzUBYySHANVFi09q3taoYJ1Ovq4w3YnC4Yy6OM1rI1g55mhzGGnrQhC8//Lwy
+v9fxhxEQkx8rY40KJRizfc28l3gIFzF+/mP5DqUQiVZQBiAm0m0y3LA1xTCRxyc/
+yLWCodFOojcg8H3vxDUiL+80wLRX/zXmUHjhGmg+sZVtuWTVpgdFEGR1guVAoXPM
+t6p9+Zpr+yhURRuce7IfjVxIXtjzaZ6fglCYSnakhxvBMWvvhhD2Sx60bl0sBpsT
+oj4VZmd7i01bde1H2BlJ9ca23uGPylvmC+kVFVoqnMv7dZuhHgQY06zg+zts8Grm
+mVcTa5xKtEr2FQKCAQEA0E8t9/XV6tEH5BoiZ6SLIYeh+yk/h8hQKGn+lnzcywon
+L/EEtvqzoeIkbLATOk4ffbiYtI86GxldPUp293ups9rJCJdb7fIahJJd4Pl9d9QF
+dEWkuMC9btyBq0WjvJLIVxKLcBv9NAMnfLYIWk4OfAXQ2Kgv4OrYmxqtLdnzqIT0
+4jgQcHkaDbPIBkOAfAcyy6b6TNsdhiR/ojZ/3s7e0YpUhUkvGqHPeGkg827fznui
+mOSvuIC1eg92Kpsv/NzZkSkhh+Y4yT3mXTPkdRrIoRiwfUsBx9c3CUB1bhMlqYdP
+p9IwfcDYPmPIWqD/5EIm5CIFTh/qFuJuB/RP1xr72wKCAQEAm6BZGGdsqmYilvWI
+g0o7kuG6u0xstDrE4nctPBQmEQZHURe4uRyA6VN1Qk/GhXtFFRT/Uo55CIhFw5eb
+8YpO0jjFsSxLV/zytWQc9RTrGky+87XeSLMRQv2Pf43KX537nTdNZ4ukIOrPAu/a
+l8zDUGE1zWH8LAgYEON+KYHk+Q5GRFAIk8UyuDj2aSaMszkPvktczP0qoASDlkjk
+B14aSR4IhT/Mb3a6i2odWVdEbhCuT7A8fVETA55Z0HFKaj8uGOp9ObhIXaKrHGXR
+OWOAePNxlw9zq2nOCl/pK7HjLPHMp78t2Khw7awQlTMmTAQGlZdkcNdHY6+ptYG8
+0kd1/QKCAQAaosVB+5FK4UedByp3DjcsJmez8eZJundlc4g2PekNnVK9cpgjvZLS
+lFQMAcezoOGt9biw/c90P54IhEkKBCoC8WrbSl9a4aY6ZWzTo5NDizzXTUBuz1Zp
+opCCEfmJ10r+t0RVJX8O84hNDgmrYme/vA6kZX0ZyW3BB3w1bLUXS0j01C5l81Y9
+gkuFYYRAV7tLByLiSPJRgMFBQK7BErqzGHkOQflf8mjRAMBR4E+F25FC85r0gKCz
+5t45PHa6YpL/WL8M3YdNVDjsmgIQZ0hYMQwAOWMU5pQG/tMFLDWYo9CvqNTCf7Im
+6CNqU0PlMABBE4iSIMqR+ooPQlIjMzQZAoIBAQC9JulZdQJzjZSI5tBj+zPScJJQ
+s/+sRfQBy7SalPtc+oLYBP2Js9c1JsR4Dk2vC3V5z7BaL4Lg4YcFVRcz9B4yUVRm
+BL/E34XGLfn5KBI8NeqIvHiivvlAPgoHwHAVsqtE35E2sYjOYbnn1FEaNm6EKiL3
+Dv1auVdLlP3jALJIEwHPzc2ZOc/Sh/LolfajR427j5YlgbMAk5DdXuxz7t9qEcyk
+Nc8HFaoXiBP1jK+L5C4mVvGeI9xghNoJXhNywEmMa2Shab0pmtOMQWuvNMQa3gRC
+OKbaM3A6zjvAl3rjFRBrGORqbu1E/InLOSXWuB8knYVf8LkWEDRjOHqMO3pJ
+-----END RSA PRIVATE KEY-----
diff --git a/apex/com.android.telephony.pk8 b/apex/com.android.telephony.pk8
new file mode 100644
index 0000000..b15a884
--- /dev/null
+++ b/apex/com.android.telephony.pk8
Binary files differ
diff --git a/apex/com.android.telephony.x509.pem b/apex/com.android.telephony.x509.pem
new file mode 100644
index 0000000..8b45823
--- /dev/null
+++ b/apex/com.android.telephony.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGKTCCBBGgAwIBAgIUCmopA1YmjspwjjHHjmtsDPqxBxgwDQYJKoZIhvcNAQEL
+BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMR4wHAYDVQQDDBVjb20uYW5kcm9pZC50ZWxlcGhvbnkxIjAgBgkqhkiG9w0B
+CQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjA0MDAzNjAxWhgPNDc1NzEw
+MzAwMDM2MDFaMIGiMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW
+MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE
+CwwHQW5kcm9pZDEeMBwGA1UEAwwVY29tLmFuZHJvaWQudGVsZXBob255MSIwIAYJ
+KoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEA4qQYe8S6yte4MUDF2/JBUulJLF15CdnALkDJuGYLRom2
+DxRpq2/IqX8gpwVfZCfVjtthKGMhsdLW9gI+oKck0eVB7Q2cU/WO+1Cr9cuKuT6B
+cfJUck125gRGmowWZomDXv1to6ZrGgqBlVGA546w3CNGXW/PoKr8im+xLEgD0TPA
+3jJCbxt2v2IDjMwPChHOF57y9wRkXN5aJHgwIvayReVy3Cko/rJOuPnNo2kLKI5u
+qfM4NovYNAzKbHHPCNzqhySoh2H0iTmHLby5bvXYh32ooGPmxrP6q8PubeNb+Xfc
+yY33tE74ityiqu/2FDuK2iZyL8EZkEWHbX4ddt/JREda1Mcnt95TUudqA28xHgC4
+wRYXLUADvKDVWOYu/ydWBes1iu7ZsEhy8oFdECsZGRQm2BkdmWAsMPUXRWeiaCQ4
+GFfpB38UPczY5GTgYwKAK3mmY5EeBKRu/hFMCfYGJvio2rUujRzIGOd6ovk+G/74
+Eqdbsphrkr6+UOCUrNOYUD0j7kDHVVDiWMoeKL6Qb+dFcewdDO1rRbZv8ZeExf6l
+vacfKKcEE+wls8jqof/y7cu+McGEn4R8+KKiRDcDQqLvZN4Q+CCtNRLiXA2KdrkB
+hKfhnTBBYfDNZA1WOhGrKM+Yb97ObgAgf2HuhZ2F40F3dRZ6w8yaURXAjkdCqycC
+AwEAAaNTMFEwHQYDVR0OBBYEFDCf58Ogbx07WK+hWEylNMxKYrj6MB8GA1UdIwQY
+MBaAFDCf58Ogbx07WK+hWEylNMxKYrj6MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
+hvcNAQELBQADggIBABoGWqoOfPBsXFYzP7aNhywhuCr6ddjAi3ss9vwod6JuVuVg
+Inuw1i/qPb/bO1JFEwYVH3w0JASMwzIl9h5sQv++z0P+cWsLW02AXyszb0jGD3CI
+ni/irO5PO0DdaTSHQWc8DEv0293EcqpKWmQDQ2XWH19rJ5f+Gx1046sFlUi633D2
+sFO5dG94pCtT/TcqwwkeEwA6cV2Re8F1AW8elal3sINBU5MI/OJ3GQDe+pqp9VTg
+xIEQfZ9oP6Ewl6Ect+M2hVG5X4mCk9HQL7aSuPKo2Ej6l3Xq4aQYlpO4wOVZgCJC
+chMbdAn2pjuk8+mDSR39hdIusOgr7zLZscmaGKyoxVz9KVNwd/mmu+9/mftGfYae
+RomF15hVnkhZAw21fSnhKry62+DuKt2WIyu5LqvTvRxFs0Hvoy3wrQY/oruHlgk1
+J1v1rXm3ZTrh2qv1XG8mOdTegurEvjkYQm0E38jdlkhByIHmhDdBARX2Aeb7Lrd8
+9k/7tr9+58+45punZBBMX6iW1xfwaCDF8xfZrrC96azXP6Kv/JZTxbfNm/dqHyKK
+zM2WhELaoJg7uFO9TC5eMsnBQzDoYXsr3Mu7nC7V1Bs/D7jYmFG0dG0LQJwUUGr1
+WqJT4LB0BMnzNrMH1hTQTBVYDlfn7c6fnihNBOWQt72x2GGFQsGAMUWb7pxC
+-----END CERTIFICATE-----
diff --git a/assets/eri_311480_BA01450000000000.xml b/assets/eri_311480_BA01450000000000.xml
new file mode 100644
index 0000000..ea18c73
--- /dev/null
+++ b/assets/eri_311480_BA01450000000000.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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 that IconMode can be only 0, ON or 1, FLASHING
+ The icon is turned OFF if then IconIndex = 1 -->
+
+<EriFile VersionNumber="7"
+ NumberOfEriEntries="7"
+ EriFileType="2">
+
+ <CallPromptId Id="0"
+ CallPromptText="CallPromptId0"/>
+
+ <CallPromptId Id="1"
+ CallPromptText="CallPromptId1"/>
+
+ <CallPromptId Id="2"
+ CallPromptText="CallPromptId2"/>
+
+ <EriInfo RoamingIndicator="64"
+ IconIndex="1"
+ IconMode="0"
+ EriText="Xfinity Mobile"
+ CallPromptId="0"
+ AlertId="0"
+ DataServiceSupport="1"/>
+
+ <EriInfo RoamingIndicator="65"
+ IconIndex="1"
+ IconMode="0"
+ EriText="Xfinity Mobile"
+ CallPromptId="0"
+ AlertId="0"
+ DataServiceSupport="1"/>
+
+ <EriInfo RoamingIndicator="66"
+ IconIndex="1"
+ IconMode="0"
+ EriText="Extended Network"
+ CallPromptId="0"
+ AlertId="4"
+ DataServiceSupport="0"/>
+
+ <EriInfo RoamingIndicator="67"
+ IconIndex="0"
+ IconMode="0"
+ EriText="Extended Network"
+ CallPromptId="0"
+ AlertId="4"
+ DataServiceSupport="0"/>
+
+ <EriInfo RoamingIndicator="68"
+ IconIndex="0"
+ IconMode="0"
+ EriText="Roaming"
+ CallPromptId="0"
+ AlertId="5"
+ DataServiceSupport="0"/>
+
+ <EriInfo RoamingIndicator="69"
+ IconIndex="2"
+ IconMode="0"
+ EriText="Extended Network"
+ CallPromptId="0"
+ AlertId="4"
+ DataServiceSupport="0"/>
+
+ <EriInfo RoamingIndicator="70"
+ IconIndex="2"
+ IconMode="0"
+ EriText="Roaming"
+ CallPromptId="0"
+ AlertId="5"
+ DataServiceSupport="0"/>
+
+</EriFile>
diff --git a/ecc/conversion_toolset_v1/gen_eccdata.sh b/ecc/conversion_toolset_v1/gen_eccdata.sh
index 803f39b..9fc0efc 100644
--- a/ecc/conversion_toolset_v1/gen_eccdata.sh
+++ b/ecc/conversion_toolset_v1/gen_eccdata.sh
@@ -19,8 +19,7 @@
source "${LOCAL_TOOLSET_DIR}/env.sh"
echo "Converting eccdata..."
-${ANDROID_BUILD_TOP}/prebuilts/misc/${KERNEL}-x86/protobuf/aprotoc \
- --encode=ecc.AllInfo proto/protobuf_ecc_data.proto \
+aprotoc --encode=ecc.AllInfo proto/protobuf_ecc_data.proto \
< "${INPUT_DATA}" > "${RAW_DATA}"
echo "Compressing eccdata..."
diff --git a/ecc/conversion_toolset_v1/proto/Android.bp b/ecc/conversion_toolset_v1/proto/Android.bp
index f633f90..e1e0643 100644
--- a/ecc/conversion_toolset_v1/proto/Android.bp
+++ b/ecc/conversion_toolset_v1/proto/Android.bp
@@ -22,7 +22,7 @@
],
},
srcs: ["protobuf_ecc_data.proto"],
- sdk_version: "core_platform",
+ sdk_version: "system_current",
jarjar_rules: "jarjar-rules.txt",
java_version: "1.8",
-}
\ No newline at end of file
+}
diff --git a/ecc/gen_eccdata.sh b/ecc/gen_eccdata.sh
index 4c1f616..2888585 100755
--- a/ecc/gen_eccdata.sh
+++ b/ecc/gen_eccdata.sh
@@ -20,24 +20,8 @@
exit 1
fi
-case $(uname -s) in
- Darwin)
- KERNEL=darwin
- ;;
- Linux)
- KERNEL=linux
- ;;
- *)
- echo "Unknown kernel \"`uname -s`\"" 1>&2
- exit 1
- ;;
-esac
-
-read -d "" PROTOC_COMMAND << END || :
-${ANDROID_BUILD_TOP}/prebuilts/misc/${KERNEL}-x86/protobuf/aprotoc
-END
-if ! [ -x "${PROTOC_COMMAND}" ] ; then
- echo "Missing ${PROTOC_COMMAND}." 1>&2
+if ! [ -x "$(which aprotoc)" ] ; then
+ echo "Missing aprotoc, build it with 'm aprotoc'." 1>&2
exit 1
fi
diff --git a/res/drawable/btn_emergency_information.xml b/res/drawable/btn_emergency_information.xml
index 29b4a7a..cd57bd3 100644
--- a/res/drawable/btn_emergency_information.xml
+++ b/res/drawable/btn_emergency_information.xml
@@ -16,7 +16,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp"/>
- <!-- White, Opacity 16% -->
+ <solid android:color="@color/emergency_info_btn_background_color"/>
<stroke android:width="1dp"
- android:color="#40FFFFFF"/>
-</shape>
\ No newline at end of file
+ android:color="@color/emergency_info_btn_bolder"/>
+</shape>
diff --git a/res/drawable/btn_emergency_shortcuts.xml b/res/drawable/btn_emergency_shortcuts.xml
index 449e4e0..9717e81 100644
--- a/res/drawable/btn_emergency_shortcuts.xml
+++ b/res/drawable/btn_emergency_shortcuts.xml
@@ -17,4 +17,4 @@
android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="@color/emergency_shortcut_button_background_color"/>
-</shape>
\ No newline at end of file
+</shape>
diff --git a/res/drawable/ic_sim_card.xml b/res/drawable/ic_sim_card.xml
new file mode 100644
index 0000000..f563d23
--- /dev/null
+++ b/res/drawable/ic_sim_card.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+<path
+ android:fillColor="#757575"
+ android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM13,17h-2v-2h2v2zM13,13h-2L11,8h2v5z"/>
+</vector>
diff --git a/res/drawable/phone_type_icon_background.xml b/res/drawable/phone_type_icon_background.xml
index b51c3b2..352db2e 100644
--- a/res/drawable/phone_type_icon_background.xml
+++ b/res/drawable/phone_type_icon_background.xml
@@ -15,5 +15,5 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
- <solid android:color="@color/emergency_shortcut_confirm_button_background_color"/>
-</shape>
\ No newline at end of file
+ <solid android:color="@color/emergency_shortcut_phone_type_icon_color"/>
+</shape>
diff --git a/res/layout/band_mode.xml b/res/layout/band_mode.xml
new file mode 100644
index 0000000..b43dd1d
--- /dev/null
+++ b/res/layout/band_mode.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:padding="4dip"
+ android:gravity="center_horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ListView android:id="@+id/band"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textSize="7sp">
+ </ListView>
+
+</LinearLayout>
diff --git a/res/layout/emergency_dialer.xml b/res/layout/emergency_dialer.xml
index 892b8f4..d14a679 100644
--- a/res/layout/emergency_dialer.xml
+++ b/res/layout/emergency_dialer.xml
@@ -46,7 +46,9 @@
android:layout_height="@dimen/dialpad_button_height"
android:background="@drawable/floating_action_button_red"
android:contentDescription="@string/description_dialpad_button"
- android:src="@drawable/ic_dialpad_white_24"/>
+ android:src="@drawable/ic_dialpad_white_24"
+ android:scaleType="centerInside"
+ android:backgroundTint="@color/emergency_dialpad_fab_tint_color"/>
</FrameLayout>
</FrameLayout>
diff --git a/res/layout/emergency_information.xml b/res/layout/emergency_information.xml
index 524387f..3ed1748 100644
--- a/res/layout/emergency_information.xml
+++ b/res/layout/emergency_information.xml
@@ -19,7 +19,8 @@
android:layout_height="@dimen/emergency_info_button_height"
android:layout_width="match_parent"
android:layout_marginHorizontal="@dimen/emergency_shortcut_buttons_margin_horizontal"
- android:layout_marginVertical="@dimen/emergency_info_button_margin_vertical">
+ android:layout_marginTop="@dimen/emergency_info_button_margin_top"
+ android:layout_marginBottom="@dimen/emergency_info_button_margin_bottom">
<FrameLayout
android:id="@+id/emergency_info_view"
android:layout_height="match_parent"
@@ -38,7 +39,7 @@
android:layout_marginEnd="@dimen/emergency_dialer_image_margin_end"
android:layout_height="@dimen/emergency_info_image_height"
android:layout_width="@dimen/emergency_info_image_width"
- android:scaleType="centerCrop"/>
+ android:scaleType="centerInside"/>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
@@ -53,8 +54,7 @@
android:maxLines="1"
android:ellipsize="end"
android:lineHeight="@dimen/emergency_info_name_line_height"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textAppearance="@style/HeadlineTextAppearance"/>
+ android:textAppearance="@style/EmergencyInfoNameTextAppearance"/>
<TextView
android:id="@+id/emergency_info_hint"
android:layout_height="wrap_content"
@@ -62,8 +62,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/emergency_info_hint_line_height"
- android:alpha="0.7"
- android:textAppearance="@style/SubtitleTextAppearance"
+ android:textAppearance="@style/EmergencyInfoHintTextAppearance"
android:text="@string/emergency_information_hint"/>
</LinearLayout>
</LinearLayout>
@@ -88,7 +87,7 @@
android:layout_width="@dimen/emergency_info_image_width"
android:layout_marginStart="@dimen/emergency_dialer_image_margin_start"
android:layout_marginEnd="@dimen/emergency_dialer_image_margin_end"
- android:scaleType="centerCrop"/>
+ android:scaleType="centerInside"/>
<TextView
android:id="@+id/confirmed_emergency_info"
android:layout_height="wrap_content"
@@ -99,8 +98,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/confirmed_emergency_info_line_height"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textAppearance="@style/PhoneCallHintTextAppearance"
+ android:textAppearance="@style/EmergencyInfoTapHintTextAppearance"
android:text="@string/emergency_information_confirm_hint"/>
</LinearLayout>
</FrameLayout>
diff --git a/res/layout/emergency_shortcut_button.xml b/res/layout/emergency_shortcut_button.xml
index 3c5eaa2..ce4d60e 100644
--- a/res/layout/emergency_shortcut_button.xml
+++ b/res/layout/emergency_shortcut_button.xml
@@ -16,7 +16,8 @@
<com.android.phone.EmergencyShortcutButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="@dimen/emergency_shortcut_button_height"
- android:layout_width="match_parent">
+ android:layout_width="match_parent"
+ android:layout_marginBottom="@dimen/emergency_shortcut_button_margin_bottom">
<!-- Normal emergency call button view -->
<FrameLayout
android:id="@+id/emergency_call_number_info_view"
@@ -42,7 +43,8 @@
android:id="@+id/phone_type_icon"
android:layout_width="@dimen/phone_number_type_image_height"
android:layout_height="@dimen/phone_number_type_image_width"
- android:layout_gravity="center"/>
+ android:layout_gravity="center"
+ android:scaleType="centerInside"/>
</FrameLayout>
<LinearLayout
android:layout_height="wrap_content"
@@ -57,7 +59,6 @@
android:maxLines="1"
android:ellipsize="end"
android:lineHeight="@dimen/phone_number_line_height"
- android:fontFamily="@*android:string/config_headlineFontFamily"
android:textAppearance="@style/PhoneNumberTextAppearance"/>
<TextView
android:id="@+id/phone_number_description"
@@ -66,8 +67,7 @@
android:alpha="0.7"
android:maxLines="1"
android:ellipsize="end"
- android:fontFamily="sans-serif-medium"
- android:textAppearance="@style/SubtitleTextAppearance"/>
+ android:textAppearance="@style/PhoneNumberTypeAppearance"/>
</LinearLayout>
</LinearLayout>
<FrameLayout
@@ -79,7 +79,9 @@
android:id="@+id/microphone_icon"
android:layout_height="@dimen/phone_icon_height"
android:layout_width="@dimen/phone_icon_width"
- android:src="@drawable/ic_emergency_callback_mode"/>
+ android:src="@drawable/ic_emergency_callback_mode"
+ android:tint="@color/emergency_call_icon_color"
+ android:scaleType="centerInside"/>
</FrameLayout>
</FrameLayout>
@@ -111,6 +113,7 @@
android:layout_width="@dimen/phone_number_type_image_height"
android:layout_height="@dimen/phone_number_type_image_width"
android:layout_gravity="center"
+ android:scaleType="centerInside"
android:tint="@color/emergency_shortcut_confirm_button_background_color"/>
</FrameLayout>
<FrameLayout
@@ -124,8 +127,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/phone_call_hint_line_height"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textAppearance="@style/PhoneCallHintTextAppearance"/>
+ android:textAppearance="@style/PhoneNumberTapHintAppearance"/>
</FrameLayout>
</LinearLayout>
<FrameLayout
@@ -137,7 +139,9 @@
<ImageView
android:layout_height="@dimen/phone_icon_height"
android:layout_width="@dimen/phone_icon_width"
- android:src="@drawable/ic_emergency_callback_mode"/>
+ android:src="@drawable/ic_emergency_callback_mode"
+ android:tint="@color/emergency_call_icon_color"
+ android:scaleType="centerInside"/>
</FrameLayout>
</FrameLayout>
</com.android.phone.EmergencyShortcutButton>
diff --git a/res/layout/emergency_shortcut_buttons_group.xml b/res/layout/emergency_shortcut_buttons_group.xml
index 54563c9..8e2c256 100644
--- a/res/layout/emergency_shortcut_buttons_group.xml
+++ b/res/layout/emergency_shortcut_buttons_group.xml
@@ -37,8 +37,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/emergency_number_title_line_height"
- android:fontFamily="sans-serif-medium"
- android:textColor="@android:color/white"
+ android:textAppearance="@style/ShortcutViewHintTextAppearance"
android:textSize="@dimen/emergency_number_title_text_size"
android:text="@string/single_emergency_number_title"/>
</FrameLayout>
@@ -49,13 +48,13 @@
android:layout_marginStart="@dimen/emergency_number_title_container_width"
android:layout_gravity="center_vertical|end"
android:gravity="center_vertical"
- android:orientation="horizontal"
- android:alpha="0.7">
+ android:orientation="horizontal">
<ImageView
android:id="@+id/location_icon"
android:layout_width="@dimen/location_image_width"
android:layout_height="@dimen/location_image_height"
- android:src="@drawable/place_gm2_24px"/>
+ android:src="@drawable/place_gm2_24px"
+ android:scaleType="centerInside"/>
<TextView
android:id="@+id/location_text"
android:layout_height="wrap_content"
@@ -64,7 +63,7 @@
android:maxLines="2"
android:ellipsize="end"
android:lineHeight="@dimen/location_text_line_height"
- android:textColor="@android:color/white"
+ android:textAppearance="@style/ShortcutViewHintTextAppearance"
android:textSize="@dimen/emergency_location_text_size"/>
</LinearLayout>
</FrameLayout>
@@ -73,8 +72,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginHorizontal="@dimen/emergency_shortcut_buttons_margin_horizontal"
- android:orientation="vertical"
- android:divider="@drawable/emergency_shortcuts_divider"
- android:showDividers="middle">
+ android:orientation="vertical">
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/radio_info.xml b/res/layout/radio_info.xml
new file mode 100644
index 0000000..40c2e53
--- /dev/null
+++ b/res/layout/radio_info.xml
@@ -0,0 +1,414 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/Settings/assets/res/any/layout/radio_info.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout style="@style/info_layout"
+ android:descendantFocusability="beforeDescendants"
+ android:focusableInTouchMode="true">
+
+ <!-- Phone index -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/phone_index_label"
+ style="@style/info_label"
+ />
+
+ <Spinner android:id="@+id/phoneIndex"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <!-- IMEI -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_imei_label" style="@style/info_label" />
+ <TextView android:id="@+id/imei" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Phone Number -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_phone_number_label" style="@style/info_label" />
+ <TextView android:id="@+id/number" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Subscription ID -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_subid" style="@style/info_label" />
+ <TextView android:id="@+id/subid" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Default data subscription -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_dds" style="@style/info_label" />
+ <TextView android:id="@+id/dds" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- IMSI -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_imsi_label" style="@style/info_label" />
+ <TextView android:id="@+id/imsi" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Network Identifier -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_current_network_label" style="@style/info_label" />
+ <TextView android:id="@+id/operator" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Roaming -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_roaming_label" style="@style/info_label" />
+ <TextView android:id="@+id/roaming" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Data Service Status -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_gprs_service_label" style="@style/info_label" />
+ <TextView android:id="@+id/gprs" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Data Network Type -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_data_network_type_label" style="@style/info_label" />
+ <TextView android:id="@+id/data_network" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Voice Service Status -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_gsm_service_label" style="@style/info_label" />
+ <TextView android:id="@+id/gsm" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Voice Network Type -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_voice_network_type_label" style="@style/info_label" />
+ <TextView android:id="@+id/voice_network" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Signal Strength -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_signal_strength_label" style="@style/info_label" />
+ <TextView android:id="@+id/dbm" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Link Bandwidth -->
+ <LinearLayout style="@style/RadioInfo_entry_layout" android:orientation="horizontal">
+ <TextView android:text="@string/radio_info_dl_kbps" style="@style/info_label" />
+ <TextView android:id="@+id/dl_kbps" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Link Bandwidth -->
+ <LinearLayout style="@style/RadioInfo_entry_layout" android:orientation="horizontal">
+ <TextView android:text="@string/radio_info_ul_kbps" style="@style/info_label" />
+ <TextView android:id="@+id/ul_kbps" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Physical Channel Config -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_phy_chan_config" style="@style/info_label" />
+ <TextView android:id="@+id/phy_chan_config" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- Preferred Network Type -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_info_set_perferred_label"
+ style="@style/info_label"
+ />
+
+ <Spinner android:id="@+id/preferredNetworkType"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- Radio Power -->
+ <Switch android:id="@+id/radio_power"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_info_radio_power"/>
+
+ <!-- VoLTE provisioned -->
+ <Switch android:id="@+id/volte_provisioned_switch"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/volte_provisioned_switch_string"/>
+
+ <!-- VT provisioned -->
+ <Switch android:id="@+id/vt_provisioned_switch"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/vt_provisioned_switch_string"/>
+
+ <!-- Wifi Calling provisioned -->
+ <Switch android:id="@+id/wfc_provisioned_switch"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/wfc_provisioned_switch_string"/>
+
+ <!-- EAB/Presence provisioned -->
+ <Switch android:id="@+id/eab_provisioned_switch"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eab_provisioned_switch_string"/>
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- Enable/Disable CBRS data -->
+ <Switch android:id="@+id/cbrs_data_switch"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/cbrs_data_switch_string" />
+
+ <!-- Switch between SSSS(single sim single standby) and DSDS(dual sim dual standby). -->
+ <Switch android:id="@+id/dsds_switch"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/dsds_switch_string" />
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- Ping stats -->
+ <Button android:id="@+id/ping_test"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ping_test_label"
+ />
+
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_ping_hostname_v4" style="@style/info_label" />
+ <TextView android:id="@+id/pingHostnameV4" style="@style/info_value" />
+ </LinearLayout>
+
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_ping_hostname_v6" style="@style/info_label" />
+ <TextView android:id="@+id/pingHostnameV6" style="@style/info_value" />
+ </LinearLayout>
+
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_http_client_test" style="@style/info_label" />
+ <TextView android:id="@+id/httpClientTest" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- PPP Sent -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_ppp_sent_label"
+ style="@style/info_label" />
+ <TextView android:id="@+id/sent" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- PPP Received -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_ppp_received_label"
+ style="@style/info_label" />
+ <TextView android:id="@+id/received" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- PPP Sent since last received -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_ppp_resets_label"
+ style="@style/info_label" />
+ <TextView android:id="@+id/sentSinceReceived" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- Call Status -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_call_status_label" style="@style/info_label" />
+ <TextView android:id="@+id/call" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Message Waiting Indicator -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_message_waiting_label" style="@style/info_label" />
+ <TextView android:id="@+id/mwi" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Call Forwarding Indicator -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_call_redirect_label" style="@style/info_label" />
+ <TextView android:id="@+id/cfi" style="@style/info_value" />
+ </LinearLayout>
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- CellInfoListRate Selection -->
+ <!-- Location -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_signal_location_label" style="@style/info_label" />
+ <TextView android:id="@+id/location" style="@style/info_value" />
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_info_cell_info_refresh_rate"
+ style="@style/info_label"
+ />
+
+ <Spinner android:id="@+id/cell_info_rate_select"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <!-- CellInfo -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:text="@string/radio_info_cellinfo_label"
+ style="@style/info_label" />
+ </LinearLayout>
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <TextView android:id="@+id/cellinfo"
+ style="@style/info_value"
+ android:minHeight="300dip"
+ android:textSize="12sp" />
+ </LinearLayout>
+
+ <!-- Horizontal Rule -->
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <!-- Launch OEM-specific Info/Settings Activity (if any) -->
+ <!-- Carrier Provisioning -->
+ <LinearLayout style="@style/RadioInfo_entry_layout"
+ android:orientation="horizontal" >
+ <Button android:id="@+id/carrier_provisioning"
+ android:layout_marginTop="8dip"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/carrier_provisioning"
+ android:textSize="14sp"/>
+ <Button android:id="@+id/trigger_carrier_provisioning"
+ android:layout_marginTop="8dip"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/trigger_carrier_provisioning"
+ android:textSize="14sp"/>
+ <Button android:id="@+id/oem_info"
+ android:layout_marginTop="8dip"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:text="@string/oem_radio_info_label"
+ android:textSize="14sp"/>
+ </LinearLayout>
+
+ <!-- SMSC -->
+ <RelativeLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/smsc_label"
+ android:text="@string/radio_info_smsc_label"
+ android:layout_alignBaseline="@+id/update_smsc"
+ style="@style/info_label" />
+ <Button android:id="@+id/refresh_smsc"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_info_smsc_refresh_label"
+ android:layout_alignParentEnd="true"
+ />
+ <Button android:id="@+id/update_smsc"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_info_smsc_update_label"
+ android:layout_toStartOf="@+id/refresh_smsc"
+ android:layout_alignBaseline="@+id/refresh_smsc"
+ />
+ <EditText android:id="@+id/smsc"
+ style="@style/form_value"
+ android:layout_alignBaseline="@+id/refresh_smsc"
+ android:layout_toStartOf="@id/update_smsc"
+ android:layout_toEndOf="@id/smsc_label" />
+ </RelativeLayout>
+
+ <!-- Test setting to ignore bad DNS, useful in lab environments -->
+ <LinearLayout style="@style/RadioInfo_entry_layout">
+ <Button android:id="@+id/dns_check_toggle"
+ android:textSize="14sp"
+ android:layout_marginTop="8dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_info_toggle_dns_check_label"
+ />
+ <TextView android:id="@+id/dnsCheckState" style="@style/info_value" />
+ </LinearLayout>
+
+
+ </LinearLayout>
+</ScrollView>
diff --git a/res/values-h535dp/dimens.xml b/res/values-h535dp/dimens.xml
new file mode 100644
index 0000000..c3b2341
--- /dev/null
+++ b/res/values-h535dp/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <!-- Height and vertical margin for the emergency information button. -->
+ <dimen name="emergency_info_button_height">80dp</dimen>
+ <dimen name="emergency_info_button_margin_top">35dp</dimen>
+ <dimen name="emergency_info_button_margin_bottom">40dp</dimen>
+
+ <!-- The text size of emergency info name and hint -->
+ <dimen name="emergency_info_name_text_size">18.3sp</dimen>
+ <dimen name="emergency_info_hint_text_size">13.3sp</dimen>
+ <dimen name="emergency_info_tap_hint_text_size">15sp</dimen>
+
+ <!-- The height for title of emergency number and location info. -->
+ <dimen name="emergency_number_title_height">40dp</dimen>
+ <!-- The text size for emergency number title.-->
+ <dimen name="emergency_number_title_text_size">11.6sp</dimen>
+
+ <!-- The height and width for the image of location info.-->
+ <dimen name="location_image_height">13.3dp</dimen>
+ <dimen name="location_image_width">13.3dp</dimen>
+
+ <!-- The text size for emergency location.-->
+ <dimen name="emergency_location_text_size">11.6sp</dimen>
+
+ <!-- The height and margin for button of emergency shortcut. -->
+ <dimen name="emergency_shortcut_button_height">80dp</dimen>
+ <dimen name="emergency_shortcut_button_margin_bottom">6.7dp</dimen>
+
+ <!-- The text size of emergency number, type and hint -->
+ <dimen name="emergency_shortcut_number_text_size">27sp</dimen>
+ <dimen name="emergency_shortcut_type_text_size">11.6sp</dimen>
+ <dimen name="emergency_shortcut_tap_hint_text_size">15sp</dimen>
+
+ <!-- The height and width for the image of phone number type.-->
+ <dimen name="phone_number_type_image_height">33dp</dimen>
+ <dimen name="phone_number_type_image_width">33dp</dimen>
+
+ <!-- The height and width of phone icon.-->
+ <dimen name="phone_icon_height">20dp</dimen>
+ <dimen name="phone_icon_width">20dp</dimen>
+
+ <!-- Margin of dialpad button -->
+ <dimen name="emergency_dialer_dialpad_button_margin">27dp</dimen>
+
+ <!-- The height and width for the dialpad button -->
+ <dimen name="dialpad_button_height">47dp</dimen>
+ <dimen name="dialpad_button_width">47dp</dimen>
+</resources>
diff --git a/res/values-h590dp/dimens.xml b/res/values-h590dp/dimens.xml
new file mode 100644
index 0000000..3684f4c
--- /dev/null
+++ b/res/values-h590dp/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <!-- Height and vertical margin for the emergency information button. -->
+ <dimen name="emergency_info_button_height">87dp</dimen>
+ <dimen name="emergency_info_button_margin_top">44dp</dimen>
+ <dimen name="emergency_info_button_margin_bottom">44dp</dimen>
+
+ <!-- The text size of emergency info name and hint -->
+ <dimen name="emergency_info_name_text_size">20sp</dimen>
+ <dimen name="emergency_info_hint_text_size">14.5sp</dimen>
+ <dimen name="emergency_info_tap_hint_text_size">16.4sp</dimen>
+
+ <!-- The height for title of emergency number and location info. -->
+ <dimen name="emergency_number_title_height">44dp</dimen>
+ <!-- The text size for emergency number title.-->
+ <dimen name="emergency_number_title_text_size">12.7sp</dimen>
+
+ <!-- The height and width for the image of location info.-->
+ <dimen name="location_image_height">14.5dp</dimen>
+ <dimen name="location_image_width">14.5dp</dimen>
+
+ <!-- The text size for emergency location.-->
+ <dimen name="emergency_location_text_size">12.7sp</dimen>
+
+ <!-- The height and margin for button of emergency shortcut. -->
+ <dimen name="emergency_shortcut_button_height">87dp</dimen>
+ <dimen name="emergency_shortcut_button_margin_bottom">7.3dp</dimen>
+
+ <!-- The text size of emergency number, type and hint -->
+ <dimen name="emergency_shortcut_number_text_size">29sp</dimen>
+ <dimen name="emergency_shortcut_type_text_size">12.7sp</dimen>
+ <dimen name="emergency_shortcut_tap_hint_text_size">16.4sp</dimen>
+
+ <!-- The height and width for the image of phone number type.-->
+ <dimen name="phone_number_type_image_height">36dp</dimen>
+ <dimen name="phone_number_type_image_width">36dp</dimen>
+
+ <!-- The height and width of phone icon.-->
+ <dimen name="phone_icon_height">22dp</dimen>
+ <dimen name="phone_icon_width">22dp</dimen>
+
+ <!-- Margin of dialpad button -->
+ <dimen name="emergency_dialer_dialpad_button_margin">29dp</dimen>
+
+ <!-- The height and width for the dialpad button -->
+ <dimen name="dialpad_button_height">51dp</dimen>
+ <dimen name="dialpad_button_width">51dp</dimen>
+</resources>
diff --git a/res/values-h720dp/dimens.xml b/res/values-h720dp/dimens.xml
new file mode 100644
index 0000000..7bb1f27
--- /dev/null
+++ b/res/values-h720dp/dimens.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <!-- Height and vertical margin for the emergency information button. -->
+ <dimen name="emergency_info_button_height">96dp</dimen>
+ <dimen name="emergency_info_button_margin_top">72dp</dimen>
+ <dimen name="emergency_info_button_margin_bottom">60dp</dimen>
+
+ <!-- The text size of emergency info name and hint -->
+ <dimen name="emergency_info_name_text_size">22sp</dimen>
+ <dimen name="emergency_info_hint_text_size">16sp</dimen>
+ <dimen name="emergency_info_tap_hint_text_size">18sp</dimen>
+
+ <!-- The height for title of emergency number and location info. -->
+ <dimen name="emergency_number_title_height">48dp</dimen>
+ <!-- The text size for emergency number title.-->
+ <dimen name="emergency_number_title_text_size">14sp</dimen>
+
+ <!-- The height and width for the image of location info.-->
+ <dimen name="location_image_height">16dp</dimen>
+ <dimen name="location_image_width">16dp</dimen>
+
+ <!-- The text size for emergency location.-->
+ <dimen name="emergency_location_text_size">14sp</dimen>
+
+ <!-- The height and margin for button of emergency shortcut. -->
+ <dimen name="emergency_shortcut_button_height">96dp</dimen>
+ <dimen name="emergency_shortcut_button_margin_bottom">8dp</dimen>
+
+ <!-- The text size of emergency number, type and hint -->
+ <dimen name="emergency_shortcut_number_text_size">32sp</dimen>
+ <dimen name="emergency_shortcut_type_text_size">14sp</dimen>
+ <dimen name="emergency_shortcut_tap_hint_text_size">18sp</dimen>
+
+ <!-- The height and width for the image of phone number type.-->
+ <dimen name="phone_number_type_image_height">40dp</dimen>
+ <dimen name="phone_number_type_image_width">40dp</dimen>
+
+ <!-- The height and width of phone icon.-->
+ <dimen name="phone_icon_height">24dp</dimen>
+ <dimen name="phone_icon_width">24dp</dimen>
+
+ <!-- Margin of dialpad button -->
+ <dimen name="emergency_dialer_dialpad_button_margin">64dp</dimen>
+
+ <!-- The height and width for the dialpad button -->
+ <dimen name="dialpad_button_height">56dp</dimen>
+ <dimen name="dialpad_button_width">56dp</dimen>
+</resources>
diff --git a/res/values-mcc208-mnc10-fr/strings.xml b/res/values-mcc208-mnc10-fr/strings.xml
index ac5ef59..eae41e9 100644
--- a/res/values-mcc208-mnc10-fr/strings.xml
+++ b/res/values-mcc208-mnc10-fr/strings.xml
@@ -3,4 +3,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="enhanced_4g_lte_mode_summary">Utiliser la fonction Voix 4G (HD) lorsqu\'elle est disponible</string>
<string name="enhanced_4g_lte_mode_title">Activer Voix 4G (HD)</string>
+ <string name="wifi_calling">Activer Appels Wi-Fi</string>
</resources>
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
new file mode 100644
index 0000000..1f336b9
--- /dev/null
+++ b/res/values-night/colors.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+
+ <!-- Colors for shortcut view -->
+ <color name="emergency_info_btn_bolder">#85ffffff</color>
+ <color name="emergency_info_btn_background_color">#202124</color>
+ <color name="emergency_shortcut_button_background_color">#202124</color>
+ <color name="emergency_shortcut_phone_type_icon_color">#f28b82</color>
+ <color name="emergency_shortcut_confirm_button_background_color">#3c4043</color>
+ <color name="emergency_shortcut_confirm_phone_type_icon_color">#f28b82</color>
+ <color name="emergency_call_icon_color">#e8eaed</color>
+ <color name="emergency_dialpad_fab_tint_color">#f28b82</color>
+</resources>
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index 648ceeb..fa5c8a1 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -27,4 +27,44 @@
<item name="android:dialogTheme">@style/DialerAlertDialogTheme</item>
</style>
+ <style name="EmergencyInfoNameTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textSize">@dimen/emergency_info_name_text_size</item>
+ </style>
+
+ <style name="EmergencyInfoHintTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@color/white_70_percent</item>
+ <item name="android:textSize">@dimen/emergency_info_hint_text_size</item>
+ </style>
+
+ <style name="EmergencyInfoTapHintTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textSize">@dimen/emergency_info_tap_hint_text_size</item>
+ </style>
+
+ <style name="ShortcutViewHintTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@android:color/white</item>
+ </style>
+
+ <style name="PhoneNumberTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textSize">@dimen/emergency_shortcut_number_text_size</item>
+ </style>
+
+ <style name="PhoneNumberTypeAppearance">
+ <item name="android:fontFamily">roboto</item>
+ <item name="android:textColor">@color/white_70_percent</item>
+ <item name="android:textSize">@dimen/emergency_shortcut_type_text_size</item>
+ </style>
+
+ <style name="PhoneNumberTapHintAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textSize">@dimen/emergency_shortcut_tap_hint_text_size</item>
+ </style>
</resources>
diff --git a/res/values-sw345dp/dimens.xml b/res/values-sw345dp/dimens.xml
deleted file mode 100644
index 84db80b..0000000
--- a/res/values-sw345dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 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
- -->
-
-<resources>
- <dimen name="emergency_info_button_height">88dp</dimen>
- <dimen name="emergency_info_button_margin_vertical">48dp</dimen>
- <dimen name="emergency_shortcut_button_height">88dp</dimen>
- <dimen name="emergency_number_title_text_size">12sp</dimen>
- <dimen name="emergency_location_text_size">12sp</dimen>
-</resources>
\ No newline at end of file
diff --git a/res/values-sw360dp/dimens.xml b/res/values-sw360dp/dimens.xml
deleted file mode 100644
index c578065..0000000
--- a/res/values-sw360dp/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2018 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
- -->
-
-<resources>
- <dimen name="emergency_info_button_height">96dp</dimen>
- <dimen name="emergency_info_button_margin_vertical">56dp</dimen>
- <dimen name="emergency_shortcut_button_height">96dp</dimen>
- <dimen name="emergency_number_title_text_size">14sp</dimen>
- <dimen name="emergency_location_text_size">14sp</dimen>
-</resources>
\ No newline at end of file
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 4854db7..1309f77 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -437,7 +437,7 @@
<string name="deleting_fdn_contact" msgid="5669163206349319969">"正在删除固定拨号联系人..."</string>
<string name="fdn_contact_deleted" msgid="7154162327112259569">"已删除固定拨号联系人。"</string>
<string name="pin2_invalid" msgid="5470854099230755944">"固定拨号未更新,因为输入的 PIN 码有误。"</string>
- <string name="fdn_invalid_number" msgid="2062898833049589309">"未能更新 FDN,因为号码超过 20 位数。"</string>
+ <string name="fdn_invalid_number" msgid="2062898833049589309">"未能更新 FDN,因为号码超过 <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> 位数。"</string>
<string name="pin2_or_fdn_invalid" msgid="6025144083384701197">"固定拨号未更新。PIN2 码有误,或电话号码遭拒。"</string>
<string name="fdn_failed" msgid="540018079008319747">"固定拨号操作失败。"</string>
<string name="simContacts_emptyLoading" msgid="2203331234764498011">"正在从SIM卡读取..."</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3ccaa6c..02fa380 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -439,7 +439,7 @@
<string name="deleting_fdn_contact" msgid="5669163206349319969">"正在刪除固定撥號…"</string>
<string name="fdn_contact_deleted" msgid="7154162327112259569">"已刪除固定撥號。"</string>
<string name="pin2_invalid" msgid="5470854099230755944">"您所輸入的 PIN 不正確,因此未更新 FDN。"</string>
- <string name="fdn_invalid_number" msgid="2062898833049589309">"固定撥號的號碼超過 20 位數,因此無法更新。"</string>
+ <string name="fdn_invalid_number" msgid="2062898833049589309">"固定撥號的號碼超過 <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> 位數,因此無法更新。"</string>
<string name="pin2_or_fdn_invalid" msgid="6025144083384701197">"未更新 FDN。可能是因為 PIN2 碼不正確或電話號碼遭拒。"</string>
<string name="fdn_failed" msgid="540018079008319747">"FDN 操作失敗。"</string>
<string name="simContacts_emptyLoading" msgid="2203331234764498011">"正在從 SIM 卡讀取…"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index fca8acf..8d84baf 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -15,6 +15,21 @@
-->
<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/colors.xml b/res/values/colors.xml
index 679eba6..2db3aa0 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -54,8 +54,6 @@
<color name="dialer_dialpad_touch_tint">#330288d1</color>
<color name="floating_action_button_touch_tint">#80ffffff</color>
- <color name="emergency_shortcut_button_background_color">#40FFFFFF</color>
- <color name="emergency_shortcut_confirm_button_background_color">#E25142</color>
<!-- Color matches dialer settings light M2 theme.-->
<color name="dialer_background_color">#ffffff</color>
@@ -63,4 +61,14 @@
<color name="dialer_primary_text_color">#202124</color>
<color name="dialer_secondary_text_color">#5f6368</color>
+ <!-- Colors for shortcut view -->
+ <color name="emergency_info_btn_bolder">#40ffffff</color>
+ <color name="emergency_info_btn_background_color">@android:color/transparent</color>
+ <color name="emergency_shortcut_button_background_color">#eae8e9</color>
+ <color name="emergency_shortcut_phone_type_icon_color">#e25142</color>
+ <color name="emergency_shortcut_confirm_button_background_color">#e25142</color>
+ <color name="emergency_shortcut_confirm_phone_type_icon_color">#e25142</color>
+ <color name="emergency_call_icon_color">#3c4043</color>
+ <color name="emergency_dialpad_fab_tint_color">@null</color>
+ <color name="white_70_percent">#b3ffffff</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 23d20fc..e1742d8 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -118,20 +118,12 @@
<dimen name="emergency_dialer_image_margin_start">20dp</dimen>
<dimen name="emergency_dialer_image_margin_end">16dp</dimen>
- <!-- Margin of dialpad button -->
- <dimen name="emergency_dialer_dialpad_button_margin">40dp</dimen>
-
<!-- Horizontal margin for button of emergency shortcut. -->
<dimen name="emergency_shortcut_buttons_margin_horizontal">16dp</dimen>
<!-- Horizontal padding for group of emergency number title-->
<dimen name="emergency_number_title_group_padding_horizontal">16dp</dimen>
- <!-- Height and vertical margin for the emergency information button. -->
- <dimen name="emergency_info_button_height">80dp</dimen>
- <dimen name="emergency_info_button_margin_vertical">40dp</dimen>
- <dimen name="emergency_info_button_fix_margin_vertical">40dp</dimen>
-
<!-- Margin for the emergency information button text. -->
<dimen name="emergency_info_text_margin_end">20dp</dimen>
@@ -143,32 +135,10 @@
<dimen name="emergency_shortcuts_function_icon_height">24dp</dimen>
<dimen name="emergency_shortcuts_function_icon_width">24dp</dimen>
- <!-- The height and width for the dialpad button -->
- <dimen name="dialpad_button_height">56dp</dimen>
- <dimen name="dialpad_button_width">56dp</dimen>
-
- <!-- The height for title of emergency number and location info. -->
- <dimen name="emergency_number_title_height">48dp</dimen>
-
- <!-- The height and width for the image of location info.-->
- <dimen name="location_image_height">16dp</dimen>
- <dimen name="location_image_width">16dp</dimen>
-
- <!-- The height for button of emergency shortcut. -->
- <dimen name="emergency_shortcut_button_height">80dp</dimen>
-
<!-- The height and width for the circle image of phone number type.-->
<dimen name="phone_number_type_circle_image_height">40dp</dimen>
<dimen name="phone_number_type_circle_image_width">40dp</dimen>
- <!-- The height and width for the image of phone number type.-->
- <dimen name="phone_number_type_image_height">24dp</dimen>
- <dimen name="phone_number_type_image_width">24dp</dimen>
-
- <!-- The height and width of phone icon.-->
- <dimen name="phone_icon_height">24dp</dimen>
- <dimen name="phone_icon_width">24dp</dimen>
-
<!-- Margin for the emergency shortcut button.-->
<dimen name="emergency_shortcuts_margin_end">60dp</dimen>
@@ -197,12 +167,6 @@
<!-- The width for emergency number title container.-->
<dimen name="emergency_number_title_container_width">210dp</dimen>
- <!-- The text size for emergency number title.-->
- <dimen name="emergency_number_title_text_size">13sp</dimen>
-
- <!-- The text size for emergency location.-->
- <dimen name="emergency_location_text_size">13sp</dimen>
-
<!-- The text size for titles in settings page.-->
<dimen name="dialer_head1_font_size">18dp</dimen>
@@ -211,4 +175,54 @@
<!-- The text size for description in settings page.-->
<dimen name="dialer_secondary_font_size">14dp</dimen>
+
+
+ <!-- Shortcut view vertical dimens. These values are tuned according to display
+ height in values-h<height>dp/dimens.xml -->
+ <!-- Height and vertical margin for the emergency information button. -->
+ <dimen name="emergency_info_button_height">74dp</dimen>
+ <dimen name="emergency_info_button_margin_top">32dp</dimen>
+ <dimen name="emergency_info_button_margin_bottom">37dp</dimen>
+
+ <!-- The text size of emergency info name and hint -->
+ <dimen name="emergency_info_name_text_size">16.9sp</dimen>
+ <dimen name="emergency_info_hint_text_size">12.3sp</dimen>
+ <dimen name="emergency_info_tap_hint_text_size">13.8sp</dimen>
+
+ <!-- The height for title of emergency number and location info. -->
+ <dimen name="emergency_number_title_height">37dp</dimen>
+ <!-- The text size for emergency number title.-->
+ <dimen name="emergency_number_title_text_size">10.7sp</dimen>
+
+ <!-- The height and width for the image of location info.-->
+ <dimen name="location_image_height">12.3dp</dimen>
+ <dimen name="location_image_width">12.3dp</dimen>
+
+ <!-- The text size for emergency location.-->
+ <dimen name="emergency_location_text_size">10.7sp</dimen>
+
+ <!-- The height and margin for button of emergency shortcut. -->
+ <dimen name="emergency_shortcut_button_height">74dp</dimen>
+ <dimen name="emergency_shortcut_button_margin_bottom">6.2dp</dimen>
+
+ <!-- The text size of emergency number, type and hint -->
+ <dimen name="emergency_shortcut_number_text_size">25sp</dimen>
+ <dimen name="emergency_shortcut_type_text_size">10.7sp</dimen>
+ <dimen name="emergency_shortcut_tap_hint_text_size">13.8sp</dimen>
+
+ <!-- The height and width for the image of phone number type.-->
+ <dimen name="phone_number_type_image_height">31dp</dimen>
+ <dimen name="phone_number_type_image_width">31dp</dimen>
+
+ <!-- The height and width of phone icon.-->
+ <dimen name="phone_icon_height">18.4dp</dimen>
+ <dimen name="phone_icon_width">18.4dp</dimen>
+
+ <!-- Margin of dialpad button -->
+ <dimen name="emergency_dialer_dialpad_button_margin">25dp</dimen>
+
+ <!-- The height and width for the dialpad button -->
+ <dimen name="dialpad_button_height">43dp</dimen>
+ <dimen name="dialpad_button_width">43dp</dimen>
+ <!-- End of Shortcut view vertical dimens. -->
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c68deb2..b97ef10 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -581,6 +581,12 @@
<string name="roaming_check_price_warning">Check with your network provider for pricing.</string>
<!-- Mobile network settings screen, dialog message title when user selects the Data roaming check box -->
<string name="roaming_alert_title">Allow data roaming?</string>
+ <!-- Notification title for limited sim function during dual sim -->
+ <string name="limited_sim_function_notification_title">Limited SIM functionality</string>
+ <!-- Notification message for limited sim function during dual sim [CHAR LIMIT=80]-->
+ <string name="limited_sim_function_with_phone_num_notification_message"><xliff:g id="carrier_name">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="phone_number">%2$s</xliff:g>.</string>
+ <!-- Notification message for limited sim function during dual sim [CHAR LIMIT=80]-->
+ <string name="limited_sim_function_notification_message"><xliff:g id="carrier_name">%1$s</xliff:g> calls and data services may be blocked while using another SIM.</string>
<!-- Mobile network settings screen, data usage setting check box name -->
<string name="data_usage_title">App data usage</string>
<!-- Summary about how much data has been used in a date range [CHAR LIMIT=100] -->
@@ -971,7 +977,7 @@
<!-- FDN settings: error message displayed in a popup (toast) -->
<string name="pin2_invalid">FDN wasn\'t updated because you typed an incorrect PIN.</string>
<!-- FDN settings: error message displayed in a popup (toast) -->
- <string name="fdn_invalid_number">FDN wasn\'t updated because the number exceeds 20 digits.</string>
+ <string name="fdn_invalid_number">FDN wasn\'t updated because the number exceeds <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> digits.</string>
<!-- FDN settings: error message displayed in a popup (toast), when the entered
FDN number was inappropriate, OR, PIN2 the user entered was incorrect.
Because of API restriction, there's no way to determine which is the exact
@@ -1190,6 +1196,9 @@
emergency calling is not currently available. -->
<string name="dial_emergency_calling_not_available">Emergency calling not available</string>
+ <!-- Only handle pin entry when phone user is system-->
+ <string name="pin_puk_system_user_only">Only the owner of the device can enter PIN/PUK codes.</string>
+
<!-- Text for description of police calling type -->
<string name="police_type_description">Police</string>
<!-- Text for description of ambulance calling type -->
@@ -1547,6 +1556,15 @@
message is shown when an in-progress call is ended due to the battery being low. -->
<string name="callFailed_low_battery">Video call ended due to low battery.</string>
+ <!-- In-call screen: error message shown when the user attempts to place an emergency call
+ over wifi calling (WFC), but emergency services are not available in the current
+ location. -->
+ <string name="callFailed_emergency_call_over_wfc_not_available">Emergency calls over Wi-Fi calling not available in this location.</string>
+
+ <!-- In-call screen: error message shown when the user attempts to place a call over wifi
+ calling (WFC), but wifi calling is not available in the current location. -->
+ <string name="callFailed_wfc_service_not_available_in_this_location">Wi-Fi calling is not available in this location.</string>
+
<!-- The title for the change voicemail PIN activity -->
<string name="change_pin_title">Change Voicemail PIN</string>
<!-- The label for the continue button in change voicemail PIN activity -->
@@ -1758,7 +1776,7 @@
<!-- Call barring settings screen, change password -->
<string name="call_barring_change_pwd_description">Change call barring password</string>
<!-- Call barring settings screen, not possible to change call barring password -->
- <string name="call_barring_change_pwd_description_disabled">Cannot change call barring password.</string>
+ <string name="call_barring_change_pwd_description_disabled">Cannot change call barring password</string>
<!-- Call barring settings screen, change password -->
<string name="call_barring_pwd_not_match">Passwords do not match</string>
<!-- Call barring settings screen, change password -->
@@ -1872,4 +1890,209 @@
<string name="supp_service_over_ut_precautions_roaming_dual_sim">To use <xliff:g id="supp service">%1$s</xliff:g>, make sure mobile data and data roaming are turned on for SIM <xliff:g id="sim number">%2$d</xliff:g>. You can change these in mobile network settings.</string>
<!-- supplementary services over ut precaution exit dialog choice -->
<string name="supp_service_over_ut_precautions_dialog_dismiss">Dismiss</string>
+
+ <!-- Data Connection Enable. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="radio_info_data_connection_enable">Enable Data Connection</string>
+ <!-- Data Connection Disable. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="radio_info_data_connection_disable">Disable Data Connection</string>
+
+ <!-- VoLTE provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="volte_provisioned_switch_string">VoLTE Provisioned</string>
+
+ <!-- Video calling provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="vt_provisioned_switch_string">Video Calling Provisioned</string>
+
+ <!-- Wifi Calling provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="wfc_provisioned_switch_string">Wifi Calling Provisioned</string>
+
+ <!-- EAB provisioning flag on. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="eab_provisioned_switch_string">EAB/Presence Provisioned</string>
+
+ <!-- Cbrs enable disable flag. Only shown in diagnostic screen, so precise translation is not needed -->
+ <string name="cbrs_data_switch_string">Cbrs Data</string>
+
+ <!-- Dsds enable/disable flag. Only shown in diagnostic screen, so precise translation is not needed, [CHAR LIMIT=none] -->
+ <string name="dsds_switch_string">Enable DSDS</string>
+
+ <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+ <string name="dsds_dialog_title">Restart Device?</string>
+
+ <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+ <string name="dsds_dialog_message">You need to restart your device to change this setting.</string>
+
+ <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+ <string name="dsds_dialog_confirm">Restart</string>
+
+ <!-- UI debug setting: Enable/Disable DSDS [CHAR LIMIT=none] -->
+ <string name="dsds_dialog_cancel">Cancel</string>
+
+ <!-- Title for controlling on/off for Mobile phone's radio power. Only shown in diagnostic screen, so precise translation is not needed. -->
+ <string name="radio_info_radio_power">Mobile Radio Power</string>
+
+ <!-- Phone Info screen. Menu item label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_menu_viewADN">View SIM Address Book</string>
+ <!-- Phone Info screen. Menu item label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_menu_viewFDN">View Fixed Dialing Numbers</string>
+ <!-- Phone Info screen. Menu item label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_menu_viewSDN">View Service Dialing Numbers</string>
+ <!-- Phone Info screen. Menu item label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_menu_getIMS">IMS Service Status</string>
+
+ <!-- Phone Info screen. IMS Registration Title. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ims_reg_status_title">IMS Status</string>
+
+ <!-- Phone Info screen. IMS Status - Registered. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ims_reg_status_registered">Registered</string>
+ <!-- Phone Info screen. Ims Status - Unregistered. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ims_reg_status_not_registered">Not Registered</string>
+
+ <!-- Phone Info screen. Ims Feature Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ims_feature_status_available">Available</string>
+ <!-- Phone Info screen. Ims Feature status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ims_feature_status_unavailable">Unavailable</string>
+
+ <!-- Phone Info screen. IMS Registration. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ims_reg_status">IMS Registration: <xliff:g id="status" example="registered">%1$s</xliff:g>\u000AVoice over LTE: <xliff:g id="availability" example="available">%2$s</xliff:g>\u000AVoice over WiFi: <xliff:g id="availability" example="available">%3$s</xliff:g>\u000AVideo Calling: <xliff:g id="availability" example="available">%4$s</xliff:g>\u000AUT Interface: <xliff:g id="availability" example="available">%5$s</xliff:g></string>
+
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_service_in">In Service</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_service_out">Out of Service</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_service_emergency">Emergency Calls Only</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_service_off">Radio Off</string>
+
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_roaming_in">Roaming</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_roaming_not">Not Roaming</string>
+
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_phone_idle">Idle</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_phone_ringing">Ringing</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_phone_offhook">Call in Progress</string>
+
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_data_disconnected">Disconnected</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_data_connecting">Connecting</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_data_connected">Connected</string>
+ <!-- Phone Info screen. Status label. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_data_suspended">Suspended</string>
+
+ <!-- Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_unknown">Unknown</string>
+ <!-- Phone Info screen. Units shown after a value. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_display_packets">pkts</string>
+ <!-- Phone Info screen. Units shown after a value. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_display_bytes">bytes</string>
+ <!-- Phone Info screen. Units shown after a value. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_display_dbm">dBm</string>
+ <!-- Phone Info screen. Units shown after a value. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_display_asu">asu</string>
+ <!-- Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_lac">LAC</string>
+ <!-- Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radioInfo_cid">CID</string>
+
+
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_subid">Current subId:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_dds">SubId of default data SIM:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_dl_kbps">DL Bandwidth (kbps):</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ul_kbps">UL Bandwidth (kbps):</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_signal_location_label">Cell Location Info (deprecated):</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_phy_chan_config">LTE Physical Channel Configuration:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_cell_info_refresh_rate">Cell Info Refresh Rate:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_cellinfo_label">All Cell Measurement Info:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_gprs_service_label">Data Service:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_roaming_label">Roaming:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_imei_label">IMEI:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, DO NOT TRANSLATE. -->
+ <string name="radio_info_imsi_label">IMSI:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_call_redirect_label">Call Redirect:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ppp_resets_label">Number of PPP Reset Since Boot:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_current_network_label">Current Network:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ppp_received_label">Data Received:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_gsm_service_label">Voice Service:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_signal_strength_label">Signal Strength:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_call_status_label">Voice Call Status:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ppp_sent_label">Data Sent:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_message_waiting_label">Message Waiting:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_phone_number_label">Phone Number:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_band_mode_label">Select Radio Band</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_voice_network_type_label">Voice Network Type:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_data_network_type_label">Data Network Type:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="phone_index_label">Select phone index</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_set_perferred_label">Set Preferred Network Type:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ping_hostname_v4">Ping Hostname(www.google.com) IPv4:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_ping_hostname_v6">Ping Hostname(www.google.com) IPv6:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_http_client_test">HTTP Client Test:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="ping_test_label">Run Ping Test</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_smsc_label">SMSC:</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_smsc_update_label">Update</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_smsc_refresh_label">Refresh</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="radio_info_toggle_dns_check_label">Toggle DNS Check</string>
+ <!-- Radio Info screen. Label for a status item. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="oem_radio_info_label">OEM-specific Info/Settings</string>
+
+ <!-- Band Mode Selection -->
+ <!-- Band mode screen. Title of activity. -->
+ <string name="band_mode_title">Set Radio Band Mode</string>
+ <!-- Band mode screen. Loading message. -->
+ <string name="band_mode_loading">Loading Band List\u2026</string>
+ <!-- Band mode screen. Button caption to set the bandmode. -->
+ <string name="band_mode_set">Set</string>
+ <!-- Band mode screen. Status message when unsuccessful. -->
+ <string name="band_mode_failed">Unsuccessful</string>
+ <!-- Band mode screen. Statusm essage when successful. -->
+ <string name="band_mode_succeeded">Successful</string>
+
+ <!-- The title of the activity to see phone info -->
+ <string name="phone_info_label" product="tablet">Tablet info</string>
+ <!-- The title of the activity to see phone info -->
+ <string name="phone_info_label" product="default">Phone info</string>
+
+ <!-- Carrier Provisioning Info [CHAR LIMIT=NONE] -->
+ <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 5f9a453..e95142b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -45,6 +45,23 @@
<item name="android:layout_height">wrap_content</item>
</style>
+ <style name="RadioInfo_entry_layout">
+ <item name="android:orientation">horizontal</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ </style>
+
+ <style name="info_value">
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:textAppearance">@style/TextAppearance.info_value</item>
+ </style>
+
+ <style name="form_value">
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_width">match_parent</item>
+ </style>
+
<style name="TextAppearance" parent="android:TextAppearance">
</style>
@@ -53,6 +70,11 @@
<item name="android:textStyle">bold</item>
</style>
+ <style name="TextAppearance.info_value">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textStyle">normal</item>
+ </style>
+
<!-- Preference Style for the phone number preferences -->
<style name="EditPhoneNumberPreference">
<item name="enableButtonText">@string/enable</item>
@@ -189,15 +211,6 @@
<item name="android:textColor">?android:attr/textColorPrimaryInverseDisableOnly</item>
</style>
- <style name="NetworkOperatorsSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
- <item name="android:forceDarkAllowed">true</item>
- <item name="android:switchPreferenceStyle">@style/SettingsSwitchPreference</item>
- <item name="android:preferenceCategoryStyle">@style/SettingsPreferenceCategory</item>
- <item name="android:preferenceStyle">@style/SettingsPreference</item>
- <item name="android:dialogPreferenceStyle">@style/SettingsDialogPreference</item>
- <item name="android:preferenceScreenStyle">@style/SettingsPreferenceScreen</item>
- </style>
-
<style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
<item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
<item name="android:minHeight">3dip</item>
@@ -335,25 +348,44 @@
<item name="android:listDivider">@null</item>
</style>
- <style name="HeadlineTextAppearance">
- <item name="android:textColor">@android:color/white</item>
- <item name="android:textSize">22sp</item>
+ <style name="EmergencyInfoNameTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@*android:color/primary_text_default_material_dark</item>
+ <item name="android:textSize">@dimen/emergency_info_name_text_size</item>
</style>
- <style name="SubtitleTextAppearance" parent="@style/HeadlineTextAppearance">
- <item name="android:textSize">14sp</item>
+ <style name="EmergencyInfoHintTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@*android:color/secondary_text_default_material_dark</item>
+ <item name="android:textSize">@dimen/emergency_info_hint_text_size</item>
+ </style>
+
+ <style name="EmergencyInfoTapHintTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textSize">@dimen/emergency_info_tap_hint_text_size</item>
+ </style>
+
+ <style name="ShortcutViewHintTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@*android:color/secondary_text_default_material_dark</item>
</style>
<style name="PhoneNumberTextAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@*android:color/primary_text_default_material_light</item>
+ <item name="android:textSize">@dimen/emergency_shortcut_number_text_size</item>
+ </style>
+
+ <style name="PhoneNumberTypeAppearance">
+ <item name="android:fontFamily">roboto</item>
+ <item name="android:textColor">@*android:color/secondary_text_default_material_light</item>
+ <item name="android:textSize">@dimen/emergency_shortcut_type_text_size</item>
+ </style>
+
+ <style name="PhoneNumberTapHintAppearance">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:textColor">@android:color/white</item>
- <item name="android:textSize">32sp</item>
- </style>
-
- <style name="PhoneCallHintTextAppearance" parent="@style/PhoneNumberTextAppearance">
- <item name="android:textSize">18sp</item>
- </style>
-
- <style name="ShortcutsHintTextAppearance" parent="@style/HeadlineTextAppearance">
- <item name="android:textSize">16sp</item>
+ <item name="android:textSize">@dimen/emergency_shortcut_tap_hint_text_size</item>
</style>
</resources>
diff --git a/res/values/styles_preference.xml b/res/values/styles_preference.xml
index ceea4a6..fef4f72 100644
--- a/res/values/styles_preference.xml
+++ b/res/values/styles_preference.xml
@@ -29,10 +29,6 @@
<item name="android:singleLineTitle">false</item>
</style>
- <style name="SettingsPreferenceCategory" parent="@*android:style/Preference.DeviceDefault.Category">
- <item name="android:layout">@layout/preference_category_material_settings_with_divider</item>
- </style>
-
<style name="SettingsDialogPreference" parent="@*android:style/Preference.DeviceDefault.DialogPreference">
<item name="android:singleLineTitle">false</item>
<item name="android:iconSpaceReserved">true</item>
diff --git a/res/xml/callbarring_options.xml b/res/xml/callbarring_options.xml
index 6f2c48a..6506031 100644
--- a/res/xml/callbarring_options.xml
+++ b/res/xml/callbarring_options.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+ xmlns:phone="http://schemas.android.com/apk/res-auto"
android:title="@string/call_barring_settings">
<!-- Note for all com.android.phone.EditPinPreference objects
diff --git a/res/xml/callforward_options.xml b/res/xml/callforward_options.xml
index 0ff1e90..775151e 100644
--- a/res/xml/callforward_options.xml
+++ b/res/xml/callforward_options.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+ xmlns:phone="http://schemas.android.com/apk/res-auto"
android:title="@string/call_forwarding_settings">
diff --git a/res/xml/voicemail_settings.xml b/res/xml/voicemail_settings.xml
index 021a764..cbe1cf4 100644
--- a/res/xml/voicemail_settings.xml
+++ b/res/xml/voicemail_settings.xml
@@ -15,7 +15,7 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:phone="http://schemas.android.com/apk/res/com.android.phone"
+ xmlns:phone="http://schemas.android.com/apk/res-auto"
android:title="@string/voicemail">
<com.android.phone.settings.VoicemailProviderListPreference
diff --git a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
index ec77ff1..1cf7f4b 100644
--- a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
+++ b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
@@ -126,7 +126,7 @@
* @param context The context.
*/
void verifyAndPurgeInvalidPhoneAccounts(Context context) {
- TelecomManager telecomManager = TelecomManager.from(context);
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
SipProfileDb profileDb = new SipProfileDb(context);
List<PhoneAccountHandle> accountHandles = telecomManager.getPhoneAccountsSupportingScheme(
PhoneAccount.SCHEME_SIP);
@@ -188,7 +188,8 @@
// Un-register its PhoneAccount.
PhoneAccountHandle handle = SipUtil.createAccountHandle(context, sipProfileName);
- TelecomManager.from(context).unregisterPhoneAccount(handle);
+ TelecomManager tm = context.getSystemService(TelecomManager.class);
+ tm.unregisterPhoneAccount(handle);
}
/**
@@ -235,7 +236,7 @@
private void startSipProfiles(Context context, String sipProfileName, boolean enableProfile) {
final SipPreferences sipPreferences = new SipPreferences(context);
boolean isReceivingCalls = sipPreferences.isReceivingCallsEnabled();
- TelecomManager telecomManager = TelecomManager.from(context);
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
SipManager sipManager = SipManager.newInstance(context);
SipProfileDb profileDb = new SipProfileDb(context);
List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
diff --git a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
index 19f5882..3212c00 100644
--- a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
+++ b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
@@ -73,7 +73,7 @@
if (accountHandle != null) {
Bundle extras = new Bundle();
extras.putParcelable(SipUtil.EXTRA_INCOMING_CALL_INTENT, intent);
- TelecomManager tm = TelecomManager.from(context);
+ TelecomManager tm = context.getSystemService(TelecomManager.class);
PhoneAccount phoneAccount = tm.getPhoneAccount(accountHandle);
if (phoneAccount != null && phoneAccount.isEnabled()) {
tm.addNewIncomingCall(accountHandle, extras);
diff --git a/sip/src/com/android/services/telephony/sip/SipProfileDb.java b/sip/src/com/android/services/telephony/sip/SipProfileDb.java
index bb1c7ec..812ebb4 100644
--- a/sip/src/com/android/services/telephony/sip/SipProfileDb.java
+++ b/sip/src/com/android/services/telephony/sip/SipProfileDb.java
@@ -16,11 +16,10 @@
package com.android.services.telephony.sip;
-import com.android.internal.os.AtomicFile;
-
import android.content.Context;
import android.net.sip.SipProfile;
import android.text.TextUtils;
+import android.util.AtomicFile;
import android.util.EventLog;
import android.util.Log;
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 320fc24..3f57cae 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -19,13 +19,16 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+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.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.UserManager;
@@ -109,6 +112,50 @@
* Click Listeners, handle click based on objects attached to UI.
*/
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ log("onReceive: " + intent.getAction());
+
+ if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) {
+ log("ACTION_AIRPLANE_MODE_CHANGED");
+
+ boolean isAirplaneModeOn = intent.getBooleanExtra("state", false);
+ handleAirplaneModeChange(isAirplaneModeOn);
+ }
+ }
+ };
+
+ private void handleAirplaneModeChange(boolean isAirplaneModeOn) {
+ PersistableBundle b = null;
+ if (mSubscriptionInfoHelper.hasSubId()) {
+ b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+ mSubscriptionInfoHelper.getSubId());
+ } else {
+ b = PhoneGlobals.getInstance().getCarrierConfig();
+ }
+
+ if (b != null && b.getBoolean(
+ CarrierConfigManager.KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL)) {
+ PreferenceScreen preferenceScreen = getPreferenceScreen();
+ Preference callForwarding = preferenceScreen.findPreference(
+ GsmUmtsCallOptions.CALL_FORWARDING_KEY);
+ Preference callBarring = preferenceScreen.findPreference(
+ GsmUmtsCallOptions.CALL_BARRING_KEY);
+ Preference additional = preferenceScreen.findPreference(
+ GsmUmtsCallOptions.ADDITIONAL_GSM_SETTINGS_KEY);
+ if (callForwarding != null) {
+ callForwarding.setEnabled(!isAirplaneModeOn);
+ }
+ if (callBarring != null) {
+ callBarring.setEnabled(!isAirplaneModeOn);
+ }
+ if (additional != null) {
+ additional.setEnabled(!isAirplaneModeOn);
+ }
+ }
+ }
+
// Click listener for all toggle events
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
@@ -181,9 +228,9 @@
startActivity(intent);
}
};
- builder.setMessage(getResources().getString(
+ builder.setMessage(getResourcesForSubId().getString(
R.string.enable_video_calling_dialog_msg))
- .setNeutralButton(getResources().getString(
+ .setNeutralButton(getResourcesForSubId().getString(
R.string.enable_video_calling_dialog_settings),
networkSettingsClickListener)
.setPositiveButton(android.R.string.ok, null)
@@ -202,7 +249,8 @@
if (DBG) log("onCreate: Intent is " + getIntent());
// Make sure we are running as an admin user.
- if (!UserManager.get(this).isAdminUser()) {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ if (!userManager.isAdminUser()) {
Toast.makeText(this, R.string.call_settings_admin_user_only,
Toast.LENGTH_SHORT).show();
finish();
@@ -210,10 +258,10 @@
}
mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
- mSubscriptionInfoHelper.setActionBarTitle(
- getActionBar(), getResources(), R.string.call_settings_with_label);
mPhone = mSubscriptionInfoHelper.getPhone();
- mTelecomManager = TelecomManager.from(this);
+ mSubscriptionInfoHelper.setActionBarTitle(
+ getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
+ mTelecomManager = getSystemService(TelecomManager.class);
}
private void updateImsManager(Phone phone) {
@@ -228,8 +276,8 @@
}
private void listenPhoneState(boolean listen) {
- TelephonyManager telephonyManager =
- (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(mPhone.getSubId());
telephonyManager.listen(mPhoneStateListener, listen
? PhoneStateListener.LISTEN_CALL_STATE : PhoneStateListener.LISTEN_NONE);
}
@@ -238,10 +286,7 @@
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
- // Use TelecomManager#getCallStete instead of 'state' parameter because it needs
- // to check the current state of all phone calls.
- boolean isCallStateIdle =
- mTelecomManager.getCallState() == TelephonyManager.CALL_STATE_IDLE;
+ boolean isCallStateIdle = state == TelephonyManager.CALL_STATE_IDLE;
if (mEnableVideoCalling != null) {
mEnableVideoCalling.setEnabled(isCallStateIdle);
}
@@ -267,6 +312,7 @@
protected void onPause() {
super.onPause();
listenPhoneState(false);
+ unregisterReceiver(mReceiver);
// Remove callback for provisioning changes.
try {
@@ -317,7 +363,7 @@
mButtonAutoRetry = (SwitchPreference) findPreference(BUTTON_RETRY_KEY);
mEnableVideoCalling = (SwitchPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
- mButtonWifiCalling = findPreference(getResources().getString(
+ mButtonWifiCalling = findPreference(getResourcesForSubId().getString(
R.string.wifi_calling_settings_key));
PersistableBundle carrierConfig =
@@ -379,6 +425,10 @@
} catch (ImsException e) {
Log.w(LOG_TAG, "onResume: Unable to register callback for provisioning changes.");
}
+
+ IntentFilter intentFilter =
+ new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ registerReceiver(mReceiver, intentFilter);
}
private void updateVtWfc() {
@@ -426,8 +476,7 @@
} else if (!mImsMgr.isWfcEnabledByPlatform() || !mImsMgr.isWfcProvisionedOnDevice()) {
prefSet.removePreference(mButtonWifiCalling);
} else {
- String title = SubscriptionManager.getResourcesForSubId(mPhone.getContext(),
- mPhone.getSubId()).getString(R.string.wifi_calling);
+ String title = getResourcesForSubId().getString(R.string.wifi_calling);
mButtonWifiCalling.setTitle(title);
int resId = com.android.internal.R.string.wifi_calling_off_summary;
@@ -449,7 +498,7 @@
if (DBG) log("Unexpected WFC mode value: " + wfcMode);
}
}
- mButtonWifiCalling.setSummary(resId);
+ mButtonWifiCalling.setSummary(getResourcesForSubId().getString(resId));
Intent intent = mButtonWifiCalling.getIntent();
if (intent != null) {
intent.putExtra(Settings.EXTRA_SUB_ID, mPhone.getSubId());
@@ -511,9 +560,9 @@
setIntent(newIntent);
mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
- mSubscriptionInfoHelper.setActionBarTitle(
- getActionBar(), getResources(), R.string.call_settings_with_label);
mPhone = mSubscriptionInfoHelper.getPhone();
+ mSubscriptionInfoHelper.setActionBarTitle(
+ getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
}
private static void log(String msg) {
@@ -542,4 +591,12 @@
activity.startActivity(intent);
activity.finish();
}
+
+ private Resources getResourcesForSubId() {
+ if (mPhone != null) {
+ return SubscriptionManager.getResourcesForSubId(mPhone.getContext(), mPhone.getSubId());
+ } else {
+ return getResources();
+ }
+ }
}
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index d1eb3dc..d451ccf 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -20,6 +20,7 @@
import static android.service.carrier.CarrierService.ICarrierServiceWrapper.RESULT_ERROR;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -55,7 +56,7 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.SubscriptionInfoUpdater;
import com.android.internal.telephony.TelephonyPermissions;
-import com.android.internal.util.ArrayUtils;
+import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
@@ -94,6 +95,8 @@
private PersistableBundle[] mConfigFromDefaultApp;
// Carrier configs from privileged carrier config app, indexed by phoneID.
private PersistableBundle[] mConfigFromCarrierApp;
+ // Persistent Carrier configs that are provided via the override test API, indexed by phone ID.
+ private PersistableBundle[] mPersistentOverrideConfigs;
// Carrier configs that are provided via the override test API, indexed by phone ID.
private PersistableBundle[] mOverrideConfigs;
// Service connection for binding to config app.
@@ -107,7 +110,7 @@
private final BroadcastReceiver mBootReceiver = new ConfigLoaderBroadcastReceiver();
// Broadcast receiver for SIM and pkg intents, register intent filter in constructor.
private final BroadcastReceiver mPackageReceiver = new ConfigLoaderBroadcastReceiver();
- private final LocalLog mCarrierConfigLoadingLog = new LocalLog(50);
+ private final LocalLog mCarrierConfigLoadingLog = new LocalLog(100);
// Message codes; see mHandler below.
@@ -141,6 +144,8 @@
private static final int EVENT_FETCH_CARRIER_TIMEOUT = 15;
// SubscriptionInfoUpdater has finished updating the sub for the carrier config.
private static final int EVENT_SUBSCRIPTION_INFO_UPDATED = 16;
+ // Multi-SIM config changed.
+ private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 17;
private static final int BIND_TIMEOUT_MILLIS = 30000;
@@ -149,6 +154,8 @@
private static final String TAG_VERSION = "package_version";
private static final String TAG_BUNDLE = "bundle_data";
+ private static final String OVERRIDE_PACKAGE_ADDITION = "-override";
+
// SharedPreferences key for last known build fingerprint.
private static final String KEY_FINGERPRINT = "build_fingerprint";
@@ -174,31 +181,26 @@
public void handleMessage(Message msg) {
final int phoneId = msg.arg1;
logWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+ if (!SubscriptionManager.isValidPhoneId(phoneId)
+ && msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
+ return;
+ }
switch (msg.what) {
case EVENT_CLEAR_CONFIG:
{
- /* Ignore clear configuration request if device is being shutdown. */
- Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone != null) {
- if (phone.isShuttingDown()) {
- break;
- }
- }
-
- mConfigFromDefaultApp[phoneId] = null;
- mConfigFromCarrierApp[phoneId] = null;
- mServiceConnection[phoneId] = null;
- broadcastConfigChangedIntent(phoneId, false);
+ clearConfigForPhone(phoneId, true);
break;
}
case EVENT_SYSTEM_UNLOCKED:
{
- for (int i = 0; i < TelephonyManager.from(mContext).getMaxPhoneCount(); ++i) {
+ 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.
if (mHasSentConfigChange[i]) {
+ logWithLocalLog("System unlocked");
updateConfigForPhoneId(i);
}
}
@@ -211,8 +213,10 @@
// Only update if there are cached config removed to avoid updating config for
// unrelated packages.
if (clearCachedConfigForPackage(carrierPackageName)) {
- int numPhones = TelephonyManager.from(mContext).getMaxPhoneCount();
+ int numPhones = TelephonyManager.from(mContext).getActiveModemCount();
for (int i = 0; i < numPhones; ++i) {
+ logWithLocalLog("Package changed: " + carrierPackageName
+ + ", phone=" + i);
updateConfigForPhoneId(i);
}
}
@@ -221,8 +225,17 @@
case EVENT_DO_FETCH_DEFAULT:
{
- final PersistableBundle config =
- restoreConfigFromXml(mPlatformCarrierConfigPackage, phoneId);
+ // Restore persistent override values.
+ PersistableBundle config = restoreConfigFromXml(
+ mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
+ if (config != null) {
+ log("Loaded persistent override config from XML. package="
+ + mPlatformCarrierConfigPackage
+ + " phoneId=" + phoneId);
+ mPersistentOverrideConfigs[phoneId] = config;
+ }
+
+ config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
if (config != null) {
log(
"Loaded config from XML. package="
@@ -284,8 +297,8 @@
}
PersistableBundle config =
resultData.getParcelable(KEY_CONFIG_BUNDLE);
- saveConfigToXml(
- mPlatformCarrierConfigPackage, phoneId, config);
+ saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
+ carrierId, config);
mConfigFromDefaultApp[phoneId] = config;
sendMessage(
obtainMessage(
@@ -352,7 +365,7 @@
{
final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
final PersistableBundle config =
- restoreConfigFromXml(carrierPackageName, phoneId);
+ restoreConfigFromXml(carrierPackageName, "", phoneId);
if (config != null) {
log(
"Loaded config from XML. package="
@@ -415,8 +428,8 @@
}
PersistableBundle config =
resultData.getParcelable(KEY_CONFIG_BUNDLE);
- saveConfigToXml(
- getCarrierPackageForPhoneId(phoneId), phoneId, config);
+ saveConfigToXml(getCarrierPackageForPhoneId(phoneId), "",
+ phoneId, carrierId, config);
mConfigFromCarrierApp[phoneId] = config;
sendMessage(
obtainMessage(
@@ -494,6 +507,9 @@
case EVENT_SUBSCRIPTION_INFO_UPDATED:
broadcastConfigChangedIntent(phoneId);
break;
+ case EVENT_MULTI_SIM_CONFIG_CHANGED:
+ onMultiSimConfigChanged();
+ break;
}
}
}
@@ -523,9 +539,10 @@
pkgFilter.addDataScheme("package");
context.registerReceiver(mPackageReceiver, pkgFilter);
- int numPhones = TelephonyManager.from(context).getMaxPhoneCount();
+ int numPhones = TelephonyManager.from(context).getSupportedModemCount();
mConfigFromDefaultApp = new PersistableBundle[numPhones];
mConfigFromCarrierApp = new PersistableBundle[numPhones];
+ mPersistentOverrideConfigs = new PersistableBundle[numPhones];
mOverrideConfigs = new PersistableBundle[numPhones];
mServiceConnection = new CarrierServiceConnection[numPhones];
mHasSentConfigChange = new boolean[numPhones];
@@ -553,6 +570,23 @@
}
}
+ private void clearConfigForPhone(int phoneId, boolean sendBroadcast) {
+ /* Ignore clear configuration request if device is being shutdown. */
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ if (phone.isShuttingDown()) {
+ return;
+ }
+ }
+
+ mConfigFromDefaultApp[phoneId] = null;
+ mConfigFromCarrierApp[phoneId] = null;
+ mServiceConnection[phoneId] = null;
+ mHasSentConfigChange[phoneId] = false;
+
+ if (sendBroadcast) broadcastConfigChangedIntent(phoneId, false);
+ }
+
private void notifySubscriptionInfoUpdater(int phoneId) {
String configPackagename;
PersistableBundle configToSend;
@@ -566,6 +600,14 @@
configPackagename = mPlatformCarrierConfigPackage;
configToSend = mConfigFromDefaultApp[phoneId];
}
+
+ // mOverrideConfigs is for testing. And it will override current configs.
+ PersistableBundle config = mOverrideConfigs[phoneId];
+ if (config != null) {
+ configToSend = new PersistableBundle(configToSend);
+ configToSend.putAll(config);
+ }
+
mSubscriptionInfoUpdater.updateSubscriptionByCarrierConfigAndNotifyComplete(
phoneId, configPackagename, configToSend,
mHandler.obtainMessage(EVENT_SUBSCRIPTION_INFO_UPDATED, phoneId, -1));
@@ -710,10 +752,13 @@
* In case of errors or invalid input, no file will be written.
*
* @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.
*/
- private void saveConfigToXml(String packageName, int phoneId, PersistableBundle config) {
+ private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
+ CarrierIdentifier carrierId, PersistableBundle config) {
if (SubscriptionManager.getSimStateForSlotIndex(phoneId)
!= TelephonyManager.SIM_STATE_LOADED) {
loge("Skip save config because SIM records are not loaded.");
@@ -721,7 +766,7 @@
}
final String iccid = getIccIdForPhoneId(phoneId);
- final int cid = getSpecificCarrierIdForPhoneId(phoneId);
+ final int cid = carrierId.getSpecificCarrierId();
if (packageName == null || iccid == null) {
loge("Cannot save config with null packageName or iccid.");
return;
@@ -747,7 +792,7 @@
try {
outFile = new FileOutputStream(
new File(mContext.getFilesDir(),
- getFilenameForConfig(packageName, iccid, cid)));
+ getFilenameForConfig(packageName, extraString, iccid, cid)));
FastXmlSerializer out = new FastXmlSerializer();
out.setOutput(outFile, "utf-8");
out.startDocument("utf-8", true);
@@ -781,11 +826,13 @@
* current version, then null will be returned.
*
* @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.
* @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.
*/
- private PersistableBundle restoreConfigFromXml(String packageName, int phoneId) {
+ private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
+ int phoneId) {
final String version = getPackageVersion(packageName);
if (version == null) {
loge("Failed to get package version for: " + packageName);
@@ -805,11 +852,12 @@
}
PersistableBundle restoredBundle = null;
+ File file = null;
FileInputStream inFile = null;
try {
- inFile = new FileInputStream(
- new File(mContext.getFilesDir(),
- getFilenameForConfig(packageName, iccid, cid)));
+ file = new File(mContext.getFilesDir(),
+ getFilenameForConfig(packageName, extraString, iccid, cid));
+ inFile = new FileInputStream(file);
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(inFile, "utf-8");
@@ -831,7 +879,9 @@
inFile.close();
}
catch (FileNotFoundException e) {
- loge(e.toString());
+ // 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 (XmlPullParserException e) {
loge(e.toString());
@@ -872,13 +922,13 @@
}
/** Builds a canonical file name for a config file. */
- private String getFilenameForConfig(@NonNull String packageName, @NonNull String iccid,
- int cid) {
+ private String getFilenameForConfig(@NonNull String packageName, @NonNull String extraString,
+ @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
// cases SIM OTA resolves to different carrier while iccid remains the same.
- return "carrierconfig-" + packageName + "-" + iccid + "-" + cid + ".xml";
+ return "carrierconfig-" + packageName + extraString + "-" + iccid + "-" + cid + ".xml";
}
/** Return the current version code of a package, or null if the name is not found. */
@@ -908,6 +958,13 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
}
+ private void onMultiSimConfigChanged() {
+ for (int i = TelephonyManager.from(mContext).getActiveModemCount();
+ i < mConfigFromDefaultApp.length; i++) {
+ clearConfigForPhone(i, false);
+ }
+ }
+
@Override
public @NonNull PersistableBundle getConfigForSubId(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
@@ -921,24 +978,32 @@
PersistableBundle config = mConfigFromDefaultApp[phoneId];
if (config != null) {
retConfig.putAll(config);
- retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+ if (getCarrierPackageForPhoneId(phoneId) == null) {
+ retConfig.putBoolean(
+ CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+ }
}
config = mConfigFromCarrierApp[phoneId];
if (config != null) {
retConfig.putAll(config);
retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
}
+ config = mPersistentOverrideConfigs[phoneId];
+ if (config != null) {
+ retConfig.putAll(config);
+ retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+ }
config = mOverrideConfigs[phoneId];
if (config != null) {
retConfig.putAll(config);
- retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
}
}
return retConfig;
}
@Override
- public void overrideConfig(int subscriptionId, PersistableBundle overrides) {
+ public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrides,
+ boolean persistent) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE, null);
//TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
@@ -947,18 +1012,38 @@
log("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
return;
}
+ overrideConfig(mOverrideConfigs, phoneId, overrides);
- if (overrides == null) {
- mOverrideConfigs[phoneId] = new PersistableBundle();
- } else if (mOverrideConfigs[phoneId] == null) {
- mOverrideConfigs[phoneId] = overrides;
- } else {
- mOverrideConfigs[phoneId].putAll(overrides);
+ if (persistent) {
+ overrideConfig(mPersistentOverrideConfigs, phoneId, overrides);
+
+ if (overrides != null) {
+ final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
+ saveConfigToXml(mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId,
+ carrierId, mPersistentOverrideConfigs[phoneId]);
+ } else {
+ final String iccid = getIccIdForPhoneId(phoneId);
+ final int cid = getSpecificCarrierIdForPhoneId(phoneId);
+ String fileName = getFilenameForConfig(mPlatformCarrierConfigPackage,
+ OVERRIDE_PACKAGE_ADDITION, iccid, cid);
+ File fileToDelete = new File(mContext.getFilesDir(), fileName);
+ fileToDelete.delete();
+ }
}
-
notifySubscriptionInfoUpdater(phoneId);
}
+ private void overrideConfig(@NonNull PersistableBundle[] currentOverrides, int phoneId,
+ @Nullable PersistableBundle overrides) {
+ if (overrides == null) {
+ currentOverrides[phoneId] = new PersistableBundle();
+ } else if (currentOverrides[phoneId] == null) {
+ currentOverrides[phoneId] = overrides;
+ } else {
+ currentOverrides[phoneId].putAll(overrides);
+ }
+ }
+
@Override
public void notifyConfigChangedForSubId(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
@@ -1030,6 +1115,8 @@
// display ConfigFromCarrierApp
printConfig(mConfigFromCarrierApp[i], pw, "mConfigFromCarrierApp");
pw.println("");
+ printConfig(mPersistentOverrideConfigs[i], pw, "mPersistentOverrideConfigs");
+ pw.println("");
printConfig(mOverrideConfigs[i], pw, "mOverrideConfigs");
}
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 74b8a45..35af20d 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -136,9 +136,9 @@
a.recycle();
//get the summary settings, use CheckBoxPreference as the standard.
- 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 = context.obtainStyledAttributes(attrs, R.styleable.CheckBoxPreference, 0, 0);
+ mSummaryOn = a.getString(R.styleable.CheckBoxPreference_summaryOn);
+ mSummaryOff = a.getString(R.styleable.CheckBoxPreference_summaryOff);
a.recycle();
}
diff --git a/src/com/android/phone/EmergencyActionGroup.java b/src/com/android/phone/EmergencyActionGroup.java
index b8bf7a8..53ec1eb 100644
--- a/src/com/android/phone/EmergencyActionGroup.java
+++ b/src/com/android/phone/EmergencyActionGroup.java
@@ -157,21 +157,19 @@
public void onClick(View v) {
Intent intent = (Intent) v.getTag(R.id.tag_intent);
- switch (v.getId()) {
- case R.id.action1:
- case R.id.action2:
- case R.id.action3:
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- getContext().startActivity(intent);
- } else {
- revealTheButton(v);
- }
- break;
- case R.id.selected_container:
- if (!mHiding) {
- getContext().startActivity(intent);
- }
- break;
+ if (v.getId() == R.id.action1 || v.getId() == R.id.action2 || v.getId() == R.id.action3) {
+ AccessibilityManager accessibilityMgr =
+ (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityMgr.isTouchExplorationEnabled()) {
+ getContext().startActivity(intent);
+ } else {
+ revealTheButton(v);
+ }
+ } else if (v.getId() == R.id.selected_container) {
+ if (!mHiding) {
+ getContext().startActivity(intent);
+ }
}
}
diff --git a/src/com/android/phone/EmergencyAssistanceHelper.java b/src/com/android/phone/EmergencyAssistanceHelper.java
index 33788d3..79ee515 100644
--- a/src/com/android/phone/EmergencyAssistanceHelper.java
+++ b/src/com/android/phone/EmergencyAssistanceHelper.java
@@ -31,8 +31,6 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.internal.util.CollectionUtils;
-
import java.util.List;
/**
@@ -120,8 +118,9 @@
private static String getDefaultEmergencyPackage(Context context) {
long identity = Binder.clearCallingIdentity();
try {
- return CollectionUtils.firstOrNull(context.getSystemService(RoleManager.class)
- .getRoleHolders(RoleManager.ROLE_EMERGENCY));
+ List<String> roleHolders = context.getSystemService(RoleManager.class)
+ .getRoleHolders(RoleManager.ROLE_EMERGENCY);
+ return roleHolders.isEmpty() ? null : roleHolders.get(0);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 3cf74c8..7531aca 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -31,6 +32,7 @@
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.net.Uri;
@@ -68,12 +70,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.colorextraction.drawable.ScrimDrawable;
-import com.android.phone.EmergencyDialerMetricsLogger.DialedFrom;
-import com.android.phone.EmergencyDialerMetricsLogger.PhoneNumberType;
-import com.android.phone.EmergencyDialerMetricsLogger.UiModeErrorCode;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.util.ViewUtil;
import com.android.phone.common.widget.ResizingTextEditText;
@@ -100,7 +96,8 @@
*/
public class EmergencyDialer extends Activity implements View.OnClickListener,
View.OnLongClickListener, View.OnKeyListener, TextWatcher,
- DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener,
+ DialpadKeyButton.OnPressedListener,
+ WallpaperManager.OnColorsChangedListener,
EmergencyShortcutButton.OnConfirmClickListener,
EmergencyInfoGroup.OnConfirmClickListener {
@@ -212,8 +209,7 @@
private String mLastNumber; // last number we tried to dial. Used to restore error dialog.
// Background gradient
- private ColorExtractor mColorExtractor;
- private ScrimDrawable mBackgroundDrawable;
+ private ColorDrawable mBackgroundDrawable;
private boolean mSupportsDarkText;
private boolean mIsWfcEmergencyCallingWarningEnabled;
@@ -222,8 +218,6 @@
private int mEntryType;
private ShortcutViewUtils.Config mShortcutViewConfig;
- private EmergencyDialerMetricsLogger mMetricsLogger;
-
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
@@ -258,8 +252,6 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mMetricsLogger = new EmergencyDialerMetricsLogger(this);
-
mEntryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
Log.d(LOG_TAG, "Launched from " + entryTypeToString(mEntryType));
@@ -276,15 +268,13 @@
Log.d(LOG_TAG, "Enable emergency dialer shortcut: "
+ mShortcutViewConfig.isEnabled());
- mColorExtractor = new ColorExtractor(this);
-
if (mShortcutViewConfig.isEnabled()) {
// Shortcut view doesn't support dark text theme.
updateTheme(false);
} else {
- GradientColors lockScreenColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
- ColorExtractor.TYPE_EXTRA_DARK);
- updateTheme(lockScreenColors.supportsDarkText());
+ WallpaperColors wallpaperColors =
+ getWallpaperManager().getWallpaperColors(WallpaperManager.FLAG_LOCK);
+ updateTheme(supportsDarkText(wallpaperColors));
}
setContentView(R.layout.emergency_dialer);
@@ -298,7 +288,7 @@
mDefaultDigitsTextSize = mDigits.getScaledTextSize();
maybeAddNumberFormatting();
- mBackgroundDrawable = new ScrimDrawable();
+ mBackgroundDrawable = new ColorDrawable();
Point displaySize = new Point();
((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getSize(displaySize);
@@ -471,16 +461,15 @@
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
- switch (view.getId()) {
- case R.id.digits:
- // Happen when "Done" button of the IME is pressed. This can happen when this
- // Activity is forced into landscape mode due to a desk dock.
- if (keyCode == KeyEvent.KEYCODE_ENTER
- && event.getAction() == KeyEvent.ACTION_UP) {
- placeCall();
- return true;
- }
- break;
+ if (view.getId()
+ == R.id.digits) { // Happen when "Done" button of the IME is pressed. This can
+ // happen when this
+ // Activity is forced into landscape mode due to a desk dock.
+ if (keyCode == KeyEvent.KEYCODE_ENTER
+ && event.getAction() == KeyEvent.ACTION_UP) {
+ placeCall();
+ return true;
+ }
}
return false;
}
@@ -501,10 +490,6 @@
if (!TextUtils.isEmpty(phoneNumber)) {
if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
- // Write metrics when user has intention to make a call from a shortcut button.
- mMetricsLogger.logPlaceCall(DialedFrom.SHORTCUT, PhoneNumberType.HAS_SHORTCUT,
- mShortcutViewConfig.getPhoneInfo());
-
placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
mShortcutViewConfig.getPhoneInfo());
} else {
@@ -524,27 +509,22 @@
@Override
public void onClick(View view) {
- switch (view.getId()) {
- case R.id.deleteButton: {
- keyPressed(KeyEvent.KEYCODE_DEL);
- return;
+ if (view.getId() == R.id.deleteButton) {
+ keyPressed(KeyEvent.KEYCODE_DEL);
+ return;
+ } else if (view.getId() == R.id.floating_action_button) {
+ view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ placeCall();
+ return;
+ } else if (view.getId() == R.id.digits) {
+ if (mDigits.length() != 0) {
+ mDigits.setCursorVisible(true);
}
- case R.id.floating_action_button: {
- view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
- placeCall();
- return;
- }
- case R.id.digits: {
- if (mDigits.length() != 0) {
- mDigits.setCursorVisible(true);
- }
- return;
- }
- case R.id.floating_action_button_dialpad: {
- mDigits.getText().clear();
- switchView(mDialpadView, mEmergencyShortcutView, true);
- return;
- }
+ return;
+ } else if (view.getId() == R.id.floating_action_button_dialpad) {
+ mDigits.getText().clear();
+ switchView(mDialpadView, mEmergencyShortcutView, true);
+ return;
}
}
@@ -553,67 +533,54 @@
if (!pressed) {
return;
}
- switch (view.getId()) {
- case R.id.one: {
- playTone(ToneGenerator.TONE_DTMF_1);
- keyPressed(KeyEvent.KEYCODE_1);
- return;
- }
- case R.id.two: {
- playTone(ToneGenerator.TONE_DTMF_2);
- keyPressed(KeyEvent.KEYCODE_2);
- return;
- }
- case R.id.three: {
- playTone(ToneGenerator.TONE_DTMF_3);
- keyPressed(KeyEvent.KEYCODE_3);
- return;
- }
- case R.id.four: {
- playTone(ToneGenerator.TONE_DTMF_4);
- keyPressed(KeyEvent.KEYCODE_4);
- return;
- }
- case R.id.five: {
- playTone(ToneGenerator.TONE_DTMF_5);
- keyPressed(KeyEvent.KEYCODE_5);
- return;
- }
- case R.id.six: {
- playTone(ToneGenerator.TONE_DTMF_6);
- keyPressed(KeyEvent.KEYCODE_6);
- return;
- }
- case R.id.seven: {
- playTone(ToneGenerator.TONE_DTMF_7);
- keyPressed(KeyEvent.KEYCODE_7);
- return;
- }
- case R.id.eight: {
- playTone(ToneGenerator.TONE_DTMF_8);
- keyPressed(KeyEvent.KEYCODE_8);
- return;
- }
- case R.id.nine: {
- playTone(ToneGenerator.TONE_DTMF_9);
- keyPressed(KeyEvent.KEYCODE_9);
- return;
- }
- case R.id.zero: {
- playTone(ToneGenerator.TONE_DTMF_0);
- keyPressed(KeyEvent.KEYCODE_0);
- return;
- }
- case R.id.pound: {
- playTone(ToneGenerator.TONE_DTMF_P);
- keyPressed(KeyEvent.KEYCODE_POUND);
- return;
- }
- case R.id.star: {
- playTone(ToneGenerator.TONE_DTMF_S);
- keyPressed(KeyEvent.KEYCODE_STAR);
- return;
- }
+ if (view.getId() == R.id.one) {
+ playTone(ToneGenerator.TONE_DTMF_1);
+ keyPressed(KeyEvent.KEYCODE_1);
+ return;
+ } else if (view.getId() == R.id.two) {
+ playTone(ToneGenerator.TONE_DTMF_2);
+ keyPressed(KeyEvent.KEYCODE_2);
+ return;
+ } else if (view.getId() == R.id.three) {
+ playTone(ToneGenerator.TONE_DTMF_3);
+ keyPressed(KeyEvent.KEYCODE_3);
+ return;
+ } else if (view.getId() == R.id.four) {
+ playTone(ToneGenerator.TONE_DTMF_4);
+ keyPressed(KeyEvent.KEYCODE_4);
+ return;
+ } else if (view.getId() == R.id.five) {
+ playTone(ToneGenerator.TONE_DTMF_5);
+ keyPressed(KeyEvent.KEYCODE_5);
+ return;
+ } else if (view.getId() == R.id.six) {
+ playTone(ToneGenerator.TONE_DTMF_6);
+ keyPressed(KeyEvent.KEYCODE_6);
+ return;
+ } else if (view.getId() == R.id.seven) {
+ playTone(ToneGenerator.TONE_DTMF_7);
+ keyPressed(KeyEvent.KEYCODE_7);
+ return;
+ } else if (view.getId() == R.id.eight) {
+ playTone(ToneGenerator.TONE_DTMF_8);
+ keyPressed(KeyEvent.KEYCODE_8);
+ return;
+ } else if (view.getId() == R.id.nine) {
+ playTone(ToneGenerator.TONE_DTMF_9);
+ keyPressed(KeyEvent.KEYCODE_9);
+ return;
+ } else if (view.getId() == R.id.zero) {
+ playTone(ToneGenerator.TONE_DTMF_0);
+ keyPressed(KeyEvent.KEYCODE_0);
+ return;
+ } else if (view.getId() == R.id.pound) {
+ playTone(ToneGenerator.TONE_DTMF_P);
+ keyPressed(KeyEvent.KEYCODE_POUND);
+ return;
+ } else if (view.getId() == R.id.star) {
+ playTone(ToneGenerator.TONE_DTMF_S);
+ keyPressed(KeyEvent.KEYCODE_STAR);
+ return;
}
}
@@ -623,16 +590,13 @@
@Override
public boolean onLongClick(View view) {
int id = view.getId();
- switch (id) {
- case R.id.deleteButton: {
- mDigits.getText().clear();
- return true;
- }
- case R.id.zero: {
- removePreviousDigitIfPossible();
- keyPressed(KeyEvent.KEYCODE_PLUS);
- return true;
- }
+ if (id == R.id.deleteButton) {
+ mDigits.getText().clear();
+ return true;
+ } else if (id == R.id.zero) {
+ removePreviousDigitIfPossible();
+ keyPressed(KeyEvent.KEYCODE_PLUS);
+ return true;
}
return false;
}
@@ -641,21 +605,20 @@
protected void onStart() {
super.onStart();
- mMetricsLogger.logLaunchEmergencyDialer(mEntryType,
- mShortcutViewConfig.isEnabled() ? UiModeErrorCode.SUCCESS
- : UiModeErrorCode.UNSPECIFIED_ERROR);
-
if (mShortcutViewConfig.isEnabled()) {
// Shortcut view doesn't support dark text theme.
- mBackgroundDrawable.setColor(Color.BLACK, false);
+ mBackgroundDrawable.setColor(Color.BLACK);
updateTheme(false);
} else {
- mColorExtractor.addOnColorsChangedListener(this);
- GradientColors lockScreenColors = mColorExtractor.getColors(WallpaperManager.FLAG_LOCK,
- ColorExtractor.TYPE_EXTRA_DARK);
- // Do not animate when view isn't visible yet, just set an initial state.
- mBackgroundDrawable.setColor(lockScreenColors.getMainColor(), false);
- updateTheme(lockScreenColors.supportsDarkText());
+ WallpaperManager wallpaperManager = getWallpaperManager();
+ if (wallpaperManager.isWallpaperSupported()) {
+ wallpaperManager.addOnColorsChangedListener(this, null);
+ }
+
+ WallpaperColors wallpaperColors =
+ wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
+ mBackgroundDrawable.setColor(getPrimaryColor(wallpaperColors));
+ updateTheme(supportsDarkText(wallpaperColors));
}
if (mShortcutViewConfig.isEnabled()) {
@@ -696,7 +659,11 @@
@Override
protected void onStop() {
super.onStop();
- mColorExtractor.removeOnColorsChangedListener(this);
+
+ WallpaperManager wallpaperManager = getWallpaperManager();
+ if (wallpaperManager.isWallpaperSupported()) {
+ wallpaperManager.removeOnColorsChangedListener(this);
+ }
}
/**
@@ -741,28 +708,20 @@
// nothing and just returns input number.
mLastNumber = PhoneNumberUtils.convertToEmergencyNumber(this, mLastNumber);
- @DialedFrom final int dialedFrom =
- mShortcutViewConfig.isEnabled() ? DialedFrom.FASTER_LAUNCHER_DIALPAD
- : DialedFrom.TRADITIONAL_DIALPAD;
- @PhoneNumberType final int phoneNumberType;
-
boolean isEmergencyNumber;
ShortcutViewUtils.PhoneInfo phoneToMakeCall = null;
if (mShortcutAdapter != null && mShortcutAdapter.hasShortcut(mLastNumber)) {
isEmergencyNumber = true;
phoneToMakeCall = mShortcutViewConfig.getPhoneInfo();
- phoneNumberType = PhoneNumberType.HAS_SHORTCUT;
} else if (mShortcutViewConfig.hasPromotedEmergencyNumber(mLastNumber)) {
// If a number from SIM/network/... is categoried as police/ambulance/fire,
// hasPromotedEmergencyNumber() will return true, but it maybe not promoted as a
// shortcut button because a number provided by database has higher priority.
isEmergencyNumber = true;
phoneToMakeCall = mShortcutViewConfig.getPhoneInfo();
- phoneNumberType = PhoneNumberType.NO_SHORTCUT;
} else {
isEmergencyNumber = getSystemService(TelephonyManager.class)
.isEmergencyNumber(mLastNumber);
- phoneNumberType = PhoneNumberType.NO_SHORTCUT;
}
if (isEmergencyNumber) {
@@ -775,19 +734,11 @@
return;
}
- // Write metrics when user has intention to make a call from dialpad
- mMetricsLogger.logPlaceCall(dialedFrom, phoneNumberType, phoneToMakeCall);
-
placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
phoneToMakeCall);
} else {
if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
- // Write metrics when user has intention to make a call of a non-emergency number, even
- // this number is rejected.
- mMetricsLogger.logPlaceCall(dialedFrom, PhoneNumberType.NOT_EMERGENCY_NUMBER,
- phoneToMakeCall);
-
showDialog(BAD_EMERGENCY_NUMBER_DIALOG);
}
mDigits.getText().delete(0, mDigits.getText().length());
@@ -937,12 +888,10 @@
}
@Override
- public void onColorsChanged(ColorExtractor extractor, int which) {
+ public void onColorsChanged(WallpaperColors colors, int which) {
if ((which & WallpaperManager.FLAG_LOCK) != 0) {
- GradientColors colors = extractor.getColors(WallpaperManager.FLAG_LOCK,
- ColorExtractor.TYPE_EXTRA_DARK);
- mBackgroundDrawable.setColor(colors.getMainColor(), true /* animated */);
- updateTheme(colors.supportsDarkText());
+ mBackgroundDrawable.setColor(getPrimaryColor(colors));
+ updateTheme(supportsDarkText(colors));
}
}
@@ -1097,11 +1046,6 @@
if (mEmergencyShortcutButtonList.size() > 1) {
emergencyNumberTitle.setText(getString(
R.string.numerous_emergency_numbers_title));
- // Update mEmergencyInfoGroup margin to avoid UI overlay when
- // emergency shortcut button more than 2.
- if (mEmergencyShortcutButtonList.size() > 2) {
- mEmergencyInfoGroup.updateLayoutMargin();
- }
} else {
emergencyNumberTitle.setText(getText(R.string.single_emergency_number_title));
}
@@ -1236,4 +1180,28 @@
return "Unknown-" + callSource;
}
}
+
+ private WallpaperManager getWallpaperManager() {
+ return getSystemService(WallpaperManager.class);
+ }
+
+ private static boolean supportsDarkText(WallpaperColors colors) {
+ if (colors != null) {
+ return (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+ }
+ // It's possible that wallpaper colors are null (e.g. when colors are being
+ // processed or a live wallpaper is used). In this case, fallback to same
+ // behavior as when shortcut view is enabled.
+ return false;
+ }
+
+ private static int getPrimaryColor(WallpaperColors colors) {
+ if (colors != null) {
+ return colors.getPrimaryColor().toArgb();
+ }
+ // It's possible that wallpaper colors are null (e.g. when colors are being
+ // processed or a live wallpaper is used). In this case, fallback to same
+ // behavior as when shortcut view is enabled.
+ return Color.BLACK;
+ }
}
diff --git a/src/com/android/phone/EmergencyDialerMetricsLogger.java b/src/com/android/phone/EmergencyDialerMetricsLogger.java
deleted file mode 100644
index b9ba352..0000000
--- a/src/com/android/phone/EmergencyDialerMetricsLogger.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * 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 android.annotation.IntDef;
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.Build;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * EmergencyCallMetricsLogger is a utility to collect metrics of emergency calls
- */
-class EmergencyDialerMetricsLogger {
- private static final String LOG_TAG = "EmergencyDialerLogger";
-
- @IntDef({
- DialedFrom.TRADITIONAL_DIALPAD,
- DialedFrom.SHORTCUT,
- DialedFrom.FASTER_LAUNCHER_DIALPAD,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface DialedFrom {
- int TRADITIONAL_DIALPAD = 0;
- int SHORTCUT = 1;
- int FASTER_LAUNCHER_DIALPAD = 2;
- }
-
- @IntDef({
- LaunchedFrom.UNDEFINED,
- LaunchedFrom.LOCK_SCREEN,
- LaunchedFrom.POWER_KEY_MENU,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface LaunchedFrom {
- int UNDEFINED = 0;
- int LOCK_SCREEN = 1;
- int POWER_KEY_MENU = 2;
- }
-
- @IntDef({
- PhoneNumberType.HAS_SHORTCUT,
- PhoneNumberType.NO_SHORTCUT,
- PhoneNumberType.NOT_EMERGENCY_NUMBER,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface PhoneNumberType {
- int HAS_SHORTCUT = 0;
- int NO_SHORTCUT = 1;
- int NOT_EMERGENCY_NUMBER = 2;
- }
-
- @IntDef({
- UiModeErrorCode.UNSPECIFIED_ERROR,
- UiModeErrorCode.SUCCESS,
- UiModeErrorCode.CONFIG_ENTRY_POINT,
- UiModeErrorCode.CONFIG_SIM_OPERATOR,
- UiModeErrorCode.UNSUPPORTED_COUNTRY,
- UiModeErrorCode.AIRPLANE_MODE,
- UiModeErrorCode.NO_PROMOTED_NUMBER,
- UiModeErrorCode.NO_CAPABLE_PHONE,
- })
- @Retention(RetentionPolicy.SOURCE)
- @interface UiModeErrorCode {
- int UNSPECIFIED_ERROR = -1;
- int SUCCESS = 0;
- int CONFIG_ENTRY_POINT = 1;
- int CONFIG_SIM_OPERATOR = 2;
- int UNSUPPORTED_COUNTRY = 3;
- int AIRPLANE_MODE = 4;
- int NO_PROMOTED_NUMBER = 5;
- int NO_CAPABLE_PHONE = 6;
- }
-
- private class TelephonyInfo {
- private final String mNetworkCountryIso;
- private final String mNetworkOperator;
-
- TelephonyInfo(String networkCountryIso, String networkOperator) {
- mNetworkCountryIso = networkCountryIso;
- mNetworkOperator = networkOperator;
- }
- }
-
- private final MetricsLogger mMetricsLogger = new MetricsLogger();
-
- private final Context mAppContext;
-
- @LaunchedFrom
- private int mLaunchedFrom;
- @UiModeErrorCode
- private int mUiModeErrorCode;
-
- EmergencyDialerMetricsLogger(Context context) {
- mAppContext = context.getApplicationContext();
- }
-
- /**
- * Log when Emergency Dialer is launched.
- * - Where the user launch Emergency Dialer from.
- * - Whether shortcut view is enabled, and the reason why it's not enabled.
- *
- * @param entryType
- * @param uiModeErrorCode
- */
- public void logLaunchEmergencyDialer(int entryType,
- @UiModeErrorCode int uiModeErrorCode) {
- final @EmergencyDialerMetricsLogger.LaunchedFrom int launchedFrom;
- if (entryType == EmergencyDialer.ENTRY_TYPE_LOCKSCREEN_BUTTON) {
- launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.LOCK_SCREEN;
- } else if (entryType == EmergencyDialer.ENTRY_TYPE_POWER_MENU) {
- launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.POWER_KEY_MENU;
- } else {
- launchedFrom = EmergencyDialerMetricsLogger.LaunchedFrom.UNDEFINED;
- }
-
- mLaunchedFrom = launchedFrom;
- mUiModeErrorCode = uiModeErrorCode;
- }
-
- /**
- * Log when user tring to place an emergency call.
- * - Which UI (traditional dialpad, shortcut button, dialpad in shortcut view) the user place
- * the call from.
- * - The number is promoted in shortcut view or not, or not even an emergency number?
- * - Whether the device is locked.
- * - Network country ISO and network operator.
- *
- * @param dialedFrom
- * @param phoneNumberType
- * @param phoneInfo
- */
- public void logPlaceCall(@DialedFrom int dialedFrom,
- @PhoneNumberType int phoneNumberType,
- @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) {
- TelephonyInfo telephonyInfo = getTelephonyInfo(phoneNumberType, phoneInfo);
- final KeyguardManager keyguard = mAppContext.getSystemService(KeyguardManager.class);
-
- logBeforeMakeCall(dialedFrom, phoneNumberType, keyguard.isKeyguardLocked(),
- telephonyInfo.mNetworkCountryIso, telephonyInfo.mNetworkOperator);
- }
-
- private TelephonyInfo getTelephonyInfo(@PhoneNumberType int phoneNumberType,
- @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) {
- final TelephonyManager telephonyManager = mAppContext.getSystemService(
- TelephonyManager.class);
- final TelephonyManager subTelephonyManager;
- final String networkCountryIso;
- final String networkOperator;
- if (phoneNumberType == PhoneNumberType.HAS_SHORTCUT && phoneInfo != null) {
- subTelephonyManager = telephonyManager.createForSubscriptionId(phoneInfo.getSubId());
- networkCountryIso = phoneInfo.getCountryIso();
- } else {
- // No specific phone to make this call. Take information of default network.
- subTelephonyManager = null;
- networkCountryIso = telephonyManager.getNetworkCountryIso();
- }
- if (subTelephonyManager != null) {
- networkOperator = subTelephonyManager.getNetworkOperator();
- } else {
- // This could be:
- // - No specific phone to make this call.
- // - Subscription changed! Maybe the device roamed to another network?
- // Take information of default network.
- networkOperator = telephonyManager.getNetworkOperator();
- }
-
- return new TelephonyInfo(networkCountryIso, networkOperator);
- }
-
- private void logBeforeMakeCall(@DialedFrom int dialedFrom,
- @PhoneNumberType int phoneNumberType,
- boolean isDeviceLocked,
- String networkCountryIso,
- String networkOperator) {
- if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
- Log.d(LOG_TAG, "EmergencyDialer session: dialedFrom=" + dialFromToString(dialedFrom)
- + ", launchedFrom=" + launchedFromToString(mLaunchedFrom)
- + ", uimode=" + uiModeErrorCodeToString(mUiModeErrorCode)
- + ", type=" + phoneNumberTypeToString(phoneNumberType)
- + ", locked=" + isDeviceLocked
- + ", country=" + networkCountryIso
- + ", operator=" + networkOperator);
- }
- mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL_V2)
- .setType(MetricsEvent.TYPE_ACTION)
- .setSubtype(dialedFrom)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_LAUNCH_FROM, mLaunchedFrom)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_UI_MODE_ERROR_CODE,
- mUiModeErrorCode)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE,
- phoneNumberType)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_DEVICE_LOCKED,
- isDeviceLocked ? 1 : 0)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_NETWORK_COUNTRY_ISO,
- networkCountryIso)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_NETWORK_OPERATOR,
- networkOperator)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_RADIO_VERSION,
- Build.getRadioVersion())
- );
- }
-
- private String dialFromToString(@DialedFrom int dialedFrom) {
- switch (dialedFrom) {
- case DialedFrom.TRADITIONAL_DIALPAD:
- return "traditional";
- case DialedFrom.SHORTCUT:
- return "shortcut";
- case DialedFrom.FASTER_LAUNCHER_DIALPAD:
- return "dialpad";
- default:
- return "unknown_error";
- }
- }
-
- private String launchedFromToString(@LaunchedFrom int launchedFrom) {
- switch (launchedFrom) {
- case LaunchedFrom.UNDEFINED:
- return "undefined";
- case LaunchedFrom.LOCK_SCREEN:
- return "lockscreen";
- case LaunchedFrom.POWER_KEY_MENU:
- return "powermenu";
- default:
- return "unknown_error";
- }
- }
-
- private String phoneNumberTypeToString(@PhoneNumberType int phoneNumberType) {
- switch (phoneNumberType) {
- case PhoneNumberType.HAS_SHORTCUT:
- return "has_shortcut";
- case PhoneNumberType.NO_SHORTCUT:
- return "no_shortcut";
- case PhoneNumberType.NOT_EMERGENCY_NUMBER:
- return "not_emergency";
- default:
- return "unknown_error";
- }
- }
-
- private String uiModeErrorCodeToString(@UiModeErrorCode int uiModeErrorCode) {
- switch (uiModeErrorCode) {
- case UiModeErrorCode.UNSPECIFIED_ERROR:
- return "unspecified_error";
- case UiModeErrorCode.SUCCESS:
- return "success";
- case UiModeErrorCode.CONFIG_ENTRY_POINT:
- return "config_entry_point";
- case UiModeErrorCode.CONFIG_SIM_OPERATOR:
- return "config_sim_operator";
- case UiModeErrorCode.UNSUPPORTED_COUNTRY:
- return "unsupported_country";
- case UiModeErrorCode.AIRPLANE_MODE:
- return "airplane_mode";
- case UiModeErrorCode.NO_PROMOTED_NUMBER:
- return "no_promoted_number";
- case UiModeErrorCode.NO_CAPABLE_PHONE:
- return "no_capable_phone";
- default:
- return "unknown_error";
- }
- }
-}
diff --git a/src/com/android/phone/EmergencyInfoGroup.java b/src/com/android/phone/EmergencyInfoGroup.java
index 14c24d8..f5aca7f 100644
--- a/src/com/android/phone/EmergencyInfoGroup.java
+++ b/src/com/android/phone/EmergencyInfoGroup.java
@@ -35,7 +35,6 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -154,7 +153,7 @@
private Drawable getCircularUserIcon() {
final UserManager userManager = (UserManager) getContext().getSystemService(
Context.USER_SERVICE);
- Bitmap bitmapUserIcon = userManager.getUserIcon(UserHandle.getCallingUserId());
+ Bitmap bitmapUserIcon = userManager.getUserIcon();
if (bitmapUserIcon == null) {
// get default user icon.
@@ -208,23 +207,21 @@
@Override
public void onClick(View view) {
- switch (view.getId()) {
- case R.id.emergency_info_view:
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- if (mOnConfirmClickListener != null) {
- mOnConfirmClickListener.onConfirmClick(this);
- }
- } else {
- revealSelectedButton();
- }
- break;
- case R.id.emergency_info_confirm_view:
+ if (view.getId() == R.id.emergency_info_view) {
+ AccessibilityManager accessibilityMgr =
+ (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityMgr.isTouchExplorationEnabled()) {
if (mOnConfirmClickListener != null) {
mOnConfirmClickListener.onConfirmClick(this);
}
- break;
- default:
- break;
+ } else {
+ revealSelectedButton();
+ }
+ } else if (view.getId() == R.id.emergency_info_confirm_view) {
+ if (mOnConfirmClickListener != null) {
+ mOnConfirmClickListener.onConfirmClick(this);
+ }
}
}
@@ -284,18 +281,4 @@
hideSelectedButton();
}
};
-
- /**
- * Update layout margin when emergency shortcut button more than 2.
- */
- public void updateLayoutMargin() {
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) getLayoutParams();
-
- params.topMargin = getResources().getDimensionPixelSize(
- R.dimen.emergency_info_button_fix_margin_vertical);
- params.bottomMargin = getResources().getDimensionPixelSize(
- R.dimen.emergency_info_button_fix_margin_vertical);
-
- setLayoutParams(params);
- }
}
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index 59b3794..d147ce4 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -19,8 +19,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
-import android.metrics.LogMaker;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -32,9 +30,6 @@
import androidx.annotation.NonNull;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
/**
* Emergency shortcut button displays a local emergency phone number information(including phone
* number, and phone type). To decrease false clicking, it need to click twice to confirm to place
@@ -66,12 +61,6 @@
private boolean mConfirmViewHiding;
- /**
- * The time, in millis, since boot when user taps on shortcut button to reveal confirm view.
- * This is used for metrics when calculating the interval between reveal tap and confirm tap.
- */
- private long mTimeOfRevealTapInMillis = 0;
-
public EmergencyShortcutButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -193,32 +182,23 @@
@Override
public void onClick(View view) {
- switch (view.getId()) {
- case R.id.emergency_call_number_info_view:
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- // TalkBack itself includes a prompt to confirm click action implicitly,
- // so we don't need an additional confirmation with second tap on button.
- if (mOnConfirmClickListener != null) {
- mOnConfirmClickListener.onConfirmClick(this);
- }
- } else {
- revealSelectedButton();
- }
- break;
- case R.id.emergency_call_confirm_view:
- if (mTimeOfRevealTapInMillis != 0) {
- long timeBetweenTwoTaps =
- SystemClock.elapsedRealtime() - mTimeOfRevealTapInMillis;
- // Reset reveal time to zero for next reveal-confirm taps pair.
- mTimeOfRevealTapInMillis = 0;
-
- writeMetricsForConfirmTap(timeBetweenTwoTaps);
- }
-
+ if (view.getId() == R.id.emergency_call_number_info_view) {
+ AccessibilityManager accessibilityMgr =
+ (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ if (accessibilityMgr.isTouchExplorationEnabled()) {
+ // TalkBack itself includes a prompt to confirm click action implicitly,
+ // so we don't need an additional confirmation with second tap on button.
if (mOnConfirmClickListener != null) {
mOnConfirmClickListener.onConfirmClick(this);
}
- break;
+ } else {
+ revealSelectedButton();
+ }
+ } else if (view.getId() == R.id.emergency_call_confirm_view) {
+ if (mOnConfirmClickListener != null) {
+ mOnConfirmClickListener.onConfirmClick(this);
+ }
}
}
@@ -226,7 +206,6 @@
mConfirmViewHiding = false;
mConfirmView.setVisibility(View.VISIBLE);
- mTimeOfRevealTapInMillis = SystemClock.elapsedRealtime();
int centerX = mCallNumberInfoView.getLeft() + mCallNumberInfoView.getWidth() / 2;
int centerY = mCallNumberInfoView.getTop() + mCallNumberInfoView.getHeight() / 2;
Animator reveal = ViewAnimationUtils.createCircularReveal(
@@ -263,8 +242,6 @@
@Override
public void onAnimationEnd(Animator animation) {
mConfirmView.setVisibility(INVISIBLE);
- // Reset reveal time to zero for next reveal-confirm taps pair.
- mTimeOfRevealTapInMillis = 0;
}
});
reveal.start();
@@ -279,12 +256,4 @@
hideSelectedButton();
}
};
-
- private void writeMetricsForConfirmTap(long timeBetweenTwoTaps) {
- LogMaker logContent = new LogMaker(MetricsEvent.EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP)
- .setType(MetricsEvent.TYPE_ACTION)
- .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL,
- timeBetweenTwoTaps);
- MetricsLogger.action(logContent);
- }
}
diff --git a/src/com/android/phone/GsmUmtsCallOptions.java b/src/com/android/phone/GsmUmtsCallOptions.java
index 88cae54..51d1b66 100644
--- a/src/com/android/phone/GsmUmtsCallOptions.java
+++ b/src/com/android/phone/GsmUmtsCallOptions.java
@@ -21,6 +21,7 @@
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
+import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.view.MenuItem;
@@ -32,7 +33,7 @@
public static final String CALL_FORWARDING_KEY = "call_forwarding_key";
public static final String CALL_BARRING_KEY = "call_barring_key";
- private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
+ public static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
@Override
protected void onCreate(Bundle icicle) {
@@ -69,12 +70,22 @@
b = PhoneGlobals.getInstance().getCarrierConfig();
}
+ boolean isAirplaneModeOff = true;
+ if (b != null && b.getBoolean(
+ CarrierConfigManager.KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL)) {
+ int airplaneMode = Settings.Global.getInt(
+ subInfoHelper.getPhone().getContext().getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, PhoneGlobals.AIRPLANE_OFF);
+ isAirplaneModeOff = PhoneGlobals.AIRPLANE_ON != airplaneMode;
+ }
+
Preference callForwardingPref = prefScreen.findPreference(CALL_FORWARDING_KEY);
if (callForwardingPref != null) {
if (b != null && b.getBoolean(
CarrierConfigManager.KEY_CALL_FORWARDING_VISIBILITY_BOOL)) {
callForwardingPref.setIntent(
subInfoHelper.getIntent(GsmUmtsCallForwardOptions.class));
+ callForwardingPref.setEnabled(isAirplaneModeOff);
} else {
prefScreen.removePreference(callForwardingPref);
}
@@ -89,6 +100,7 @@
CarrierConfigManager.KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL))) {
additionalGsmSettingsPref.setIntent(
subInfoHelper.getIntent(GsmUmtsAdditionalCallOptions.class));
+ additionalGsmSettingsPref.setEnabled(isAirplaneModeOff);
} else {
prefScreen.removePreference(additionalGsmSettingsPref);
}
@@ -98,6 +110,7 @@
if (callBarringPref != null) {
if (b != null && b.getBoolean(CarrierConfigManager.KEY_CALL_BARRING_VISIBILITY_BOOL)) {
callBarringPref.setIntent(subInfoHelper.getIntent(GsmUmtsCallBarringOptions.class));
+ callBarringPref.setEnabled(isAirplaneModeOff);
} else {
prefScreen.removePreference(callBarringPref);
}
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
new file mode 100644
index 0000000..06d2367
--- /dev/null
+++ b/src/com/android/phone/ImsRcsController.java
@@ -0,0 +1,229 @@
+/*
+ * 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 android.content.Context;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.imsphone.ImsPhone;
+
+import java.util.List;
+
+/**
+ * Implementation of the IImsRcsController interface.
+ */
+public class ImsRcsController extends IImsRcsController.Stub {
+ private static final String TAG = "ImsRcsController";
+
+ /** The singleton instance. */
+ private static ImsRcsController sInstance;
+
+ private PhoneGlobals mApp;
+
+ /**
+ * Initialize the singleton ImsRcsController instance.
+ * This is only done once, at startup, from PhoneApp.onCreate().
+ */
+ static ImsRcsController init(PhoneGlobals app) {
+ synchronized (ImsRcsController.class) {
+ if (sInstance == null) {
+ sInstance = new ImsRcsController(app);
+ } else {
+ Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ return sInstance;
+ }
+ }
+
+ /** Private constructor; @see init() */
+ private ImsRcsController(PhoneGlobals app) {
+ Log.i(TAG, "ImsRcsController");
+ mApp = app;
+ ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
+ }
+
+ /**
+ * Register a capability callback which will provide RCS availability updates for the
+ * subscription specified.
+ *
+ * @param subId the subscription ID
+ * @param callback The ImsCapabilityCallback to be registered.
+ */
+ @Override
+ public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
+ throws RemoteException {
+ enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ getRcsFeatureManager(subId).registerRcsAvailabilityCallback(callback);
+ } catch (com.android.ims.ImsException e) {
+ Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
+ throw new ServiceSpecificException(e.getCode());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Remove the registered capability callback.
+ *
+ * @param subId the subscription ID
+ * @param callback The ImsCapabilityCallback to be removed.
+ */
+ @Override
+ public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
+ 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());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Query for the capability of an IMS RCS service
+ *
+ * @param subId the subscription ID
+ * @param capability the RCS capability to query.
+ * @param radioTech the radio tech that this capability failed for
+ * @return true if the RCS capability is capable for this subscription, false otherwise.
+ */
+ @Override
+ public boolean isCapable(int subId,
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+ enforceReadPrivilegedPermission("isCapable");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getRcsFeatureManager(subId).isCapable(capability, radioTech);
+ } catch (com.android.ims.ImsException e) {
+ Log.e(TAG, "isCapable: sudId=" + subId
+ + ", capability=" + capability + ", " + e.getMessage());
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Query the availability of an IMS RCS capability.
+ *
+ * @param subId the subscription ID
+ * @param capability the RCS capability to query.
+ * @return true if the RCS capability is currently available for the associated subscription,
+ * false otherwise.
+ */
+ @Override
+ public boolean isAvailable(int subId,
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ enforceReadPrivilegedPermission("isAvailable");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getRcsFeatureManager(subId).isAvailable(capability);
+ } catch (com.android.ims.ImsException e) {
+ Log.e(TAG, "isAvailable: sudId=" + subId
+ + ", capability=" + capability + ", " + e.getMessage());
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void requestCapabilities(int subId, List<Uri> contactNumbers,
+ IRcsUceControllerCallback c) {
+ enforceReadPrivilegedPermission("requestCapabilities");
+ }
+
+ @Override
+ public int getUcePublishState(int subId) {
+ enforceReadPrivilegedPermission("getUcePublishState");
+ return -1;
+ }
+
+ @Override
+ public boolean isUceSettingEnabled(int subId) {
+ enforceReadPrivilegedPermission("isUceSettingEnabled");
+ return false;
+ }
+
+ @Override
+ public void setUceSettingEnabled(int subId, boolean isEnabled) {
+ enforceModifyPermission();
+ }
+
+ /**
+ * Make sure either called from same process as self (phone) or IPC caller has read privilege.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceReadPrivilegedPermission(String message) {
+ mApp.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+ }
+
+ /**
+ * Make sure the caller has the MODIFY_PHONE_STATE permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceModifyPermission() {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
+ }
+
+ /**
+ * Retrieve RcsFeatureManager instance.
+ *
+ * @param subId the subscription ID
+ * @return The RcsFeatureManager instance
+ * @throws SecurityException if getting Phone or RcsFeatureManager instance failed.
+ */
+ private RcsFeatureManager getRcsFeatureManager(int subId) {
+ 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_UNSUPPORTED_OPERATION,
+ "Cannot find ImsPhone instance: " + subId);
+ }
+ RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
+ if (rcsFeatureManager == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+ "Cannot find RcsFeatureManager instance: " + subId);
+ }
+ return rcsFeatureManager;
+ }
+}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 43c5b11..fe55335 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.READ_PHONE_STATE;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -41,7 +42,6 @@
import android.preference.PreferenceManager;
import android.provider.ContactsContract.PhoneLookup;
import android.provider.Settings;
-import android.telecom.DefaultDialerManager;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
@@ -63,6 +63,7 @@
import com.android.internal.telephony.util.NotificationChannelController;
import com.android.phone.settings.VoicemailSettingsActivity;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -93,6 +94,7 @@
static final int CALL_FORWARD_NOTIFICATION = 4;
static final int DATA_ROAMING_NOTIFICATION = 5;
static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
+ static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
// Event for network selection notification.
private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
@@ -102,6 +104,8 @@
private static final int STATE_UNKNOWN_SERVICE = -1;
+ private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
+
/** The singleton NotificationMgr instance. */
private static NotificationMgr sInstance;
@@ -118,6 +122,9 @@
// used to track the notification of selected network unavailable, per subscription id.
private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
+ // used to track the notification of limited sim function under dual sim, per subscription id.
+ private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
+
// used to track whether the message waiting indicator is visible, per subscription id.
private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
@@ -156,7 +163,7 @@
(StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
mSubscriptionManager = SubscriptionManager.from(mContext);
- mTelecomManager = TelecomManager.from(mContext);
+ mTelecomManager = app.getSystemService(TelecomManager.class);
mTelephonyManager = (TelephonyManager) app.getSystemService(Context.TELEPHONY_SERVICE);
}
@@ -368,12 +375,11 @@
final Notification notification = builder.build();
List<UserInfo> users = mUserManager.getUsers(true);
- for (int i = 0; i < users.size(); i++) {
- final UserInfo user = users.get(i);
+ for (UserInfo user : users) {
final UserHandle userHandle = user.getUserHandle();
- if (!mUserManager.hasUserRestriction(
+ if (!hasUserRestriction(
UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
- && !user.isManagedProfile()) {
+ && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, vmCount, vmNumber,
pendingIntent, isSettingsIntent, userHandle, isRefresh)) {
notifyAsUser(
@@ -386,12 +392,11 @@
}
} else {
List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
- for (int i = 0; i < users.size(); i++) {
- final UserInfo user = users.get(i);
+ for (UserInfo user : users) {
final UserHandle userHandle = user.getUserHandle();
- if (!mUserManager.hasUserRestriction(
+ if (!hasUserRestriction(
UserManager.DISALLOW_OUTGOING_CALLS, userHandle)
- && !user.isManagedProfile()) {
+ && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
if (!maybeSendVoicemailNotificationUsingDefaultDialer(phone, 0, null, null,
false, userHandle, isRefresh)) {
cancelAsUser(
@@ -404,6 +409,12 @@
}
}
+ private boolean hasUserRestriction(String restrictionKey, UserHandle userHandle) {
+ final List<UserManager.EnforcingUser> sources = mUserManager
+ .getUserRestrictionSources(restrictionKey, userHandle);
+ return (sources != null && !sources.isEmpty());
+ }
+
/**
* Sends a broadcast with the voicemail notification information to the default dialer. This
* method is also used to indicate to the default dialer when to clear the
@@ -458,8 +469,8 @@
}
private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
- String dialerPackage = DefaultDialerManager
- .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
+ String dialerPackage = mContext.getSystemService(TelecomManager.class)
+ .getDefaultDialerPackage(userHandle.getIdentifier());
return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
.setPackage(dialerPackage);
}
@@ -546,7 +557,7 @@
} else {
List<UserInfo> users = mUserManager.getUsers(true);
for (UserInfo user : users) {
- if (user.isManagedProfile()) {
+ if (mUserManager.isManagedProfile(user.getUserHandle().getIdentifier())) {
continue;
}
UserHandle userHandle = user.getUserHandle();
@@ -638,6 +649,95 @@
}
/**
+ * Shows the "Limited SIM functionality" warning notification, which appears when using a
+ * special carrier under dual sim. limited function applies for DSDS in general when two SIM
+ * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
+ */
+ public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
+ if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
+ + " subId: " + subId);
+ if (mLimitedSimFunctionNotify.contains(subId)) {
+ // handle the case that user swipe the notification but condition triggers
+ // frequently which cause the same notification consistently displayed.
+ if (DBG) log("showLimitedSimFunctionWarningNotification, "
+ + "not display again if already displayed");
+ return;
+ }
+ // Navigate to "Network Selection Settings" which list all subscriptions.
+ PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
+ new Intent(ACTION_MOBILE_NETWORK_LIST), 0);
+ // Display phone number from the other sub
+ String line1Num = null;
+ SubscriptionManager subMgr = (SubscriptionManager) mContext.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
+ for (SubscriptionInfo sub : subList) {
+ if (sub.getSubscriptionId() != subId) {
+ line1Num = mTelephonyManager.getLine1Number(sub.getSubscriptionId());
+ }
+ }
+ final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
+ String.format(mContext.getText(
+ R.string.limited_sim_function_notification_message).toString(), carrierName) :
+ String.format(mContext.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message).toString(),
+ carrierName, line1Num);
+ final Notification.Builder builder = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_sim_card)
+ .setContentTitle(mContext.getText(
+ R.string.limited_sim_function_notification_title))
+ .setContentText(contentText)
+ .setOnlyAlertOnce(true)
+ .setOngoing(true)
+ .setChannelId(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
+ .setContentIntent(contentIntent);
+ final Notification notification = new Notification.BigTextStyle(builder).bigText(
+ contentText).build();
+
+ notifyAsUser(Integer.toString(subId),
+ LIMITED_SIM_FUNCTION_NOTIFICATION,
+ notification, UserHandle.ALL);
+ mLimitedSimFunctionNotify.add(subId);
+ }
+
+ /**
+ * Dismiss the "Limited SIM functionality" warning notification for the given subId.
+ */
+ public void dismissLimitedSimFunctionWarningNotification(int subId) {
+ if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ // dismiss all notifications
+ for (int id : mLimitedSimFunctionNotify) {
+ cancelAsUser(Integer.toString(id),
+ LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+ }
+ mLimitedSimFunctionNotify.clear();
+ } else if (mLimitedSimFunctionNotify.contains(subId)) {
+ cancelAsUser(Integer.toString(subId),
+ LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+ mLimitedSimFunctionNotify.remove(subId);
+ }
+ }
+
+ /**
+ * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
+ */
+ public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
+ if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
+ // dismiss notification for inactive subscriptions.
+ // handle the corner case that SIM change by SIM refresh doesn't clear the notification
+ // from the old SIM if both old & new SIM configured to display the notification.
+ mLimitedSimFunctionNotify.removeIf(id -> {
+ if (!mSubscriptionManager.isActiveSubId(id)) {
+ cancelAsUser(Integer.toString(id),
+ LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+ return true;
+ }
+ return false;
+ });
+ }
+
+ /**
* Display the network selection "no service" notification
* @param operator is the numeric operator number
* @param subId is the subscription ID
diff --git a/src/com/android/phone/PhoneDisplayMessage.java b/src/com/android/phone/PhoneDisplayMessage.java
index 2b86a61..199fbb8 100644
--- a/src/com/android/phone/PhoneDisplayMessage.java
+++ b/src/com/android/phone/PhoneDisplayMessage.java
@@ -78,10 +78,11 @@
sDisplayMessageDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
sDisplayMessageDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
sDisplayMessageDialog.show();
- PhoneGlobals.getInstance().wakeUpScreen();
}
/**
@@ -91,6 +92,9 @@
if (DBG) log("Dissmissing Display Info Record...");
if (sDisplayMessageDialog != null) {
+ sDisplayMessageDialog.getWindow().clearFlags(
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
sDisplayMessageDialog.dismiss();
sDisplayMessageDialog = null;
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index a3a7945..5d4d9b3 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -37,16 +37,16 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UpdateLock;
import android.os.UserManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.sysprop.TelephonyProperties;
import android.telecom.TelecomManager;
import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
@@ -76,6 +76,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
/**
* Global state for the telephony subsystem when running in the primary
@@ -146,6 +147,7 @@
CallerInfoCache callerInfoCache;
NotificationMgr notificationMgr;
public PhoneInterfaceManager phoneMgr;
+ public ImsRcsController imsRcsController;
CarrierConfigLoader configLoader;
private Phone phoneInEcm;
@@ -185,8 +187,6 @@
private PowerManager.WakeLock mPartialWakeLock;
private KeyguardManager mKeyguardManager;
- private UpdateLock mUpdateLock;
-
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final LocalLog mDataRoamingNotifLog = new LocalLog(50);
@@ -272,7 +272,8 @@
// not want this running if the device is still in the FBE encrypted state.
// This is the same procedure that is triggered in the SipIncomingCallReceiver
// upon BOOT_COMPLETED.
- UserManager userManager = UserManager.get(sMe);
+ UserManager userManager =
+ (UserManager) sMe.getSystemService(Context.USER_SERVICE);
if (userManager != null && userManager.isUserUnlocked()) {
SipUtil.startSipService();
}
@@ -345,12 +346,6 @@
mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
- // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
- // during phone calls.
- mUpdateLock = new UpdateLock("phone");
-
- if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
-
// Create the CallerInfoCache singleton, which remembers custom ring tone and
// send-to-voicemail settings.
//
@@ -359,6 +354,8 @@
phoneMgr = PhoneInterfaceManager.init(this);
+ imsRcsController = ImsRcsController.init(this);
+
configLoader = CarrierConfigLoader.init(this);
// Create the CallNotifier singleton, which handles
@@ -372,6 +369,10 @@
// register for MMI/USSD
mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);
+ // Initialize cell status using current airplane mode.
+ handleAirplaneModeChange(this, Settings.Global.getInt(getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, AIRPLANE_OFF));
+
// Register for misc other intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
@@ -503,19 +504,6 @@
mPUKEntryProgressDialog = dialog;
}
- /**
- * If we are not currently keeping the screen on, then poke the power
- * manager to wake up the screen for the user activity timeout duration.
- */
- /* package */ void wakeUpScreen() {
- synchronized (this) {
- if (mWakeState == WakeState.SLEEP) {
- if (DBG) Log.d(LOG_TAG, "pulse screen lock");
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.phone:WAKE");
- }
- }
- }
-
KeyguardManager getKeyguardManager() {
return mKeyguardManager;
}
@@ -566,7 +554,7 @@
Log.i(LOG_TAG, "Turning radio off - airplane");
Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
- SystemProperties.set("persist.radio.airplane_mode_on", "1");
+ TelephonyProperties.airplane_mode_on(true); // true means int value 1
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
PhoneUtils.setRadioPower(false);
}
@@ -577,7 +565,7 @@
PhoneConstants.CELL_ON_FLAG);
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
1);
- SystemProperties.set("persist.radio.airplane_mode_on", "0");
+ TelephonyProperties.airplane_mode_on(false); // false means int value 0
PhoneUtils.setRadioPower(true);
}
@@ -589,7 +577,9 @@
if (tm != null && tm.isInEmergencyCall()) {
// Switch airplane mode back to off.
- ConnectivityManager.from(this).setAirplaneMode(false);
+ ConnectivityManager cm =
+ (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
+ cm.setAirplaneMode(false);
Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
.show();
Log.i(LOG_TAG, "Ignoring airplane mode: emergency call. Turning airplane off");
@@ -672,6 +662,7 @@
// Roaming status could be overridden by carrier config, so we need to update it.
if (VDBG) Log.v(LOG_TAG, "carrier config changed.");
updateDataRoamingStatus();
+ updateLimitedSimFunctionForDualSim();
} else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
// We also need to pay attention when default data subscription changes.
if (VDBG) Log.v(LOG_TAG, "default data sub changed.");
@@ -809,6 +800,38 @@
return getPhone(subId).getServiceState().getDataRoaming();
}
+ private void updateLimitedSimFunctionForDualSim() {
+ if (DBG) Log.d(LOG_TAG, "updateLimitedSimFunctionForDualSim");
+ // check conditions to display limited SIM function notification under dual SIM
+ SubscriptionManager subMgr = (SubscriptionManager) getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
+ if (subList != null && subList.size() > 1) {
+ CarrierConfigManager configMgr = (CarrierConfigManager)
+ getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ for (SubscriptionInfo info : subList) {
+ PersistableBundle b = configMgr.getConfigForSubId(info.getSubscriptionId());
+ if (b != null) {
+ if (b.getBoolean(CarrierConfigManager
+ .KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL)) {
+ notificationMgr.showLimitedSimFunctionWarningNotification(
+ info.getSubscriptionId(),
+ info.getDisplayName().toString());
+ } else {
+ notificationMgr.dismissLimitedSimFunctionWarningNotification(
+ info.getSubscriptionId());
+ }
+ }
+ }
+ } else {
+ // cancel notifications for all subs
+ notificationMgr.dismissLimitedSimFunctionWarningNotification(
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+ notificationMgr.dismissLimitedSimFunctionWarningNotificationForInactiveSubs();
+
+ }
+
public Phone getPhoneInEcm() {
return phoneInEcm;
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index e5e9fe7..296525d 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -45,6 +45,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -56,13 +57,16 @@
import android.os.UserManager;
import android.os.WorkSource;
import android.preference.PreferenceManager;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Telephony;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
+import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoWcdma;
@@ -92,11 +96,11 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.ImsException;
import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsCapabilityCallback;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsConfigCallback;
@@ -104,6 +108,7 @@
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsConfigImplBase;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -140,8 +145,6 @@
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.ServiceStateTracker;
-import com.android.internal.telephony.SmsApplication;
-import com.android.internal.telephony.SmsApplication.SmsApplicationData;
import com.android.internal.telephony.SmsController;
import com.android.internal.telephony.SmsPermissions;
import com.android.internal.telephony.SubscriptionController;
@@ -174,7 +177,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -259,6 +261,10 @@
private static final int EVENT_ENABLE_MODEM_DONE = 69;
private static final int CMD_GET_MODEM_STATUS = 70;
private static final int EVENT_GET_MODEM_STATUS_DONE = 71;
+ private static final int CMD_SET_FORBIDDEN_PLMNS = 72;
+ 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;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -299,6 +305,12 @@
private static final int MANUFACTURER_CODE_LENGTH = 8;
/**
+ * Experiment flag to enable erase modem config on reset network, default value is false
+ */
+ public static final String RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED =
+ "reset_network_erase_modem_config_enabled";
+
+ /**
* A request object to use for transmitting data to an ICC.
*/
private static final class IccAPDUArgument {
@@ -1074,11 +1086,13 @@
try {
if (ar.exception != null) {
Log.e(LOG_TAG, "Exception retrieving CellInfo=" + ar.exception);
- cb.onError(TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR,
- new android.os.ParcelableException(ar.exception));
+ cb.onError(
+ TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR,
+ ar.exception.getClass().getName(),
+ ar.exception.toString());
} else if (ar.result == null) {
Log.w(LOG_TAG, "Timeout Waiting for CellInfo!");
- cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null);
+ cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null, null);
} else {
// use the result as returned
cb.onCellInfo((List<CellInfo>) ar.result);
@@ -1166,6 +1180,58 @@
}
notifyRequester(request);
break;
+ case EVENT_SET_FORBIDDEN_PLMNS_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null && ar.result != null) {
+ request.result = ar.result;
+ } else {
+ request.result = -1;
+ loge("Failed to set Forbidden Plmns");
+ if (ar.result == null) {
+ loge("setForbidenPlmns: Empty response");
+ } else if (ar.exception != null) {
+ loge("setForbiddenPlmns: Exception: " + ar.exception);
+ request.result = -1;
+ } else {
+ loge("setForbiddenPlmns: Unknown exception");
+ }
+ }
+ notifyRequester(request);
+ break;
+ case CMD_SET_FORBIDDEN_PLMNS:
+ request = (MainThreadRequest) msg.obj;
+ uiccCard = getUiccCardFromRequest(request);
+ if (uiccCard == null) {
+ loge("setForbiddenPlmns: UiccCard is null");
+ request.result = -1;
+ notifyRequester(request);
+ break;
+ }
+ Pair<Integer, List<String>> setFplmnsArgs =
+ (Pair<Integer, List<String>>) request.argument;
+ appType = setFplmnsArgs.first;
+ List<String> fplmns = setFplmnsArgs.second;
+ uiccApp = uiccCard.getApplicationByType(appType);
+ if (uiccApp == null) {
+ loge("setForbiddenPlmns: no app with specified type -- " + appType);
+ request.result = -1;
+ loge("Failed to get UICC App");
+ notifyRequester(request);
+ } else {
+ onCompleted = obtainMessage(EVENT_SET_FORBIDDEN_PLMNS_DONE, request);
+ ((SIMRecords) uiccApp.getIccRecords())
+ .setForbiddenPlmns(onCompleted, fplmns);
+ }
+ break;
+ case CMD_ERASE_MODEM_CONFIG:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_ERASE_MODEM_CONFIG_DONE, request);
+ defaultPhone.eraseModemConfig(onCompleted);
+ break;
+ case EVENT_ERASE_MODEM_CONFIG_DONE:
+ handleNullReturnEvent(msg, "eraseModemConfig");
+ break;
default:
Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
break;
@@ -1374,6 +1440,20 @@
return PhoneFactory.getPhone(mSubscriptionController.getPhoneId(subId));
}
+ private void sendEraseModemConfig(Phone phone) {
+ if (phone != null) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, phone.getSubId(), "eraseModemConfig");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Boolean success = (Boolean) sendRequest(CMD_ERASE_MODEM_CONFIG, null);
+ if (DBG) log("eraseModemConfig:" + ' ' + (success ? "ok" : "fail"));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
public void dial(String number) {
dialForSubscriber(getPreferredVoiceSubscription(), number);
}
@@ -1698,6 +1778,7 @@
}
public boolean needMobileRadioShutdown() {
+ enforceReadPrivilegedPermission("needMobileRadioShutdown");
/*
* If any of the Radios are available, it will need to be
* shutdown. So return true if any Radio is available.
@@ -1952,7 +2033,15 @@
}
@Override
- public String getNetworkCountryIsoForPhone(int phoneId) {
+ public String getNetworkCountryIsoForPhone(int phoneId, String callingPackage) {
+ if (!TextUtils.isEmpty(callingPackage)) {
+ final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, subId, callingPackage, "getNetworkCountryIsoForPhone")) {
+ return "";
+ }
+ }
+
// 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();
@@ -2349,6 +2438,11 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
}
+ private void enforceActiveEmergencySessionPermission() {
+ mApp.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
+ }
+
/**
* Make sure the caller has the CALL_PHONE permission.
*
@@ -2358,9 +2452,8 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
}
- private void enforceConnectivityInternalPermission() {
- mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "ConnectivityService");
+ private void enforceSettingsPermission() {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS, null);
}
private String createTelUrl(String number) {
@@ -2588,7 +2681,8 @@
@Override
public Bundle getVisualVoicemailSettings(String callingPackage, int subId) {
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- String systemDialer = TelecomManager.from(mApp).getSystemDialerPackage();
+ TelecomManager tm = mApp.getSystemService(TelecomManager.class);
+ String systemDialer = tm.getSystemDialerPackage();
if (!TextUtils.equals(callingPackage, systemDialer)) {
throw new SecurityException("caller must be system dialer");
}
@@ -2814,8 +2908,8 @@
public void sendDialerSpecialCode(String callingPackage, String inputCode) {
final Phone defaultPhone = getDefaultPhone();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- String defaultDialer = TelecomManager.from(defaultPhone.getContext())
- .getDefaultDialerPackage();
+ TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
+ String defaultDialer = tm.getDefaultDialerPackage();
if (!TextUtils.equals(callingPackage, defaultDialer)) {
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
getDefaultSubscription(), "sendDialerSpecialCode");
@@ -2858,6 +2952,10 @@
public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c)
throws RemoteException {
enforceReadPrivilegedPermission("registerImsRegistrationCallback");
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
final long token = Binder.clearCallingIdentity();
try {
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -2876,24 +2974,97 @@
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
- Binder.withCleanCallingIdentity(() -> {
- try {
- // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
- ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
- .removeRegistrationCallbackForSubscription(c, subId);
- } catch (ImsException e) {
- Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId
- + "is inactive, ignoring unregister.");
- // If the subscription is no longer active, just return, since the callback
- // will already have been removed internally.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
+ ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
+ .removeRegistrationCallbackForSubscription(c, subId);
+ } catch (ImsException e) {
+ Log.i(LOG_TAG, "unregisterImsRegistrationCallback: " + subId
+ + "is inactive, ignoring unregister.");
+ // If the subscription is no longer active, just return, since the callback
+ // will already have been removed internally.
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Get the IMS service registration state for the MmTelFeature associated with this sub id.
+ */
+ @Override
+ public void getImsMmTelRegistrationState(int subId, IIntegerConsumer consumer) {
+ enforceReadPrivilegedPermission("getImsMmTelRegistrationState");
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ Log.w(LOG_TAG, "getImsMmTelRegistrationState: called with an invalid subscription '"
+ + subId + "'");
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
}
- });
+ phone.getImsRegistrationState(regState -> {
+ try {
+ consumer.accept((regState == null)
+ ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
+ } catch (RemoteException e) {
+ // Ignore if the remote process is no longer available to call back.
+ Log.w(LOG_TAG, "getImsMmTelRegistrationState: callback not available.");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ /**
+ * Get the transport type for the IMS service registration state.
+ */
+ @Override
+ public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) {
+ enforceReadPrivilegedPermission("getImsMmTelRegistrationTransportType");
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ Phone phone = getPhone(subId);
+ if (phone == null) {
+ Log.w(LOG_TAG, "getImsMmTelRegistrationState: called with an invalid subscription '"
+ + subId + "'");
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+ phone.getImsRegistrationTech(regTech -> {
+ // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
+ int regTechConverted = (regTech == null)
+ ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
+ regTechConverted = RegistrationManager.IMS_REG_TO_ACCESS_TYPE_MAP.get(
+ regTechConverted);
+ try {
+ consumer.accept(regTechConverted);
+ } catch (RemoteException e) {
+ // Ignore if the remote process is no longer available to call back.
+ Log.w(LOG_TAG, "getImsMmTelRegistrationState: callback not available.");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c)
throws RemoteException {
enforceReadPrivilegedPermission("registerMmTelCapabilityCallback");
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
// TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
final long token = Binder.clearCallingIdentity();
try {
@@ -2909,22 +3080,23 @@
@Override
public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) {
enforceReadPrivilegedPermission("unregisterMmTelCapabilityCallback");
-
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
}
- Binder.withCleanCallingIdentity(() -> {
- try {
- // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
- ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Refactor to remove ImsManager dependence and query through ImsPhone.
+ ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
.removeCapabilitiesCallbackForSubscription(c, subId);
- } catch (ImsException e) {
- Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId
- + "is inactive, ignoring unregister.");
- // If the subscription is no longer active, just return, since the callback
- // will already have been removed internally.
- }
- });
+ } catch (ImsException e) {
+ Log.i(LOG_TAG, "unregisterMmTelCapabilityCallback: " + subId
+ + "is inactive, ignoring unregister.");
+ // If the subscription is no longer active, just return, since the callback
+ // will already have been removed internally.
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
@@ -2959,6 +3131,44 @@
}
}
+ /**
+ * Determines if the MmTel feature capability is supported by the carrier configuration for this
+ * subscription.
+ * @param subId The subscription to use to check the configuration.
+ * @param callback The callback that will be used to send the result.
+ * @param capability The MmTelFeature capability that will be used to send the result.
+ * @param transportType The transport type of the MmTelFeature capability.
+ */
+ @Override
+ public void isMmTelCapabilitySupported(int subId, IIntegerConsumer callback, int capability,
+ int transportType) {
+ enforceReadPrivilegedPermission("isMmTelCapabilitySupported");
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int slotId = getSlotIndex(subId);
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.w(LOG_TAG, "isMmTelCapabilitySupported: called with an inactive subscription '"
+ + subId + "'");
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+ ImsManager.getInstance(mApp, slotId).isSupported(capability,
+ transportType, aBoolean -> {
+ try {
+ callback.accept((aBoolean == null) ? 0 : (aBoolean ? 1 : 0));
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "isMmTelCapabilitySupported: remote caller is not "
+ + "running. Ignore");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public boolean isAdvancedCallingSettingEnabled(int subId) {
enforceReadPrivilegedPermission("enforceReadPrivilegedPermission");
@@ -4031,7 +4241,7 @@
if (response instanceof String[]) {
return (String[]) response;
}
- // Response is an Exception of some kind,
+ // Response is an Exception of some kind
// which is signalled to the user as a NULL retval
return null;
} finally {
@@ -4039,6 +4249,47 @@
}
}
+ /**
+ * Set the forbidden PLMN list from the given app type (ex APPTYPE_USIM) on a particular
+ * subscription.
+ *
+ * @param subId the id of the subscription.
+ * @param appType the uicc app type, must be USIM or SIM.
+ * @param fplmns the Forbiden plmns list that needed to be written to the SIM.
+ * @param callingPackage the op Package name.
+ * @return number of fplmns that is successfully written to the SIM.
+ */
+ public int setForbiddenPlmns(
+ int subId, int appType, List<String> fplmns, String callingPackage) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, subId, callingPackage, "setForbiddenPlmns")) {
+ if (DBG) logv("no permissions for setForbiddenplmns");
+ throw new IllegalStateException("No Permissions for setForbiddenPlmns");
+ }
+ if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
+ loge("setForbiddenPlmnList(): App Type must be USIM or SIM");
+ throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM");
+ }
+ if (fplmns == null) {
+ throw new IllegalArgumentException("Fplmn List provided is null");
+ }
+ for (String fplmn : fplmns) {
+ if (!CellIdentity.isValidPlmn(fplmn)) {
+ throw new IllegalArgumentException("Invalid fplmn provided: " + fplmn);
+ }
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Object response = sendRequest(
+ CMD_SET_FORBIDDEN_PLMNS,
+ new Pair<Integer, List<String>>(new Integer(appType), fplmns),
+ subId);
+ return (int) response;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public String sendEnvelopeWithStatus(int subId, String content) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
@@ -4378,12 +4629,48 @@
// may happen if the device does not support IMS.
return "";
}
- return resolver.getImsServiceConfiguration(slotId, isCarrierImsService);
+ // TODO: change API to query RCS separately.
+ return resolver.getImsServiceConfiguration(slotId, isCarrierImsService,
+ ImsFeature.FEATURE_MMTEL);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
+ /**
+ * Get the MmTelFeature state associated with the requested subscription id.
+ * @param subId The subscription that the MmTelFeature is associated with.
+ * @param callback A callback with an integer containing the
+ * {@link android.telephony.ims.feature.ImsFeature.ImsState} associated with the MmTelFeature.
+ */
+ @Override
+ public void getImsMmTelFeatureState(int subId, IIntegerConsumer callback) {
+ enforceReadPrivilegedPermission("getImsMmTelFeatureState");
+ if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "IMS not available on device.");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int slotId = getSlotIndex(subId);
+ if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ Log.w(LOG_TAG, "getImsMmTelFeatureState: called with an inactive subscription '"
+ + subId + "'");
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION);
+ }
+ ImsManager.getInstance(mApp, slotId).getImsServiceState(anInteger -> {
+ try {
+ callback.accept(anInteger == null ? ImsFeature.STATE_UNAVAILABLE : anInteger);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "getImsMmTelFeatureState: remote caller is no longer running. "
+ + "Ignore");
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void setImsRegistrationState(boolean registered) {
enforceModifyPermission();
@@ -4659,7 +4946,7 @@
* @hide
*/
@Override
- public boolean getTetherApnRequiredForSubscriber(int subId) {
+ public boolean isTetheringApnRequiredForSubscriber(int subId) {
enforceModifyPermission();
final long identity = Binder.clearCallingIdentity();
final Phone phone = getPhone(subId);
@@ -4792,6 +5079,52 @@
}
}
+ private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim,
+ 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);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SubscriptionInfo subInfo = subController.getSubscriptionInfo(phone.getSubId());
+ SubscriptionManager subManager = (SubscriptionManager)
+ phone.getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ for (String pkg : packages) {
+ if (subManager.canManageSubscription(subInfo, pkg)) {
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
+ }
+ return privilegeFromSim;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, Phone phone,
+ String pkgName) {
+ //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;
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SubscriptionInfo subInfo = subController.getSubscriptionInfo(phone.getSubId());
+ SubscriptionManager subManager = (SubscriptionManager)
+ phone.getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ return subManager.canManageSubscription(subInfo, pkgName)
+ ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : privilegeFromSim;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
public int getCarrierPrivilegeStatus(int subId) {
final Phone phone = getPhone(subId);
@@ -4804,8 +5137,10 @@
loge("getCarrierPrivilegeStatus: No UICC");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
- return card.getCarrierPrivilegeStatusForCurrentTransaction(
- phone.getContext().getPackageManager());
+
+ return getCarrierPrivilegeStatusFromCarrierConfigRules(
+ card.getCarrierPrivilegeStatusForCurrentTransaction(
+ phone.getContext().getPackageManager()), phone);
}
@Override
@@ -4821,7 +5156,9 @@
loge("getCarrierPrivilegeStatusForUid: No UICC");
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
- return profile.getCarrierPrivilegeStatusForUid(phone.getContext().getPackageManager(), uid);
+ return getCarrierPrivilegeStatusFromCarrierConfigRules(
+ profile.getCarrierPrivilegeStatusForUid(
+ phone.getContext().getPackageManager(), uid), phone);
}
@Override
@@ -4836,8 +5173,9 @@
loge("checkCarrierPrivilegesForPackage: No UICC on subId " + subId);
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
}
-
- return card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName);
+ return getCarrierPrivilegeStatusFromCarrierConfigRules(
+ card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
+ getPhone(phoneId), pkgName);
}
@Override
@@ -4852,7 +5190,9 @@
continue;
}
- result = card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName);
+ result = getCarrierPrivilegeStatusFromCarrierConfigRules(
+ card.getCarrierPrivilegeStatus(mApp.getPackageManager(), pkgName),
+ getPhone(i), pkgName);
if (result == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
break;
}
@@ -4887,9 +5227,10 @@
if (packages == null) {
// Only check packages in user 0 for now
packages = pm.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
- | PackageManager.GET_SIGNATURES, UserHandle.USER_SYSTEM);
+ PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+ | PackageManager.GET_SIGNING_CERTIFICATES,
+ UserHandle.USER_SYSTEM);
}
for (int p = packages.size() - 1; p >= 0; p--) {
PackageInfo pkgInfo = packages.get(p);
@@ -4906,9 +5247,17 @@
@Override
public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+ enforceReadPrivilegedPermission("getPackagesWithCarrierPrivilegesForAllPhones");
+
+ final long identity = Binder.clearCallingIdentity();
+
List<String> privilegedPackages = new ArrayList<>();
- for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
- privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i));
+ try {
+ for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+ privilegedPackages.addAll(getPackagesWithCarrierPrivileges(i));
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
return privilegedPackages;
}
@@ -5111,6 +5460,49 @@
}
@Override
+ public String[] getMergedSubscriberIdsFromGroup(int subId, String callingPackage) {
+ enforceReadPrivilegedPermission("getMergedSubscriberIdsFromGroup");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final TelephonyManager telephonyManager = mApp.getSystemService(
+ TelephonyManager.class);
+ String subscriberId = telephonyManager.getSubscriberId(subId);
+ if (subscriberId == null) {
+ if (DBG) {
+ log("getMergedSubscriberIdsFromGroup can't find subscriberId for subId "
+ + subId);
+ }
+ return null;
+ }
+
+ final SubscriptionInfo info = SubscriptionController.getInstance()
+ .getSubscriptionInfo(subId);
+ final ParcelUuid groupUuid = info.getGroupUuid();
+ // If it doesn't belong to any group, return just subscriberId of itself.
+ if (groupUuid == null) {
+ return new String[]{subscriberId};
+ }
+
+ // Get all subscriberIds from the group.
+ final List<String> mergedSubscriberIds = new ArrayList<>();
+ final List<SubscriptionInfo> groupInfos = SubscriptionController.getInstance()
+ .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName());
+ for (SubscriptionInfo subInfo : groupInfos) {
+ subscriberId = telephonyManager.getSubscriberId(subInfo.getSubscriptionId());
+ if (subscriberId != null) {
+ mergedSubscriberIds.add(subscriberId);
+ }
+ }
+
+ return mergedSubscriberIds.toArray(new String[mergedSubscriberIds.size()]);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+
+ }
+ }
+
+ @Override
public boolean setOperatorBrandOverride(int subId, String brand) {
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
subId, "setOperatorBrandOverride");
@@ -5280,7 +5672,7 @@
@Override
public boolean isTtyModeSupported() {
- TelecomManager telecomManager = TelecomManager.from(mApp);
+ TelecomManager telecomManager = mApp.getSystemService(TelecomManager.class);
return telecomManager.isTtySupported();
}
@@ -5327,12 +5719,7 @@
public boolean isRttEnabled(int subscriptionId) {
final long identity = Binder.clearCallingIdentity();
try {
- boolean isRttSupported = isRttSupported(subscriptionId);
- boolean isUserRttSettingOn = Settings.Secure.getInt(
- mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
- boolean shouldIgnoreUserRttSetting = mApp.getCarrierConfigForSubId(subscriptionId)
- .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
- return isRttSupported && (isUserRttSettingOn || shouldIgnoreUserRttSetting);
+ return isRttSupported(subscriptionId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5391,6 +5778,21 @@
}
@Override
+ public int getSubIdForPhoneAccountHandle(
+ PhoneAccountHandle phoneAccountHandle, String callingPackage) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, getDefaultSubscription(),
+ callingPackage, "getSubIdForPhoneAccountHandle")) {
+ throw new SecurityException("Requires READ_PHONE_STATE permission.");
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -5458,7 +5860,7 @@
@Override
public void factoryReset(int subId) {
- enforceConnectivityInternalPermission();
+ enforceSettingsPermission();
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
return;
}
@@ -5479,6 +5881,18 @@
// in and combined with those stale ones. In case this happens again,
// user can reset all network settings which will clean up this table.
cleanUpSmsRawTable(getDefaultPhone().getContext());
+ // Clean up IMS settings as well here.
+ int slotId = getSlotIndex(subId);
+ if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ ImsManager.getInstance(mApp, slotId).factoryReset();
+ }
+
+ // Erase modem config if erase modem on network setting is enabled.
+ String configValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TELEPHONY,
+ RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED);
+ if (configValue != null && Boolean.parseBoolean(configValue)) {
+ sendEraseModemConfig(getDefaultPhone());
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -5719,8 +6133,8 @@
PhoneAccountHandle phoneAccountHandle, Uri uri) {
final Phone defaultPhone = getDefaultPhone();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!TextUtils.equals(callingPackage,
- TelecomManager.from(defaultPhone.getContext()).getDefaultDialerPackage())) {
+ TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
+ if (!TextUtils.equals(callingPackage, tm.getDefaultDialerPackage())) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle),
"setVoicemailRingtoneUri");
@@ -5776,8 +6190,8 @@
PhoneAccountHandle phoneAccountHandle, boolean enabled) {
final Phone defaultPhone = getDefaultPhone();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!TextUtils.equals(callingPackage,
- TelecomManager.from(defaultPhone.getContext()).getDefaultDialerPackage())) {
+ TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
+ if (!TextUtils.equals(callingPackage, tm.getDefaultDialerPackage())) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle),
"setVoicemailVibrationEnabled");
@@ -6120,7 +6534,8 @@
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
- (new TelephonyShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);
+ (new TelephonyShellCommand(this, getDefaultPhone().getContext()))
+ .exec(this, in, out, err, args, callback, resultReceiver);
}
/**
@@ -6700,75 +7115,6 @@
}
}
- private void ensureUserRunning(int userId) {
- if (!mUserManager.isUserRunning(userId)) {
- throw new IllegalStateException("User " + userId + " does not exist or not running");
- }
- }
-
- /**
- * Returns a list of SMS apps on a given user.
- *
- * Only the shell user (UID 2000 or 0) can call it.
- * Target user must be running.
- */
- @Override
- public String[] getSmsApps(int userId) {
- TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getSmsApps");
- ensureUserRunning(userId);
-
- final Collection<SmsApplicationData> apps =
- SmsApplication.getApplicationCollectionAsUser(mApp, userId);
-
- String[] ret = new String[apps.size()];
- int i = 0;
- for (SmsApplicationData app : apps) {
- ret[i++] = app.mPackageName;
- }
- return ret;
- }
-
- /**
- * Returns the default SMS app package name on a given user.
- *
- * Only the shell user (UID 2000 or 0) can call it.
- * Target user must be running.
- */
- @Override
- public String getDefaultSmsApp(int userId) {
- TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDefaultSmsApp");
- ensureUserRunning(userId);
-
- final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(mApp,
- /* updateIfNeeded= */ true, userId);
- return cn == null ? null : cn.getPackageName();
- }
-
- /**
- * Set a package as the default SMS app on a given user.
- *
- * Only the shell user (UID 2000 or 0) can call it.
- * Target user must be running.
- */
- @Override
- public void setDefaultSmsApp(int userId, String packageName) {
- TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDefaultSmsApp");
- ensureUserRunning(userId);
-
- boolean found = false;
- for (String pkg : getSmsApps(userId)) {
- if (TextUtils.equals(packageName, pkg)) {
- found = true;
- break;
- }
- }
- if (!found) {
- throw new IllegalArgumentException("Package " + packageName + " is not an SMS app");
- }
-
- SmsApplication.setDefaultApplicationAsUser(packageName, mApp, userId);
- }
-
@Override
public Map<Integer, List<EmergencyNumber>> getEmergencyNumberList(
String callingPackage) {
@@ -6865,6 +7211,57 @@
}
@Override
+ public int getEmergencyNumberDbVersion(int subId) {
+ enforceReadPrivilegedPermission("getEmergencyNumberDbVersion");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("getEmergencyNumberDbVersion fails with invalid subId: " + subId);
+ return TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION;
+ }
+ return phone.getEmergencyNumberDbVersion();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void notifyOtaEmergencyNumberDbInstalled() {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (Phone phone: PhoneFactory.getPhones()) {
+ EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+ if (tracker != null) {
+ tracker.updateOtaEmergencyNumberDatabase();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath) {
+ enforceActiveEmergencySessionPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ for (Phone phone: PhoneFactory.getPhones()) {
+ EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+ if (tracker != null) {
+ tracker.updateTestOtaEmergencyNumberDbFilePath(otaFilePath);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public List<String> getCertsFromCarrierPrivilegeAccessRules(int subId) {
enforceReadPrivilegedPermission("getCertsFromCarrierPrivilegeAccessRules");
Phone phone = getPhone(subId);
@@ -7116,10 +7513,8 @@
*/
@Override
public boolean isDataEnabledForApn(int apnType, int subId, String callingPackage) {
- if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
- mApp, subId, callingPackage, "isDataEnabledForApn")) {
- throw new SecurityException("Needs READ_PHONE_STATE for isDataEnabledForApn");
- }
+ enforceReadPrivilegedPermission("Needs READ_PRIVILEGED_PHONE_STATE for "
+ + "isDataEnabledForApn");
// Now that all security checks passes, perform the operation as ourselves.
final long identity = Binder.clearCallingIdentity();
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index e46cfc0..9835ba1 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -31,6 +31,7 @@
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;
@@ -43,10 +44,7 @@
import android.widget.Toast;
import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.CallerInfoAsyncQuery;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.MmiCode;
@@ -54,10 +52,8 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyCapabilities;
-import com.android.phone.CallGatewayManager.RawGatewayInfo;
import com.android.phone.settings.SuppServicesUiUtil;
-import java.util.Arrays;
import java.util.List;
/**
@@ -131,79 +127,35 @@
}
/**
- * @see placeCall below
- */
- public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
- boolean isEmergencyCall) {
- return placeCall(context, phone, number, contactRef, isEmergencyCall,
- CallGatewayManager.EMPTY_INFO, null);
- }
-
- /**
* Dial the number using the phone passed in.
*
- * If the connection is establised, this method issues a sync call
- * that may block to query the caller info.
- * TODO: Change the logic to use the async query.
- *
* @param context To perform the CallerInfo query.
* @param phone the Phone object.
* @param number to be dialed as requested by the user. This is
* NOT the phone number to connect to. It is used only to build the
* call card and to update the call log. See above for restrictions.
- * @param contactRef that triggered the call. Typically a 'tel:'
- * uri but can also be a 'content://contacts' one.
- * @param isEmergencyCall indicates that whether or not this is an
- * emergency call
- * @param gatewayUri Is the address used to setup the connection, null
- * if not using a gateway
- * @param callGateway Class for setting gateway data on a successful call.
*
* @return either CALL_STATUS_DIALED or CALL_STATUS_FAILED
*/
- public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
- boolean isEmergencyCall, RawGatewayInfo gatewayInfo, CallGatewayManager callGateway) {
- final Uri gatewayUri = gatewayInfo.gatewayUri;
+ public static int placeOtaspCall(Context context, Phone phone, String number) {
+ final Uri gatewayUri = null;
if (VDBG) {
log("placeCall()... number: '" + number + "'"
- + ", GW:'" + gatewayUri + "'"
- + ", contactRef:" + contactRef
- + ", isEmergencyCall: " + isEmergencyCall);
+ + ", GW:'" + gatewayUri + "'");
} else {
log("placeCall()... number: " + toLogSafePhoneNumber(number)
- + ", GW: " + (gatewayUri != null ? "non-null" : "null")
- + ", emergency? " + isEmergencyCall);
+ + ", GW: " + (gatewayUri != null ? "non-null" : "null"));
}
final PhoneGlobals app = PhoneGlobals.getInstance();
boolean useGateway = false;
- if (null != gatewayUri &&
- !isEmergencyCall &&
- PhoneUtils.isRoutableViaGateway(number)) { // Filter out MMI, OTA and other codes.
- useGateway = true;
- }
+ Uri contactRef = null;
int status = CALL_STATUS_DIALED;
Connection connection;
String numberToDial;
- if (useGateway) {
- // TODO: 'tel' should be a constant defined in framework base
- // somewhere (it is in webkit.)
- if (null == gatewayUri || !PhoneAccount.SCHEME_TEL.equals(gatewayUri.getScheme())) {
- Log.e(LOG_TAG, "Unsupported URL:" + gatewayUri);
- return CALL_STATUS_FAILED;
- }
-
- // We can use getSchemeSpecificPart because we don't allow #
- // in the gateway numbers (treated a fragment delim.) However
- // if we allow more complex gateway numbers sequence (with
- // passwords or whatnot) that use #, this may break.
- // TODO: Need to support MMI codes.
- numberToDial = gatewayUri.getSchemeSpecificPart();
- } else {
- numberToDial = number;
- }
+ numberToDial = number;
try {
connection = app.mCM.dial(phone, numberToDial, VideoProfile.STATE_AUDIO_ONLY);
@@ -228,33 +180,6 @@
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
updateCdmaCallStateOnNewOutgoingCall(app, connection);
}
-
- if (gatewayUri == null) {
- // phone.dial() succeeded: we're now in a normal phone call.
- // attach the URI to the CallerInfo Object if it is there,
- // otherwise just attach the Uri Reference.
- // if the uri does not have a "content" scheme, then we treat
- // it as if it does NOT have a unique reference.
- String content = context.getContentResolver().SCHEME_CONTENT;
- if ((contactRef != null) && (contactRef.getScheme().equals(content))) {
- Object userDataObject = connection.getUserData();
- if (userDataObject == null) {
- connection.setUserData(contactRef);
- } else {
- // TODO: This branch is dead code, we have
- // just created the connection which has
- // no user data (null) by default.
- if (userDataObject instanceof CallerInfo) {
- ((CallerInfo) userDataObject).contactRefUri = contactRef;
- } else {
- ((CallerInfoToken) userDataObject).currentInfo.contactRefUri =
- contactRef;
- }
- }
- }
- }
-
- startGetCallerInfo(context, connection, null, null, gatewayInfo);
}
return status;
@@ -299,7 +224,7 @@
MmiCode mmiCode,
Message buttonCallbackMessage,
Dialog previousAlert) {
- log("displayMMIInitiate: " + android.telecom.Log.pii(mmiCode.toString()));
+ log("displayMMIInitiate: " + Rlog.pii(LOG_TAG, mmiCode.toString()));
if (previousAlert != null) {
previousAlert.dismiss();
}
@@ -662,533 +587,12 @@
return canceled;
}
- /**
- * Returns the caller-id info corresponding to the specified Connection.
- * (This is just a simple wrapper around CallerInfo.getCallerInfo(): we
- * extract a phone number from the specified Connection, and feed that
- * number into CallerInfo.getCallerInfo().)
- *
- * The returned CallerInfo may be null in certain error cases, like if the
- * specified Connection was null, or if we weren't able to get a valid
- * phone number from the Connection.
- *
- * Finally, if the getCallerInfo() call did succeed, we save the resulting
- * CallerInfo object in the "userData" field of the Connection.
- *
- * NOTE: This API should be avoided, with preference given to the
- * asynchronous startGetCallerInfo API.
- */
- static CallerInfo getCallerInfo(Context context, Connection c) {
- CallerInfo info = null;
-
- if (c != null) {
- //See if there is a URI attached. If there is, this means
- //that there is no CallerInfo queried yet, so we'll need to
- //replace the URI with a full CallerInfo object.
- Object userDataObject = c.getUserData();
- if (userDataObject instanceof Uri) {
- info = CallerInfo.getCallerInfo(context, (Uri) userDataObject);
- if (info != null) {
- c.setUserData(info);
- }
- } else {
- if (userDataObject instanceof CallerInfoToken) {
- //temporary result, while query is running
- info = ((CallerInfoToken) userDataObject).currentInfo;
- } else {
- //final query result
- info = (CallerInfo) userDataObject;
- }
- if (info == null) {
- // No URI, or Existing CallerInfo, so we'll have to make do with
- // querying a new CallerInfo using the connection's phone number.
- String number = c.getAddress();
-
- if (DBG) log("getCallerInfo: number = " + toLogSafePhoneNumber(number));
-
- if (!TextUtils.isEmpty(number)) {
- info = CallerInfo.getCallerInfo(context, number);
- if (info != null) {
- c.setUserData(info);
- }
- }
- }
- }
- }
- return info;
- }
-
- /**
- * Class returned by the startGetCallerInfo call to package a temporary
- * CallerInfo Object, to be superceded by the CallerInfo Object passed
- * into the listener when the query with token mAsyncQueryToken is complete.
- */
- public static class CallerInfoToken {
- /**indicates that there will no longer be updates to this request.*/
- public boolean isFinal;
-
- public CallerInfo currentInfo;
- public CallerInfoAsyncQuery asyncQuery;
- }
-
- /**
- * place a temporary callerinfo object in the hands of the caller and notify
- * caller when the actual query is done.
- */
- static CallerInfoToken startGetCallerInfo(Context context, Connection c,
- CallerInfoAsyncQuery.OnQueryCompleteListener listener, Object cookie,
- RawGatewayInfo info) {
- CallerInfoToken cit;
-
- if (c == null) {
- //TODO: perhaps throw an exception here.
- cit = new CallerInfoToken();
- cit.asyncQuery = null;
- return cit;
- }
-
- Object userDataObject = c.getUserData();
-
- // There are now 3 states for the Connection's userData object:
- //
- // (1) Uri - query has not been executed yet
- //
- // (2) CallerInfoToken - query is executing, but has not completed.
- //
- // (3) CallerInfo - query has executed.
- //
- // In each case we have slightly different behaviour:
- // 1. If the query has not been executed yet (Uri or null), we start
- // query execution asynchronously, and note it by attaching a
- // CallerInfoToken as the userData.
- // 2. If the query is executing (CallerInfoToken), we've essentially
- // reached a state where we've received multiple requests for the
- // same callerInfo. That means that once the query is complete,
- // we'll need to execute the additional listener requested.
- // 3. If the query has already been executed (CallerInfo), we just
- // return the CallerInfo object as expected.
- // 4. Regarding isFinal - there are cases where the CallerInfo object
- // will not be attached, like when the number is empty (caller id
- // blocking). This flag is used to indicate that the
- // CallerInfoToken object is going to be permanent since no
- // query results will be returned. In the case where a query
- // has been completed, this flag is used to indicate to the caller
- // that the data will not be updated since it is valid.
- //
- // Note: For the case where a number is NOT retrievable, we leave
- // the CallerInfo as null in the CallerInfoToken. This is
- // something of a departure from the original code, since the old
- // code manufactured a CallerInfo object regardless of the query
- // outcome. From now on, we will append an empty CallerInfo
- // object, to mirror previous behaviour, and to avoid Null Pointer
- // Exceptions.
-
- if (userDataObject instanceof Uri) {
- // State (1): query has not been executed yet
-
- //create a dummy callerinfo, populate with what we know from URI.
- cit = new CallerInfoToken();
- cit.currentInfo = new CallerInfo();
- cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
- (Uri) userDataObject, sCallerInfoQueryListener, c);
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
- cit.isFinal = false;
-
- c.setUserData(cit);
-
- if (DBG) log("startGetCallerInfo: query based on Uri: " + userDataObject);
-
- } else if (userDataObject == null) {
- // No URI, or Existing CallerInfo, so we'll have to make do with
- // querying a new CallerInfo using the connection's phone number.
- String number = c.getAddress();
-
- if (info != null && info != CallGatewayManager.EMPTY_INFO) {
- // Gateway number, the connection number is actually the gateway number.
- // need to lookup via dialed number.
- number = info.trueNumber;
- }
-
- if (DBG) {
- log("PhoneUtils.startGetCallerInfo: new query for phone number...");
- log("- number (address): " + toLogSafePhoneNumber(number));
- log("- c: " + c);
- log("- phone: " + c.getCall().getPhone());
- int phoneType = c.getCall().getPhone().getPhoneType();
- log("- phoneType: " + phoneType);
- switch (phoneType) {
- case PhoneConstants.PHONE_TYPE_NONE: log(" ==> PHONE_TYPE_NONE"); break;
- case PhoneConstants.PHONE_TYPE_GSM: log(" ==> PHONE_TYPE_GSM"); break;
- case PhoneConstants.PHONE_TYPE_IMS: log(" ==> PHONE_TYPE_IMS"); break;
- case PhoneConstants.PHONE_TYPE_CDMA: log(" ==> PHONE_TYPE_CDMA"); break;
- case PhoneConstants.PHONE_TYPE_SIP: log(" ==> PHONE_TYPE_SIP"); break;
- case PhoneConstants.PHONE_TYPE_THIRD_PARTY:
- log(" ==> PHONE_TYPE_THIRD_PARTY");
- break;
- default: log(" ==> Unknown phone type"); break;
- }
- }
-
- cit = new CallerInfoToken();
- cit.currentInfo = new CallerInfo();
-
- // Store CNAP information retrieved from the Connection (we want to do this
- // here regardless of whether the number is empty or not).
- cit.currentInfo.cnapName = c.getCnapName();
- cit.currentInfo.name = cit.currentInfo.cnapName; // This can still get overwritten
- // by ContactInfo later
- cit.currentInfo.numberPresentation = c.getNumberPresentation();
- cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
- if (VDBG) {
- log("startGetCallerInfo: number = " + number);
- log("startGetCallerInfo: CNAP Info from FW(1): name="
- + cit.currentInfo.cnapName
- + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
- }
-
- // handling case where number is null (caller id hidden) as well.
- if (!TextUtils.isEmpty(number)) {
- // Check for special CNAP cases and modify the CallerInfo accordingly
- // to be sure we keep the right information to display/log later
- number = modifyForSpecialCnapCases(context, cit.currentInfo, number,
- cit.currentInfo.numberPresentation);
-
- cit.currentInfo.phoneNumber = number;
- // For scenarios where we may receive a valid number from the network but a
- // restricted/unavailable presentation, we do not want to perform a contact query
- // (see note on isFinal above). So we set isFinal to true here as well.
- if (cit.currentInfo.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
- cit.isFinal = true;
- } else {
- if (DBG) log("==> Actually starting CallerInfoAsyncQuery.startQuery()...");
- cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
- number, sCallerInfoQueryListener, c);
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
- cit.isFinal = false;
- }
- } else {
- // This is the case where we are querying on a number that
- // is null or empty, like a caller whose caller id is
- // blocked or empty (CLIR). The previous behaviour was to
- // throw a null CallerInfo object back to the user, but
- // this departure is somewhat cleaner.
- if (DBG) log("startGetCallerInfo: No query to start, send trivial reply.");
- cit.isFinal = true; // please see note on isFinal, above.
- }
-
- c.setUserData(cit);
-
- if (DBG) {
- log("startGetCallerInfo: query based on number: " + toLogSafePhoneNumber(number));
- }
-
- } else if (userDataObject instanceof CallerInfoToken) {
- // State (2): query is executing, but has not completed.
-
- // just tack on this listener to the queue.
- cit = (CallerInfoToken) userDataObject;
-
- // handling case where number is null (caller id hidden) as well.
- if (cit.asyncQuery != null) {
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
-
- if (DBG) log("startGetCallerInfo: query already running, adding listener: " +
- listener.getClass().toString());
- } else {
- // handling case where number/name gets updated later on by the network
- String updatedNumber = c.getAddress();
-
- if (info != null) {
- // Gateway number, the connection number is actually the gateway number.
- // need to lookup via dialed number.
- updatedNumber = info.trueNumber;
- }
-
- if (DBG) {
- log("startGetCallerInfo: updatedNumber initially = "
- + toLogSafePhoneNumber(updatedNumber));
- }
- if (!TextUtils.isEmpty(updatedNumber)) {
- // Store CNAP information retrieved from the Connection
- cit.currentInfo.cnapName = c.getCnapName();
- // This can still get overwritten by ContactInfo
- cit.currentInfo.name = cit.currentInfo.cnapName;
- cit.currentInfo.numberPresentation = c.getNumberPresentation();
- cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
- updatedNumber = modifyForSpecialCnapCases(context, cit.currentInfo,
- updatedNumber, cit.currentInfo.numberPresentation);
-
- cit.currentInfo.phoneNumber = updatedNumber;
- if (DBG) {
- log("startGetCallerInfo: updatedNumber="
- + toLogSafePhoneNumber(updatedNumber));
- }
- if (VDBG) {
- log("startGetCallerInfo: CNAP Info from FW(2): name="
- + cit.currentInfo.cnapName
- + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
- } else if (DBG) {
- log("startGetCallerInfo: CNAP Info from FW(2)");
- }
- // For scenarios where we may receive a valid number from the network but a
- // restricted/unavailable presentation, we do not want to perform a contact query
- // (see note on isFinal above). So we set isFinal to true here as well.
- if (cit.currentInfo.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
- cit.isFinal = true;
- } else {
- cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
- updatedNumber, sCallerInfoQueryListener, c);
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
- cit.isFinal = false;
- }
- } else {
- if (DBG) log("startGetCallerInfo: No query to attach to, send trivial reply.");
- if (cit.currentInfo == null) {
- cit.currentInfo = new CallerInfo();
- }
- // Store CNAP information retrieved from the Connection
- cit.currentInfo.cnapName = c.getCnapName(); // This can still get
- // overwritten by ContactInfo
- cit.currentInfo.name = cit.currentInfo.cnapName;
- cit.currentInfo.numberPresentation = c.getNumberPresentation();
- cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
- if (VDBG) {
- log("startGetCallerInfo: CNAP Info from FW(3): name="
- + cit.currentInfo.cnapName
- + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
- } else if (DBG) {
- log("startGetCallerInfo: CNAP Info from FW(3)");
- }
- cit.isFinal = true; // please see note on isFinal, above.
- }
- }
- } else {
- // State (3): query is complete.
-
- // The connection's userDataObject is a full-fledged
- // CallerInfo instance. Wrap it in a CallerInfoToken and
- // return it to the user.
-
- cit = new CallerInfoToken();
- cit.currentInfo = (CallerInfo) userDataObject;
- cit.asyncQuery = null;
- cit.isFinal = true;
- // since the query is already done, call the listener.
- if (DBG) log("startGetCallerInfo: query already done, returning CallerInfo");
- if (DBG) log("==> cit.currentInfo = " + cit.currentInfo);
- }
- return cit;
- }
-
- /**
- * Static CallerInfoAsyncQuery.OnQueryCompleteListener instance that
- * we use with all our CallerInfoAsyncQuery.startQuery() requests.
- */
- private static final int QUERY_TOKEN = -1;
- static CallerInfoAsyncQuery.OnQueryCompleteListener sCallerInfoQueryListener =
- new CallerInfoAsyncQuery.OnQueryCompleteListener () {
- /**
- * When the query completes, we stash the resulting CallerInfo
- * object away in the Connection's "userData" (where it will
- * later be retrieved by the in-call UI.)
- */
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- if (DBG) log("query complete, updating connection.userdata");
- Connection conn = (Connection) cookie;
-
- // Added a check if CallerInfo is coming from ContactInfo or from Connection.
- // If no ContactInfo, then we want to use CNAP information coming from network
- if (DBG) log("- onQueryComplete: CallerInfo:" + ci);
- if (ci.contactExists || ci.isEmergencyNumber() || ci.isVoiceMailNumber()) {
- // If the number presentation has not been set by
- // the ContactInfo, use the one from the
- // connection.
-
- // TODO: Need a new util method to merge the info
- // from the Connection in a CallerInfo object.
- // Here 'ci' is a new CallerInfo instance read
- // from the DB. It has lost all the connection
- // info preset before the query (see PhoneUtils
- // line 1334). We should have a method to merge
- // back into this new instance the info from the
- // connection object not set by the DB. If the
- // Connection already has a CallerInfo instance in
- // userData, then we could use this instance to
- // fill 'ci' in. The same routine could be used in
- // PhoneUtils.
- if (0 == ci.numberPresentation) {
- ci.numberPresentation = conn.getNumberPresentation();
- }
- } else {
- // No matching contact was found for this number.
- // Return a new CallerInfo based solely on the CNAP
- // information from the network.
-
- CallerInfo newCi = getCallerInfo(null, conn);
-
- // ...but copy over the (few) things we care about
- // from the original CallerInfo object:
- if (newCi != null) {
- newCi.phoneNumber = ci.phoneNumber; // To get formatted phone number
- newCi.geoDescription = ci.geoDescription; // To get geo description string
- ci = newCi;
- }
- }
-
- if (DBG) log("==> Stashing CallerInfo " + ci + " into the connection...");
- conn.setUserData(ci);
- }
- };
-
-
- /**
- * Returns a single "name" for the specified given a CallerInfo object.
- * If the name is null, return defaultString as the default value, usually
- * context.getString(R.string.unknown).
- */
- static String getCompactNameFromCallerInfo(CallerInfo ci, Context context) {
- if (DBG) log("getCompactNameFromCallerInfo: info = " + ci);
-
- String compactName = null;
- if (ci != null) {
- if (TextUtils.isEmpty(ci.name)) {
- // Perform any modifications for special CNAP cases to
- // the phone number being displayed, if applicable.
- compactName = modifyForSpecialCnapCases(context, ci, ci.phoneNumber,
- ci.numberPresentation);
- } else {
- // Don't call modifyForSpecialCnapCases on regular name. See b/2160795.
- compactName = ci.name;
- }
- }
-
- if ((compactName == null) || (TextUtils.isEmpty(compactName))) {
- // If we're still null/empty here, then check if we have a presentation
- // string that takes precedence that we could return, otherwise display
- // "unknown" string.
- if (ci != null && ci.numberPresentation == PhoneConstants.PRESENTATION_RESTRICTED) {
- compactName = context.getString(R.string.private_num);
- } else if (ci != null && ci.numberPresentation == PhoneConstants.PRESENTATION_PAYPHONE) {
- compactName = context.getString(R.string.payphone);
- } else {
- compactName = context.getString(R.string.unknown);
- }
- }
- if (VDBG) log("getCompactNameFromCallerInfo: compactName=" + compactName);
- return compactName;
- }
-
- static boolean isInEmergencyCall(CallManager cm) {
- Call fgCall = cm.getActiveFgCall();
- // isIdle includes checks for the DISCONNECTING/DISCONNECTED state.
- if(!fgCall.isIdle()) {
- for (Connection cn : fgCall.getConnections()) {
- if (PhoneNumberUtils.isLocalEmergencyNumber(PhoneGlobals.getInstance(),
- cn.getAddress())) {
- return true;
- }
- }
- }
- return false;
- }
//
// Misc UI policy helper functions
//
/**
- * Based on the input CNAP number string,
- * @return _RESTRICTED or _UNKNOWN for all the special CNAP strings.
- * Otherwise, return CNAP_SPECIAL_CASE_NO.
- */
- private static int checkCnapSpecialCases(String n) {
- if (n.equals("PRIVATE") ||
- n.equals("P") ||
- n.equals("RES")) {
- if (DBG) log("checkCnapSpecialCases, PRIVATE string: " + n);
- return PhoneConstants.PRESENTATION_RESTRICTED;
- } else if (n.equals("UNAVAILABLE") ||
- n.equals("UNKNOWN") ||
- n.equals("UNA") ||
- n.equals("U")) {
- if (DBG) log("checkCnapSpecialCases, UNKNOWN string: " + n);
- return PhoneConstants.PRESENTATION_UNKNOWN;
- } else {
- if (DBG) log("checkCnapSpecialCases, normal str. number: " + n);
- return CNAP_SPECIAL_CASE_NO;
- }
- }
-
- /**
- * Handles certain "corner cases" for CNAP. When we receive weird phone numbers
- * from the network to indicate different number presentations, convert them to
- * expected number and presentation values within the CallerInfo object.
- * @param number number we use to verify if we are in a corner case
- * @param presentation presentation value used to verify if we are in a corner case
- * @return the new String that should be used for the phone number
- */
- /* package */ static String modifyForSpecialCnapCases(Context context, CallerInfo ci,
- String number, int presentation) {
- // Obviously we return number if ci == null, but still return number if
- // number == null, because in these cases the correct string will still be
- // displayed/logged after this function returns based on the presentation value.
- if (ci == null || number == null) return number;
-
- if (DBG) {
- log("modifyForSpecialCnapCases: initially, number="
- + toLogSafePhoneNumber(number)
- + ", presentation=" + presentation + " ci " + ci);
- }
-
- // "ABSENT NUMBER" is a possible value we could get from the network as the
- // phone number, so if this happens, change it to "Unknown" in the CallerInfo
- // and fix the presentation to be the same.
- final String[] absentNumberValues =
- context.getResources().getStringArray(R.array.absent_num);
- if (Arrays.asList(absentNumberValues).contains(number)
- && presentation == PhoneConstants.PRESENTATION_ALLOWED) {
- number = context.getString(R.string.unknown);
- ci.numberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
- }
-
- // Check for other special "corner cases" for CNAP and fix them similarly. Corner
- // cases only apply if we received an allowed presentation from the network, so check
- // if we think we have an allowed presentation, or if the CallerInfo presentation doesn't
- // match the presentation passed in for verification (meaning we changed it previously
- // because it's a corner case and we're being called from a different entry point).
- if (ci.numberPresentation == PhoneConstants.PRESENTATION_ALLOWED
- || (ci.numberPresentation != presentation
- && presentation == PhoneConstants.PRESENTATION_ALLOWED)) {
- int cnapSpecialCase = checkCnapSpecialCases(number);
- if (cnapSpecialCase != CNAP_SPECIAL_CASE_NO) {
- // For all special strings, change number & numberPresentation.
- if (cnapSpecialCase == PhoneConstants.PRESENTATION_RESTRICTED) {
- number = context.getString(R.string.private_num);
- } else if (cnapSpecialCase == PhoneConstants.PRESENTATION_UNKNOWN) {
- number = context.getString(R.string.unknown);
- }
- if (DBG) {
- log("SpecialCnap: number=" + toLogSafePhoneNumber(number)
- + "; presentation now=" + cnapSpecialCase);
- }
- ci.numberPresentation = cnapSpecialCase;
- }
- }
- if (DBG) {
- log("modifyForSpecialCnapCases: returning number string="
- + toLogSafePhoneNumber(number));
- }
- return number;
- }
-
- //
- // Support for 3rd party phone service providers.
- //
-
- /**
* Check if a phone number can be route through a 3rd party
* gateway. The number must be a global phone number in numerical
* form (1-800-666-SEXY won't work).
diff --git a/src/com/android/phone/ShortcutViewUtils.java b/src/com/android/phone/ShortcutViewUtils.java
index 8e5ab42..e3c5b64 100644
--- a/src/com/android/phone/ShortcutViewUtils.java
+++ b/src/com/android/phone/ShortcutViewUtils.java
@@ -33,6 +33,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.telephony.util.ArrayUtils;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -225,8 +227,8 @@
PhoneAccountHandle defaultHandle = telecomManager.getDefaultOutgoingPhoneAccount(
PhoneAccount.SCHEME_TEL);
if (defaultHandle != null) {
- PhoneInfo phone = loadPhoneInfo(defaultHandle, telephonyManager, telecomManager,
- promotedLists);
+ PhoneInfo phone = loadPhoneInfo(context, defaultHandle, telephonyManager,
+ telecomManager, promotedLists);
if (phone.isSufficientForEmergencyCall(context)) {
return phone;
}
@@ -240,7 +242,7 @@
List<PhoneAccountHandle> allHandles = telecomManager.getCallCapablePhoneAccounts();
if (allHandles != null && !allHandles.isEmpty()) {
for (PhoneAccountHandle handle : allHandles) {
- PhoneInfo phone = loadPhoneInfo(handle, telephonyManager, telecomManager,
+ PhoneInfo phone = loadPhoneInfo(context, handle, telephonyManager, telecomManager,
promotedLists);
if (phone.isSufficientForEmergencyCall(context)) {
return phone;
@@ -272,8 +274,11 @@
return false;
}
- private static PhoneInfo loadPhoneInfo(@NonNull PhoneAccountHandle handle,
- @NonNull TelephonyManager telephonyManager, @NonNull TelecomManager telecomManager,
+ private static PhoneInfo loadPhoneInfo(
+ @NonNull Context context,
+ @NonNull PhoneAccountHandle handle,
+ @NonNull TelephonyManager telephonyManager,
+ @NonNull TelecomManager telecomManager,
Map<Integer, List<EmergencyNumber>> promotedLists) {
boolean canPlaceEmergencyCall = false;
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -293,12 +298,69 @@
}
if (promotedLists != null) {
- emergencyNumberList = promotedLists.get(subId);
+ emergencyNumberList = removeCarrierSpecificPrefixes(context, subId,
+ promotedLists.get(subId));
}
return new PhoneInfo(handle, canPlaceEmergencyCall, subId, countryIso, emergencyNumberList);
}
+ @Nullable
+ private static String[] getCarrierSpecificPrefixes(@NonNull Context context, int subId) {
+ CarrierConfigManager configMgr = context.getSystemService(CarrierConfigManager.class);
+ if (configMgr == null) {
+ return null;
+ }
+ PersistableBundle b = configMgr.getConfigForSubId(subId);
+ return b == null ? null : b.getStringArray(
+ CarrierConfigManager.KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY);
+ }
+
+ // Removes carrier specific emergency number prefixes (if there is any) from every emergency
+ // number and create a new list without duplications. Returns the original list if there is no
+ // prefixes.
+ @NonNull
+ private static List<EmergencyNumber> removeCarrierSpecificPrefixes(
+ @NonNull Context context,
+ int subId,
+ @NonNull List<EmergencyNumber> emergencyNumberList) {
+ String[] prefixes = getCarrierSpecificPrefixes(context, subId);
+ if (ArrayUtils.isEmpty(prefixes)) {
+ return emergencyNumberList;
+ }
+
+ List<EmergencyNumber> newList = new ArrayList<>(emergencyNumberList.size());
+ for (EmergencyNumber emergencyNumber : emergencyNumberList) {
+ // If no prefix was removed from emergencyNumber, add it to the newList directly.
+ EmergencyNumber newNumber = emergencyNumber;
+ String number = emergencyNumber.getNumber();
+ for (String prefix : prefixes) {
+ // If emergencyNumber starts with this prefix, remove this prefix to retrieve the
+ // actual emergency number.
+ // However, if emergencyNumber is exactly the same with this prefix, it could be
+ // either a real emergency number, or composed with another prefix. It shouldn't be
+ // processed with this prefix whatever.
+ if (!TextUtils.isEmpty(prefix) && number.startsWith(prefix)
+ && !number.equals(prefix)) {
+ newNumber = new EmergencyNumber(
+ number.substring(prefix.length()),
+ emergencyNumber.getCountryIso(),
+ emergencyNumber.getMnc(),
+ emergencyNumber.getEmergencyServiceCategoryBitmask(),
+ emergencyNumber.getEmergencyUrns(),
+ emergencyNumber.getEmergencyNumberSourceBitmask(),
+ emergencyNumber.getEmergencyCallRouting());
+ // There should not be more than one prefix attached to a number.
+ break;
+ }
+ }
+ if (!newList.contains(newNumber)) {
+ newList.add(newNumber);
+ }
+ }
+ return newList;
+ }
+
@NonNull
private static Map<Integer, List<EmergencyNumber>> getPromotedEmergencyNumberLists(
@NonNull TelephonyManager telephonyManager) {
diff --git a/src/com/android/phone/SpecialCharSequenceMgr.java b/src/com/android/phone/SpecialCharSequenceMgr.java
index 514b2c9..674449e 100644
--- a/src/com/android/phone/SpecialCharSequenceMgr.java
+++ b/src/com/android/phone/SpecialCharSequenceMgr.java
@@ -21,6 +21,7 @@
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.os.UserManager;
import android.provider.Settings;
import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionInfo;
@@ -233,32 +234,45 @@
// if a dialstring is an MMI code.
if ((input.startsWith("**04") || input.startsWith("**05"))
&& input.endsWith("#")) {
- PhoneGlobals app = PhoneGlobals.getInstance();
- Phone phone;
- int subId;
- if (input.startsWith("**04")) {
- subId = getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED, context);
- } else {
- subId = getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED, context);
- }
- if (SubscriptionManager.isValidSubscriptionId(subId)) {
- log("get phone with subId: " + subId);
- phone = PhoneGlobals.getPhone(subId);
- } else {
- log("get default phone");
- phone = PhoneGlobals.getPhone();
- }
- boolean isMMIHandled = phone.handlePinMmi(input);
+ UserManager userManager = (UserManager) pukInputActivity
+ .getSystemService(Context.USER_SERVICE);
+ if (userManager.isSystemUser()) {
+ PhoneGlobals app = PhoneGlobals.getInstance();
+ Phone phone;
+ int subId;
+ if (input.startsWith("**04")) {
+ subId = getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED, context);
+ } else {
+ subId = getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED, context);
+ }
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ log("get phone with subId: " + subId);
+ phone = PhoneGlobals.getPhone(subId);
+ } else {
+ log("get default phone");
+ phone = PhoneGlobals.getPhone();
+ }
+ boolean isMMIHandled = phone.handlePinMmi(input);
- // if the PUK code is recognized then indicate to the
- // phone app that an attempt to unPUK the device was
- // made with this activity. The PUK code may still
- // fail though, but we won't know until the MMI code
- // returns a result.
- if (isMMIHandled && input.startsWith("**05")) {
- app.setPukEntryActivity(pukInputActivity);
+ // if the PUK code is recognized then indicate to the
+ // phone app that an attempt to unPUK the device was
+ // made with this activity. The PUK code may still
+ // fail though, but we won't know until the MMI code
+ // returns a result.
+ if (isMMIHandled && input.startsWith("**05")) {
+ app.setPukEntryActivity(pukInputActivity);
+ }
+ return isMMIHandled;
+ } else {
+ AlertDialog dialog = new AlertDialog.Builder(context)
+ .setMessage(R.string.pin_puk_system_user_only)
+ .setPositiveButton(R.string.ok, null)
+ .setCancelable(true).create();
+ dialog.show();
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ return true;
}
- return isMMIHandled;
}
return false;
}
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index a34abc0..0806fd1 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -16,20 +16,27 @@
package com.android.phone;
+import android.content.Context;
import android.os.Binder;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ShellCommand;
-import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.emergency.EmergencyNumber;
import android.util.Log;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
/**
* Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
@@ -45,9 +52,12 @@
private static final int DEFAULT_PHONE_ID = 0;
private static final String IMS_SUBCOMMAND = "ims";
- private static final String SMS_SUBCOMMAND = "sms";
private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
+ private static final String CARRIER_CONFIG_SUBCOMMAND = "cc";
+ private static final String DATA_TEST_MODE = "data";
+ private static final String DATA_ENABLE = "enable";
+ private static final String DATA_DISABLE = "disable";
private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
@@ -58,18 +68,68 @@
// support CEP data.
private static final String IMS_CEP = "conference-event-package";
- private static final String SMS_GET_APPS = "get-apps";
- private static final String SMS_GET_DEFAULT_APP = "get-default-app";
- private static final String SMS_SET_DEFAULT_APP = "set-default-app";
-
private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package";
private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call";
+ private static final String CC_GET_VALUE = "get-value";
+ private static final String CC_SET_VALUE = "set-value";
+ private static final String CC_CLEAR_VALUES = "clear-values";
+
// Take advantage of existing methods that already contain permissions checks when possible.
private final ITelephony mInterface;
- public TelephonyShellCommand(ITelephony binder) {
+ private SubscriptionManager mSubscriptionManager;
+ private CarrierConfigManager mCarrierConfigManager;
+
+ private enum CcType {
+ BOOLEAN, DOUBLE, DOUBLE_ARRAY, INT, INT_ARRAY, LONG, LONG_ARRAY, STRING,
+ STRING_ARRAY, UNKNOWN
+ }
+
+ private class CcOptionParseResult {
+ public int mSubId;
+ public boolean mPersistent;
+ }
+
+ // Maps carrier config keys to type. It is possible to infer the type for most carrier config
+ // keys by looking at the end of the string which usually tells the type.
+ // For instance: "xxxx_string", "xxxx_string_array", etc.
+ // The carrier config keys in this map does not follow this convention. It is therefore not
+ // possible to infer the type for these keys by looking at the string.
+ private static final Map<String, CcType> CC_TYPE_MAP = new HashMap<String, CcType>() {{
+ put(CarrierConfigManager.Gps.KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_GPS_LOCK_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_LPP_PROFILE_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_NFW_PROXY_APPS_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_ES_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_HOST_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_MODE_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_PORT_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_VER_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING,
+ CcType.STRING);
+ put(CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ CcType.STRING_ARRAY);
+ put(CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ CcType.STRING_ARRAY);
+ put(CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_USER_AGENT_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES, CcType.STRING_ARRAY);
+ }
+ };
+
+ public TelephonyShellCommand(ITelephony binder, Context context) {
mInterface = binder;
+ mCarrierConfigManager =
+ (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ mSubscriptionManager = (SubscriptionManager)
+ context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
}
@Override
@@ -82,13 +142,15 @@
case IMS_SUBCOMMAND: {
return handleImsCommand();
}
- case SMS_SUBCOMMAND: {
- return handleSmsCommand();
- }
case NUMBER_VERIFICATION_SUBCOMMAND:
return handleNumberVerificationCommand();
case EMERGENCY_NUMBER_TEST_MODE:
return handleEmergencyNumberTestModeCommand();
+ case CARRIER_CONFIG_SUBCOMMAND: {
+ return handleCcCommand();
+ }
+ case DATA_TEST_MODE:
+ return handleDataTestModeCommand();
default: {
return handleDefaultCommands(cmd);
}
@@ -103,13 +165,16 @@
pw.println(" Print this help text.");
pw.println(" ims");
pw.println(" IMS Commands.");
- pw.println(" sms");
- pw.println(" SMS Commands.");
pw.println(" emergency-number-test-mode");
pw.println(" Emergency Number Test Mode Commands.");
+ pw.println(" data");
+ pw.println(" Data Test Mode Commands.");
+ pw.println(" cc");
+ pw.println(" Carrier Config Commands.");
onHelpIms();
- onHelpSms();
onHelpEmergencyNumber();
+ onHelpDataTestMode();
+ onHelpCc();
}
private void onHelpIms() {
@@ -139,18 +204,6 @@
pw.println(" enables or disables handling or network conference event package data.");
}
- private void onHelpSms() {
- PrintWriter pw = getOutPrintWriter();
- pw.println("SMS Commands:");
- pw.println(" sms get-apps [--user USER_ID]");
- pw.println(" Print all SMS apps on a user.");
- pw.println(" sms get-default-app [--user USER_ID]");
- pw.println(" Get the default SMS app.");
- pw.println(" sms set-default-app [--user USER_ID] PACKAGE_NAME");
- pw.println(" Set PACKAGE_NAME as the default SMS app.");
- }
-
-
private void onHelpNumberVerification() {
PrintWriter pw = getOutPrintWriter();
pw.println("Number verification commands");
@@ -162,6 +215,13 @@
pw.println(" 1 if the call would have been intercepted, 0 otherwise.");
}
+ private void onHelpDataTestMode() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Mobile Data Test Mode Commands:");
+ pw.println(" data enable: enable mobile data connectivity");
+ pw.println(" data disable: disable mobile data connectivity");
+ }
+
private void onHelpEmergencyNumber() {
PrintWriter pw = getOutPrintWriter();
pw.println("Emergency Number Test Mode Commands:");
@@ -176,6 +236,34 @@
pw.println(" -p: get the full emergency number list in the test mode.");
}
+ private void onHelpCc() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Carrier Config Commands:");
+ pw.println(" cc get-value [-s SLOT_ID] [KEY]");
+ pw.println(" Print carrier config values.");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to read carrier config value for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ pw.println(" KEY: The key to the carrier config value to print. All values are printed");
+ pw.println(" if KEY is not specified.");
+ pw.println(" cc set-value [-s SLOT_ID] [-p] KEY [NEW_VALUE]");
+ pw.println(" Set carrier config KEY to NEW_VALUE.");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to set carrier config value for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ pw.println(" -p: Value will be stored persistent");
+ pw.println(" NEW_VALUE specifies the new value for carrier config KEY. Null will be");
+ pw.println(" used if NEW_VALUE is not set. Strings should be encapsulated with");
+ pw.println(" quotation marks. Spaces needs to be escaped. Example: \"Hello\\ World\"");
+ pw.println(" Separate items in arrays with space . Example: \"item1\" \"item2\"");
+ pw.println(" cc clear-values [-s SLOT_ID]");
+ pw.println(" Clear all carrier override values that has previously been set");
+ pw.println(" with set-value");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to clear carrier config values for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ }
+
private int handleImsCommand() {
String arg = getNextArg();
if (arg == null) {
@@ -204,6 +292,41 @@
return -1;
}
+ private int handleDataTestModeCommand() {
+ PrintWriter errPw = getErrPrintWriter();
+ String arg = getNextArgRequired();
+ if (arg == null) {
+ onHelpDataTestMode();
+ return 0;
+ }
+ switch (arg) {
+ case DATA_ENABLE: {
+ try {
+ mInterface.enableDataConnectivity();
+ } catch (RemoteException ex) {
+ Log.w(LOG_TAG, "data enable, error " + ex.getMessage());
+ errPw.println("Exception: " + ex.getMessage());
+ return -1;
+ }
+ break;
+ }
+ case DATA_DISABLE: {
+ try {
+ mInterface.disableDataConnectivity();
+ } catch (RemoteException ex) {
+ Log.w(LOG_TAG, "data disable, error " + ex.getMessage());
+ errPw.println("Exception: " + ex.getMessage());
+ return -1;
+ }
+ break;
+ }
+ default:
+ onHelpDataTestMode();
+ break;
+ }
+ return 0;
+ }
+
private int handleEmergencyNumberTestModeCommand() {
PrintWriter errPw = getErrPrintWriter();
String opt = getNextOption();
@@ -501,85 +624,65 @@
return slotId;
}
- private int handleSmsCommand() {
- String arg = getNextArg();
- if (arg == null) {
- onHelpSms();
- return 0;
- }
+ // Parse options related to Carrier Config Commands.
+ private CcOptionParseResult parseCcOptions(String tag, boolean allowOptionPersistent) {
+ PrintWriter errPw = getErrPrintWriter();
+ CcOptionParseResult result = new CcOptionParseResult();
+ result.mSubId = SubscriptionManager.getDefaultSubscriptionId();
+ result.mPersistent = false;
- try {
- switch (arg) {
- case SMS_GET_APPS: {
- return handleSmsGetApps();
- }
- case SMS_GET_DEFAULT_APP: {
- return handleSmsGetDefaultApp();
- }
- case SMS_SET_DEFAULT_APP: {
- return handleSmsSetDefaultApp();
- }
- default:
- getErrPrintWriter().println("Unknown command " + arg);
- }
- } catch (RemoteException e) {
- getErrPrintWriter().println("RemoteException: " + e.getMessage());
- }
-
- return -1;
- }
-
- private int maybeParseUserIdArg() {
- int userId = UserHandle.USER_SYSTEM;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
- case "--user": {
+ case "-s": {
try {
- userId = Integer.parseInt(getNextArgRequired());
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Invalid user ID for --user");
- return -1;
+ result.mSubId = slotStringToSubId(tag, getNextArgRequired());
+ if (!SubscriptionManager.isValidSubscriptionId(result.mSubId)) {
+ errPw.println(tag + "No valid subscription found.");
+ return null;
+ }
+
+ } catch (IllegalArgumentException e) {
+ // Missing slot id
+ errPw.println(tag + "SLOT_ID expected after -s.");
+ return null;
}
break;
}
+ case "-p": {
+ if (allowOptionPersistent) {
+ result.mPersistent = true;
+ } else {
+ errPw.println(tag + "Unexpected option " + opt);
+ return null;
+ }
+ break;
+ }
+ default: {
+ errPw.println(tag + "Unknown option " + opt);
+ return null;
+ }
}
}
- return userId;
+ return result;
}
- private int handleSmsGetApps() throws RemoteException {
- final int userId = maybeParseUserIdArg();
- if (userId < 0) {
- return -1;
+ private int slotStringToSubId(String tag, String slotString) {
+ int slotId = -1;
+ try {
+ slotId = Integer.parseInt(slotString);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println(tag + slotString + " is not a valid SLOT_ID.");
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
- for (String packageName : mInterface.getSmsApps(userId)) {
- getOutPrintWriter().println(packageName);
+ SubscriptionInfo subInfo =
+ mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(slotId);
+ if (subInfo == null) {
+ getErrPrintWriter().println(tag + "No subscription found in slot " + slotId + ".");
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
- return 0;
- }
-
- private int handleSmsGetDefaultApp() throws RemoteException {
- final int userId = maybeParseUserIdArg();
- if (userId < 0) {
- return -1;
- }
-
- getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
- return 0;
- }
-
- private int handleSmsSetDefaultApp() throws RemoteException {
- final int userId = maybeParseUserIdArg();
- if (userId < 0) {
- return -1;
- }
-
- String packageName = getNextArgRequired();
- mInterface.setDefaultSmsApp(userId, packageName);
- getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
- return 0;
+ return subInfo.getSubscriptionId();
}
private boolean checkShellUid() {
@@ -587,4 +690,432 @@
return Binder.getCallingUid() == Process.SHELL_UID
|| Binder.getCallingUid() == Process.ROOT_UID;
}
+
+ private int handleCcCommand() {
+ // Verify that the user is allowed to run the command. Only allowed in rooted device in a
+ // non user build.
+ if (Binder.getCallingUid() != Process.ROOT_UID || TelephonyUtils.IS_USER) {
+ getErrPrintWriter().println("cc: Permission denied.");
+ return -1;
+ }
+
+ String arg = getNextArg();
+ if (arg == null) {
+ onHelpCc();
+ return 0;
+ }
+
+ switch (arg) {
+ case CC_GET_VALUE: {
+ return handleCcGetValue();
+ }
+ case CC_SET_VALUE: {
+ return handleCcSetValue();
+ }
+ case CC_CLEAR_VALUES: {
+ return handleCcClearValues();
+ }
+ default: {
+ getErrPrintWriter().println("cc: Unknown argument: " + arg);
+ }
+ }
+ return -1;
+ }
+
+ // cc get-value
+ private int handleCcGetValue() {
+ PrintWriter errPw = getErrPrintWriter();
+ String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_GET_VALUE + ": ";
+ String key = null;
+
+ // Parse all options
+ CcOptionParseResult options = parseCcOptions(tag, false);
+ if (options == null) {
+ return -1;
+ }
+
+ // Get bundle containing all carrier configuration values.
+ PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+ if (bundle == null) {
+ errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+ return -1;
+ }
+
+ // Get the key.
+ key = getNextArg();
+ if (key != null) {
+ // A key was provided. Verify if it is a valid key
+ if (!bundle.containsKey(key)) {
+ errPw.println(tag + key + " is not a valid key.");
+ return -1;
+ }
+
+ // Print the carrier config value for key.
+ getOutPrintWriter().println(ccValueToString(key, getType(tag, key, bundle), bundle));
+ } else {
+ // No key provided. Show all values.
+ // Iterate over a sorted list of all carrier config keys and print them.
+ TreeSet<String> sortedSet = new TreeSet<String>(bundle.keySet());
+ for (String k : sortedSet) {
+ getOutPrintWriter().println(ccValueToString(k, getType(tag, k, bundle), bundle));
+ }
+ }
+ return 0;
+ }
+
+ // cc set-value
+ private int handleCcSetValue() {
+ PrintWriter errPw = getErrPrintWriter();
+ String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUE + ": ";
+
+ // Parse all options
+ CcOptionParseResult options = parseCcOptions(tag, true);
+ if (options == null) {
+ return -1;
+ }
+
+ // Get bundle containing all current carrier configuration values.
+ PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+ if (originalValues == null) {
+ errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+ return -1;
+ }
+
+ // Get the key.
+ String key = getNextArg();
+ if (key == null || key.equals("")) {
+ errPw.println(tag + "KEY is missing");
+ return -1;
+ }
+
+ // Verify if the key is valid
+ if (!originalValues.containsKey(key)) {
+ errPw.println(tag + key + " is not a valid key.");
+ return -1;
+ }
+
+ // Remaining arguments is a list of new values. Add them all into an ArrayList.
+ ArrayList<String> valueList = new ArrayList<String>();
+ while (peekNextArg() != null) {
+ valueList.add(getNextArg());
+ }
+
+ // Find the type of the carrier config value
+ CcType type = getType(tag, key, originalValues);
+ if (type == CcType.UNKNOWN) {
+ errPw.println(tag + "ERROR: Not possible to override key with unknown type.");
+ return -1;
+ }
+
+ // Create an override bundle containing the key and value that should be overriden.
+ PersistableBundle overrideBundle = getOverrideBundle(tag, type, key, valueList);
+ if (overrideBundle == null) {
+ return -1;
+ }
+
+ // Override the value
+ mCarrierConfigManager.overrideConfig(options.mSubId, overrideBundle, options.mPersistent);
+
+ // Find bundle containing all new carrier configuration values after the override.
+ PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+ if (newValues == null) {
+ errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+ return -1;
+ }
+
+ // Print the original and new value.
+ String originalValueString = ccValueToString(key, type, originalValues);
+ String newValueString = ccValueToString(key, type, newValues);
+ getOutPrintWriter().println("Previous value: \n" + originalValueString);
+ getOutPrintWriter().println("New value: \n" + newValueString);
+
+ return 0;
+ }
+
+ // cc clear-values
+ private int handleCcClearValues() {
+ PrintWriter errPw = getErrPrintWriter();
+ String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_CLEAR_VALUES + ": ";
+
+ // Parse all options
+ CcOptionParseResult options = parseCcOptions(tag, false);
+ if (options == null) {
+ return -1;
+ }
+
+ // Clear all values that has previously been set.
+ mCarrierConfigManager.overrideConfig(options.mSubId, null, true);
+ getOutPrintWriter()
+ .println("All previously set carrier config override values has been cleared");
+ return 0;
+ }
+
+ private CcType getType(String tag, String key, PersistableBundle bundle) {
+ // Find the type by checking the type of the current value stored in the bundle.
+ Object value = bundle.get(key);
+
+ if (CC_TYPE_MAP.containsKey(key)) {
+ return CC_TYPE_MAP.get(key);
+ } else if (value != null) {
+ if (value instanceof Boolean) {
+ return CcType.BOOLEAN;
+ } else if (value instanceof Double) {
+ return CcType.DOUBLE;
+ } else if (value instanceof double[]) {
+ return CcType.DOUBLE_ARRAY;
+ } else if (value instanceof Integer) {
+ return CcType.INT;
+ } else if (value instanceof int[]) {
+ return CcType.INT_ARRAY;
+ } else if (value instanceof Long) {
+ return CcType.LONG;
+ } else if (value instanceof long[]) {
+ return CcType.LONG_ARRAY;
+ } else if (value instanceof String) {
+ return CcType.STRING;
+ } else if (value instanceof String[]) {
+ return CcType.STRING_ARRAY;
+ }
+ } else {
+ // Current value was null and can therefore not be used in order to find the type.
+ // Check the name of the key to infer the type. This check is not needed for primitive
+ // data types (boolean, double, int and long), since they can not be null.
+ if (key.endsWith("double_array")) {
+ return CcType.DOUBLE_ARRAY;
+ }
+ if (key.endsWith("int_array")) {
+ return CcType.INT_ARRAY;
+ }
+ if (key.endsWith("long_array")) {
+ return CcType.LONG_ARRAY;
+ }
+ if (key.endsWith("string")) {
+ return CcType.STRING;
+ }
+ if (key.endsWith("string_array") || key.endsWith("strings")) {
+ return CcType.STRING_ARRAY;
+ }
+ }
+
+ // Not possible to infer the type by looking at the current value or the key.
+ PrintWriter errPw = getErrPrintWriter();
+ errPw.println(tag + "ERROR: " + key + " has unknown type.");
+ return CcType.UNKNOWN;
+ }
+
+ private String ccValueToString(String key, CcType type, PersistableBundle bundle) {
+ String result;
+ StringBuilder valueString = new StringBuilder();
+ String typeString = type.toString();
+ Object value = bundle.get(key);
+
+ if (value == null) {
+ valueString.append("null");
+ } else {
+ switch (type) {
+ case DOUBLE_ARRAY: {
+ // Format the string representation of the int array as value1 value2......
+ double[] valueArray = (double[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ valueString.append(valueArray[i]);
+ }
+ break;
+ }
+ case INT_ARRAY: {
+ // Format the string representation of the int array as value1 value2......
+ int[] valueArray = (int[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ valueString.append(valueArray[i]);
+ }
+ break;
+ }
+ case LONG_ARRAY: {
+ // Format the string representation of the int array as value1 value2......
+ long[] valueArray = (long[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ valueString.append(valueArray[i]);
+ }
+ break;
+ }
+ case STRING: {
+ valueString.append("\"" + value.toString() + "\"");
+ break;
+ }
+ case STRING_ARRAY: {
+ // Format the string representation of the string array as "value1" "value2"....
+ String[] valueArray = (String[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ if (valueArray[i] != null) {
+ valueString.append("\"" + valueArray[i] + "\"");
+ } else {
+ valueString.append("null");
+ }
+ }
+ break;
+ }
+ default: {
+ valueString.append(value.toString());
+ }
+ }
+ }
+ return String.format("%-70s %-15s %s", key, typeString, valueString);
+ }
+
+ private PersistableBundle getOverrideBundle(String tag, CcType type, String key,
+ ArrayList<String> valueList) {
+ PrintWriter errPw = getErrPrintWriter();
+ PersistableBundle bundle = new PersistableBundle();
+
+ // First verify that a valid number of values has been provided for the type.
+ switch (type) {
+ case BOOLEAN:
+ case DOUBLE:
+ case INT:
+ case LONG: {
+ if (valueList.size() != 1) {
+ errPw.println(tag + "Expected 1 value for type " + type
+ + ". Found: " + valueList.size());
+ return null;
+ }
+ break;
+ }
+ case STRING: {
+ if (valueList.size() > 1) {
+ errPw.println(tag + "Expected 0 or 1 values for type " + type
+ + ". Found: " + valueList.size());
+ return null;
+ }
+ break;
+ }
+ }
+
+ // Parse the value according to type and add it to the Bundle.
+ switch (type) {
+ case BOOLEAN: {
+ if ("true".equalsIgnoreCase(valueList.get(0))) {
+ bundle.putBoolean(key, true);
+ } else if ("false".equalsIgnoreCase(valueList.get(0))) {
+ bundle.putBoolean(key, false);
+ } else {
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+ return null;
+ }
+ break;
+ }
+ case DOUBLE: {
+ try {
+ bundle.putDouble(key, Double.parseDouble(valueList.get(0)));
+ } catch (NumberFormatException nfe) {
+ // Not a valid double
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+ return null;
+ }
+ break;
+ }
+ case DOUBLE_ARRAY: {
+ double[] valueDoubleArray = null;
+ if (valueList.size() > 0) {
+ valueDoubleArray = new double[valueList.size()];
+ for (int i = 0; i < valueList.size(); i++) {
+ try {
+ valueDoubleArray[i] = Double.parseDouble(valueList.get(i));
+ } catch (NumberFormatException nfe) {
+ // Not a valid double
+ errPw.println(
+ tag + "Unable to parse " + valueList.get(i) + " as a double.");
+ return null;
+ }
+ }
+ }
+ bundle.putDoubleArray(key, valueDoubleArray);
+ break;
+ }
+ case INT: {
+ try {
+ bundle.putInt(key, Integer.parseInt(valueList.get(0)));
+ } catch (NumberFormatException nfe) {
+ // Not a valid integer
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as an " + type);
+ return null;
+ }
+ break;
+ }
+ case INT_ARRAY: {
+ int[] valueIntArray = null;
+ if (valueList.size() > 0) {
+ valueIntArray = new int[valueList.size()];
+ for (int i = 0; i < valueList.size(); i++) {
+ try {
+ valueIntArray[i] = Integer.parseInt(valueList.get(i));
+ } catch (NumberFormatException nfe) {
+ // Not a valid integer
+ errPw.println(tag
+ + "Unable to parse " + valueList.get(i) + " as an integer.");
+ return null;
+ }
+ }
+ }
+ bundle.putIntArray(key, valueIntArray);
+ break;
+ }
+ case LONG: {
+ try {
+ bundle.putLong(key, Long.parseLong(valueList.get(0)));
+ } catch (NumberFormatException nfe) {
+ // Not a valid long
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+ return null;
+ }
+ break;
+ }
+ case LONG_ARRAY: {
+ long[] valueLongArray = null;
+ if (valueList.size() > 0) {
+ valueLongArray = new long[valueList.size()];
+ for (int i = 0; i < valueList.size(); i++) {
+ try {
+ valueLongArray[i] = Long.parseLong(valueList.get(i));
+ } catch (NumberFormatException nfe) {
+ // Not a valid long
+ errPw.println(
+ tag + "Unable to parse " + valueList.get(i) + " as a long");
+ return null;
+ }
+ }
+ }
+ bundle.putLongArray(key, valueLongArray);
+ break;
+ }
+ case STRING: {
+ String value = null;
+ if (valueList.size() > 0) {
+ value = valueList.get(0);
+ }
+ bundle.putString(key, value);
+ break;
+ }
+ case STRING_ARRAY: {
+ String[] valueStringArray = null;
+ if (valueList.size() > 0) {
+ valueStringArray = new String[valueList.size()];
+ valueList.toArray(valueStringArray);
+ }
+ bundle.putStringArray(key, valueStringArray);
+ break;
+ }
+ }
+ return bundle;
+ }
}
diff --git a/src/com/android/phone/euicc/EuiccPublicActionUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccPublicActionUiDispatcherActivity.java
new file mode 100644
index 0000000..64a40b9
--- /dev/null
+++ b/src/com/android/phone/euicc/EuiccPublicActionUiDispatcherActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.euicc;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.service.euicc.EuiccService;
+import android.telephony.euicc.EuiccManager;
+import android.util.Log;
+
+/**
+ * Trampoline activity to forward public eUICC intents to the active UI implementation.
+ *
+ * <p>Unlike {@link EuiccUiDispatcherActivity}, this activity does not require any permissions to
+ * start.
+ */
+public class EuiccPublicActionUiDispatcherActivity extends EuiccUiDispatcherActivity {
+ private static final String TAG = "EuiccPublicActionUiDispatcherActivity";
+
+ @Override
+ @Nullable
+ protected Intent getEuiccUiIntent() {
+ String action = getIntent().getAction();
+
+ Intent intent = new Intent();
+ // Propagate the extras from the original Intent.
+ intent.putExtras(getIntent());
+ switch (action) {
+ case EuiccManager.ACTION_START_EUICC_ACTIVATION:
+ intent.setAction(EuiccService.ACTION_START_EUICC_ACTIVATION);
+ break;
+ default:
+ Log.w(TAG, "Unsupported action: " + action);
+ return null;
+ }
+
+ return intent;
+ }
+}
diff --git a/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
index 0f083c9..fd741a0 100644
--- a/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccResolutionUiDispatcherActivity.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.service.euicc.EuiccService;
import android.telephony.euicc.EuiccManager;
+import android.util.ArraySet;
import android.util.Log;
/**
@@ -33,6 +34,14 @@
public class EuiccResolutionUiDispatcherActivity extends EuiccUiDispatcherActivity {
private static final String TAG = "EuiccResUiDispatcher";
+ /** List of all valid resolution actions for validation purposes. */
+ private static final ArraySet<String> RESOLUTION_ACTIONS = new ArraySet<>();
+ static {
+ RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
+ RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
+ RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS);
+ }
+
@Override
@Nullable
protected Intent getEuiccUiIntent() {
@@ -45,7 +54,7 @@
String euiccUiAction =
getIntent().getStringExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_ACTION);
- if (!EuiccService.RESOLUTION_ACTIONS.contains(euiccUiAction)) {
+ if (!RESOLUTION_ACTIONS.contains(euiccUiAction)) {
Log.w(TAG, "Unknown resolution action: " + euiccUiAction);
return null;
}
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index 7c7b75d..c47e014 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -27,6 +28,7 @@
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.service.euicc.EuiccService;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
@@ -94,7 +96,7 @@
grantDefaultPermissionsToActiveLuiApp(activityInfo);
- euiccUiIntent.setComponent(activityInfo.getComponentName());
+ euiccUiIntent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
return euiccUiIntent;
}
@@ -138,7 +140,7 @@
protected void grantDefaultPermissionsToActiveLuiApp(ActivityInfo activityInfo) {
try {
mPackageManager.grantDefaultPermissionsToActiveLuiApp(
- activityInfo.packageName, getUserId());
+ activityInfo.packageName, UserHandle.myUserId());
} catch (RemoteException e) {
Log.e(TAG, "Failed to grant permissions to active LUI app.", e);
}
@@ -150,10 +152,11 @@
try {
Set<String> luiApps = getAllLuiAppPackageNames(intent);
String[] luiAppsArray = luiApps.toArray(new String[luiApps.size()]);
- mPackageManager.revokeDefaultPermissionsFromLuiApps(luiAppsArray, getUserId());
+ mPackageManager.revokeDefaultPermissionsFromLuiApps(luiAppsArray,
+ UserHandle.myUserId());
} catch (RemoteException e) {
Log.e(TAG, "Failed to revoke LUI app permissions.");
- throw e.rethrowAsRuntimeException();
+ throw new RuntimeException(e);
}
}
diff --git a/src/com/android/phone/otasp/OtaspActivationService.java b/src/com/android/phone/otasp/OtaspActivationService.java
index 7490880..6ed2ea8 100644
--- a/src/com/android/phone/otasp/OtaspActivationService.java
+++ b/src/com/android/phone/otasp/OtaspActivationService.java
@@ -150,11 +150,9 @@
mPhone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_PROVISION_STATUS_UPDATE, null);
mPhone.registerForPreciseCallStateChanged(mHandler, EVENT_CALL_STATE_CHANGED, null);
logd("startNonInteractiveOtasp: placing call to '" + OTASP_NUMBER + "'...");
- int callStatus = PhoneUtils.placeCall(this,
+ int callStatus = PhoneUtils.placeOtaspCall(this,
getPhone(),
- OTASP_NUMBER,
- null, // contactRef
- false); // isEmergencyCall
+ OTASP_NUMBER);
if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
if (DBG) logd(" ==> success return from placeCall(): callStatus = " + callStatus);
} else {
diff --git a/src/com/android/phone/otasp/OtaspSimStateReceiver.java b/src/com/android/phone/otasp/OtaspSimStateReceiver.java
index 78f7baf..bb4022a 100644
--- a/src/com/android/phone/otasp/OtaspSimStateReceiver.java
+++ b/src/com/android/phone/otasp/OtaspSimStateReceiver.java
@@ -18,9 +18,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -32,15 +34,32 @@
private static final boolean DBG = true;
private Context mContext;
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener(){
+ private static final int EVENT_OTASP_CHANGED = 1;
+
+ private Handler mOtaspHandler = new Handler() {
@Override
- public void onOtaspChanged(int otaspMode) {
- logd("onOtaspChanged: otaspMode=" + otaspMode);
- if (otaspMode == TelephonyManager.OTASP_NEEDED) {
- logd("otasp activation required, start otaspActivationService");
- mContext.startService(new Intent(mContext, OtaspActivationService.class));
- } else if (otaspMode == TelephonyManager.OTASP_NOT_NEEDED) {
- OtaspActivationService.updateActivationState(mContext, true);
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_OTASP_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null && ar.result != null) {
+ int otaspMode = (Integer) ar.result;
+ logd("EVENT_OTASP_CHANGED: otaspMode=" + otaspMode);
+ if (otaspMode == TelephonyManager.OTASP_NEEDED) {
+ logd("otasp activation required, start otaspActivationService");
+ mContext.startService(
+ new Intent(mContext, OtaspActivationService.class));
+ } else if (otaspMode == TelephonyManager.OTASP_NOT_NEEDED) {
+ OtaspActivationService.updateActivationState(mContext, true);
+ }
+ } else {
+ logd("EVENT_OTASP_CHANGED: exception=" + ar.exception);
+ }
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
}
}
};
@@ -74,13 +93,17 @@
if(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
if (DBG) logd("Received intent: " + intent.getAction());
if (PhoneGlobals.getPhone().getIccRecordsLoaded() && isCarrierSupported()) {
- final TelephonyManager telephonyManager = TelephonyManager.from(context);
- telephonyManager.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_OTASP_CHANGED);
+ registerOtaspChangedHandler();
}
}
}
+ // It's fine to call mutiple times, as the registrants are de-duped by Handler object.
+ private void registerOtaspChangedHandler() {
+ final Phone phone = PhoneGlobals.getPhone();
+ phone.registerForOtaspChange(mOtaspHandler, EVENT_OTASP_CHANGED, null);
+ }
+
private static void logd(String s) {
Log.d(TAG, s);
}
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index 5a70fe2..c9aa630 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -166,17 +166,6 @@
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;
}
@@ -206,20 +195,11 @@
}
private boolean shouldShowRttSetting() {
- CarrierConfigManager configManager =
- (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
// Go through all the subs -- if we want to display the RTT setting for any of them, do
// display it.
for (int subId : SubscriptionController.getInstance().getActiveSubIdList(true)) {
- // In order to display the setting, we want:
- // 1. The subscription supports RTT
- // 2. The subscription isn't configured by the carrier to have the setting always-on
- // (see the documentation for the carrier config key)
if (PhoneGlobals.getInstance().phoneMgr.isRttSupported(subId)) {
- if (!configManager.getConfigForSubId(subId).getBoolean(
- CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false)) {
- return true;
- }
+ return true;
}
}
return false;
diff --git a/src/com/android/phone/settings/BandMode.java b/src/com/android/phone/settings/BandMode.java
new file mode 100644
index 0000000..853075a
--- /dev/null
+++ b/src/com/android/phone/settings/BandMode.java
@@ -0,0 +1,255 @@
+/*
+ * 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.settings;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.R;
+
+/**
+ * Radio Band Mode Selection Class
+ *
+ * It will query baseband about all available band modes and display them
+ * in screen. It will display all six band modes if the query failed.
+ *
+ * After user select one band, it will send the selection to baseband.
+ *
+ * It will alter user the result of select operation and exit, no matter success
+ * or not.
+ *
+ */
+public class BandMode extends Activity {
+ private static final String LOG_TAG = "phone";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_BAND_SCAN_COMPLETED = 100;
+ private static final int EVENT_BAND_SELECTION_DONE = 200;
+
+ //Directly maps to RIL_RadioBandMode from ril.h
+ private static final String[] BAND_NAMES = new String[] {
+ "Automatic",
+ "Europe",
+ "United States",
+ "Japan",
+ "Australia",
+ "Australia 2",
+ "Cellular 800",
+ "PCS",
+ "Class 3 (JTACS)",
+ "Class 4 (Korea-PCS)",
+ "Class 5",
+ "Class 6 (IMT2000)",
+ "Class 7 (700Mhz-Upper)",
+ "Class 8 (1800Mhz-Upper)",
+ "Class 9 (900Mhz)",
+ "Class 10 (800Mhz-Secondary)",
+ "Class 11 (Europe PAMR 400Mhz)",
+ "Class 15 (US-AWS)",
+ "Class 16 (US-2500Mhz)"
+ };
+
+ private ListView mBandList;
+ private ArrayAdapter mBandListAdapter;
+ private BandListItem mTargetBand = null;
+ private DialogInterface mProgressPanel;
+
+ private Phone mPhone = null;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+ setContentView(R.layout.band_mode);
+
+ mPhone = PhoneFactory.getDefaultPhone();
+
+ mBandList = (ListView) findViewById(R.id.band);
+ mBandListAdapter = new ArrayAdapter<BandListItem>(this,
+ android.R.layout.simple_list_item_1);
+ mBandList.setAdapter(mBandListAdapter);
+ mBandList.setOnItemClickListener(mBandSelectionHandler);
+
+ loadBandList();
+ }
+
+ private AdapterView.OnItemClickListener mBandSelectionHandler =
+ new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v,
+ int position, long id) {
+
+ getWindow().setFeatureInt(
+ Window.FEATURE_INDETERMINATE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_ON);
+
+ mTargetBand = (BandListItem) parent.getAdapter().getItem(position);
+
+ if (DBG) log("Select band : " + mTargetBand.toString());
+
+ Message msg =
+ mHandler.obtainMessage(EVENT_BAND_SELECTION_DONE);
+ mPhone.setBandMode(mTargetBand.getBand(), msg);
+ }
+ };
+
+ private static class BandListItem {
+ private int mBandMode = Phone.BM_UNSPECIFIED;
+
+ BandListItem(int bm) {
+ mBandMode = bm;
+ }
+
+ public int getBand() {
+ return mBandMode;
+ }
+
+ public String toString() {
+ if (mBandMode >= BAND_NAMES.length) return "Band mode " + mBandMode;
+ return BAND_NAMES[mBandMode];
+ }
+ }
+
+ private void loadBandList() {
+ String str = getString(R.string.band_mode_loading);
+
+ if (DBG) log(str);
+
+
+ //ProgressDialog.show(this, null, str, true, true, null);
+ mProgressPanel = new AlertDialog.Builder(this)
+ .setMessage(str)
+ .show();
+
+ Message msg = mHandler.obtainMessage(EVENT_BAND_SCAN_COMPLETED);
+ mPhone.queryAvailableBandMode(msg);
+
+ }
+
+ private void bandListLoaded(AsyncResult result) {
+ if (DBG) log("network list loaded");
+
+ if (mProgressPanel != null) mProgressPanel.dismiss();
+
+ clearList();
+
+ boolean addBandSuccess = false;
+ BandListItem item;
+
+ if (result.result != null) {
+ int [] bands = (int []) result.result;
+
+ if (bands.length == 0) {
+ Log.wtf(LOG_TAG, "No Supported Band Modes");
+ return;
+ }
+
+ int size = bands[0];
+
+ if (size > 0) {
+ mBandListAdapter.add(
+ new BandListItem(Phone.BM_UNSPECIFIED)); //Always include AUTOMATIC
+ for (int i = 1; i <= size; i++) {
+ if (bands[i] == Phone.BM_UNSPECIFIED) {
+ continue;
+ }
+ item = new BandListItem(bands[i]);
+ mBandListAdapter.add(item);
+ if (DBG) log("Add " + item.toString());
+ }
+ addBandSuccess = true;
+ }
+ }
+
+ if (!addBandSuccess) {
+ if (DBG) log("Error in query, add default list");
+ for (int i = 0; i < Phone.BM_NUM_BAND_MODES; i++) {
+ item = new BandListItem(i);
+ mBandListAdapter.add(item);
+ if (DBG) log("Add default " + item.toString());
+ }
+ }
+ mBandList.requestFocus();
+ }
+
+ private void displayBandSelectionResult(Throwable ex) {
+ String status = getString(R.string.band_mode_set)
+ + " [" + mTargetBand.toString() + "] ";
+
+ if (ex != null) {
+ status = status + getString(R.string.band_mode_failed);
+ } else {
+ status = status + getString(R.string.band_mode_succeeded);
+ }
+
+ mProgressPanel = new AlertDialog.Builder(this)
+ .setMessage(status)
+ .setPositiveButton(android.R.string.ok, null).show();
+ }
+
+ private void clearList() {
+ while (mBandListAdapter.getCount() > 0) {
+ mBandListAdapter.remove(
+ mBandListAdapter.getItem(0));
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(LOG_TAG, "[BandsList] " + msg);
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_BAND_SCAN_COMPLETED:
+ ar = (AsyncResult) msg.obj;
+
+ bandListLoaded(ar);
+ break;
+
+ case EVENT_BAND_SELECTION_DONE:
+ ar = (AsyncResult) msg.obj;
+
+ getWindow().setFeatureInt(
+ Window.FEATURE_INDETERMINATE_PROGRESS,
+ Window.PROGRESS_VISIBILITY_OFF);
+
+ if (!isFinishing()) {
+ displayBandSelectionResult(ar.exception);
+ }
+ break;
+ }
+ }
+ };
+
+
+}
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 0846be1..3811a77 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -8,7 +8,6 @@
import android.graphics.drawable.Icon;
import android.net.sip.SipManager;
import android.os.Bundle;
-import android.os.PersistableBundle;
import android.os.UserManager;
import android.preference.ListPreference;
import android.preference.Preference;
@@ -102,7 +101,7 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mTelecomManager = TelecomManager.from(getActivity());
+ mTelecomManager = getActivity().getSystemService(TelecomManager.class);
mTelephonyManager = TelephonyManager.from(getActivity());
mSubscriptionManager = SubscriptionManager.from(getActivity());
}
@@ -558,12 +557,18 @@
List<SubscriptionInfo> subscriptions =
mSubscriptionManager.getActiveSubscriptionInfoList();
- if ((subscriptions == null) || (subscriptions.size() <= 1)) {
+ if (subscriptions == null) {
return null;
}
- List<String> componentNames = subscriptions
- .stream()
+ List<SubscriptionInfo> effectiveSubscriptions = subscriptions.stream()
+ .filter(subInfo -> !subInfo.isOpportunistic())
+ .collect(Collectors.toList());
+ if (effectiveSubscriptions.size() < 2) {
+ return null;
+ }
+
+ List<String> componentNames = effectiveSubscriptions.stream()
.map(subInfo -> configManager.getConfigForSubId(subInfo.getSubscriptionId()))
.filter(bundle -> (bundle != null))
.map(bundle -> bundle.getString(
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
new file mode 100644
index 0000000..ce5b839
--- /dev/null
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -0,0 +1,1772 @@
+/*
+ * 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.settings;
+
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.provider.Settings.Global.PREFERRED_NETWORK_MODE;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentityCdma;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellIdentityWcdma;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellLocation;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.PhoneStateListener;
+import android.telephony.PhysicalChannelConfig;
+import android.telephony.PreciseCallState;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog.Builder;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.phone.R;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Radio Information Class
+ *
+ * Allows user to read and alter some of the radio related information.
+ *
+ */
+public class RadioInfo extends AppCompatActivity {
+ private static final String TAG = "RadioInfo";
+
+ private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
+
+ private static final String[] PREFERRED_NETWORK_LABELS = {
+ "WCDMA preferred",
+ "GSM only",
+ "WCDMA only",
+ "GSM auto (PRL)",
+ "CDMA auto (PRL)",
+ "CDMA only",
+ "EvDo only",
+ "Global auto (PRL)",
+ "LTE/CDMA auto (PRL)",
+ "LTE/UMTS auto (PRL)",
+ "LTE/CDMA/UMTS auto (PRL)",
+ "LTE only",
+ "LTE/WCDMA",
+ "TD-SCDMA only",
+ "TD-SCDMA/WCDMA",
+ "LTE/TD-SCDMA",
+ "TD-SCDMA/GSM",
+ "TD-SCDMA/UMTS",
+ "LTE/TD-SCDMA/WCDMA",
+ "LTE/TD-SCDMA/UMTS",
+ "TD-SCDMA/CDMA/UMTS",
+ "Global/TD-SCDMA",
+ "Unknown"
+ };
+
+ private static String[] sPhoneIndexLabels;
+
+ private static final int sCellInfoListRateDisabled = Integer.MAX_VALUE;
+ private static final int sCellInfoListRateMax = 0;
+
+ private static final String OEM_RADIO_INFO_INTENT =
+ "com.android.phone.settings.OEM_RADIO_INFO";
+
+ private static final String DSDS_MODE_PROPERTY = "ro.boot.hardware.dsds";
+
+ /**
+ * A value indicates the device is always on dsds mode.
+ * @see {@link #DSDS_MODE_PROPERTY}
+ */
+ private static final int ALWAYS_ON_DSDS_MODE = 1;
+
+ private static final int IMS_VOLTE_PROVISIONED_CONFIG_ID =
+ ImsConfig.ConfigConstants.VLT_SETTING_ENABLED;
+
+ private static final int IMS_VT_PROVISIONED_CONFIG_ID =
+ ImsConfig.ConfigConstants.LVC_SETTING_ENABLED;
+
+ private static final int IMS_WFC_PROVISIONED_CONFIG_ID =
+ ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED;
+
+ private static final int EAB_PROVISIONED_CONFIG_ID =
+ ImsConfig.ConfigConstants.EAB_SETTING_ENABLED;
+
+ //Values in must match CELL_INFO_REFRESH_RATES
+ private static final String[] CELL_INFO_REFRESH_RATE_LABELS = {
+ "Disabled",
+ "Immediate",
+ "Min 5s",
+ "Min 10s",
+ "Min 60s"
+ };
+
+ //Values in seconds, must match CELL_INFO_REFRESH_RATE_LABELS
+ private static final int [] CELL_INFO_REFRESH_RATES = {
+ sCellInfoListRateDisabled,
+ sCellInfoListRateMax,
+ 5000,
+ 10000,
+ 60000
+ };
+
+ private static void log(String s) {
+ Log.d(TAG, s);
+ }
+
+ private static final int EVENT_CFI_CHANGED = 302;
+
+ private static final int EVENT_QUERY_PREFERRED_TYPE_DONE = 1000;
+ private static final int EVENT_SET_PREFERRED_TYPE_DONE = 1001;
+ private static final int EVENT_QUERY_SMSC_DONE = 1005;
+ private static final int EVENT_UPDATE_SMSC_DONE = 1006;
+ private static final int EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED = 1007;
+
+ private static final int MENU_ITEM_SELECT_BAND = 0;
+ private static final int MENU_ITEM_VIEW_ADN = 1;
+ private static final int MENU_ITEM_VIEW_FDN = 2;
+ private static final int MENU_ITEM_VIEW_SDN = 3;
+ private static final int MENU_ITEM_GET_IMS_STATUS = 4;
+ private static final int MENU_ITEM_TOGGLE_DATA = 5;
+
+ private TextView mDeviceId; //DeviceId is the IMEI in GSM and the MEID in CDMA
+ private TextView mLine1Number;
+ private TextView mSubscriptionId;
+ private TextView mDds;
+ private TextView mSubscriberId;
+ private TextView mCallState;
+ private TextView mOperatorName;
+ private TextView mRoamingState;
+ private TextView mGsmState;
+ private TextView mGprsState;
+ private TextView mVoiceNetwork;
+ private TextView mDataNetwork;
+ private TextView mDBm;
+ private TextView mMwi;
+ private TextView mCfi;
+ private TextView mLocation;
+ private TextView mCellInfo;
+ private TextView mSent;
+ private TextView mReceived;
+ private TextView mPingHostnameV4;
+ private TextView mPingHostnameV6;
+ private TextView mHttpClientTest;
+ private TextView mPhyChanConfig;
+ private TextView mDnsCheckState;
+ private TextView mDownlinkKbps;
+ private TextView mUplinkKbps;
+ private EditText mSmsc;
+ private Switch mRadioPowerOnSwitch;
+ private Button mCellInfoRefreshRateButton;
+ private Button mDnsCheckToggleButton;
+ private Button mPingTestButton;
+ private Button mUpdateSmscButton;
+ private Button mRefreshSmscButton;
+ private Button mOemInfoButton;
+ private Button mCarrierProvisioningButton;
+ private Button mTriggerCarrierProvisioningButton;
+ private Switch mImsVolteProvisionedSwitch;
+ private Switch mImsVtProvisionedSwitch;
+ private Switch mImsWfcProvisionedSwitch;
+ private Switch mEabProvisionedSwitch;
+ private Switch mCbrsDataSwitch;
+ private Switch mDsdsSwitch;
+ private Spinner mPreferredNetworkType;
+ private Spinner mSelectPhoneIndex;
+ private Spinner mCellInfoRefreshRateSpinner;
+
+ private static final long RUNNABLE_TIMEOUT_MS = 5 * 60 * 1000L;
+
+ private ThreadPoolExecutor mQueuedWork;
+
+ private ConnectivityManager mConnectivityManager;
+ private TelephonyManager mTelephonyManager;
+ private ImsManager mImsManager = null;
+ private Phone mPhone = null;
+
+ private String mPingHostnameResultV4;
+ private String mPingHostnameResultV6;
+ private String mHttpClientTestResult;
+ private boolean mMwiValue = false;
+ private boolean mCfiValue = false;
+
+ private List<CellInfo> mCellInfoResult = null;
+ private CellLocation mCellLocationResult = null;
+
+ private int mPreferredNetworkTypeResult;
+ private int mCellInfoRefreshRateIndex;
+ private int mSelectedPhoneIndex;
+
+ private final NetworkRequest mDefaultNetworkRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ public void onCapabilitiesChanged(Network n, NetworkCapabilities nc) {
+ int dlbw = nc.getLinkDownstreamBandwidthKbps();
+ int ulbw = nc.getLinkUpstreamBandwidthKbps();
+ updateBandwidths(dlbw, ulbw);
+ }
+ };
+
+ // not final because we need to recreate this object to register on a new subId (b/117555407)
+ private PhoneStateListener mPhoneStateListener = new RadioInfoPhoneStateListener();
+ private class RadioInfoPhoneStateListener extends PhoneStateListener {
+ @Override
+ public void onDataConnectionStateChanged(int state) {
+ updateDataState();
+ updateNetworkType();
+ }
+
+ @Override
+ public void onDataActivity(int direction) {
+ updateDataStats2();
+ }
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ updateNetworkType();
+ updatePhoneState(state);
+ }
+
+ @Override
+ public void onPreciseCallStateChanged(PreciseCallState preciseState) {
+ updateNetworkType();
+ }
+
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ updateLocation(location);
+ }
+
+ @Override
+ public void onMessageWaitingIndicatorChanged(boolean mwi) {
+ mMwiValue = mwi;
+ updateMessageWaiting();
+ }
+
+ @Override
+ public void onCallForwardingIndicatorChanged(boolean cfi) {
+ mCfiValue = cfi;
+ updateCallRedirect();
+ }
+
+ @Override
+ public void onCellInfoChanged(List<CellInfo> arrayCi) {
+ log("onCellInfoChanged: arrayCi=" + arrayCi);
+ mCellInfoResult = arrayCi;
+ updateCellInfo(mCellInfoResult);
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ log("onSignalStrengthChanged: SignalStrength=" + signalStrength);
+ updateSignalStrength(signalStrength);
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ log("onServiceStateChanged: ServiceState=" + serviceState);
+ updateServiceState(serviceState);
+ updateRadioPowerState();
+ updateNetworkType();
+ updateImsProvisionedState();
+ }
+
+ }
+
+ private void updatePhysicalChannelConfiguration(List<PhysicalChannelConfig> configs) {
+ StringBuilder sb = new StringBuilder();
+ String div = "";
+ sb.append("{");
+ if (configs != null) {
+ for (PhysicalChannelConfig c : configs) {
+ sb.append(div).append(c);
+ div = ",";
+ }
+ }
+ sb.append("}");
+ mPhyChanConfig.setText(sb.toString());
+ }
+
+ private void updatePreferredNetworkType(int type) {
+ if (type >= PREFERRED_NETWORK_LABELS.length || type < 0) {
+ log("EVENT_QUERY_PREFERRED_TYPE_DONE: unknown "
+ + "type=" + type);
+ type = PREFERRED_NETWORK_LABELS.length - 1; //set to Unknown
+ }
+ mPreferredNetworkTypeResult = type;
+
+ mPreferredNetworkType.setSelection(mPreferredNetworkTypeResult, true);
+ }
+
+ private void updatePhoneIndex(int phoneIndex, int subId) {
+ // unregister listeners on the old subId
+ unregisterPhoneStateListener();
+ mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
+
+ // update the subId
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
+
+ // update the phoneId
+ mImsManager = ImsManager.getInstance(getApplicationContext(), phoneIndex);
+ mPhone = PhoneFactory.getPhone(phoneIndex);
+
+ updateAllFields();
+ }
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ switch (msg.what) {
+ case EVENT_QUERY_PREFERRED_TYPE_DONE:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null && ar.result != null) {
+ updatePreferredNetworkType(((int []) ar.result)[0]);
+ } else {
+ //In case of an exception, we will set this to unknown
+ updatePreferredNetworkType(PREFERRED_NETWORK_LABELS.length - 1);
+ }
+ break;
+ case EVENT_SET_PREFERRED_TYPE_DONE:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ log("Set preferred network type failed.");
+ }
+ break;
+ case EVENT_QUERY_SMSC_DONE:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ mSmsc.setText("refresh error");
+ } else {
+ mSmsc.setText((String) ar.result);
+ }
+ break;
+ case EVENT_UPDATE_SMSC_DONE:
+ mUpdateSmscButton.setEnabled(true);
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ mSmsc.setText("update error");
+ }
+ break;
+ case EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception != null) {
+ mPhyChanConfig.setText(("update error"));
+ }
+ updatePhysicalChannelConfiguration((List<PhysicalChannelConfig>) ar.result);
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (!android.os.Process.myUserHandle().isSystem()) {
+ Log.e(TAG, "Not run from system user, don't do anything.");
+ finish();
+ return;
+ }
+
+ setContentView(R.layout.radio_info);
+
+ log("Started onCreate");
+
+ 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();
+
+ mImsManager = ImsManager.getInstance(getApplicationContext(),
+ SubscriptionManager.getDefaultVoicePhoneId());
+
+ sPhoneIndexLabels = getPhoneIndexLabels(mTelephonyManager);
+
+ mDeviceId = (TextView) findViewById(R.id.imei);
+ mLine1Number = (TextView) findViewById(R.id.number);
+ mSubscriptionId = (TextView) findViewById(R.id.subid);
+ mDds = (TextView) findViewById(R.id.dds);
+ mSubscriberId = (TextView) findViewById(R.id.imsi);
+ mCallState = (TextView) findViewById(R.id.call);
+ mOperatorName = (TextView) findViewById(R.id.operator);
+ mRoamingState = (TextView) findViewById(R.id.roaming);
+ mGsmState = (TextView) findViewById(R.id.gsm);
+ mGprsState = (TextView) findViewById(R.id.gprs);
+ mVoiceNetwork = (TextView) findViewById(R.id.voice_network);
+ mDataNetwork = (TextView) findViewById(R.id.data_network);
+ mDBm = (TextView) findViewById(R.id.dbm);
+ mMwi = (TextView) findViewById(R.id.mwi);
+ mCfi = (TextView) findViewById(R.id.cfi);
+ mLocation = (TextView) findViewById(R.id.location);
+ mCellInfo = (TextView) findViewById(R.id.cellinfo);
+ mCellInfo.setTypeface(Typeface.MONOSPACE);
+
+ mSent = (TextView) findViewById(R.id.sent);
+ mReceived = (TextView) findViewById(R.id.received);
+ mSmsc = (EditText) findViewById(R.id.smsc);
+ mDnsCheckState = (TextView) findViewById(R.id.dnsCheckState);
+ mPingHostnameV4 = (TextView) findViewById(R.id.pingHostnameV4);
+ mPingHostnameV6 = (TextView) findViewById(R.id.pingHostnameV6);
+ mHttpClientTest = (TextView) findViewById(R.id.httpClientTest);
+
+ mPhyChanConfig = (TextView) findViewById(R.id.phy_chan_config);
+
+ mPreferredNetworkType = (Spinner) findViewById(R.id.preferredNetworkType);
+ ArrayAdapter<String> mPreferredNetworkTypeAdapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_item, PREFERRED_NETWORK_LABELS);
+ mPreferredNetworkTypeAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mPreferredNetworkType.setAdapter(mPreferredNetworkTypeAdapter);
+
+ mSelectPhoneIndex = (Spinner) findViewById(R.id.phoneIndex);
+ ArrayAdapter<String> phoneIndexAdapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_item, sPhoneIndexLabels);
+ phoneIndexAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mSelectPhoneIndex.setAdapter(phoneIndexAdapter);
+
+ mCellInfoRefreshRateSpinner = (Spinner) findViewById(R.id.cell_info_rate_select);
+ ArrayAdapter<String> cellInfoAdapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_spinner_item, CELL_INFO_REFRESH_RATE_LABELS);
+ cellInfoAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mCellInfoRefreshRateSpinner.setAdapter(cellInfoAdapter);
+
+ mImsVolteProvisionedSwitch = (Switch) findViewById(R.id.volte_provisioned_switch);
+ mImsVtProvisionedSwitch = (Switch) findViewById(R.id.vt_provisioned_switch);
+ mImsWfcProvisionedSwitch = (Switch) findViewById(R.id.wfc_provisioned_switch);
+ mEabProvisionedSwitch = (Switch) findViewById(R.id.eab_provisioned_switch);
+
+ if (!ImsManager.isImsSupportedOnDevice(mPhone.getContext())) {
+ mImsVolteProvisionedSwitch.setVisibility(View.GONE);
+ mImsVtProvisionedSwitch.setVisibility(View.GONE);
+ mImsWfcProvisionedSwitch.setVisibility(View.GONE);
+ mEabProvisionedSwitch.setVisibility(View.GONE);
+ }
+
+ mCbrsDataSwitch = (Switch) findViewById(R.id.cbrs_data_switch);
+ mCbrsDataSwitch.setVisibility(isCbrsSupported() ? View.VISIBLE : View.GONE);
+
+ mDsdsSwitch = findViewById(R.id.dsds_switch);
+ if (isDsdsSupported() && !dsdsModeOnly()) {
+ mDsdsSwitch.setVisibility(View.VISIBLE);
+ mDsdsSwitch.setOnClickListener(v -> {
+ if (mTelephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
+ // Undo the click action until user clicks the confirm dialog.
+ mDsdsSwitch.toggle();
+ showDsdsChangeDialog();
+ } else {
+ performDsdsSwitch();
+ }
+ });
+ mDsdsSwitch.setChecked(isDsdsEnabled());
+ } else {
+ mDsdsSwitch.setVisibility(View.GONE);
+ }
+
+ mRadioPowerOnSwitch = (Switch) findViewById(R.id.radio_power);
+
+ mDownlinkKbps = (TextView) findViewById(R.id.dl_kbps);
+ mUplinkKbps = (TextView) findViewById(R.id.ul_kbps);
+ updateBandwidths(0, 0);
+
+ mPingTestButton = (Button) findViewById(R.id.ping_test);
+ mPingTestButton.setOnClickListener(mPingButtonHandler);
+ mUpdateSmscButton = (Button) findViewById(R.id.update_smsc);
+ mUpdateSmscButton.setOnClickListener(mUpdateSmscButtonHandler);
+ mRefreshSmscButton = (Button) findViewById(R.id.refresh_smsc);
+ mRefreshSmscButton.setOnClickListener(mRefreshSmscButtonHandler);
+ mDnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
+ mDnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
+ mCarrierProvisioningButton = (Button) findViewById(R.id.carrier_provisioning);
+ mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+ mTriggerCarrierProvisioningButton = (Button) findViewById(
+ R.id.trigger_carrier_provisioning);
+ mTriggerCarrierProvisioningButton.setOnClickListener(
+ mTriggerCarrierProvisioningButtonHandler);
+
+ mOemInfoButton = (Button) findViewById(R.id.oem_info);
+ mOemInfoButton.setOnClickListener(mOemInfoButtonHandler);
+ PackageManager pm = getPackageManager();
+ Intent oemInfoIntent = new Intent(OEM_RADIO_INFO_INTENT);
+ List<ResolveInfo> oemInfoIntentList = pm.queryIntentActivities(oemInfoIntent, 0);
+ if (oemInfoIntentList.size() == 0) {
+ mOemInfoButton.setEnabled(false);
+ }
+
+ mCellInfoRefreshRateIndex = 0; //disabled
+ mPreferredNetworkTypeResult = PREFERRED_NETWORK_LABELS.length - 1; //Unknown
+ mSelectedPhoneIndex = 0; //phone 0
+
+ //FIXME: Replace with TelephonyManager call
+ mPhone.getPreferredNetworkType(
+ mHandler.obtainMessage(EVENT_QUERY_PREFERRED_TYPE_DONE));
+
+ restoreFromBundle(icicle);
+ }
+
+ @Override
+ public Intent getParentActivityIntent() {
+ Intent parentActivity = super.getParentActivityIntent();
+ if (parentActivity == null) {
+ parentActivity = (new Intent()).setClassName("com.android.settings",
+ "com.android.settings.Settings$TestingSettingsActivity");
+ }
+ return parentActivity;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ log("Started onResume");
+
+ updateAllFields();
+ }
+
+ private void updateAllFields() {
+ updateMessageWaiting();
+ updateCallRedirect();
+ updateDataState();
+ updateDataStats2();
+ updateRadioPowerState();
+ updateImsProvisionedState();
+ updateProperties();
+ updateDnsCheckState();
+ updateNetworkType();
+
+ updateLocation(mCellLocationResult);
+ updateCellInfo(mCellInfoResult);
+ updateSubscriptionIds();
+
+ mPingHostnameV4.setText(mPingHostnameResultV4);
+ mPingHostnameV6.setText(mPingHostnameResultV6);
+ mHttpClientTest.setText(mHttpClientTestResult);
+
+ mCellInfoRefreshRateSpinner.setOnItemSelectedListener(mCellInfoRefreshRateHandler);
+ //set selection after registering listener to force update
+ mCellInfoRefreshRateSpinner.setSelection(mCellInfoRefreshRateIndex);
+
+ //set selection before registering to prevent update
+ mPreferredNetworkType.setSelection(mPreferredNetworkTypeResult, true);
+ mPreferredNetworkType.setOnItemSelectedListener(mPreferredNetworkHandler);
+
+ // set phone index
+ mSelectPhoneIndex.setSelection(mSelectedPhoneIndex, true);
+ mSelectPhoneIndex.setOnItemSelectedListener(mSelectPhoneIndexHandler);
+
+ mRadioPowerOnSwitch.setOnCheckedChangeListener(mRadioPowerOnChangeListener);
+ mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
+ mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
+ mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
+ mEabProvisionedSwitch.setOnCheckedChangeListener(mEabCheckedChangeListener);
+
+ if (isCbrsSupported()) {
+ mCbrsDataSwitch.setChecked(getCbrsDataState());
+ mCbrsDataSwitch.setOnCheckedChangeListener(mCbrsDataSwitchChangeListener);
+ }
+
+ unregisterPhoneStateListener();
+ registerPhoneStateListener();
+ mPhone.registerForPhysicalChannelConfig(mHandler,
+ EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED, null);
+
+ mConnectivityManager.registerNetworkCallback(
+ mDefaultNetworkRequest, mNetworkCallback, mHandler);
+
+ mSmsc.clearFocus();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ log("onPause: unregister phone & data intents");
+
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+
+ }
+
+ private void restoreFromBundle(Bundle b) {
+ if (b == null) {
+ return;
+ }
+
+ mPingHostnameResultV4 = b.getString("mPingHostnameResultV4", "");
+ mPingHostnameResultV6 = b.getString("mPingHostnameResultV6", "");
+ mHttpClientTestResult = b.getString("mHttpClientTestResult", "");
+
+ mPingHostnameV4.setText(mPingHostnameResultV4);
+ mPingHostnameV6.setText(mPingHostnameResultV6);
+ mHttpClientTest.setText(mHttpClientTestResult);
+
+ mPreferredNetworkTypeResult = b.getInt("mPreferredNetworkTypeResult",
+ PREFERRED_NETWORK_LABELS.length - 1);
+
+ mSelectedPhoneIndex = b.getInt("mSelectedPhoneIndex", 0);
+
+ mCellInfoRefreshRateIndex = b.getInt("mCellInfoRefreshRateIndex", 0);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putString("mPingHostnameResultV4", mPingHostnameResultV4);
+ outState.putString("mPingHostnameResultV6", mPingHostnameResultV6);
+ outState.putString("mHttpClientTestResult", mHttpClientTestResult);
+
+ outState.putInt("mPreferredNetworkTypeResult", mPreferredNetworkTypeResult);
+ outState.putInt("mSelectedPhoneIndex", mSelectedPhoneIndex);
+ outState.putInt("mCellInfoRefreshRateIndex", mCellInfoRefreshRateIndex);
+
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, MENU_ITEM_SELECT_BAND, 0, R.string.radio_info_band_mode_label)
+ .setOnMenuItemClickListener(mSelectBandCallback)
+ .setAlphabeticShortcut('b');
+ menu.add(1, MENU_ITEM_VIEW_ADN, 0,
+ R.string.radioInfo_menu_viewADN).setOnMenuItemClickListener(mViewADNCallback);
+ menu.add(1, MENU_ITEM_VIEW_FDN, 0,
+ R.string.radioInfo_menu_viewFDN).setOnMenuItemClickListener(mViewFDNCallback);
+ menu.add(1, MENU_ITEM_VIEW_SDN, 0,
+ R.string.radioInfo_menu_viewSDN).setOnMenuItemClickListener(mViewSDNCallback);
+ if (ImsManager.isImsSupportedOnDevice(mPhone.getContext())) {
+ menu.add(1, MENU_ITEM_GET_IMS_STATUS,
+ 0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
+ }
+ menu.add(1, MENU_ITEM_TOGGLE_DATA,
+ 0, R.string.radio_info_data_connection_disable)
+ .setOnMenuItemClickListener(mToggleData);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // Get the TOGGLE DATA menu item in the right state.
+ MenuItem item = menu.findItem(MENU_ITEM_TOGGLE_DATA);
+ int state = mTelephonyManager.getDataState();
+ boolean visible = true;
+
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTED:
+ case TelephonyManager.DATA_SUSPENDED:
+ item.setTitle(R.string.radio_info_data_connection_disable);
+ break;
+ case TelephonyManager.DATA_DISCONNECTED:
+ item.setTitle(R.string.radio_info_data_connection_enable);
+ break;
+ default:
+ visible = false;
+ break;
+ }
+ item.setVisible(visible);
+ return true;
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mQueuedWork.shutdown();
+ }
+
+ // returns array of string labels for each phone index. The array index is equal to the phone
+ // index.
+ private static String[] getPhoneIndexLabels(TelephonyManager tm) {
+ int phones = tm.getPhoneCount();
+ String[] labels = new String[phones];
+ for (int i = 0; i < phones; i++) {
+ labels[i] = "Phone " + i;
+ }
+ return labels;
+ }
+
+ private void unregisterPhoneStateListener() {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mPhone.unregisterForPhysicalChannelConfig(mHandler);
+
+ // clear all fields so they are blank until the next listener event occurs
+ mOperatorName.setText("");
+ mGprsState.setText("");
+ mDataNetwork.setText("");
+ mVoiceNetwork.setText("");
+ mSent.setText("");
+ mReceived.setText("");
+ mCallState.setText("");
+ mLocation.setText("");
+ mMwiValue = false;
+ mMwi.setText("");
+ mCfiValue = false;
+ mCfi.setText("");
+ mCellInfo.setText("");
+ mDBm.setText("");
+ mGsmState.setText("");
+ mRoamingState.setText("");
+ mPhyChanConfig.setText("");
+ }
+
+ // register mPhoneStateListener for relevant fields using the current TelephonyManager
+ private void registerPhoneStateListener() {
+ mPhoneStateListener = new RadioInfoPhoneStateListener();
+ mTelephonyManager.listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_CALL_STATE
+ //b/27803938 - RadioInfo currently cannot read PRECISE_CALL_STATE
+ // | PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+ | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY
+ | PhoneStateListener.LISTEN_CELL_LOCATION
+ | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+ | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+ | PhoneStateListener.LISTEN_CELL_INFO
+ | PhoneStateListener.LISTEN_SERVICE_STATE
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+ }
+
+ private void updateDnsCheckState() {
+ //FIXME: Replace with a TelephonyManager call
+ mDnsCheckState.setText(mPhone.isDnsCheckDisabled()
+ ? "0.0.0.0 allowed" : "0.0.0.0 not allowed");
+ }
+
+ private void updateBandwidths(int dlbw, int ulbw) {
+ dlbw = (dlbw < 0 || dlbw == Integer.MAX_VALUE) ? -1 : dlbw;
+ ulbw = (ulbw < 0 || ulbw == Integer.MAX_VALUE) ? -1 : ulbw;
+ mDownlinkKbps.setText(String.format("%-5d", dlbw));
+ mUplinkKbps.setText(String.format("%-5d", ulbw));
+ }
+
+
+ private void updateSignalStrength(SignalStrength signalStrength) {
+ Resources r = getResources();
+
+ int signalDbm = signalStrength.getDbm();
+
+ int signalAsu = signalStrength.getAsuLevel();
+
+ if (-1 == signalAsu) signalAsu = 0;
+
+ mDBm.setText(String.valueOf(signalDbm) + " "
+ + r.getString(R.string.radioInfo_display_dbm) + " "
+ + String.valueOf(signalAsu) + " "
+ + r.getString(R.string.radioInfo_display_asu));
+ }
+
+ private void updateLocation(CellLocation location) {
+ Resources r = getResources();
+ if (location instanceof GsmCellLocation) {
+ GsmCellLocation loc = (GsmCellLocation) location;
+ int lac = loc.getLac();
+ int cid = loc.getCid();
+ mLocation.setText(r.getString(R.string.radioInfo_lac) + " = "
+ + ((lac == -1) ? "unknown" : Integer.toHexString(lac))
+ + " "
+ + r.getString(R.string.radioInfo_cid) + " = "
+ + ((cid == -1) ? "unknown" : Integer.toHexString(cid)));
+ } else if (location instanceof CdmaCellLocation) {
+ CdmaCellLocation loc = (CdmaCellLocation) location;
+ int bid = loc.getBaseStationId();
+ int sid = loc.getSystemId();
+ int nid = loc.getNetworkId();
+ int lat = loc.getBaseStationLatitude();
+ int lon = loc.getBaseStationLongitude();
+ mLocation.setText("BID = "
+ + ((bid == -1) ? "unknown" : Integer.toHexString(bid))
+ + " "
+ + "SID = "
+ + ((sid == -1) ? "unknown" : Integer.toHexString(sid))
+ + " "
+ + "NID = "
+ + ((nid == -1) ? "unknown" : Integer.toHexString(nid))
+ + "\n"
+ + "LAT = "
+ + ((lat == -1) ? "unknown" : Integer.toHexString(lat))
+ + " "
+ + "LONG = "
+ + ((lon == -1) ? "unknown" : Integer.toHexString(lon)));
+ } else {
+ mLocation.setText("unknown");
+ }
+
+
+ }
+
+ private String getCellInfoDisplayString(int i) {
+ return (i != Integer.MAX_VALUE) ? Integer.toString(i) : "";
+ }
+
+ private String getCellInfoDisplayString(long i) {
+ return (i != Long.MAX_VALUE) ? Long.toString(i) : "";
+ }
+
+ private String getConnectionStatusString(CellInfo ci) {
+ String regStr = "";
+ String connStatStr = "";
+ String connector = "";
+
+ if (ci.isRegistered()) {
+ regStr = "R";
+ }
+ switch (ci.getCellConnectionStatus()) {
+ case CellInfo.CONNECTION_PRIMARY_SERVING: connStatStr = "P"; break;
+ case CellInfo.CONNECTION_SECONDARY_SERVING: connStatStr = "S"; break;
+ case CellInfo.CONNECTION_NONE: connStatStr = "N"; break;
+ case CellInfo.CONNECTION_UNKNOWN: /* Field is unsupported */ break;
+ default: break;
+ }
+ if (!TextUtils.isEmpty(regStr) && !TextUtils.isEmpty(connStatStr)) {
+ connector = "+";
+ }
+
+ return regStr + connector + connStatStr;
+ }
+
+ private String buildCdmaInfoString(CellInfoCdma ci) {
+ CellIdentityCdma cidCdma = ci.getCellIdentity();
+ CellSignalStrengthCdma ssCdma = ci.getCellSignalStrength();
+
+ return String.format("%-3.3s %-5.5s %-5.5s %-5.5s %-6.6s %-6.6s %-6.6s %-6.6s %-5.5s",
+ getConnectionStatusString(ci),
+ getCellInfoDisplayString(cidCdma.getSystemId()),
+ getCellInfoDisplayString(cidCdma.getNetworkId()),
+ getCellInfoDisplayString(cidCdma.getBasestationId()),
+ getCellInfoDisplayString(ssCdma.getCdmaDbm()),
+ getCellInfoDisplayString(ssCdma.getCdmaEcio()),
+ getCellInfoDisplayString(ssCdma.getEvdoDbm()),
+ getCellInfoDisplayString(ssCdma.getEvdoEcio()),
+ getCellInfoDisplayString(ssCdma.getEvdoSnr()));
+ }
+
+ private String buildGsmInfoString(CellInfoGsm ci) {
+ CellIdentityGsm cidGsm = ci.getCellIdentity();
+ CellSignalStrengthGsm ssGsm = ci.getCellSignalStrength();
+
+ return String.format("%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-4.4s %-4.4s\n",
+ getConnectionStatusString(ci),
+ getCellInfoDisplayString(cidGsm.getMcc()),
+ getCellInfoDisplayString(cidGsm.getMnc()),
+ getCellInfoDisplayString(cidGsm.getLac()),
+ getCellInfoDisplayString(cidGsm.getCid()),
+ getCellInfoDisplayString(cidGsm.getArfcn()),
+ getCellInfoDisplayString(cidGsm.getBsic()),
+ getCellInfoDisplayString(ssGsm.getDbm()));
+ }
+
+ private String buildLteInfoString(CellInfoLte ci) {
+ CellIdentityLte cidLte = ci.getCellIdentity();
+ CellSignalStrengthLte ssLte = ci.getCellSignalStrength();
+
+ return String.format(
+ "%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s %-6.6s %-2.2s %-4.4s %-4.4s %-2.2s\n",
+ getConnectionStatusString(ci),
+ getCellInfoDisplayString(cidLte.getMcc()),
+ getCellInfoDisplayString(cidLte.getMnc()),
+ getCellInfoDisplayString(cidLte.getTac()),
+ getCellInfoDisplayString(cidLte.getCi()),
+ getCellInfoDisplayString(cidLte.getPci()),
+ getCellInfoDisplayString(cidLte.getEarfcn()),
+ getCellInfoDisplayString(cidLte.getBandwidth()),
+ getCellInfoDisplayString(ssLte.getDbm()),
+ getCellInfoDisplayString(ssLte.getRsrq()),
+ getCellInfoDisplayString(ssLte.getTimingAdvance()));
+ }
+
+ private String buildWcdmaInfoString(CellInfoWcdma ci) {
+ CellIdentityWcdma cidWcdma = ci.getCellIdentity();
+ CellSignalStrengthWcdma ssWcdma = ci.getCellSignalStrength();
+
+ return String.format("%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-3.3s %-4.4s\n",
+ getConnectionStatusString(ci),
+ getCellInfoDisplayString(cidWcdma.getMcc()),
+ getCellInfoDisplayString(cidWcdma.getMnc()),
+ getCellInfoDisplayString(cidWcdma.getLac()),
+ getCellInfoDisplayString(cidWcdma.getCid()),
+ getCellInfoDisplayString(cidWcdma.getUarfcn()),
+ getCellInfoDisplayString(cidWcdma.getPsc()),
+ getCellInfoDisplayString(ssWcdma.getDbm()));
+ }
+
+ private String buildCellInfoString(List<CellInfo> arrayCi) {
+ String value = new String();
+ StringBuilder cdmaCells = new StringBuilder(),
+ gsmCells = new StringBuilder(),
+ lteCells = new StringBuilder(),
+ wcdmaCells = new StringBuilder();
+
+ if (arrayCi != null) {
+ for (CellInfo ci : arrayCi) {
+
+ if (ci instanceof CellInfoLte) {
+ lteCells.append(buildLteInfoString((CellInfoLte) ci));
+ } else if (ci instanceof CellInfoWcdma) {
+ wcdmaCells.append(buildWcdmaInfoString((CellInfoWcdma) ci));
+ } else if (ci instanceof CellInfoGsm) {
+ gsmCells.append(buildGsmInfoString((CellInfoGsm) ci));
+ } else if (ci instanceof CellInfoCdma) {
+ cdmaCells.append(buildCdmaInfoString((CellInfoCdma) ci));
+ }
+ }
+ if (lteCells.length() != 0) {
+ value += String.format(
+ "LTE\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-3.3s"
+ + " %-6.6s %-2.2s %-4.4s %-4.4s %-2.2s\n",
+ "SRV", "MCC", "MNC", "TAC", "CID", "PCI",
+ "EARFCN", "BW", "RSRP", "RSRQ", "TA");
+ value += lteCells.toString();
+ }
+ if (wcdmaCells.length() != 0) {
+ value += String.format(
+ "WCDMA\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-3.3s %-4.4s\n",
+ "SRV", "MCC", "MNC", "LAC", "CID", "UARFCN", "PSC", "RSCP");
+ value += wcdmaCells.toString();
+ }
+ if (gsmCells.length() != 0) {
+ value += String.format(
+ "GSM\n%-3.3s %-3.3s %-3.3s %-5.5s %-5.5s %-6.6s %-4.4s %-4.4s\n",
+ "SRV", "MCC", "MNC", "LAC", "CID", "ARFCN", "BSIC", "RSSI");
+ value += gsmCells.toString();
+ }
+ if (cdmaCells.length() != 0) {
+ value += String.format(
+ "CDMA/EVDO\n%-3.3s %-5.5s %-5.5s %-5.5s"
+ + " %-6.6s %-6.6s %-6.6s %-6.6s %-5.5s\n",
+ "SRV", "SID", "NID", "BSID",
+ "C-RSSI", "C-ECIO", "E-RSSI", "E-ECIO", "E-SNR");
+ value += cdmaCells.toString();
+ }
+ } else {
+ value = "unknown";
+ }
+
+ return value.toString();
+ }
+
+ private void updateCellInfo(List<CellInfo> arrayCi) {
+ mCellInfo.setText(buildCellInfoString(arrayCi));
+ }
+
+ private void updateSubscriptionIds() {
+ mSubscriptionId.setText(Integer.toString(mPhone.getSubId()));
+ mDds.setText(Integer.toString(SubscriptionManager.getDefaultDataSubscriptionId()));
+ }
+
+ private void updateMessageWaiting() {
+ mMwi.setText(String.valueOf(mMwiValue));
+ }
+
+ private void updateCallRedirect() {
+ mCfi.setText(String.valueOf(mCfiValue));
+ }
+
+
+ private void updateServiceState(ServiceState serviceState) {
+ int state = serviceState.getState();
+ Resources r = getResources();
+ String display = r.getString(R.string.radioInfo_unknown);
+
+ switch (state) {
+ case ServiceState.STATE_IN_SERVICE:
+ display = r.getString(R.string.radioInfo_service_in);
+ break;
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ case ServiceState.STATE_EMERGENCY_ONLY:
+ display = r.getString(R.string.radioInfo_service_emergency);
+ break;
+ case ServiceState.STATE_POWER_OFF:
+ display = r.getString(R.string.radioInfo_service_off);
+ break;
+ }
+
+ mGsmState.setText(display);
+
+ if (serviceState.getRoaming()) {
+ mRoamingState.setText(R.string.radioInfo_roaming_in);
+ } else {
+ mRoamingState.setText(R.string.radioInfo_roaming_not);
+ }
+
+ mOperatorName.setText(serviceState.getOperatorAlphaLong());
+ }
+
+ private void updatePhoneState(int state) {
+ Resources r = getResources();
+ String display = r.getString(R.string.radioInfo_unknown);
+
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE:
+ display = r.getString(R.string.radioInfo_phone_idle);
+ break;
+ case TelephonyManager.CALL_STATE_RINGING:
+ display = r.getString(R.string.radioInfo_phone_ringing);
+ break;
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ display = r.getString(R.string.radioInfo_phone_offhook);
+ break;
+ }
+
+ mCallState.setText(display);
+ }
+
+ private void updateDataState() {
+ int state = mTelephonyManager.getDataState();
+ Resources r = getResources();
+ String display = r.getString(R.string.radioInfo_unknown);
+
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTED:
+ display = r.getString(R.string.radioInfo_data_connected);
+ break;
+ case TelephonyManager.DATA_CONNECTING:
+ display = r.getString(R.string.radioInfo_data_connecting);
+ break;
+ case TelephonyManager.DATA_DISCONNECTED:
+ display = r.getString(R.string.radioInfo_data_disconnected);
+ break;
+ case TelephonyManager.DATA_SUSPENDED:
+ display = r.getString(R.string.radioInfo_data_suspended);
+ break;
+ }
+
+ mGprsState.setText(display);
+ }
+
+ private void updateNetworkType() {
+ if (mPhone != null) {
+ ServiceState ss = mPhone.getServiceState();
+ mDataNetwork.setText(ServiceState.rilRadioTechnologyToString(
+ mPhone.getServiceState().getRilDataRadioTechnology()));
+ mVoiceNetwork.setText(ServiceState.rilRadioTechnologyToString(
+ mPhone.getServiceState().getRilVoiceRadioTechnology()));
+ }
+ }
+
+ private void updateProperties() {
+ String s;
+ Resources r = getResources();
+
+ s = mPhone.getDeviceId();
+ if (s == null) s = r.getString(R.string.radioInfo_unknown);
+ mDeviceId.setText(s);
+
+ s = mPhone.getSubscriberId();
+ if (s == null) s = r.getString(R.string.radioInfo_unknown);
+ mSubscriberId.setText(s);
+
+ //FIXME: Replace with a TelephonyManager call
+ s = mPhone.getLine1Number();
+ if (s == null) s = r.getString(R.string.radioInfo_unknown);
+ mLine1Number.setText(s);
+ }
+
+ private void updateDataStats2() {
+ Resources r = getResources();
+
+ long txPackets = TrafficStats.getMobileTxPackets();
+ long rxPackets = TrafficStats.getMobileRxPackets();
+ long txBytes = TrafficStats.getMobileTxBytes();
+ long rxBytes = TrafficStats.getMobileRxBytes();
+
+ String packets = r.getString(R.string.radioInfo_display_packets);
+ String bytes = r.getString(R.string.radioInfo_display_bytes);
+
+ mSent.setText(txPackets + " " + packets + ", " + txBytes + " " + bytes);
+ mReceived.setText(rxPackets + " " + packets + ", " + rxBytes + " " + bytes);
+ }
+
+ /**
+ * Ping a host name
+ */
+ private void pingHostname() {
+ try {
+ try {
+ Process p4 = Runtime.getRuntime().exec("ping -c 1 www.google.com");
+ int status4 = p4.waitFor();
+ if (status4 == 0) {
+ mPingHostnameResultV4 = "Pass";
+ } else {
+ mPingHostnameResultV4 = String.format("Fail(%d)", status4);
+ }
+ } catch (IOException e) {
+ mPingHostnameResultV4 = "Fail: IOException";
+ }
+ try {
+ Process p6 = Runtime.getRuntime().exec("ping6 -c 1 www.google.com");
+ int status6 = p6.waitFor();
+ if (status6 == 0) {
+ mPingHostnameResultV6 = "Pass";
+ } else {
+ mPingHostnameResultV6 = String.format("Fail(%d)", status6);
+ }
+ } catch (IOException e) {
+ mPingHostnameResultV6 = "Fail: IOException";
+ }
+ } catch (InterruptedException e) {
+ mPingHostnameResultV4 = mPingHostnameResultV6 = "Fail: InterruptedException";
+ }
+ }
+
+ /**
+ * This function checks for basic functionality of HTTP Client.
+ */
+ private void httpClientTest() {
+ HttpURLConnection urlConnection = null;
+ try {
+ // TODO: Hardcoded for now, make it UI configurable
+ URL url = new URL("https://www.google.com");
+ urlConnection = (HttpURLConnection) url.openConnection();
+ if (urlConnection.getResponseCode() == 200) {
+ mHttpClientTestResult = "Pass";
+ } else {
+ mHttpClientTestResult = "Fail: Code: " + urlConnection.getResponseMessage();
+ }
+ } catch (IOException e) {
+ mHttpClientTestResult = "Fail: IOException";
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ }
+ }
+
+ private void refreshSmsc() {
+ mQueuedWork.execute(new Runnable() {
+ public void run() {
+ //FIXME: Replace with a TelephonyManager call
+ mPhone.getSmscAddress(mHandler.obtainMessage(EVENT_QUERY_SMSC_DONE));
+ }
+ });
+ }
+
+ private void updateAllCellInfo() {
+
+ mCellInfo.setText("");
+ mLocation.setText("");
+
+ final Runnable updateAllCellInfoResults = new Runnable() {
+ public void run() {
+ updateLocation(mCellLocationResult);
+ updateCellInfo(mCellInfoResult);
+ }
+ };
+
+ mQueuedWork.execute(new Runnable() {
+ @Override
+ public void run() {
+ mCellInfoResult = mTelephonyManager.getAllCellInfo();
+ mCellLocationResult = mTelephonyManager.getCellLocation();
+
+ mHandler.post(updateAllCellInfoResults);
+ }
+ });
+ }
+
+ private void updatePingState() {
+ // Set all to unknown since the threads will take a few secs to update.
+ mPingHostnameResultV4 = getResources().getString(R.string.radioInfo_unknown);
+ mPingHostnameResultV6 = getResources().getString(R.string.radioInfo_unknown);
+ mHttpClientTestResult = getResources().getString(R.string.radioInfo_unknown);
+
+ mPingHostnameV4.setText(mPingHostnameResultV4);
+ mPingHostnameV6.setText(mPingHostnameResultV6);
+ mHttpClientTest.setText(mHttpClientTestResult);
+
+ final Runnable updatePingResults = new Runnable() {
+ public void run() {
+ mPingHostnameV4.setText(mPingHostnameResultV4);
+ mPingHostnameV6.setText(mPingHostnameResultV6);
+ mHttpClientTest.setText(mHttpClientTestResult);
+ }
+ };
+
+ Thread hostname = new Thread() {
+ @Override
+ public void run() {
+ pingHostname();
+ mHandler.post(updatePingResults);
+ }
+ };
+ hostname.start();
+
+ Thread httpClient = new Thread() {
+ @Override
+ public void run() {
+ httpClientTest();
+ mHandler.post(updatePingResults);
+ }
+ };
+ httpClient.start();
+ }
+
+ private MenuItem.OnMenuItemClickListener mViewADNCallback =
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ // XXX We need to specify the component here because if we don't
+ // the activity manager will try to resolve the type by calling
+ // the content provider, which causes it to be loaded in a process
+ // other than the Dialer process, which causes a lot of stuff to
+ // break.
+ intent.setClassName("com.android.phone", "com.android.phone.SimContacts");
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mViewFDNCallback =
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ // XXX We need to specify the component here because if we don't
+ // the activity manager will try to resolve the type by calling
+ // the content provider, which causes it to be loaded in a process
+ // other than the Dialer process, which causes a lot of stuff to
+ // break.
+ intent.setClassName("com.android.phone", "com.android.phone.settings.fdn.FdnList");
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mViewSDNCallback =
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(
+ Intent.ACTION_VIEW, Uri.parse("content://icc/sdn"));
+ // XXX We need to specify the component here because if we don't
+ // the activity manager will try to resolve the type by calling
+ // the content provider, which causes it to be loaded in a process
+ // other than the Dialer process, which causes a lot of stuff to
+ // break.
+ intent.setClassName("com.android.phone", "com.android.phone.ADNList");
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mGetImsStatus =
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ boolean isImsRegistered = mPhone.isImsRegistered();
+ boolean availableVolte = mPhone.isVolteEnabled();
+ boolean availableWfc = mPhone.isWifiCallingEnabled();
+ boolean availableVt = mPhone.isVideoEnabled();
+ boolean availableUt = mPhone.isUtEnabled();
+
+ final String imsRegString = isImsRegistered
+ ? getString(R.string.radio_info_ims_reg_status_registered)
+ : getString(R.string.radio_info_ims_reg_status_not_registered);
+
+ final String available = getString(R.string.radio_info_ims_feature_status_available);
+ final String unavailable = getString(
+ R.string.radio_info_ims_feature_status_unavailable);
+
+ String imsStatus = getString(R.string.radio_info_ims_reg_status,
+ imsRegString,
+ availableVolte ? available : unavailable,
+ availableWfc ? available : unavailable,
+ availableVt ? available : unavailable,
+ availableUt ? available : unavailable);
+
+ AlertDialog imsDialog = new AlertDialog.Builder(RadioInfo.this)
+ .setMessage(imsStatus)
+ .setTitle(getString(R.string.radio_info_ims_reg_status_title))
+ .create();
+
+ imsDialog.show();
+
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mSelectBandCallback =
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent();
+ intent.setClass(RadioInfo.this, BandMode.class);
+ startActivity(intent);
+ return true;
+ }
+ };
+
+ private MenuItem.OnMenuItemClickListener mToggleData =
+ new MenuItem.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ int state = mTelephonyManager.getDataState();
+ switch (state) {
+ case TelephonyManager.DATA_CONNECTED:
+ mTelephonyManager.setDataEnabled(false);
+ break;
+ case TelephonyManager.DATA_DISCONNECTED:
+ mTelephonyManager.setDataEnabled(true);
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ return true;
+ }
+ };
+
+ private boolean isRadioOn() {
+ //FIXME: Replace with a TelephonyManager call
+ return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
+ }
+
+ private void updateRadioPowerState() {
+ //delightful hack to prevent on-checked-changed calls from
+ //actually forcing the radio preference to its transient/current value.
+ mRadioPowerOnSwitch.setOnCheckedChangeListener(null);
+ mRadioPowerOnSwitch.setChecked(isRadioOn());
+ mRadioPowerOnSwitch.setOnCheckedChangeListener(mRadioPowerOnChangeListener);
+ }
+
+ void setImsVolteProvisionedState(boolean state) {
+ Log.d(TAG, "setImsVolteProvisioned state: " + ((state) ? "on" : "off"));
+ setImsConfigProvisionedState(IMS_VOLTE_PROVISIONED_CONFIG_ID, state);
+ }
+
+ void setImsVtProvisionedState(boolean state) {
+ Log.d(TAG, "setImsVtProvisioned() state: " + ((state) ? "on" : "off"));
+ setImsConfigProvisionedState(IMS_VT_PROVISIONED_CONFIG_ID, state);
+ }
+
+ void setImsWfcProvisionedState(boolean state) {
+ Log.d(TAG, "setImsWfcProvisioned() state: " + ((state) ? "on" : "off"));
+ setImsConfigProvisionedState(IMS_WFC_PROVISIONED_CONFIG_ID, state);
+ }
+
+ void setEabProvisionedState(boolean state) {
+ Log.d(TAG, "setEabProvisioned() state: " + ((state) ? "on" : "off"));
+ setImsConfigProvisionedState(EAB_PROVISIONED_CONFIG_ID, state);
+ }
+
+ void setImsConfigProvisionedState(int configItem, boolean state) {
+ if (mPhone != null && mImsManager != null) {
+ mQueuedWork.execute(new Runnable() {
+ public void run() {
+ try {
+ mImsManager.getConfigInterface().setProvisionedValue(
+ configItem, state ? 1 : 0);
+ } catch (ImsException e) {
+ Log.e(TAG, "setImsConfigProvisioned() exception:", e);
+ }
+ }
+ });
+ }
+ }
+
+ OnCheckedChangeListener mRadioPowerOnChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ log("toggle radio power: currently " + (isRadioOn() ? "on" : "off"));
+ mPhone.setRadioPower(isChecked);
+ }
+ };
+
+ private boolean isImsVolteProvisioned() {
+ if (mPhone != null && mImsManager != null) {
+ return mImsManager.isVolteEnabledByPlatform(mPhone.getContext())
+ && mImsManager.isVolteProvisionedOnDevice(mPhone.getContext());
+ }
+ return false;
+ }
+
+ OnCheckedChangeListener mImsVolteCheckedChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setImsVolteProvisionedState(isChecked);
+ }
+ };
+
+ private boolean isImsVtProvisioned() {
+ if (mPhone != null && mImsManager != null) {
+ return mImsManager.isVtEnabledByPlatform(mPhone.getContext())
+ && mImsManager.isVtProvisionedOnDevice(mPhone.getContext());
+ }
+ return false;
+ }
+
+ OnCheckedChangeListener mImsVtCheckedChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setImsVtProvisionedState(isChecked);
+ }
+ };
+
+ private boolean isImsWfcProvisioned() {
+ if (mPhone != null && mImsManager != null) {
+ return mImsManager.isWfcEnabledByPlatform(mPhone.getContext())
+ && mImsManager.isWfcProvisionedOnDevice(mPhone.getContext());
+ }
+ return false;
+ }
+
+ OnCheckedChangeListener mImsWfcCheckedChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setImsWfcProvisionedState(isChecked);
+ }
+ };
+
+ private boolean isEabProvisioned() {
+ return isFeatureProvisioned(EAB_PROVISIONED_CONFIG_ID, false);
+ }
+
+ OnCheckedChangeListener mEabCheckedChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ setEabProvisionedState(isChecked);
+ }
+ };
+
+ private boolean isFeatureProvisioned(int featureId, boolean defaultValue) {
+ boolean provisioned = defaultValue;
+ if (mImsManager != null) {
+ try {
+ ImsConfig imsConfig = mImsManager.getConfigInterface();
+ if (imsConfig != null) {
+ provisioned =
+ (imsConfig.getProvisionedValue(featureId)
+ == ImsConfig.FeatureValueConstants.ON);
+ }
+ } catch (ImsException ex) {
+ Log.e(TAG, "isFeatureProvisioned() exception:", ex);
+ }
+ }
+
+ log("isFeatureProvisioned() featureId=" + featureId + " provisioned=" + provisioned);
+ return provisioned;
+ }
+
+ private static boolean isEabEnabledByPlatform(Context context) {
+ if (context != null) {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager != null && configManager.getConfig().getBoolean(
+ CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updateImsProvisionedState() {
+ if (!ImsManager.isImsSupportedOnDevice(mPhone.getContext())) {
+ return;
+ }
+ log("updateImsProvisionedState isImsVolteProvisioned()=" + isImsVolteProvisioned());
+ //delightful hack to prevent on-checked-changed calls from
+ //actually forcing the ims provisioning to its transient/current value.
+ mImsVolteProvisionedSwitch.setOnCheckedChangeListener(null);
+ mImsVolteProvisionedSwitch.setChecked(isImsVolteProvisioned());
+ mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
+ mImsVolteProvisionedSwitch.setEnabled(!IS_USER_BUILD
+ && mImsManager.isVolteEnabledByPlatform(mPhone.getContext()));
+
+ mImsVtProvisionedSwitch.setOnCheckedChangeListener(null);
+ mImsVtProvisionedSwitch.setChecked(isImsVtProvisioned());
+ mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
+ mImsVtProvisionedSwitch.setEnabled(!IS_USER_BUILD
+ && mImsManager.isVtEnabledByPlatform(mPhone.getContext()));
+
+ mImsWfcProvisionedSwitch.setOnCheckedChangeListener(null);
+ mImsWfcProvisionedSwitch.setChecked(isImsWfcProvisioned());
+ mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
+ mImsWfcProvisionedSwitch.setEnabled(!IS_USER_BUILD
+ && mImsManager.isWfcEnabledByPlatform(mPhone.getContext()));
+
+ mEabProvisionedSwitch.setOnCheckedChangeListener(null);
+ mEabProvisionedSwitch.setChecked(isEabProvisioned());
+ mEabProvisionedSwitch.setOnCheckedChangeListener(mEabCheckedChangeListener);
+ mEabProvisionedSwitch.setEnabled(!IS_USER_BUILD
+ && isEabEnabledByPlatform(mPhone.getContext()));
+ }
+
+ OnClickListener mDnsCheckButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ //FIXME: Replace with a TelephonyManager call
+ mPhone.disableDnsCheck(!mPhone.isDnsCheckDisabled());
+ updateDnsCheckState();
+ }
+ };
+
+ OnClickListener mOemInfoButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(OEM_RADIO_INFO_INTENT);
+ try {
+ startActivity(intent);
+ } catch (android.content.ActivityNotFoundException ex) {
+ log("OEM-specific Info/Settings Activity Not Found : " + ex);
+ // If the activity does not exist, there are no OEM
+ // settings, and so we can just do nothing...
+ }
+ }
+ };
+
+ OnClickListener mPingButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ updatePingState();
+ }
+ };
+
+ OnClickListener mUpdateSmscButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ mUpdateSmscButton.setEnabled(false);
+ mQueuedWork.execute(new Runnable() {
+ public void run() {
+ mPhone.setSmscAddress(mSmsc.getText().toString(),
+ mHandler.obtainMessage(EVENT_UPDATE_SMSC_DONE));
+ }
+ });
+ }
+ };
+
+ OnClickListener mRefreshSmscButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ refreshSmsc();
+ }
+ };
+
+ OnClickListener mCarrierProvisioningButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ final Intent intent = new Intent("com.android.settings.CARRIER_PROVISIONING");
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(
+ "com.android.omadm.service/.DMIntentReceiver");
+ intent.setComponent(serviceComponent);
+ sendBroadcast(intent);
+ }
+ };
+
+ OnClickListener mTriggerCarrierProvisioningButtonHandler = new OnClickListener() {
+ public void onClick(View v) {
+ final Intent intent = new Intent("com.android.settings.TRIGGER_CARRIER_PROVISIONING");
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(
+ "com.android.omadm.service/.DMIntentReceiver");
+ intent.setComponent(serviceComponent);
+ sendBroadcast(intent);
+ }
+ };
+
+ AdapterView.OnItemSelectedListener mPreferredNetworkHandler =
+ new AdapterView.OnItemSelectedListener() {
+
+ public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+ if (mPreferredNetworkTypeResult != pos && pos >= 0
+ && pos <= PREFERRED_NETWORK_LABELS.length - 2) {
+ mPreferredNetworkTypeResult = pos;
+
+ // TODO: Possibly migrate this to TelephonyManager.setPreferredNetworkType()
+ // which today still has some issues (mostly that the "set" is conditional
+ // on a successful modem call, which is not what we want). Instead we always
+ // want this setting to be set, so that if the radio hiccups and this setting
+ // is for some reason unsuccessful, future calls to the radio will reflect
+ // the users's preference which is set here.
+ final int subId = mPhone.getSubId();
+ if (SubscriptionManager.isUsableSubIdValue(subId)) {
+ Settings.Global.putInt(mPhone.getContext().getContentResolver(),
+ PREFERRED_NETWORK_MODE + subId, mPreferredNetworkTypeResult);
+ }
+ log("Calling setPreferredNetworkType(" + mPreferredNetworkTypeResult + ")");
+ Message msg = mHandler.obtainMessage(EVENT_SET_PREFERRED_TYPE_DONE);
+ mPhone.setPreferredNetworkType(mPreferredNetworkTypeResult, msg);
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ };
+
+ AdapterView.OnItemSelectedListener mSelectPhoneIndexHandler =
+ new AdapterView.OnItemSelectedListener() {
+
+ public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+ if (pos >= 0 && pos <= sPhoneIndexLabels.length - 1) {
+ // the array position is equal to the phone index
+ int phoneIndex = pos;
+ Phone[] phones = PhoneFactory.getPhones();
+ if (phones == null || phones.length <= phoneIndex) {
+ return;
+ }
+ // getSubId says it takes a slotIndex, but it actually takes a phone index
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ int[] subIds = SubscriptionManager.getSubId(phoneIndex);
+ if (subIds != null && subIds.length > 0) {
+ subId = subIds[0];
+ }
+ mSelectedPhoneIndex = phoneIndex;
+
+ updatePhoneIndex(phoneIndex, subId);
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ };
+
+ AdapterView.OnItemSelectedListener mCellInfoRefreshRateHandler =
+ new AdapterView.OnItemSelectedListener() {
+
+ public void onItemSelected(AdapterView parent, View v, int pos, long id) {
+ mCellInfoRefreshRateIndex = pos;
+ mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[pos]);
+ updateAllCellInfo();
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ };
+
+ boolean isCbrsSupported() {
+ return getResources().getBoolean(
+ com.android.internal.R.bool.config_cbrs_supported);
+ }
+
+ void updateCbrsDataState(boolean state) {
+ Log.d(TAG, "setCbrsDataSwitchState() state:" + ((state) ? "on" : "off"));
+ if (mTelephonyManager != null) {
+ mQueuedWork.execute(new Runnable() {
+ public void run() {
+ mTelephonyManager.setOpportunisticNetworkState(state);
+ mCbrsDataSwitch.setChecked(getCbrsDataState());
+ }
+ });
+ }
+ }
+
+ boolean getCbrsDataState() {
+ boolean state = false;
+ if (mTelephonyManager != null) {
+ state = mTelephonyManager.isOpportunisticNetworkEnabled();
+ }
+ Log.d(TAG, "getCbrsDataState() state:" + ((state) ? "on" : "off"));
+ return state;
+ }
+
+ OnCheckedChangeListener mCbrsDataSwitchChangeListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ updateCbrsDataState(isChecked);
+ }
+ };
+
+ private void showDsdsChangeDialog() {
+ final AlertDialog confirmDialog = new Builder(RadioInfo.this)
+ .setTitle(R.string.dsds_dialog_title)
+ .setMessage(R.string.dsds_dialog_message)
+ .setPositiveButton(R.string.dsds_dialog_confirm, mOnDsdsDialogConfirmedListener)
+ .setNegativeButton(R.string.dsds_dialog_cancel, mOnDsdsDialogConfirmedListener)
+ .create();
+ confirmDialog.show();
+ }
+
+ private static boolean isDsdsSupported() {
+ return (TelephonyManager.getDefault().isMultiSimSupported()
+ == TelephonyManager.MULTISIM_ALLOWED);
+ }
+
+ private static boolean isDsdsEnabled() {
+ return TelephonyManager.getDefault().getPhoneCount() > 1;
+ }
+
+ private void performDsdsSwitch() {
+ mTelephonyManager.switchMultiSimConfig(mDsdsSwitch.isChecked() ? 2 : 1);
+ }
+
+ /**
+ * @return {@code True} if the device is only supported dsds mode.
+ */
+ private boolean dsdsModeOnly() {
+ String dsdsMode = SystemProperties.get(DSDS_MODE_PROPERTY);
+ return !TextUtils.isEmpty(dsdsMode) && Integer.parseInt(dsdsMode) == ALWAYS_ON_DSDS_MODE;
+ }
+
+ DialogInterface.OnClickListener mOnDsdsDialogConfirmedListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mDsdsSwitch.toggle();
+ performDsdsSwitch();
+ }
+ }
+ };
+}
diff --git a/src/com/android/phone/settings/TtyModeListPreference.java b/src/com/android/phone/settings/TtyModeListPreference.java
index 89cac47..ba415c5 100644
--- a/src/com/android/phone/settings/TtyModeListPreference.java
+++ b/src/com/android/phone/settings/TtyModeListPreference.java
@@ -18,17 +18,23 @@
import android.content.Context;
import android.content.Intent;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.util.AttributeSet;
import android.util.Log;
import com.android.phone.PhoneGlobals;
import com.android.phone.R;
+import java.util.Arrays;
+
public class TtyModeListPreference extends ListPreference
implements Preference.OnPreferenceChangeListener {
private static final String LOG_TAG = TtyModeListPreference.class.getSimpleName();
@@ -44,6 +50,19 @@
int settingsTtyMode = Settings.Secure.getInt(getContext().getContentResolver(),
Settings.Secure.PREFERRED_TTY_MODE,
TelecomManager.TTY_MODE_OFF);
+ if (shouldHideHcoAndVco()) {
+ setEntries(Arrays.copyOfRange(
+ getContext().getResources().getTextArray(R.array.tty_mode_entries), 0, 2));
+ setEntryValues(Arrays.copyOfRange(
+ getContext().getResources().getTextArray(R.array.tty_mode_values), 0, 2));
+ if (settingsTtyMode == TelecomManager.TTY_MODE_HCO
+ || settingsTtyMode == TelecomManager.TTY_MODE_VCO) {
+ // If the persisted setting is HCO or VCO, set it to full here
+ settingsTtyMode = TelecomManager.TTY_MODE_FULL;
+ Settings.Secure.putInt(getContext().getContentResolver(), Secure.PREFERRED_TTY_MODE,
+ settingsTtyMode);
+ }
+ }
setValue(Integer.toString(settingsTtyMode));
updatePreferredTtyModeSummary(settingsTtyMode);
}
@@ -101,6 +120,18 @@
}
}
+ private boolean shouldHideHcoAndVco() {
+ CarrierConfigManager carrierConfigManager =
+ getContext().getSystemService(CarrierConfigManager.class);
+ PersistableBundle config = carrierConfigManager.getConfigForSubId(
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ boolean carrierShouldHideHcoVco = config != null && config.getBoolean(
+ CarrierConfigManager.KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+ boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr.isRttSupported(
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ return carrierShouldHideHcoVco && isRttSupported;
+ }
+
private static void log(String msg) {
Log.d(LOG_TAG, msg);
}
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index c358e27..0eda140 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -29,6 +29,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.provider.ContactsContract.CommonDataKinds;
import android.telephony.PhoneNumberUtils;
import android.text.Editable;
@@ -52,6 +53,7 @@
import com.android.phone.PhoneGlobals;
import com.android.phone.R;
import com.android.phone.SubscriptionInfoHelper;
+import android.telephony.CarrierConfigManager;
/**
* Activity to let the user add or edit an FDN contact.
@@ -100,6 +102,7 @@
}
/** flag to track saving state */
private boolean mDataBusy;
+ private int mFdnNumberLimitLength = 20;
@Override
protected void onCreate(Bundle icicle) {
@@ -111,6 +114,17 @@
setContentView(R.layout.edit_fdn_contact_screen);
setupView();
setTitle(mAddContact ? R.string.add_fdn_contact : R.string.edit_fdn_contact);
+ PersistableBundle b = null;
+ if (mSubscriptionInfoHelper.hasSubId()) {
+ b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+ mSubscriptionInfoHelper.getSubId());
+ } else {
+ b = PhoneGlobals.getInstance().getCarrierConfig();
+ }
+ if (b != null) {
+ mFdnNumberLimitLength = b.getInt(
+ CarrierConfigManager.KEY_FDN_NUMBER_LENGTH_LIMIT_INT);
+ }
displayProgress(false);
}
@@ -294,7 +308,7 @@
* TODO: Fix this logic.
*/
private boolean isValidNumber(String number) {
- return (number.length() <= 20) && (number.length() > 0);
+ return (number.length() <= mFdnNumberLimitLength) && (number.length() > 0);
}
@@ -397,7 +411,8 @@
} else {
if (DBG) log("handleResult: failed!");
if (invalidNumber) {
- showStatus(getResources().getText(R.string.fdn_invalid_number));
+ showStatus(getResources().getString(R.string.fdn_invalid_number,
+ mFdnNumberLimitLength));
} else {
if (PhoneFactory.getDefaultPhone().getIccCard().getIccPin2Blocked()) {
showStatus(getResources().getText(R.string.fdn_enable_puk2_requested));
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
index 50ac669..6b60303 100644
--- a/src/com/android/phone/vvm/RemoteVvmTaskManager.java
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
@@ -38,6 +39,7 @@
import android.telephony.VisualVoicemailSms;
import android.text.TextUtils;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.phone.Assert;
import com.android.phone.R;
@@ -167,7 +169,8 @@
}
if (info.serviceInfo == null) {
VvmLog.w(TAG,
- "Component " + info.getComponentInfo() + " is not a service, ignoring");
+ "Component " + TelephonyUtils.getComponentInfo(info)
+ + " is not a service, ignoring");
continue;
}
if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE
@@ -180,7 +183,8 @@
VvmLog.w(TAG, "target package " + targetPackage
+ " is no longer the active VisualVoicemailService, ignoring");
}
- return info.getComponentInfo().getComponentName();
+ ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(info);
+ return new ComponentName(componentInfo.packageName, componentInfo.name);
}
return null;
@@ -199,7 +203,8 @@
if (info.isEmpty()) {
return null;
}
- return info.get(0).getComponentInfo().getComponentName();
+ ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(info.get(0));
+ return new ComponentName(componentInfo.packageName, componentInfo.name);
}
@Override
@@ -226,6 +231,11 @@
Assert.isMainThread();
mTaskReferenceCount++;
+ if (intent == null) {
+ VvmLog.i(TAG, "received intent is null");
+ checkReference();
+ return START_NOT_STICKY;
+ }
PhoneAccountHandle phoneAccountHandle = intent.getExtras()
.getParcelable(VisualVoicemailService.DATA_PHONE_ACCOUNT_HANDLE);
int subId = PhoneAccountHandleConverter.toSubId(phoneAccountHandle);
diff --git a/src/com/android/phone/vvm/VvmDumpHandler.java b/src/com/android/phone/vvm/VvmDumpHandler.java
index 866927e..82c5bb5 100644
--- a/src/com/android/phone/vvm/VvmDumpHandler.java
+++ b/src/com/android/phone/vvm/VvmDumpHandler.java
@@ -19,7 +19,7 @@
indentedWriter.println("******* OmtpVvm *******");
indentedWriter.println("======= Configs =======");
indentedWriter.increaseIndent();
- for (PhoneAccountHandle handle : TelecomManager.from(context)
+ for (PhoneAccountHandle handle : context.getSystemService(TelecomManager.class)
.getCallCapablePhoneAccounts()) {
int subId = PhoneAccountHandleConverter.toSubId(handle);
indentedWriter.println(
diff --git a/src/com/android/services/telephony/CdmaConference.java b/src/com/android/services/telephony/CdmaConference.java
index 69ff2a4..693fd16 100755
--- a/src/com/android/services/telephony/CdmaConference.java
+++ b/src/com/android/services/telephony/CdmaConference.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.os.PersistableBundle;
-import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.PhoneAccountHandle;
import android.telephony.CarrierConfigManager;
@@ -32,7 +31,7 @@
/**
* CDMA-based conference call.
*/
-public class CdmaConference extends Conference implements Holdable {
+public class CdmaConference extends TelephonyConferenceBase implements Holdable {
private int mCapabilities;
private int mProperties;
private boolean mIsHoldable;
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 5d987f7..9afcd0a 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -48,7 +48,8 @@
* the conference from being created for 3 seconds. This is a more pleasant experience for the user.
*/
final class CdmaConferenceController {
- private final Connection.Listener mConnectionListener = new Connection.Listener() {
+ private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
@Override
public void onStateChanged(Connection c, int state) {
recalculateConference();
@@ -139,7 +140,7 @@
private void addInternal(CdmaConnection connection) {
mCdmaConnections.add(connection);
- connection.addConnectionListener(mConnectionListener);
+ connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
recalculateConference();
}
@@ -151,7 +152,7 @@
return;
}
- connection.removeConnectionListener(mConnectionListener);
+ connection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
mCdmaConnections.remove(connection);
recalculateConference();
}
@@ -197,14 +198,14 @@
for (CdmaConnection connection : conferenceConnections) {
if (!existingChildConnections.contains(connection)) {
Log.i(this, "Adding connection to conference call: %s", connection);
- mConference.addConnection(connection);
+ mConference.addTelephonyConnection(connection);
}
existingChildConnections.remove(connection);
}
// 3) Remove any lingering old/disconnected/destroyed connections
for (Connection oldConnection : existingChildConnections) {
- mConference.removeConnection(oldConnection);
+ mConference.removeTelephonyConnection(oldConnection);
Log.i(this, "Removing connection from conference call: %s", oldConnection);
}
@@ -212,13 +213,13 @@
if (isNewlyCreated) {
Log.d(this, "Adding the conference call");
mConference.updateCallRadioTechAfterCreation();
- mConnectionService.addConference(mConference);
+ mConnectionService.addTelephonyConference(mConference);
}
} else if (conferenceConnections.isEmpty()) {
// There are no more connection so if we still have a conference, lets remove it.
if (mConference != null) {
Log.i(this, "Destroying the CDMA conference connection.");
- mConference.destroy();
+ mConference.destroyTelephonyConference();
mConference = null;
}
}
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index ca842b1..bd015e3 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -228,8 +228,8 @@
} catch (CallStateException e) {
Log.e(this, e, "Failed to hangup call waiting call");
}
- setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause,
- null, getPhone().getPhoneId()));
+ setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ telephonyDisconnectCause, null, getPhone().getPhoneId()));
}
}
@@ -325,7 +325,7 @@
}
@Override
- protected void close() {
+ public void close() {
mIsConnectionTimeReset = false;
if (getPhone() != null) {
getPhone().unregisterForLineControlInfo(mHandler);
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index b1f1b23..ed92e7f 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -17,15 +17,13 @@
package com.android.services.telephony;
import android.net.Uri;
-import android.telecom.ConferenceParticipant;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
import android.telephony.SubscriptionInfo;
import android.text.TextUtils;
-import com.android.internal.annotations.VisibleForTesting;
+import com.android.ims.internal.ConferenceParticipant;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -34,6 +32,8 @@
*/
public class ConferenceParticipantConnection extends Connection {
+ private static final String LOG_TAG = "ConferenceParticipantConnection";
+
/**
* The user entity URI For the conference participant.
*/
@@ -88,7 +88,7 @@
* @param newState The new state.
*/
public void updateState(int newState) {
- Log.v(this, "updateState endPoint: %s state: %s", Log.pii(mEndpoint),
+ Log.v(this, "updateState endPoint: %s state: %s", Rlog.pii(LOG_TAG, mEndpoint),
Connection.stateToString(newState));
if (newState == getState()) {
return;
@@ -203,13 +203,13 @@
sb.append("[ConferenceParticipantConnection objId:");
sb.append(System.identityHashCode(this));
sb.append(" endPoint:");
- sb.append(Log.pii(mEndpoint));
+ sb.append(Rlog.pii(LOG_TAG, mEndpoint));
sb.append(" address:");
- sb.append(Log.pii(getAddress()));
+ sb.append(Rlog.pii(LOG_TAG, getAddress()));
sb.append(" addressPresentation:");
sb.append(getAddressPresentation());
sb.append(" parentConnection:");
- sb.append(Log.pii(mParentConnection.getAddress()));
+ sb.append(Rlog.pii(LOG_TAG, mParentConnection.getAddress()));
sb.append(" state:");
sb.append(Connection.stateToString(getState()));
sb.append(" connectTime:");
diff --git a/src/com/android/services/telephony/DeviceState.java b/src/com/android/services/telephony/DeviceState.java
new file mode 100644
index 0000000..7cd2d7e
--- /dev/null
+++ b/src/com/android/services/telephony/DeviceState.java
@@ -0,0 +1,60 @@
+/*
+ * 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.services.telephony;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.phone.R;
+
+/**
+ * Abstracts device state and settings for better testability.
+ */
+public class DeviceState {
+
+ public boolean shouldCheckSimStateBeforeOutgoingCall(Context context) {
+ return context.getResources().getBoolean(
+ R.bool.config_checkSimStateBeforeOutgoingCall);
+ }
+
+ public boolean isSuplDdsSwitchRequiredForEmergencyCall(Context context) {
+ return context.getResources().getBoolean(
+ R.bool.config_gnss_supl_requires_default_data_for_emergency);
+ }
+
+ public boolean isRadioPowerDownAllowedOnBluetooth(Context context) {
+ return context.getResources().getBoolean(R.bool.config_allowRadioPowerDownOnBluetooth);
+ }
+
+ public boolean isAirplaneModeOn(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+ }
+
+ public int getCellOnStatus(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(), Settings.Global.CELL_ON,
+ PhoneConstants.CELL_ON_FLAG);
+ }
+
+ public boolean isTtyModeEnabled(Context context) {
+ return android.provider.Settings.Secure.getInt(context.getContentResolver(),
+ android.provider.Settings.Secure.PREFERRED_TTY_MODE,
+ TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF;
+ }
+}
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 94e8c4a..9e32f00 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -117,6 +117,8 @@
private static int toTelecomDisconnectCauseCode(int telephonyDisconnectCause) {
switch (telephonyDisconnectCause) {
case android.telephony.DisconnectCause.LOCAL:
+ // The call was still disconnected locally, so this is not an error condition.
+ case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED:
return DisconnectCause.LOCAL;
case android.telephony.DisconnectCause.NORMAL:
@@ -170,6 +172,8 @@
case android.telephony.DisconnectCause.POWER_OFF:
case android.telephony.DisconnectCause.LOW_BATTERY:
case android.telephony.DisconnectCause.DIAL_LOW_BATTERY:
+ case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+ case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
case android.telephony.DisconnectCause.SERVER_ERROR:
case android.telephony.DisconnectCause.SERVER_UNREACHABLE:
case android.telephony.DisconnectCause.TIMED_OUT:
@@ -361,6 +365,12 @@
case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
resourceId = R.string.callFailed_otasp_provisioning_in_process;
break;
+ case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+ resourceId = R.string.callFailed_emergency_call_over_wfc_not_available;
+ break;
+ case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
+ resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
+ break;
default:
break;
}
@@ -748,6 +758,12 @@
case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
resourceId = R.string.callFailed_otasp_provisioning_in_process;
break;
+ case android.telephony.DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
+ resourceId = R.string.callFailed_emergency_call_over_wfc_not_available;
+ break;
+ case android.telephony.DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
+ resourceId = R.string.callFailed_wfc_service_not_available_in_this_location;
+ break;
default:
break;
}
@@ -783,6 +799,8 @@
break;
case android.telephony.DisconnectCause.IMS_ACCESS_BLOCKED:
return DisconnectCause.REASON_IMS_ACCESS_BLOCKED;
+ case android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED:
+ return DisconnectCause.REASON_EMERGENCY_CALL_PLACED;
}
// If no specific code-mapping found, then fall back to using the reason.
diff --git a/src/com/android/services/telephony/EmergencyTonePlayer.java b/src/com/android/services/telephony/EmergencyTonePlayer.java
index 8e26349..431f8d7 100644
--- a/src/com/android/services/telephony/EmergencyTonePlayer.java
+++ b/src/com/android/services/telephony/EmergencyTonePlayer.java
@@ -23,7 +23,6 @@
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.ToneGenerator;
-import android.os.SystemVibrator;
import android.os.Vibrator;
import android.provider.Settings;
@@ -49,11 +48,9 @@
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.build();
- // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure that this vibrator
- // object will be isolated from others.
- private final Vibrator mVibrator = new SystemVibrator();
private final Context mContext;
private final AudioManager mAudioManager;
+ private final Vibrator mVibrator;
private ToneGenerator mToneGenerator;
private int mSavedInCallVolume;
@@ -62,6 +59,7 @@
EmergencyTonePlayer(Context context) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
public void start() {
diff --git a/src/com/android/services/telephony/HoldTracker.java b/src/com/android/services/telephony/HoldTracker.java
index 805802f..5032b41 100644
--- a/src/com/android/services/telephony/HoldTracker.java
+++ b/src/com/android/services/telephony/HoldTracker.java
@@ -16,7 +16,6 @@
package com.android.services.telephony;
-import android.telecom.Log;
import android.telecom.PhoneAccountHandle;
import java.util.ArrayList;
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4da92e2..4c3f9c9 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -21,20 +21,19 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
-import android.telecom.Conference;
-import android.telecom.ConferenceParticipant;
import android.telecom.Connection;
import android.telecom.Connection.VideoProvider;
import android.telecom.DisconnectCause;
-import android.telecom.Log;
import android.telecom.PhoneAccountHandle;
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;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
@@ -68,7 +67,9 @@
* connection and is responsible for managing the conference participant connections which represent
* the participants.
*/
-public class ImsConference extends Conference implements Holdable {
+public class ImsConference extends TelephonyConferenceBase implements Holdable {
+
+ private static final String LOG_TAG = "ImsConference";
/**
* Abstracts out fetching a feature flag. Makes testing easier.
@@ -78,143 +79,127 @@
}
/**
- * Listener used to respond to changes to conference participants. At the conference level we
- * are most concerned with handling destruction of a conference participant.
- */
- private final Connection.Listener mParticipantListener = new Connection.Listener() {
- /**
- * Participant has been destroyed. Remove it from the conference.
- *
- * @param connection The participant which was destroyed.
- */
- @Override
- public void onDestroyed(Connection connection) {
- ConferenceParticipantConnection participant =
- (ConferenceParticipantConnection) connection;
- removeConferenceParticipant(participant);
- updateManageConference();
- }
-
- };
-
- /**
* Listener used to respond to changes to the underlying radio connection for the conference
* host connection. Used to respond to SRVCC changes.
*/
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
new TelephonyConnection.TelephonyConnectionListener() {
- @Override
- public void onOriginalConnectionConfigured(TelephonyConnection c) {
- if (c == mConferenceHost) {
- handleOriginalConnectionChange();
- }
- }
- };
+ /**
+ * Updates the state of the conference based on the new state of the host.
+ *
+ * @param c The host connection.
+ * @param state The new state
+ */
+ @Override
+ public void onStateChanged(android.telecom.Connection c, int state) {
+ setState(state);
+ }
- /**
- * Listener used to respond to changes to the connection to the IMS conference server.
- */
- private final android.telecom.Connection.Listener mConferenceHostListener =
- new android.telecom.Connection.Listener() {
+ /**
+ * Disconnects the conference when its host connection disconnects.
+ *
+ * @param c The host connection.
+ * @param disconnectCause The host connection disconnect cause.
+ */
+ @Override
+ public void onDisconnected(android.telecom.Connection c,
+ DisconnectCause disconnectCause) {
+ setDisconnected(disconnectCause);
+ }
- /**
- * Updates the state of the conference based on the new state of the host.
- *
- * @param c The host connection.
- * @param state The new state
- */
- @Override
- public void onStateChanged(android.telecom.Connection c, int state) {
- setState(state);
- }
+ @Override
+ public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
+ Log.d(this, "onVideoStateChanged video state %d", videoState);
+ setVideoState(c, videoState);
+ }
- /**
- * Disconnects the conference when its host connection disconnects.
- *
- * @param c The host connection.
- * @param disconnectCause The host connection disconnect cause.
- */
- @Override
- public void onDisconnected(android.telecom.Connection c, DisconnectCause disconnectCause) {
- setDisconnected(disconnectCause);
- }
+ @Override
+ public void onVideoProviderChanged(android.telecom.Connection c,
+ Connection.VideoProvider videoProvider) {
+ Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+ videoProvider);
+ setVideoProvider(c, videoProvider);
+ }
- /**
- * Handles changes to conference participant data as reported by the conference host
- * connection.
- *
- * @param c The connection.
- * @param participants The participant information.
- */
- @Override
- public void onConferenceParticipantsChanged(android.telecom.Connection c,
- List<ConferenceParticipant> participants) {
+ @Override
+ public void onConnectionCapabilitiesChanged(Connection c,
+ int connectionCapabilities) {
+ Log.d(this, "onConnectionCapabilitiesChanged: Connection: %s,"
+ + " connectionCapabilities: %s", c, connectionCapabilities);
+ int capabilites = ImsConference.this.getConnectionCapabilities();
+ boolean isVideoConferencingSupported = mConferenceHost == null ? false :
+ mConferenceHost.isCarrierVideoConferencingSupported();
+ setConnectionCapabilities(
+ applyHostCapabilities(capabilites, connectionCapabilities,
+ isVideoConferencingSupported));
+ }
- if (c == null || participants == null) {
- return;
- }
- Log.v(this, "onConferenceParticipantsChanged: %d participants", participants.size());
- TelephonyConnection telephonyConnection = (TelephonyConnection) c;
- handleConferenceParticipantsUpdate(telephonyConnection, participants);
- }
+ @Override
+ public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
+ Log.d(this, "onConnectionPropertiesChanged: Connection: %s,"
+ + " connectionProperties: %s", c, connectionProperties);
+ int properties = ImsConference.this.getConnectionProperties();
+ setConnectionProperties(applyHostProperties(properties, connectionProperties));
+ }
- @Override
- public void onVideoStateChanged(android.telecom.Connection c, int videoState) {
- Log.d(this, "onVideoStateChanged video state %d", videoState);
- setVideoState(c, videoState);
- }
+ @Override
+ public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
+ Log.v(this, "onStatusHintsChanged");
+ updateStatusHints();
+ }
- @Override
- public void onVideoProviderChanged(android.telecom.Connection c,
- Connection.VideoProvider videoProvider) {
- Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
- videoProvider);
- setVideoProvider(c, videoProvider);
- }
+ @Override
+ public void onExtrasChanged(Connection c, Bundle extras) {
+ Log.v(this, "onExtrasChanged: c=" + c + " Extras=" + extras);
+ putExtras(extras);
+ }
- @Override
- public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {
- Log.d(this, "onConnectionCapabilitiesChanged: Connection: %s," +
- " connectionCapabilities: %s", c, connectionCapabilities);
- int capabilites = ImsConference.this.getConnectionCapabilities();
- boolean isVideoConferencingSupported = mConferenceHost == null ? false :
- mConferenceHost.isCarrierVideoConferencingSupported();
- setConnectionCapabilities(applyHostCapabilities(capabilites, connectionCapabilities,
- isVideoConferencingSupported));
- }
+ @Override
+ public void onExtrasRemoved(Connection c, List<String> keys) {
+ Log.v(this, "onExtrasRemoved: c=" + c + " key=" + keys);
+ removeExtras(keys);
+ }
- @Override
- public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {
- Log.d(this, "onConnectionPropertiesChanged: Connection: %s," +
- " connectionProperties: %s", c, connectionProperties);
- int properties = ImsConference.this.getConnectionProperties();
- setConnectionProperties(applyHostProperties(properties, connectionProperties));
- }
+ @Override
+ public void onConnectionEvent(Connection c, String event, Bundle extras) {
+ if (Connection.EVENT_MERGE_START.equals(event)) {
+ // Do not pass a merge start event on the underlying host connection; only
+ // indicate a merge has started on the connections which are merged into a
+ // conference.
+ return;
+ }
- @Override
- public void onStatusHintsChanged(Connection c, StatusHints statusHints) {
- Log.v(this, "onStatusHintsChanged");
- updateStatusHints();
- }
+ sendConferenceEvent(event, extras);
+ }
- @Override
- public void onExtrasChanged(Connection c, Bundle extras) {
- Log.v(this, "onExtrasChanged: c=" + c + " Extras=" + extras);
- putExtras(extras);
- }
+ @Override
+ public void onOriginalConnectionConfigured(TelephonyConnection c) {
+ if (c == mConferenceHost) {
+ handleOriginalConnectionChange();
+ }
+ }
- @Override
- public void onExtrasRemoved(Connection c, List<String> keys) {
- Log.v(this, "onExtrasRemoved: c=" + c + " key=" + keys);
- removeExtras(keys);
- }
+ /**
+ * Handles changes to conference participant data as reported by the conference host
+ * connection.
+ *
+ * @param c The connection.
+ * @param participants The participant information.
+ */
+ @Override
+ public void onConferenceParticipantsChanged(android.telecom.Connection c,
+ List<ConferenceParticipant> participants) {
- @Override
- public void onConnectionEvent(Connection c, String event, Bundle extras) {
- sendConnectionEvent(event, extras);
- }
- };
+ if (c == null || participants == null) {
+ return;
+ }
+ Log.v(this, "onConferenceParticipantsChanged: %d participants",
+ participants.size());
+ TelephonyConnection telephonyConnection = (TelephonyConnection) c;
+ handleConferenceParticipantsUpdate(telephonyConnection, participants);
+ }
+ };
/**
* The telephony connection service; used to add new participant connections to Telecom.
@@ -345,15 +330,15 @@
conferenceCapabilities = changeBitmask(conferenceCapabilities,
Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
- can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+ (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0);
if (isVideoConferencingSupported) {
conferenceCapabilities = changeBitmask(conferenceCapabilities,
Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
- can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
+ (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0);
conferenceCapabilities = changeBitmask(conferenceCapabilities,
Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
- can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO));
+ (capabilities & Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO) != 0);
} else {
// If video conferencing is not supported, explicitly turn off the remote video
// capability and the ability to upgrade to video.
@@ -366,7 +351,7 @@
conferenceCapabilities = changeBitmask(conferenceCapabilities,
Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
- can(capabilities, Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO));
+ (capabilities & Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO) != 0);
conferenceCapabilities = changeBitmask(conferenceCapabilities,
Connection.CAPABILITY_CAN_PAUSE_VIDEO,
@@ -385,15 +370,15 @@
private int applyHostProperties(int conferenceProperties, int properties) {
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_HIGH_DEF_AUDIO,
- can(properties, Connection.PROPERTY_HIGH_DEF_AUDIO));
+ (properties & Connection.PROPERTY_HIGH_DEF_AUDIO) != 0);
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_WIFI,
- can(properties, Connection.PROPERTY_WIFI));
+ (properties & Connection.PROPERTY_WIFI) != 0);
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_IS_EXTERNAL_CALL,
- can(properties, Connection.PROPERTY_IS_EXTERNAL_CALL));
+ (properties & Connection.PROPERTY_IS_EXTERNAL_CALL) != 0);
conferenceProperties = changeBitmask(conferenceProperties,
Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
@@ -441,7 +426,7 @@
* Invoked when the Conference and all its {@link Connection}s should be disconnected.
* <p>
* Hangs up the call via the conference host connection. When the host connection has been
- * successfully disconnected, the {@link #mConferenceHostListener} listener receives an
+ * successfully disconnected, the {@link #mTelephonyConnectionListener} listener receives an
* {@code onDestroyed} event, which triggers the conference participant connections to be
* disconnected.
*/
@@ -619,7 +604,8 @@
* that the conference is represented appropriately on Bluetooth devices.
*/
private void updateManageConference() {
- boolean couldManageConference = can(Connection.CAPABILITY_MANAGE_CONFERENCE);
+ boolean couldManageConference =
+ (getConnectionCapabilities() & Connection.CAPABILITY_MANAGE_CONFERENCE) != 0;
boolean canManageConference = mFeatureFlagProxy.isUsingSinglePartyCallEmulation()
&& mIsEmulatingSinglePartyCall
? mConferenceParticipantConnections.size() > 1
@@ -696,7 +682,6 @@
setConnectionTime(mConferenceHost.getConnectTimeMillis());
}
- mConferenceHost.addConnectionListener(mConferenceHostListener);
mConferenceHost.addTelephonyConnectionListener(mTelephonyConnectionListener);
setConnectionCapabilities(applyHostCapabilities(getConnectionCapabilities(),
mConferenceHost.getConnectionCapabilities(),
@@ -744,6 +729,10 @@
// Determine if the conference event package represents a single party conference.
// A single party conference is one where there is no other participant other than the
// conference host and one other participant.
+ // We purposely exclude participants which have a disconnected state in the conference
+ // event package; some carriers are known to keep a disconnected participant around in
+ // subsequent CEP updates with a state of disconnected, even though its no longer part
+ // of the conference.
// Note: We consider 0 to still be a single party conference since some carriers will
// send a conference event package with JUST the host in it when the conference is
// disconnected. We don't want to change back to conference mode prior to disconnection
@@ -751,7 +740,8 @@
boolean isSinglePartyConference = participants.stream()
.filter(p -> {
Pair<Uri, Uri> pIdent = new Pair<>(p.getHandle(), p.getEndpoint());
- return !Objects.equals(mHostParticipantIdentity, pIdent);
+ return !Objects.equals(mHostParticipantIdentity, pIdent)
+ && p.getState() != Connection.STATE_DISCONNECTED;
})
.count() <= 1;
@@ -766,7 +756,14 @@
Pair<Uri, Uri> userEntity = new Pair<>(participant.getHandle(),
participant.getEndpoint());
- participantUserEntities.add(userEntity);
+ // We will exclude disconnected participants from the hash set of tracked
+ // participants. Some carriers are known to leave disconnected participants in
+ // the conference event package data which would cause them to be present in the
+ // conference even though they're disconnected. Removing them from the hash set
+ // here means we'll clean them up below.
+ if (participant.getState() != Connection.STATE_DISCONNECTED) {
+ participantUserEntities.add(userEntity);
+ }
if (!mConferenceParticipantConnections.containsKey(userEntity)) {
// Some carriers will also include the conference host in the CEP. We will
// filter that out here.
@@ -786,6 +783,13 @@
"handleConferenceParticipantsUpdate: updateState, participant = %s",
participant);
connection.updateState(participant.getState());
+ if (participant.getState() == Connection.STATE_DISCONNECTED) {
+ /**
+ * Per {@link ConferenceParticipantConnection#updateState(int)}, we will
+ * destroy the connection when its disconnected.
+ */
+ handleConnectionDestruction(connection);
+ }
connection.setVideoState(parent.getVideoState());
}
}
@@ -799,6 +803,13 @@
newParticipant.getHandle(),
newParticipant.getEndpoint()));
connection.updateState(newParticipant.getState());
+ /**
+ * Per {@link ConferenceParticipantConnection#updateState(int)}, we will
+ * destroy the connection when its disconnected.
+ */
+ if (newParticipant.getState() == Connection.STATE_DISCONNECTED) {
+ handleConnectionDestruction(connection);
+ }
connection.setVideoState(parent.getVideoState());
}
}
@@ -814,9 +825,8 @@
if (!participantUserEntities.contains(entry.getKey())) {
ConferenceParticipantConnection participant = entry.getValue();
participant.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
- participant.removeConnectionListener(mParticipantListener);
- mTelephonyConnectionService.removeConnection(participant);
- removeConnection(participant);
+ removeTelephonyConnection(participant);
+ participant.destroy();
entryIterator.remove();
oldParticipantsRemoved = true;
}
@@ -933,9 +943,8 @@
// again anyways.
entry.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED,
DisconnectCause.REASON_EMULATING_SINGLE_CALL));
- entry.removeConnectionListener(mParticipantListener);
- mTelephonyConnectionService.removeConnection(entry);
- removeConnection(entry);
+ removeTelephonyConnection(entry);
+ entry.destroy();
valueIterator.remove();
}
@@ -943,7 +952,8 @@
setConferenceState(false);
// Remove manage conference capability.
- mCouldManageConference = can(Connection.CAPABILITY_MANAGE_CONFERENCE);
+ mCouldManageConference =
+ (getConnectionCapabilities() & Connection.CAPABILITY_MANAGE_CONFERENCE) != 0;
int currentCapabilities = getConnectionCapabilities();
currentCapabilities &= ~Connection.CAPABILITY_MANAGE_CONFERENCE;
setConnectionCapabilities(currentCapabilities);
@@ -967,7 +977,6 @@
ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
parent.getOriginalConnection(), participant,
!isConferenceHost() /* isRemotelyHosted */);
- connection.addConnectionListener(mParticipantListener);
if (participant.getConnectTime() == 0) {
connection.setConnectTimeMillis(parent.getConnectTimeMillis());
connection.setConnectionStartElapsedRealTime(parent.getConnectElapsedTimeMillis());
@@ -989,7 +998,7 @@
mTelephonyConnectionService.addExistingConnection(mConferenceHostPhoneAccountHandle,
connection, this);
- addConnection(connection);
+ addTelephonyConnection(connection);
}
/**
@@ -1000,12 +1009,11 @@
private void removeConferenceParticipant(ConferenceParticipantConnection participant) {
Log.i(this, "removeConferenceParticipant: %s", participant);
- participant.removeConnectionListener(mParticipantListener);
synchronized(mUpdateSyncRoot) {
mConferenceParticipantConnections.remove(new Pair<>(participant.getUserEntity(),
participant.getEndpoint()));
}
- mTelephonyConnectionService.removeConnection(participant);
+ participant.destroy();
}
/**
@@ -1018,14 +1026,13 @@
for (ConferenceParticipantConnection connection :
mConferenceParticipantConnections.values()) {
- connection.removeConnectionListener(mParticipantListener);
// Mark disconnect cause as cancelled to ensure that the call is not logged in the
// call log.
connection.setDisconnected(new DisconnectCause(DisconnectCause.CANCELED));
- mTelephonyConnectionService.removeConnection(connection);
connection.destroy();
}
mConferenceParticipantConnections.clear();
+ updateManageConference();
}
}
@@ -1082,7 +1089,7 @@
boolean isHost = PhoneNumberUtils.compare(hostNumber, number);
Log.v(this, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
- Log.pii(hostNumber), Log.pii(number));
+ Rlog.pii(LOG_TAG, hostNumber), Rlog.pii(LOG_TAG, number));
if (isHost) {
return true;
@@ -1136,7 +1143,7 @@
mConferenceHost.isOutgoingCall());
// This is a newly created conference connection as a result of SRVCC
c.setConferenceSupported(true);
- c.setConnectionProperties(
+ c.setTelephonyConnectionProperties(
c.getConnectionProperties() | Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
c.updateState();
// Copy the connect time from the conferenceHost
@@ -1145,12 +1152,11 @@
mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c);
mTelephonyConnectionService.addConnectionToConferenceController(c);
} // CDMA case not applicable for SRVCC
- mConferenceHost.removeConnectionListener(mConferenceHostListener);
mConferenceHost.removeTelephonyConnectionListener(mTelephonyConnectionListener);
mConferenceHost = null;
setDisconnected(new DisconnectCause(DisconnectCause.OTHER));
disconnectConferenceParticipants();
- destroy();
+ destroyTelephonyConference();
}
updateStatusHints();
@@ -1189,7 +1195,7 @@
}
setDisconnected(disconnectCause);
disconnectConferenceParticipants();
- destroy();
+ destroyTelephonyConference();
break;
case Connection.STATE_ACTIVE:
setActive();
@@ -1206,8 +1212,8 @@
*/
private boolean isVideoCapable() {
int capabilities = mConferenceHost.getConnectionCapabilities();
- return can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
- && can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
+ return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0
+ && (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
}
private void updateStatusHints() {
@@ -1320,4 +1326,15 @@
public boolean isEmulatingSinglePartyCall() {
return mIsEmulatingSinglePartyCall;
}
+
+ /**
+ * Handles destruction of a {@link ConferenceParticipantConnection}.
+ * We remove the participant from the list of tracked participants in the conference and
+ * update whether the conference can be managed.
+ * @param participant the conference participant.
+ */
+ private void handleConnectionDestruction(ConferenceParticipantConnection participant) {
+ removeConferenceParticipant(participant);
+ updateManageConference();
+ }
}
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 9902700..1e10055 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -22,6 +22,7 @@
import android.telecom.ConnectionService;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
+import android.telephony.Rlog;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
@@ -38,11 +39,13 @@
* Manages conferences for IMS connections.
*/
public class ImsConferenceController {
+ private static final String LOG_TAG = "ImsConferenceController";
/**
* Conference listener; used to receive notification when a conference has been disconnected.
*/
- private final Conference.Listener mConferenceListener = new Conference.Listener() {
+ private final TelephonyConferenceBase.TelephonyConferenceListener mConferenceListener =
+ new TelephonyConferenceBase.TelephonyConferenceListener() {
@Override
public void onDestroyed(Conference conference) {
if (Log.VERBOSE) {
@@ -53,28 +56,8 @@
}
};
- /**
- * Ims conference controller connection listener. Used to respond to changes in state of the
- * Telephony connections the controller is aware of.
- */
- private final Connection.Listener mConnectionListener = new Connection.Listener() {
- @Override
- public void onStateChanged(Connection c, int state) {
- Log.v(this, "onStateChanged: %s", Log.pii(c.getAddress()));
- recalculate();
- }
-
- @Override
- public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
- Log.v(this, "onDisconnected: %s", Log.pii(c.getAddress()));
- recalculate();
- }
-
- @Override
- public void onDestroyed(Connection connection) {
- remove(connection);
- }
-
+ private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
@Override
public void onConferenceStarted() {
Log.v(this, "onConferenceStarted");
@@ -86,6 +69,23 @@
Log.v(this, "onConferenceSupportedChanged");
recalculate();
}
+
+ @Override
+ public void onStateChanged(Connection c, int state) {
+ Log.v(this, "onStateChanged: %s", Rlog.pii(LOG_TAG, c.getAddress()));
+ recalculate();
+ }
+
+ @Override
+ public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
+ Log.v(this, "onDisconnected: %s", Rlog.pii(LOG_TAG, c.getAddress()));
+ recalculate();
+ }
+
+ @Override
+ public void onDestroyed(Connection connection) {
+ remove(connection);
+ }
};
/**
@@ -148,8 +148,9 @@
}
mTelephonyConnections.add(connection);
- connection.addConnectionListener(mConnectionListener);
+ connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
recalculateConference();
+ recalculateConferenceable();
}
/**
@@ -175,7 +176,10 @@
Log.v(this, "remove connection: %s", connection);
}
- connection.removeConnectionListener(mConnectionListener);
+ if (connection instanceof TelephonyConnection) {
+ TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+ telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+ }
mTelephonyConnections.remove(connection);
recalculateConferenceable();
}
@@ -379,7 +383,7 @@
ImsConference conference = new ImsConference(mTelecomAccountRegistry, mConnectionService,
conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy);
conference.setState(conferenceHostConnection.getState());
- conference.addListener(mConferenceListener);
+ conference.addTelephonyConferenceListener(mConferenceListener);
conference.updateConferenceParticipantsAfterCreation();
mConnectionService.addConference(conference);
conferenceHostConnection.setTelecomCallId(conference.getTelecomCallId());
@@ -387,12 +391,11 @@
// Cleanup TelephonyConnection which backed the original connection and remove from telecom.
// Use the "Other" disconnect cause to ensure the call is logged to the call log but the
// disconnect tone is not played.
- connection.removeConnectionListener(mConnectionListener);
- connection.clearOriginalConnection();
- connection.setDisconnected(new DisconnectCause(DisconnectCause.OTHER,
+ connection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+ connection.setTelephonyConnectionDisconnected(new DisconnectCause(DisconnectCause.OTHER,
android.telephony.DisconnectCause.toString(
android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY)));
- connection.destroy();
+ connection.close();
mImsConferences.add(conference);
// If one of the participants failed to join the conference, recalculate will set the
// conferenceable connections for the conference to show merge calls option.
diff --git a/src/com/android/services/telephony/Log.java b/src/com/android/services/telephony/Log.java
index 9941a59..57bf849 100644
--- a/src/com/android/services/telephony/Log.java
+++ b/src/com/android/services/telephony/Log.java
@@ -16,8 +16,6 @@
package com.android.services.telephony;
-import android.content.Context;
-
/**
* Manages logging for the entire module.
*/
@@ -39,72 +37,80 @@
return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
}
- public static void initLogging(Context context) {
- // Register Telephony with the Telecom Logger.
- android.telecom.Log.setTag(TAG);
- android.telecom.Log.setSessionContext(context);
- }
-
// Relay log messages to Telecom
// TODO: Redo namespace of Telephony to use these methods directly.
public static void d(String prefix, String format, Object... args) {
- android.telecom.Log.d(prefix, format, args);
+ android.util.Log.d(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args));
}
public static void d(Object objectPrefix, String format, Object... args) {
- android.telecom.Log.d(objectPrefix, format, args);
+ android.util.Log.d(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
}
public static void i(String prefix, String format, Object... args) {
- android.telecom.Log.i(prefix, format, args);
+ android.util.Log.i(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args));
}
public static void i(Object objectPrefix, String format, Object... args) {
- android.telecom.Log.i(objectPrefix, format, args);
+ android.util.Log.i(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
}
public static void v(String prefix, String format, Object... args) {
- android.telecom.Log.v(prefix, format, args);
+ android.util.Log.v(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args));
}
public static void v(Object objectPrefix, String format, Object... args) {
- android.telecom.Log.v(objectPrefix, format, args);
+ android.util.Log.v(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
}
public static void w(String prefix, String format, Object... args) {
- android.telecom.Log.w(prefix, format, args);
+ android.util.Log.w(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args));
}
public static void w(Object objectPrefix, String format, Object... args) {
- android.telecom.Log.w(objectPrefix, format, args);
+ android.util.Log.w(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
}
public static void e(String prefix, Throwable tr, String format, Object... args) {
- android.telecom.Log.e(prefix, tr, format, args);
+ android.util.Log.e(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args), tr);
}
public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
- android.telecom.Log.e(objectPrefix, tr, format, args);
+ android.util.Log.e(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args), tr);
}
public static void wtf(String prefix, Throwable tr, String format, Object... args) {
- android.telecom.Log.wtf(prefix, tr, format, args);
+ android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args), tr);
}
public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
- android.telecom.Log.wtf(objectPrefix, tr, format, args);
+ android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args), tr);
}
public static void wtf(String prefix, String format, Object... args) {
- android.telecom.Log.wtf(prefix, format, args);
+ android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+ String.format(prefix + ": " + format, args));
}
public static void wtf(Object objectPrefix, String format, Object... args) {
- android.telecom.Log.wtf(objectPrefix, format, args);
+ android.util.Log.wtf(TAG, (args == null || args.length == 0) ? format :
+ String.format(getPrefixFromObject(objectPrefix) + ": " + format, args));
}
- public static String pii(Object pii) {
- return android.telecom.Log.pii(pii);
+ private static String getPrefixFromObject(Object obj) {
+ return obj == null ? "<null>" : obj.getClass().getSimpleName();
}
}
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 223616f..2dfeaed 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -25,6 +25,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.Rlog;
import android.text.TextUtils;
import com.android.internal.telephony.Call;
@@ -40,8 +41,6 @@
import com.android.phone.NumberVerificationManager;
import com.android.phone.PhoneUtils;
-import com.google.common.base.Preconditions;
-
import java.util.Objects;
/**
@@ -49,11 +48,21 @@
* occurence. One instance of these exists for each of the telephony-based call services.
*/
final class PstnIncomingCallNotifier {
+ private static final String LOG_TAG = "PstnIncomingCallNotifier";
+
/** New ringing connection event code. */
private static final int EVENT_NEW_RINGING_CONNECTION = 100;
private static final int EVENT_CDMA_CALL_WAITING = 101;
private static final int EVENT_UNKNOWN_CONNECTION = 102;
+ /**
+ * The max amount of time to wait before hanging up a call that was for number verification.
+ *
+ * The delay is so that the remote end has time to hang up the call after receiving the
+ * verification signal so that the call doesn't go to voicemail.
+ */
+ private static final int MAX_NUMBER_VERIFICATION_HANGUP_DELAY_MILLIS = 10000;
+
/** The phone object to listen to. */
private final Phone mPhone;
@@ -85,7 +94,9 @@
* @param phone The phone object for listening to incoming calls.
*/
PstnIncomingCallNotifier(Phone phone) {
- Preconditions.checkNotNull(phone);
+ if (phone == null) {
+ throw new NullPointerException();
+ }
mPhone = phone;
@@ -131,12 +142,16 @@
if (connection.getAddress() != null) {
if (NumberVerificationManager.getInstance()
.checkIncomingCall(connection.getAddress())) {
- // Disconnect the call if it matches
- try {
- connection.hangup();
- } catch (CallStateException e) {
- Log.e(this, e, "Error hanging up potential number verification call");
- }
+ // Disconnect the call if it matches, after a delay
+ mHandler.postDelayed(() -> {
+ try {
+ connection.hangup();
+ } catch (CallStateException e) {
+ Log.i(this, "Remote end hung up call verification call");
+ }
+ // TODO: use an app-supplied delay (needs new API), not to exceed the
+ // existing max.
+ }, MAX_NUMBER_VERIFICATION_HANGUP_DELAY_MILLIS);
return;
}
}
@@ -169,7 +184,7 @@
// Presentation of the number is allowed, so we ensure the number matches the
// one in the call waiting information.
Log.i(this, "handleCdmaCallWaiting: inform telecom of waiting call; "
- + "number = %s", Log.pii(number));
+ + "number = %s", Rlog.pii(LOG_TAG, number));
sendIncomingCallIntent(connection);
} else {
Log.w(this, "handleCdmaCallWaiting: presentation or number do not match, not"
@@ -236,7 +251,8 @@
// connection already disconnected. Do nothing
}
} else {
- TelecomManager.from(mPhone.getContext()).addNewUnknownCall(handle, extras);
+ TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
+ tm.addNewUnknownCall(handle, extras);
}
} else {
Log.i(this, "swapped an old connection, new one is: %s", connection);
@@ -271,7 +287,8 @@
// connection already disconnected. Do nothing
}
} else {
- TelecomManager.from(mPhone.getContext()).addNewIncomingCall(handle, extras);
+ TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
+ tm.addNewIncomingCall(handle, extras);
}
}
diff --git a/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java b/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
index b9cbf6e..4038dd1 100644
--- a/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
+++ b/src/com/android/services/telephony/PstnPhoneCapabilitiesNotifier.java
@@ -21,7 +21,6 @@
import android.os.Message;
import com.android.internal.telephony.Phone;
-import com.android.internal.util.Preconditions;
/**
* Listens to phone's capabilities changed event and notifies Telecomm. One instance of these exists
@@ -55,7 +54,9 @@
/*package*/
PstnPhoneCapabilitiesNotifier(Phone phone, Listener listener) {
- Preconditions.checkNotNull(phone);
+ if (phone == null) {
+ throw new NullPointerException();
+ }
mPhone = phone;
mListener = listener;
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 85f94ab..981dc96 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -49,13 +49,19 @@
}
private void setupListeners() {
- if (mListeners != null) {
- return;
+ if (mListeners == null) {
+ mListeners = new ArrayList<>(2);
}
- mListeners = new ArrayList<>(2);
- for (int i = 0; i < TelephonyManager.getDefault().getMaxPhoneCount(); i++) {
+ int activeModems = TelephonyManager.from(mContext).getActiveModemCount();
+ // Add new listeners if active modem count increased.
+ while (mListeners.size() < activeModems) {
mListeners.add(new RadioOnStateListener());
}
+ // Clean up listeners if active modem count decreased.
+ while (mListeners.size() > activeModems) {
+ mListeners.get(mListeners.size() - 1).cleanup();
+ mListeners.remove(mListeners.size() - 1);
+ }
}
/**
* Starts the "turn on radio" sequence. This is the (single) external API of the
@@ -76,7 +82,7 @@
mCallback = callback;
mInProgressListeners.clear();
mIsRadioOnCallingEnabled = false;
- for (int i = 0; i < TelephonyManager.getDefault().getMaxPhoneCount(); i++) {
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
Phone phone = PhoneFactory.getPhone(i);
if (phone == null) {
continue;
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 729f6a9..52bd9cf 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -231,7 +231,7 @@
* Note we don't call this method simply after a successful call to placeCall(), since it's
* still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
*/
- private void cleanup() {
+ public void cleanup() {
Log.d(this, "cleanup()");
// This will send a failure call back if callback has yet to be invoked. If the callback
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 6bbcdc4..5a1e6a6 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -35,13 +35,13 @@
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
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;
@@ -72,6 +72,7 @@
*/
public class TelecomAccountRegistry {
private static final boolean DBG = false; /* STOP SHIP if true */
+ private static final String LOG_TAG = "TelecomAccountRegistry";
// This icon is the one that is used when the Slot ID that we have for a particular SIM
// is not supported, i.e. SubscriptionManager.INVALID_SLOT_ID or the 5th SIM in a phone.
@@ -286,7 +287,7 @@
mIsRttCapable = false;
}
- mIsVideoCapable = mPhone.isVideoEnabled() && !mIsRttCapable;
+ mIsVideoCapable = mPhone.isVideoEnabled();
boolean isVideoEnabledByPlatform = ImsManager.getInstance(mPhone.getContext(),
mPhone.getPhoneId()).isVtEnabledByPlatform();
@@ -390,7 +391,7 @@
}
if(isMergedSim) {
groupId = GROUP_PREFIX + line1Number;
- Log.i(this, "Adding Merged Account with group: " + Log.pii(groupId));
+ Log.i(this, "Adding Merged Account with group: " + Rlog.pii(LOG_TAG, groupId));
}
PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
@@ -803,9 +804,8 @@
Log.i(this, "User changed, re-registering phone accounts.");
int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- UserHandle currentUserHandle = new UserHandle(userHandleId);
- mIsPrimaryUser = UserManager.get(mContext).getPrimaryUser().getUserHandle()
- .equals(currentUserHandle);
+ UserHandle currentUserHandle = UserHandle.of(userHandleId);
+ mIsPrimaryUser = currentUserHandle.isSystem();
// Any time the user changes, re-register the accounts.
tearDownAccounts();
@@ -820,6 +820,15 @@
}
};
+ private BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(this, "Locale change; re-registering phone accounts.");
+ tearDownAccounts();
+ setupAccounts();
+ }
+ };
+
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
@@ -865,7 +874,7 @@
TelecomAccountRegistry(Context context) {
mContext = context;
- mTelecomManager = TelecomManager.from(context);
+ mTelecomManager = context.getSystemService(TelecomManager.class);
mTelephonyManager = TelephonyManager.from(context);
mSubscriptionManager = SubscriptionManager.from(context);
}
@@ -1083,6 +1092,11 @@
filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
mContext.registerReceiver(mReceiver, filter);
+ //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));
+
// Listen to the RTT system setting so that we update it when the user flips it.
ContentObserver rttUiSettingObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@@ -1132,7 +1146,7 @@
R.bool.config_emergency_account_emergency_calls_only);
List<PhoneAccountHandle> accountHandles = emergencyCallsOnlyEmergencyAccount
? mTelecomManager.getAllPhoneAccountHandles()
- : mTelecomManager.getCallCapablePhoneAccounts(true /* includeDisabled */);
+ : mTelecomManager.getCallCapablePhoneAccounts();
for (PhoneAccountHandle handle : accountHandles) {
if (telephonyComponentName.equals(handle.getComponentName()) &&
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index c66d6f2..afbe89d 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -16,7 +16,6 @@
package com.android.services.telephony;
-import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.PhoneAccountHandle;
@@ -30,7 +29,7 @@
* TelephonyConnection-based conference call for GSM conferences and IMS conferences (which may
* be either GSM-based or CDMA-based).
*/
-public class TelephonyConference extends Conference implements Holdable {
+public class TelephonyConference extends TelephonyConferenceBase implements Holdable {
private boolean mIsHoldable;
diff --git a/src/com/android/services/telephony/TelephonyConferenceBase.java b/src/com/android/services/telephony/TelephonyConferenceBase.java
new file mode 100644
index 0000000..5e7ecf6
--- /dev/null
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -0,0 +1,191 @@
+/*
+ * 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.services.telephony;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.telecom.Conference;
+import android.telecom.Connection;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.ServiceState;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Base class for the various Telephony {@link Conference} implementations ({@link CdmaConference},
+ * {@link TelephonyConference}, and {@link ImsConference}). Adds some common listener code which
+ * all of these conferences use.
+ */
+public class TelephonyConferenceBase extends Conference {
+ private static final String TAG = "TelephonyConferenceBase";
+
+ /**
+ * Listener for conference events.
+ */
+ public abstract static class TelephonyConferenceListener {
+ /**
+ * Listener called when a connection is added or removed from a conference.
+ * @param connection The connection.
+ */
+ public void onConferenceMembershipChanged(Connection connection) {}
+
+ /**
+ * Listener called when a conference is destroyed.
+ * @param conference The conference.
+ */
+ public void onDestroyed(Conference conference) {}
+ }
+
+ private final Set<TelephonyConferenceListener> mListeners = Collections.newSetFromMap(
+ new ConcurrentHashMap<>(8, 0.9f, 1));
+
+ /**
+ * Adds a listener to this conference.
+ * @param listener The listener.
+ */
+ public void addTelephonyConferenceListener(@NonNull TelephonyConferenceListener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from this conference.
+ * @param listener The listener.
+ */
+ public void removeTelephonyConferenceListener(@NonNull TelephonyConferenceListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Constructs a new Conference with a mandatory {@link PhoneAccountHandle}
+ *
+ * @param phoneAccount The {@code PhoneAccountHandle} associated with the conference.
+ */
+ public TelephonyConferenceBase(PhoneAccountHandle phoneAccount) {
+ super(phoneAccount);
+ }
+
+ /**
+ * Adds a connection to this {@link Conference}.
+ * <p>
+ * Should be used in place of {@link Conference#addConnection(Connection)} to ensure
+ * {@link TelephonyConferenceListener}s are informed of the change.
+ *
+ * @param connection The connection.
+ */
+ public void addTelephonyConnection(@NonNull Connection connection) {
+ addConnection(connection);
+ notifyConferenceMembershipChanged(connection);
+ }
+
+ /**
+ * Removes a {@link Connection} from this {@link Conference}.
+ * <p>
+ * Should be used instead of {@link Conference#removeConnection(Connection)} to ensure
+ * {@link TelephonyConferenceListener}s are notified of the change.
+ *
+ * @param connection The connection.
+ */
+ public void removeTelephonyConnection(@NonNull Connection connection) {
+ removeConnection(connection);
+ notifyConferenceMembershipChanged(connection);
+ }
+
+ /**
+ * Destroys the current {@link Conference} and notifies {@link TelephonyConferenceListener}s of
+ * the change to conference membership.
+ * <p>
+ * Should be used instead of {@link Conference#destroy()} to ensure telephony listeners are
+ * notified.
+ */
+ public void destroyTelephonyConference() {
+ // Conference#removeConnection modifies the list of participants, so we need to use an
+ // iterator here to ensure all participants are removed.
+ // Technically Conference#destroy does this, but we want to notify listeners of the state
+ // change so we'll do it here first.
+ Iterator<Connection> connectionIterator = getConnections().iterator();
+ while (connectionIterator.hasNext()) {
+ removeTelephonyConnection(connectionIterator.next());
+ }
+ destroy();
+ notifyDestroyed();
+ }
+
+ /**
+ * Updates RIL voice radio technology used for current conference after its creation.
+ */
+ public void updateCallRadioTechAfterCreation() {
+ final Connection primaryConnection = getPrimaryConnection();
+ if (primaryConnection != null && primaryConnection instanceof TelephonyConnection) {
+ TelephonyConnection telephonyConnection = (TelephonyConnection) primaryConnection;
+ Bundle newExtras = new Bundle();
+ newExtras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToNetworkType(
+ telephonyConnection.getCallRadioTech()));
+ putExtras(newExtras);
+ } else {
+ Log.w(TAG, "No primary connection found while updateCallRadioTechAfterCreation");
+ }
+ }
+
+ /**
+ * Removes the specified capability from the set of capabilities of this {@code Conference}.
+ *
+ * @param capability The capability to remove from the set.
+ */
+ public void removeCapability(int capability) {
+ int newCapabilities = getConnectionCapabilities();
+ newCapabilities &= ~capability;
+
+ setConnectionCapabilities(newCapabilities);
+ }
+
+ /**
+ * Adds the specified capability to the set of capabilities of this {@code Conference}.
+ *
+ * @param capability The capability to add to the set.
+ */
+ public void addCapability(int capability) {
+ int newCapabilities = getConnectionCapabilities();
+ newCapabilities |= capability;
+
+ setConnectionCapabilities(newCapabilities);
+ }
+
+ /**
+ * Notifies {@link TelephonyConferenceListener}s of a connection being added or removed from
+ * the conference.
+ * @param connection The conference.
+ */
+ private void notifyConferenceMembershipChanged(@NonNull Connection connection) {
+ for (TelephonyConferenceListener listener : mListeners) {
+ listener.onConferenceMembershipChanged(connection);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConferenceListener}s of a conference being destroyed
+ */
+ private void notifyDestroyed() {
+ for (TelephonyConferenceListener listener : mListeners) {
+ listener.onDestroyed(this);
+ }
+ }
+}
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index e9eef46..95281b3 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -41,25 +41,26 @@
final class TelephonyConferenceController {
private static final int TELEPHONY_CONFERENCE_MAX_SIZE = 5;
- private final Connection.Listener mConnectionListener = new Connection.Listener() {
- @Override
- public void onStateChanged(Connection c, int state) {
- Log.v(this, "onStateChange triggered in Conf Controller : connection = "+ c
- + " state = " + state);
- recalculate();
- }
+ private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
+ new TelephonyConnection.TelephonyConnectionListener() {
+ @Override
+ public void onStateChanged(Connection c, int state) {
+ Log.v(this, "onStateChange triggered in Conf Controller : connection = " + c
+ + " state = " + state);
+ recalculate();
+ }
- /** ${inheritDoc} */
- @Override
- public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
- recalculate();
- }
+ @Override
+ public void onDisconnected(Connection c, DisconnectCause disconnectCause) {
+ recalculate();
+ }
- @Override
- public void onDestroyed(Connection connection) {
- remove(connection);
- }
- };
+ @Override
+ public void onDestroyed(Connection connection) {
+ // Only TelephonyConnections are added.
+ remove((TelephonyConnection) connection);
+ }
+ };
/** The known connections. */
private final List<TelephonyConnection> mTelephonyConnections = new ArrayList<>();
@@ -85,18 +86,18 @@
return;
}
mTelephonyConnections.add(connection);
- connection.addConnectionListener(mConnectionListener);
+ connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
recalculate();
}
- void remove(Connection connection) {
+ void remove(TelephonyConnection connection) {
if (!mTelephonyConnections.contains(connection)) {
// Debug only since TelephonyConnectionService tries to clean up the connections tracked
// when the original connection changes. It does this proactively.
Log.d(this, "remove - connection not tracked; connection=%s", connection);
return;
}
- connection.removeConnectionListener(mConnectionListener);
+ connection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
mTelephonyConnections.remove(connection);
recalculate();
}
@@ -219,7 +220,7 @@
// No more connections are conferenced, destroy any existing conference.
if (mTelephonyConference != null) {
Log.d(this, "with a conference to destroy!");
- mTelephonyConference.destroy();
+ mTelephonyConference.destroyTelephonyConference();
mTelephonyConference = null;
}
} else {
@@ -229,7 +230,7 @@
for (Connection connection : existingConnections) {
if (connection instanceof TelephonyConnection &&
!conferencedConnections.contains(connection)) {
- mTelephonyConference.removeConnection(connection);
+ mTelephonyConference.removeTelephonyConnection(connection);
}
}
if (allConnInService) {
@@ -237,7 +238,7 @@
// Add any new ones
for (Connection connection : conferencedConnections) {
if (!existingConnections.contains(connection)) {
- mTelephonyConference.addConnection(connection);
+ mTelephonyConference.addTelephonyConnection(connection);
}
}
} else {
@@ -262,7 +263,7 @@
for (Connection connection : conferencedConnections) {
Log.d(this, "Adding a connection to a conference call: %s %s",
mTelephonyConference, connection);
- mTelephonyConference.addConnection(connection);
+ mTelephonyConference.addTelephonyConnection(connection);
}
mTelephonyConference.updateCallRadioTechAfterCreation();
mConnectionService.addConference(mTelephonyConference);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 84fa40c..fb865b4 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -16,7 +16,10 @@
package com.android.services.telephony;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncResult;
@@ -26,23 +29,30 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.telecom.CallAudioState;
-import android.telecom.ConferenceParticipant;
+import android.telecom.Conference;
import android.telecom.Connection;
+import android.telecom.ConnectionService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
+import android.telephony.Annotation.RilRadioTechnology;
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.text.TextUtils;
import android.util.Pair;
import com.android.ims.ImsCall;
+import com.android.ims.internal.ConferenceParticipant;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallFailCause;
@@ -74,6 +84,8 @@
* Base class for CDMA and GSM connections.
*/
abstract class TelephonyConnection extends Connection implements Holdable {
+ private static final String LOG_TAG = "TelephonyConnection";
+
private static final int MSG_PRECISE_CALL_STATE_CHANGED = 1;
private static final int MSG_RINGBACK_TONE = 2;
private static final int MSG_HANDOVER_STATE_CHANGED = 3;
@@ -100,6 +112,7 @@
private static final int MSG_HANGUP = 17;
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 final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -110,15 +123,25 @@
updateState();
break;
case MSG_HANDOVER_STATE_CHANGED:
- Log.v(TelephonyConnection.this, "MSG_HANDOVER_STATE_CHANGED");
+ case MSG_REDIAL_CONNECTION_CHANGED:
+ String what = (msg.what == MSG_HANDOVER_STATE_CHANGED)
+ ? "MSG_HANDOVER_STATE_CHANGED" : "MSG_REDIAL_CONNECTION_CHANGED";
+ Log.v(TelephonyConnection.this, what);
AsyncResult ar = (AsyncResult) msg.obj;
com.android.internal.telephony.Connection connection =
(com.android.internal.telephony.Connection) ar.result;
+ if (connection == null) {
+ setDisconnected(DisconnectCauseUtil
+ .toTelecomDisconnectCause(DisconnectCause.OUT_OF_NETWORK,
+ "handover failure, no connection"));
+ close();
+ break;
+ }
if (mOriginalConnection != null) {
if (connection != null &&
((connection.getAddress() != null &&
mOriginalConnection.getAddress() != null &&
- mOriginalConnection.getAddress().contains(connection.getAddress())) ||
+ mOriginalConnection.getAddress().equals(connection.getAddress())) ||
connection.getState() == mOriginalConnection.getStateBeforeHandover())) {
Log.d(TelephonyConnection.this,
"SettingOriginalConnection " + mOriginalConnection.toString()
@@ -128,7 +151,7 @@
}
} else {
Log.w(TelephonyConnection.this,
- "MSG_HANDOVER_STATE_CHANGED: mOriginalConnection==null - invalid state (not cleaned up)");
+ what + ": mOriginalConnection==null - invalid state (not cleaned up)");
}
break;
case MSG_RINGBACK_TONE:
@@ -173,19 +196,20 @@
case MSG_SET_VIDEO_STATE:
int videoState = (int) msg.obj;
- setVideoState(videoState);
+ setTelephonyVideoState(videoState);
// A change to the video state of the call can influence whether or not it
// can be part of a conference, whether another call can be added, and
// whether the call should have the HD audio property set.
refreshConferenceSupported();
refreshDisableAddCall();
+ refreshHoldSupported();
updateConnectionProperties();
break;
case MSG_SET_VIDEO_PROVIDER:
VideoProvider videoProvider = (VideoProvider) msg.obj;
- setVideoProvider(videoProvider);
+ setTelephonyVideoProvider(videoProvider);
break;
case MSG_SET_AUDIO_QUALITY:
@@ -225,9 +249,9 @@
// If starting the hold tone, send a connection event to Telecom which will
// cause it to play the on hold tone.
if (playTone) {
- sendConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
+ sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_START, null);
} else {
- sendConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
+ sendTelephonyConnectionEvent(EVENT_ON_HOLD_TONE_END, null);
}
}
break;
@@ -272,7 +296,7 @@
case MSG_ON_CONNECTION_EVENT:
SomeArgs args = (SomeArgs) msg.obj;
try {
- sendConnectionEvent((String) args.arg1, (Bundle) args.arg2);
+ sendTelephonyConnectionEvent((String) args.arg1, (Bundle) args.arg2);
} finally {
args.recycle();
@@ -291,7 +315,7 @@
ssn.code);
if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
&& ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
- sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
+ sendTelephonyConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
}
sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
}
@@ -309,7 +333,8 @@
extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
getSuppServiceMessage(type, code));
- sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
+ sendTelephonyConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION,
+ extras);
}
/**
@@ -385,12 +410,26 @@
}
}
if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
- return getPhone().getContext().getText(messageId);
+ return getResourceText(messageId);
} else {
return null;
}
}
+ @VisibleForTesting
+ public CharSequence getResourceText(int id) {
+ Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(),
+ getPhone().getSubId());
+ return resources.getText(id);
+ }
+
+ @VisibleForTesting
+ public String getResourceString(int id) {
+ Resources resources = SubscriptionManager.getResourcesForSubId(getPhone().getContext(),
+ getPhone().getSubId());
+ return resources.getString(id);
+ }
+
/**
* @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
*/
@@ -406,6 +445,24 @@
public abstract static class TelephonyConnectionListener {
public void onOriginalConnectionConfigured(TelephonyConnection c) {}
public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {}
+ public void onConferenceParticipantsChanged(Connection c,
+ List<ConferenceParticipant> participants) {}
+ public void onConferenceStarted() {}
+ public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+
+ public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {}
+ public void onConnectionEvent(Connection c, String event, Bundle extras) {}
+ public void onConnectionPropertiesChanged(Connection c, int connectionProperties) {}
+ public void onExtrasChanged(Connection c, Bundle extras) {}
+ public void onExtrasRemoved(Connection c, List<String> keys) {}
+ public void onStateChanged(android.telecom.Connection c, int state) {}
+ public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
+ public void onDestroyed(Connection c) {}
+ public void onDisconnected(android.telecom.Connection c,
+ android.telecom.DisconnectCause disconnectCause) {}
+ public void onVideoProviderChanged(android.telecom.Connection c,
+ Connection.VideoProvider videoProvider) {}
+ public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
}
private final PostDialListener mPostDialListener = new PostDialListener() {
@@ -465,7 +522,7 @@
* @param vrat the RIL Voice Radio Technology used for current connection.
*/
@Override
- public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) {
+ public void onCallRadioTechChanged(@RilRadioTechnology int vrat) {
mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
}
@@ -539,7 +596,7 @@
// Inform the InCallService of the fact that the call pull failed (it may choose to
// display a message informing the user of the pull failure).
- sendConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
+ sendTelephonyConnectionEvent(Connection.EVENT_CALL_PULL_FAILED, null);
// Swap the ImsPhoneConnection we used to do the pull for the ImsExternalConnection
// which originally represented the call.
@@ -554,7 +611,7 @@
*/
@Override
public void onHandoverToWifiFailed() {
- sendConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
+ sendTelephonyConnectionEvent(TelephonyManager.EVENT_HANDOVER_TO_WIFI_FAILED, null);
}
/**
@@ -608,6 +665,7 @@
@Override
public void onRttTerminated() {
updateConnectionProperties();
+ refreshConferenceSupported();
sendRttSessionRemotelyTerminated();
}
@@ -623,6 +681,7 @@
}
};
+ private TelephonyConnectionService mTelephonyConnectionService;
protected com.android.internal.telephony.Connection mOriginalConnection;
private Call.State mConnectionState = Call.State.IDLE;
private Bundle mOriginalConnectionExtras = new Bundle();
@@ -724,6 +783,11 @@
private boolean mShowPreciseFailedCause;
/**
+ * Provides a DisconnectCause associated with a hang up request.
+ */
+ private int mHangupDisconnectCause = DisconnectCause.NOT_VALID;
+
+ /**
* Listeners to our TelephonyConnection specific callbacks
*/
private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
@@ -1068,6 +1132,7 @@
if (getConnectionCapabilities() != newCapabilities) {
setConnectionCapabilities(newCapabilities);
+ notifyConnectionCapabilitiesChanged(newCapabilities);
}
}
@@ -1104,10 +1169,15 @@
isNetworkIdentifiedEmergencyCall());
if (getConnectionProperties() != newProperties) {
- setConnectionProperties(newProperties);
+ setTelephonyConnectionProperties(newProperties);
}
}
+ public void setTelephonyConnectionProperties(int newProperties) {
+ setConnectionProperties(newProperties);
+ notifyConnectionPropertiesChanged(newProperties);
+ }
+
protected final void updateAddress() {
updateConnectionCapabilities();
updateConnectionProperties();
@@ -1171,6 +1241,8 @@
mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
getPhone().registerForHandoverStateChanged(
mHandler, MSG_HANDOVER_STATE_CHANGED, null);
+ getPhone().registerForRedialConnectionChanged(
+ mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
@@ -1180,11 +1252,11 @@
mOriginalConnection.addListener(mOriginalConnectionListener);
// Set video state and capabilities
- setVideoState(mOriginalConnection.getVideoState());
+ setTelephonyVideoState(mOriginalConnection.getVideoState());
setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
- setVideoProvider(mOriginalConnection.getVideoProvider());
+ setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
setAudioQuality(mOriginalConnection.getAudioQuality());
setTechnologyTypeExtra();
@@ -1219,8 +1291,8 @@
} else {
extrasToRemove.add(Connection.EXTRA_DISABLE_ADD_CALL);
}
- putExtras(extrasToPut);
- removeExtras(extrasToRemove);
+ putTelephonyExtras(extrasToPut);
+ removeTelephonyExtras(extrasToRemove);
// updateState can set mOriginalConnection to null if its state is DISCONNECTED, so this
// should be executed *after* the above setters have run.
@@ -1265,18 +1337,109 @@
*/
private void setTechnologyTypeExtra() {
if (getPhone() != null) {
- putExtra(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
+ Bundle newExtras = getExtras();
+ if (newExtras == null) {
+ newExtras = new Bundle();
+ }
+ newExtras.putInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE, getPhone().getPhoneType());
+ putTelephonyExtras(newExtras);
}
}
+ private void refreshHoldSupported() {
+ if (mOriginalConnection == null) {
+ Log.w(this, "refreshHoldSupported org conn is null");
+ return;
+ }
+
+ if (!mOriginalConnection.shouldAllowHoldingVideoCall() && canHoldImsCalls() !=
+ ((getConnectionCapabilities() & (CAPABILITY_HOLD | CAPABILITY_SUPPORT_HOLD)) != 0)) {
+ updateConnectionCapabilities();
+ }
+ }
+
private void refreshDisableAddCall() {
if (shouldSetDisableAddCallExtra()) {
- putExtra(Connection.EXTRA_DISABLE_ADD_CALL, true);
+ Bundle newExtras = getExtras();
+ if (newExtras == null) {
+ newExtras = new Bundle();
+ }
+ newExtras.putBoolean(Connection.EXTRA_DISABLE_ADD_CALL, true);
+ putTelephonyExtras(newExtras);
} else {
removeExtras(Connection.EXTRA_DISABLE_ADD_CALL);
}
}
+ private void refreshCodecType() {
+ Bundle newExtras = getExtras();
+ if (newExtras == null) {
+ newExtras = new Bundle();
+ }
+ int newCodecType;
+ if (isImsConnection()) {
+ newCodecType = transformCodec(getOriginalConnection().getAudioCodec());
+ } else {
+ // For SRVCC, report AUDIO_CODEC_NONE.
+ newCodecType = Connection.AUDIO_CODEC_NONE;
+ }
+ int oldCodecType = newExtras.getInt(Connection.EXTRA_AUDIO_CODEC,
+ Connection.AUDIO_CODEC_NONE);
+ if (newCodecType != oldCodecType) {
+ newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
+ putTelephonyExtras(newExtras);
+ }
+ }
+
+ private int transformCodec(int codec) {
+ switch (codec) {
+ case ImsStreamMediaProfile.AUDIO_QUALITY_NONE:
+ return Connection.AUDIO_CODEC_NONE;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
+ return Connection.AUDIO_CODEC_AMR;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB:
+ return Connection.AUDIO_CODEC_AMR_WB;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K:
+ return Connection.AUDIO_CODEC_QCELP13K;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC:
+ return Connection.AUDIO_CODEC_EVRC;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B:
+ return Connection.AUDIO_CODEC_EVRC_B;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB:
+ return Connection.AUDIO_CODEC_EVRC_WB;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW:
+ return Connection.AUDIO_CODEC_EVRC_NW;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR:
+ return Connection.AUDIO_CODEC_GSM_EFR;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR:
+ return Connection.AUDIO_CODEC_GSM_FR;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR:
+ return Connection.AUDIO_CODEC_GSM_HR;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_G711U:
+ return Connection.AUDIO_CODEC_G711U;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_G723:
+ return Connection.AUDIO_CODEC_G723;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_G711A:
+ return Connection.AUDIO_CODEC_G711A;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_G722:
+ return Connection.AUDIO_CODEC_G722;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB:
+ return Connection.AUDIO_CODEC_G711AB;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_G729:
+ return Connection.AUDIO_CODEC_G729;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB:
+ return Connection.AUDIO_CODEC_EVS_NB;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB:
+ return Connection.AUDIO_CODEC_EVS_WB;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB:
+ return Connection.AUDIO_CODEC_EVS_SWB;
+ case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB:
+ return Connection.AUDIO_CODEC_EVS_FB;
+ default:
+ return Connection.AUDIO_CODEC_NONE;
+ }
+ }
+
private boolean shouldSetDisableAddCallExtra() {
if (mOriginalConnection == null) {
return false;
@@ -1351,11 +1514,14 @@
private boolean canHoldImsCalls() {
PersistableBundle b = getCarrierConfig();
// Return true if the CarrierConfig is unavailable
- return !doesDeviceRespectHoldCarrierConfig() || b == null ||
- b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
+ return (!doesDeviceRespectHoldCarrierConfig() || b == null ||
+ b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL)) &&
+ ((mOriginalConnection != null && mOriginalConnection.shouldAllowHoldingVideoCall())
+ || !VideoProfile.isVideo(getVideoState()));
}
- private PersistableBundle getCarrierConfig() {
+ @VisibleForTesting
+ public PersistableBundle getCarrierConfig() {
Phone phone = getPhone();
if (phone == null) {
return null;
@@ -1409,6 +1575,7 @@
getPhone().unregisterForPreciseCallStateChanged(mHandler);
getPhone().unregisterForRingbackTone(mHandler);
getPhone().unregisterForHandoverStateChanged(mHandler);
+ getPhone().unregisterForRedialConnectionChanged(mHandler);
getPhone().unregisterForDisconnect(mHandler);
getPhone().unregisterForSuppServiceNotification(mHandler);
getPhone().unregisterForOnHoldTone(mHandler);
@@ -1423,6 +1590,7 @@
protected void hangup(int telephonyDisconnectCode) {
if (mOriginalConnection != null) {
+ mHangupDisconnectCause = telephonyDisconnectCode;
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
@@ -1452,7 +1620,7 @@
// 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.
- setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+ setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
android.telephony.DisconnectCause.LOCAL,
"Local Disconnect before connection established."));
close();
@@ -1554,8 +1722,8 @@
for (String key : extras.keySet()) {
Object value = extras.get(key);
if (value instanceof String) {
- Log.d(this, "updateExtras Key=" + Log.pii(key) +
- " value=" + Log.pii((String)value));
+ Log.d(this, "updateExtras Key=" + Rlog.pii(LOG_TAG, key)
+ + " value=" + Rlog.pii(LOG_TAG, value));
}
}
}
@@ -1573,12 +1741,12 @@
}
// Ensure extras are propagated to Telecom.
- putExtras(mOriginalConnectionExtras);
+ putTelephonyExtras(mOriginalConnectionExtras);
} else {
Log.d(this, "Extras update not required");
}
} else {
- Log.d(this, "updateExtras extras: " + Log.pii(extras));
+ Log.d(this, "updateExtras extras: " + Rlog.pii(LOG_TAG, extras));
}
}
}
@@ -1642,19 +1810,19 @@
setActiveInternal();
break;
case HOLDING:
- setOnHold();
+ setTelephonyConnectionOnHold();
break;
case DIALING:
case ALERTING:
if (mOriginalConnection != null && mOriginalConnection.isPulledCall()) {
- setPulling();
+ setTelephonyConnectionPulling();
} else {
- setDialing();
+ setTelephonyConnectionDialing();
}
break;
case INCOMING:
case WAITING:
- setRinging();
+ setTelephonyConnectionRinging();
break;
case DISCONNECTED:
if (shouldTreatAsEmergencyCall()
@@ -1675,11 +1843,19 @@
preciseDisconnectCause =
mOriginalConnection.getPreciseDisconnectCause();
}
- setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
- mOriginalConnection.getDisconnectCause(),
- preciseDisconnectCause,
- mOriginalConnection.getVendorDisconnectCause(),
- getPhone().getPhoneId()));
+ int disconnectCause = mOriginalConnection.getDisconnectCause();
+ if ((mHangupDisconnectCause != DisconnectCause.NOT_VALID)
+ && (mHangupDisconnectCause != disconnectCause)) {
+ Log.i(LOG_TAG, "setDisconnected: override cause: " + disconnectCause
+ + " -> " + mHangupDisconnectCause);
+ disconnectCause = mHangupDisconnectCause;
+ }
+ setTelephonyConnectionDisconnected(
+ DisconnectCauseUtil.toTelecomDisconnectCause(
+ disconnectCause,
+ preciseDisconnectCause,
+ mOriginalConnection.getVendorDisconnectCause(),
+ getPhone().getPhoneId()));
close();
}
break;
@@ -1701,6 +1877,7 @@
updateAddress();
updateMultiparty();
refreshDisableAddCall();
+ refreshCodecType();
}
/**
@@ -1759,8 +1936,8 @@
// To "optimize", we check here to see if there already exists any active calls. If so,
// we issue an update for those calls first to make sure we only have one top-level
// active call.
- if (getConnectionService() != null) {
- for (Connection current : getConnectionService().getAllConnections()) {
+ if (getTelephonyConnectionService() != null) {
+ for (Connection current : getTelephonyConnectionService().getAllConnections()) {
if (current != this && current instanceof TelephonyConnection) {
TelephonyConnection other = (TelephonyConnection) current;
if (other.getState() == STATE_ACTIVE) {
@@ -1769,13 +1946,18 @@
}
}
}
- setActive();
+ setTelephonyConnectionActive();
}
- protected void close() {
+ public void close() {
Log.v(this, "close");
clearOriginalConnection();
destroy();
+ if (mTelephonyConnectionService != null) {
+ removeTelephonyConnectionListener(
+ mTelephonyConnectionService.getTelephonyConnectionListener());
+ }
+ notifyDestroyed();
}
/**
@@ -1787,9 +1969,10 @@
* @return {@code true} if the connection is video capable, {@code false} otherwise.
*/
private boolean isVideoCapable() {
- return can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
- && can(mOriginalConnectionCapabilities,
- Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
+ return (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
+ == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL
+ && (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
+ == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL;
}
/**
@@ -1801,7 +1984,8 @@
* @return {@code true} if the connection is external, {@code false} otherwise.
*/
private boolean isExternalConnection() {
- return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION);
+ return (mOriginalConnectionCapabilities
+ & Capability.IS_EXTERNAL_CONNECTION) == Capability.IS_EXTERNAL_CONNECTION;
}
/**
@@ -1823,8 +2007,10 @@
* @return {@code true} if the connection is pullable, {@code false} otherwise.
*/
private boolean isPullable() {
- return can(mOriginalConnectionCapabilities, Capability.IS_EXTERNAL_CONNECTION)
- && can(mOriginalConnectionCapabilities, Capability.IS_PULLABLE);
+ return (mOriginalConnectionCapabilities & Capability.IS_EXTERNAL_CONNECTION)
+ == Capability.IS_EXTERNAL_CONNECTION
+ && (mOriginalConnectionCapabilities & Capability.IS_PULLABLE)
+ == Capability.IS_PULLABLE;
}
/**
@@ -1882,17 +2068,20 @@
public int applyOriginalConnectionCapabilities(int capabilities) {
// We only support downgrading to audio if both the remote and local side support
// downgrading to audio.
- boolean supportsDowngradeToAudio = can(mOriginalConnectionCapabilities,
- Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
- Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE);
+ int supportsDowngrade = Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL
+ | Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE;
+ boolean supportsDowngradeToAudio =
+ (mOriginalConnectionCapabilities & supportsDowngrade) == supportsDowngrade;
capabilities = changeBitmask(capabilities,
CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
- can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
+ (mOriginalConnectionCapabilities & Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL)
+ == Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
- boolean isLocalVideoSupported = can(mOriginalConnectionCapabilities,
- Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) && !mIsTtyEnabled;
+ boolean isLocalVideoSupported = (mOriginalConnectionCapabilities
+ & Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
+ == Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL && !mIsTtyEnabled;
capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
isLocalVideoSupported);
@@ -2119,13 +2308,13 @@
: R.string.status_hint_label_wifi_call;
Context context = getPhone().getContext();
- setStatusHints(new StatusHints(
- context.getString(labelId),
+ setTelephonyStatusHints(new StatusHints(
+ getResourceString(labelId),
Icon.createWithResource(
context, R.drawable.ic_signal_wifi_4_bar_24dp),
null /* extras */));
} else {
- setStatusHints(null);
+ setTelephonyStatusHints(null);
}
}
@@ -2203,7 +2392,8 @@
* 3. If call is a video call, carrier supports video conference calls.
* 4. If call is a wifi call and VoWIFI is disabled and carrier supports merging these calls.
*/
- private void refreshConferenceSupported() {
+ @VisibleForTesting
+ void refreshConferenceSupported() {
boolean isVideoCall = VideoProfile.isVideo(getVideoState());
Phone phone = getPhone();
if (phone == null) {
@@ -2329,7 +2519,7 @@
sb.append(" properties:");
sb.append(propertiesToString(getConnectionProperties()));
sb.append(" address:");
- sb.append(Log.pii(getAddress()));
+ sb.append(Rlog.pii(LOG_TAG, getAddress()));
sb.append(" originalConnection:");
sb.append(mOriginalConnection);
sb.append(" partOfConf:");
@@ -2343,4 +2533,346 @@
sb.append("]");
return sb.toString();
}
+
+ public final void setTelephonyConnectionService(TelephonyConnectionService connectionService) {
+ mTelephonyConnectionService = connectionService;
+ }
+
+ public final TelephonyConnectionService getTelephonyConnectionService() {
+ return mTelephonyConnectionService;
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to an active state.
+ * <p>
+ * Note: This should be used instead of {@link #setActive()} to ensure listeners are notified.
+ */
+ public void setTelephonyConnectionActive() {
+ setActive();
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to a ringing state.
+ * <p>
+ * Note: This should be used instead of {@link #setRinging()} to ensure listeners are notified.
+ */
+ public void setTelephonyConnectionRinging() {
+ setRinging();
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to an initializing state.
+ * <p>
+ * Note: This should be used instead of {@link #setInitializing()} to ensure listeners are
+ * notified.
+ */
+ public void setTelephonyConnectionInitializing() {
+ setInitializing();
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to a dialing state.
+ * <p>
+ * Note: This should be used instead of {@link #setDialing()} to ensure listeners are notified.
+ */
+ public void setTelephonyConnectionDialing() {
+ setDialing();
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to a pulling state.
+ * <p>
+ * Note: This should be used instead of {@link #setPulling()} to ensure listeners are notified.
+ */
+ public void setTelephonyConnectionPulling() {
+ setPulling();
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to a held state.
+ * <p>
+ * Note: This should be used instead of {@link #setOnHold()} to ensure listeners are notified.
+ */
+ public void setTelephonyConnectionOnHold() {
+ setOnHold();
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Set this {@link TelephonyConnection} to a held state.
+ * <p>
+ * Note: This should be used instead of
+ * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
+ */
+ public void setTelephonyConnectionDisconnected(@NonNull
+ android.telecom.DisconnectCause disconnectCause) {
+ setDisconnected(disconnectCause);
+ notifyDisconnected(disconnectCause);
+ notifyStateChanged(getState());
+ }
+
+ /**
+ * Sends a connection event for this {@link TelephonyConnection}.
+ * <p>
+ * Note: This should be used instead of {@link #sendConnectionEvent(String, Bundle)} to ensure
+ * listeners are notified.
+ */
+ public void sendTelephonyConnectionEvent(@NonNull String event, @Nullable Bundle extras) {
+ sendConnectionEvent(event, extras);
+ notifyTelephonyConnectionEvent(event, extras);
+ }
+
+ /**
+ * Sets the extras associated with this {@link TelephonyConnection}.
+ * <p>
+ * Note: This should be used instead of {@link #putExtras(Bundle)} to ensure listeners are
+ * notified.
+ */
+ public void putTelephonyExtras(@NonNull Bundle extras) {
+ putExtras(extras);
+ notifyPutExtras(extras);
+ }
+
+ /**
+ * Removes the specified extras associated with this {@link TelephonyConnection}.
+ * <p>
+ * Note: This should be used instead of {@link #removeExtras(String...)} to ensure listeners are
+ * notified.
+ */
+ public void removeTelephonyExtras(@NonNull List<String> keys) {
+ removeExtras(keys);
+ notifyRemoveExtras(keys);
+ }
+
+ /**
+ * Sets the video state associated with this {@link TelephonyConnection}.
+ * <p>
+ * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are
+ * notified.
+ */
+ public void setTelephonyVideoState(@VideoProfile.VideoState int videoState) {
+ setVideoState(videoState);
+ notifyVideoStateChanged(videoState);
+ }
+
+ /**
+ * Sets the video provider associated with this {@link TelephonyConnection}.
+ * <p>
+ * Note: This should be used instead of {@link #setVideoProvider(VideoProvider)} to ensure
+ * listeners are notified.
+ */
+ public void setTelephonyVideoProvider(@Nullable VideoProvider videoProvider) {
+ setVideoProvider(videoProvider);
+ notifyVideoProviderChanged(videoProvider);
+ }
+
+ /**
+ * Sets the status hints associated with this {@link TelephonyConnection}.
+ * <p>
+ * Note: This should be used instead of {@link #setStatusHints(StatusHints)} to ensure listeners
+ * are notified.
+ */
+ public void setTelephonyStatusHints(@Nullable StatusHints statusHints) {
+ setStatusHints(statusHints);
+ notifyStatusHintsChanged(statusHints);
+ }
+
+ /**
+ * Sets RIL voice radio technology used for current connection.
+ * <p>
+ * This property is set by the Telephony {@link ConnectionService}.
+ *
+ * @param vrat the RIL Voice Radio Technology used for current connection,
+ * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+ */
+ public final void setCallRadioTech(@RilRadioTechnology int vrat) {
+ Bundle extras = getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToNetworkType(vrat));
+ putExtras(extras);
+ // Propagates the call radio technology to its parent {@link android.telecom.Conference}
+ // This action only covers non-IMS CS conference calls.
+ // For IMS PS call conference call, it can be updated via its host connection
+ // {@link #Listener.onExtrasChanged} event.
+ if (getConference() != null) {
+ Bundle newExtras = new Bundle();
+ newExtras.putInt(
+ TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToNetworkType(vrat));
+ getConference().putExtras(newExtras);
+ }
+ }
+
+ /**
+ * Returns RIL voice radio technology used for current connection.
+ * <p>
+ * Used by the Telephony {@link ConnectionService}.
+ *
+ * @return the RIL voice radio technology used for current connection,
+ * see {@code RIL_RADIO_TECHNOLOGY_*} in {@link android.telephony.ServiceState}.
+ */
+ public final @RilRadioTechnology int getCallRadioTech() {
+ int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ Bundle extras = getExtras();
+ if (extras != null) {
+ voiceNetworkType = extras.getInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ }
+ return ServiceState.networkTypeToRilRadioTechnology(voiceNetworkType);
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of a change to conference participant data
+ * received via the {@link ImsConference} (i.e. conference event package).
+ *
+ * @param conferenceParticipants The participants.
+ */
+ private void updateConferenceParticipants(
+ @NonNull List<ConferenceParticipant> conferenceParticipants) {
+ for (TelephonyConnectionListener l : mTelephonyListeners) {
+ l.onConferenceParticipantsChanged(this, conferenceParticipants);
+ }
+ }
+
+ /**
+ * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
+ * operation has started.
+ */
+ protected void notifyConferenceStarted() {
+ for (TelephonyConnectionListener l : mTelephonyListeners) {
+ l.onConferenceStarted();
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s when a change has occurred to the Connection
+ * which impacts its ability to be a part of a conference call.
+ * @param isConferenceSupported {@code true} if the connection supports being part of a
+ * conference call, {@code false} otherwise.
+ */
+ private void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
+ for (TelephonyConnectionListener l : mTelephonyListeners) {
+ l.onConferenceSupportedChanged(this, isConferenceSupported);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of changes to the connection capabilities.
+ * @param newCapabilities the new capabilities.
+ */
+ private void notifyConnectionCapabilitiesChanged(int newCapabilities) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onConnectionCapabilitiesChanged(this, newCapabilities);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of changes to the connection properties.
+ * @param newProperties the new properties.
+ */
+ private void notifyConnectionPropertiesChanged(int newProperties) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onConnectionPropertiesChanged(this, newProperties);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s when a connection is destroyed.
+ */
+ private void notifyDestroyed() {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onDestroyed(this);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s when a connection disconnects.
+ * @param cause The disconnect cause.
+ */
+ private void notifyDisconnected(android.telecom.DisconnectCause cause) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onDisconnected(this, cause);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of connection state changes.
+ * @param newState The new state.
+ */
+ private void notifyStateChanged(int newState) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onStateChanged(this, newState);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of telephony connection events.
+ * @param event The event.
+ * @param extras Any extras.
+ */
+ private void notifyTelephonyConnectionEvent(String event, Bundle extras) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onConnectionEvent(this, event, extras);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s when extras are added to the connection.
+ * @param extras The new extras.
+ */
+ private void notifyPutExtras(Bundle extras) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onExtrasChanged(this, extras);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s when extra keys are removed from a connection.
+ * @param keys The removed keys.
+ */
+ private void notifyRemoveExtras(List<String> keys) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onExtrasRemoved(this, keys);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection.
+ * @param videoState The new video state.
+ */
+ private void notifyVideoStateChanged(@VideoProfile.VideoState int videoState) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onVideoStateChanged(this, videoState);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
+ * connection.
+ * @param videoProvider The new video provider.
+ */
+ private void notifyVideoProviderChanged(VideoProvider videoProvider) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onVideoProviderChanged(this, videoProvider);
+ }
+ }
+
+ /**
+ * Notifies {@link TelephonyConnectionListener}s of changes to the status hints for a
+ * connection.
+ * @param statusHints The new status hints.
+ */
+ private void notifyStatusHintsChanged(StatusHints statusHints) {
+ for (TelephonyConnectionListener listener : mTelephonyListeners) {
+ listener.onStatusHintsChanged(this, statusHints);
+ }
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 0e5a612..40b941e 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -28,7 +28,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
-import android.provider.Settings;
import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
@@ -103,15 +102,11 @@
}
@Override
public void addConference(TelephonyConference mTelephonyConference) {
- TelephonyConnectionService.this.addConference(mTelephonyConference);
+ TelephonyConnectionService.this.addTelephonyConference(mTelephonyConference);
}
@Override
public void addConference(ImsConference mImsConference) {
- TelephonyConnectionService.this.addConference(mImsConference);
- }
- @Override
- public void removeConnection(Connection connection) {
- TelephonyConnectionService.this.removeConnection(connection);
+ TelephonyConnectionService.this.addTelephonyConference(mImsConference);
}
@Override
public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
@@ -131,13 +126,6 @@
}
};
- private final Connection.Listener mConnectionListener = new Connection.Listener() {
- @Override
- public void onConferenceChanged(Connection connection, Conference conference) {
- mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
- }
- };
-
private final BroadcastReceiver mTtyBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -159,12 +147,7 @@
new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
private final CdmaConferenceController mCdmaConferenceController =
new CdmaConferenceController(this);
- private final ImsConferenceController mImsConferenceController =
- new ImsConferenceController(TelecomAccountRegistry.getInstance(this),
- mTelephonyConnectionServiceProxy,
- // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
- // TODO: Move to carrier config
- () -> true);
+ private ImsConferenceController mImsConferenceController;
private ComponentName mExpectedComponentName = null;
private RadioOnHelper mRadioOnHelper;
@@ -181,6 +164,7 @@
public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
private Handler mDdsSwitchHandler;
private HandlerThread mHandlerThread;
+ private DeviceState mDeviceState = new DeviceState();
/**
* Keeps track of the status of a SIM slot.
@@ -193,6 +177,8 @@
public boolean isLocked = false;
// Is the emergency number associated with the slot
public boolean hasDialedEmergencyNumber = false;
+ //SimState
+ public int simState;
public SlotStatus(int slotId, int capabilities) {
this.slotId = slotId;
@@ -200,7 +186,10 @@
}
}
- // SubscriptionManager Proxy interface for testing
+ /**
+ * SubscriptionManager dependencies for testing.
+ */
+ @VisibleForTesting
public interface SubscriptionManagerProxy {
int getDefaultVoicePhoneId();
int getSimStateForSlotIdx(int slotId);
@@ -224,7 +213,9 @@
}
};
- // TelephonyManager Proxy interface for testing
+ /**
+ * TelephonyManager dependencies for testing.
+ */
@VisibleForTesting
public interface TelephonyManagerProxy {
int getPhoneCount();
@@ -264,7 +255,9 @@
}
}
- //PhoneFactory proxy interface for testing
+ /**
+ * PhoneFactory Dependencies for testing.
+ */
@VisibleForTesting
public interface PhoneFactoryProxy {
Phone getPhone(int index);
@@ -289,22 +282,185 @@
}
};
+ /**
+ * PhoneUtils dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface PhoneUtilsProxy {
+ int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle);
+ PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone);
+ PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
+ boolean isEmergency);
+ }
+
+ private PhoneUtilsProxy mPhoneUtilsProxy = new PhoneUtilsProxy() {
+ @Override
+ public int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle) {
+ return PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
+ }
+
+ @Override
+ public PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
+ return PhoneUtils.makePstnPhoneAccountHandle(phone);
+ }
+
+ @Override
+ public PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
+ boolean isEmergency) {
+ return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(phone, prefix, isEmergency);
+ }
+ };
+
+ /**
+ * PhoneNumberUtils dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface PhoneNumberUtilsProxy {
+ String convertToEmergencyNumber(Context context, String number);
+ }
+
+ private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = new PhoneNumberUtilsProxy() {
+ @Override
+ public String convertToEmergencyNumber(Context context, String number) {
+ return PhoneNumberUtils.convertToEmergencyNumber(context, number);
+ }
+ };
+
+ /**
+ * PhoneSwitcher dependencies for testing.
+ */
+ @VisibleForTesting
+ public interface PhoneSwitcherProxy {
+ PhoneSwitcher getPhoneSwitcher();
+ }
+
+ private PhoneSwitcherProxy mPhoneSwitcherProxy = new PhoneSwitcherProxy() {
+ @Override
+ public PhoneSwitcher getPhoneSwitcher() {
+ return PhoneSwitcher.getInstance();
+ }
+ };
+
+ /**
+ * Factory for Handler creation in order to remove flakiness during t esting.
+ */
+ @VisibleForTesting
+ public interface HandlerFactory {
+ HandlerThread createHandlerThread(String name);
+ Handler createHandler(Looper looper);
+ }
+
+ private HandlerFactory mHandlerFactory = new HandlerFactory() {
+ @Override
+ public HandlerThread createHandlerThread(String name) {
+ return new HandlerThread(name);
+ }
+
+ @Override
+ public Handler createHandler(Looper looper) {
+ return new Handler(looper);
+ }
+ };
+
+ /**
+ * DisconnectCause depends on PhoneGlobals in order to get a system context. Mock out
+ * dependency for testing.
+ */
+ @VisibleForTesting
+ public interface DisconnectCauseFactory {
+ DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason);
+ DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+ String reason, int phoneId);
+ }
+
+ private DisconnectCauseFactory mDisconnectCauseFactory = new DisconnectCauseFactory() {
+ @Override
+ public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
+ String reason) {
+ return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason);
+ }
+
+ @Override
+ public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason,
+ int phoneId) {
+ return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason,
+ phoneId);
+ }
+ };
+
+ /**
+ * Overrides SubscriptionManager dependencies for testing.
+ */
@VisibleForTesting
public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
mSubscriptionManagerProxy = proxy;
}
+ /**
+ * Overrides TelephonyManager dependencies for testing.
+ */
@VisibleForTesting
public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) {
mTelephonyManagerProxy = proxy;
}
+ /**
+ * Overrides PhoneFactory dependencies for testing.
+ */
@VisibleForTesting
public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
mPhoneFactoryProxy = proxy;
}
/**
+ * Overrides configuration and settings dependencies for testing.
+ */
+ @VisibleForTesting
+ public void setDeviceState(DeviceState state) {
+ mDeviceState = state;
+ }
+
+ /**
+ * Overrides PhoneSwitcher dependencies for testing.
+ */
+ @VisibleForTesting
+ public void setPhoneSwitcherProxy(PhoneSwitcherProxy proxy) {
+ mPhoneSwitcherProxy = proxy;
+ }
+
+ /**
+ * Overrides PhoneNumberUtils dependencies for testing.
+ */
+ @VisibleForTesting
+ public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy proxy) {
+ mPhoneNumberUtilsProxy = proxy;
+ }
+
+ /**
+ * Overrides PhoneUtils dependencies for testing.
+ */
+ @VisibleForTesting
+ public void setPhoneUtilsProxy(PhoneUtilsProxy proxy) {
+ mPhoneUtilsProxy = proxy;
+ }
+
+ /**
+ * Override Handler creation factory for testing.
+ */
+ @VisibleForTesting
+ public void setHandlerFactory(HandlerFactory handlerFactory) {
+ mHandlerFactory = handlerFactory;
+ }
+
+ /**
+ * Override DisconnectCause creation for testing.
+ */
+ @VisibleForTesting
+ public void setDisconnectCauseFactory(DisconnectCauseFactory factory) {
+ mDisconnectCauseFactory = factory;
+ }
+
+ /**
* A listener to actionable events specific to the TelephonyConnection.
*/
private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -320,25 +476,37 @@
}
};
+ private final TelephonyConferenceBase.TelephonyConferenceListener mTelephonyConferenceListener =
+ new TelephonyConferenceBase.TelephonyConferenceListener() {
+ @Override
+ public void onConferenceMembershipChanged(Connection connection) {
+ mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
+ }
+ };
+
@Override
public void onCreate() {
super.onCreate();
- Log.initLogging(this);
+ mImsConferenceController = new ImsConferenceController(
+ TelecomAccountRegistry.getInstance(this),
+ mTelephonyConnectionServiceProxy,
+ // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
+ // TODO: Move to carrier config
+ () -> true);
setTelephonyManagerProxy(new TelephonyManagerProxyImpl(getApplicationContext()));
mExpectedComponentName = new ComponentName(this, this.getClass());
mEmergencyTonePlayer = new EmergencyTonePlayer(this);
TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
mHoldTracker = new HoldTracker();
- mIsTtyEnabled = isTtyModeEnabled(getApplicationContext());
+ mIsTtyEnabled = mDeviceState.isTtyModeEnabled(this);
IntentFilter intentFilter = new IntentFilter(
TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
registerReceiver(mTtyBroadcastReceiver, intentFilter);
-
- mHandlerThread = new HandlerThread("DdsSwitchHandlerThread");
+ mHandlerThread = mHandlerFactory.createHandlerThread("DdsSwitchHandlerThread");
mHandlerThread.start();
Looper looper = mHandlerThread.getLooper();
- mDdsSwitchHandler = new Handler(looper);
+ mDdsSwitchHandler = mHandlerFactory.createHandler(looper);
}
@Override
@@ -358,7 +526,7 @@
if (handle == null) {
Log.d(this, "onCreateOutgoingConnection, handle is null");
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
"No phone number supplied"));
}
@@ -373,7 +541,7 @@
if (phone == null) {
Log.d(this, "onCreateOutgoingConnection, phone is null");
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUT_OF_SERVICE,
"Phone is null"));
}
@@ -381,7 +549,7 @@
if (TextUtils.isEmpty(number)) {
Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
"Voicemail scheme provided but no voicemail number set.",
phone.getPhoneId()));
@@ -393,7 +561,7 @@
if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.INVALID_NUMBER,
"Handle scheme is not type tel"));
}
@@ -402,7 +570,7 @@
if (TextUtils.isEmpty(number)) {
Log.d(this, "onCreateOutgoingConnection, unable to parse number");
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.INVALID_NUMBER,
"Unable to parse number"));
}
@@ -423,7 +591,7 @@
if (disableActivation) {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause
.CDMA_ALREADY_ACTIVATED,
"Tried to dial *228",
@@ -447,7 +615,8 @@
// service.
if (phone == null || phone.getServiceState().getState()
!= ServiceState.STATE_IN_SERVICE) {
- String convertedNumber = PhoneNumberUtils.convertToEmergencyNumber(this, number);
+ String convertedNumber = mPhoneNumberUtilsProxy.convertToEmergencyNumber(this,
+ number);
if (!TextUtils.equals(convertedNumber, number)) {
Log.i(this, "onCreateOutgoingConnection, converted to emergency number");
number = convertedNumber;
@@ -458,8 +627,7 @@
final String numberToDial = number;
- final boolean isAirplaneModeOn = Settings.Global.getInt(getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) > 0;
+ final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
|| isRadioPowerDownOnBluetooth();
@@ -468,7 +636,7 @@
final Uri resultHandle = handle;
// By default, Connection based on the default Phone, since we need to return to Telecom
// now.
- final int originalPhoneType = PhoneFactory.getDefaultPhone().getPhoneType();
+ final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
final Connection resultConnection = getTelephonyConnection(request, numberToDial,
isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
if (mRadioOnHelper == null) {
@@ -585,12 +753,8 @@
* Whether the cellular radio is power off because the device is on Bluetooth.
*/
private boolean isRadioPowerDownOnBluetooth() {
- final Context context = getApplicationContext();
- final boolean allowed = context.getResources().getBoolean(
- R.bool.config_allowRadioPowerDownOnBluetooth);
- final int cellOn = Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.CELL_ON,
- PhoneConstants.CELL_ON_FLAG);
+ final boolean allowed = mDeviceState.isRadioPowerDownAllowedOnBluetooth(this);
+ final int cellOn = mDeviceState.getCellOnStatus(this);
return (allowed && cellOn == PhoneConstants.CELL_ON_FLAG && !isRadioOn());
}
@@ -628,11 +792,10 @@
} else {
Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
- originalConnection.setDisconnected(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ closeOrDestroyConnection(originalConnection,
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.POWER_OFF,
"Failed to turn on radio."));
- originalConnection.destroy();
}
}
@@ -655,15 +818,14 @@
boolean noActiveSimCard = SubscriptionController.getInstance()
.getActiveSubInfoCount(phone.getContext().getOpPackageName()) == 0;
// If there's no active sim card and the device is in emergency mode, use E account.
- addExistingConnection(PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
+ addExistingConnection(mPhoneUtilsProxy.makePstnPhoneAccountHandleWithPrefix(
phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
// Remove the old connection from Telecom after.
- connectionToEvaluate.setDisconnected(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ closeOrDestroyConnection(connectionToEvaluate,
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUTGOING_CANCELED,
"Reconnecting outgoing Emergency Call.",
phone.getPhoneId()));
- connectionToEvaluate.destroy();
} else {
placeOutgoingConnection((TelephonyConnection) connectionToEvaluate, phone, request);
}
@@ -689,7 +851,7 @@
if (phone == null) {
final Context context = getApplicationContext();
- if (context.getResources().getBoolean(R.bool.config_checkSimStateBeforeOutgoingCall)) {
+ if (mDeviceState.shouldCheckSimStateBeforeOutgoingCall(this)) {
// Check SIM card state before the outgoing call.
// Start the SIM unlock activity if PIN_REQUIRED.
final Phone defaultPhone = mPhoneFactoryProxy.getDefaultPhone();
@@ -714,7 +876,7 @@
}
}
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUT_OF_SERVICE,
"SIM_STATE_PIN_REQUIRED"));
}
@@ -722,7 +884,7 @@
Log.d(this, "onCreateOutgoingConnection, phone is null");
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
}
@@ -750,7 +912,7 @@
if (!allowNonEmergencyCalls) {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY,
"Cannot make non-emergency call in ECM mode.",
phone.getPhoneId()));
@@ -768,7 +930,7 @@
break;
} else {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUT_OF_SERVICE,
"ServiceState.STATE_OUT_OF_SERVICE",
phone.getPhoneId()));
@@ -779,25 +941,24 @@
break;
}
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.POWER_OFF,
"ServiceState.STATE_POWER_OFF",
phone.getPhoneId()));
default:
Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUTGOING_FAILURE,
"Unknown service state " + state,
phone.getPhoneId()));
}
}
- final Context context = getApplicationContext();
- final boolean isTtyModeEnabled = isTtyModeEnabled(context);
+ final boolean isTtyModeEnabled = mDeviceState.isTtyModeEnabled(this);
if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled
&& !isEmergencyNumber) {
- return Connection.createFailedConnection(DisconnectCauseUtil.toTelecomDisconnectCause(
+ return Connection.createFailedConnection(mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,
null, phone.getPhoneId()));
}
@@ -811,7 +972,7 @@
// Check roaming status to see if we should block custom call forwarding codes
if (blockCallForwardingNumberWhileRoaming(phone, number)) {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING,
"Call forwarding while roaming",
phone.getPhoneId()));
@@ -820,17 +981,17 @@
final TelephonyConnection connection =
createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
- request.getTelecomCallId(), request.getAddress(), request.getVideoState());
+ request.getTelecomCallId());
if (connection == null) {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.OUTGOING_FAILURE,
"Invalid phone type",
phone.getPhoneId()));
}
connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
- connection.setInitializing();
- connection.setVideoState(request.getVideoState());
+ connection.setTelephonyConnectionInitializing();
+ connection.setTelephonyVideoState(request.getVideoState());
connection.setRttTextStream(request.getRttTextStream());
connection.setTtyEnabled(isTtyModeEnabled);
return connection;
@@ -856,7 +1017,7 @@
request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
if (phone == null) {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
"Phone is null"));
}
@@ -865,7 +1026,7 @@
if (!call.getState().isRinging()) {
Log.i(this, "onCreateIncomingConnection, no ringing call");
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.INCOMING_MISSED,
"Found no ringing call",
phone.getPhoneId()));
@@ -879,19 +1040,14 @@
return Connection.createCanceledConnection();
}
- // We should rely on the originalConnection to get the video state. The request coming
- // from Telecom does not know the video state of the incoming call.
- int videoState = originalConnection != null ? originalConnection.getVideoState() :
- VideoProfile.STATE_AUDIO_ONLY;
-
TelephonyConnection connection =
createConnectionFor(phone, originalConnection, false /* isOutgoing */,
- request.getAccountHandle(), request.getTelecomCallId(),
- request.getAddress(), videoState);
+ request.getAccountHandle(), request.getTelecomCallId());
handleIncomingRtt(request, originalConnection);
if (connection == null) {
return Connection.createCanceledConnection();
} else {
+ connection.setTtyEnabled(mDeviceState.isTtyModeEnabled(getApplicationContext()));
return connection;
}
}
@@ -944,6 +1100,61 @@
}
@Override
+ public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
+ Log.i(this, "onCreateIncomingConnectionFailed, request: " + request);
+ // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
+ // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
+ PhoneAccountHandle accountHandle = request.getAccountHandle();
+ boolean isEmergency = false;
+ if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
+ accountHandle.getId())) {
+ Log.w(this, "onCreateIncomingConnectionFailed:Emergency call failed... ");
+ isEmergency = true;
+ }
+ Phone phone = getPhoneForAccount(accountHandle, isEmergency,
+ /* Note: when not an emergency, handle can be null for unknown callers */
+ request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
+ if (phone == null) {
+ Log.w(this, "onCreateIncomingConnectionFailed: can not find corresponding phone.");
+ return;
+ }
+
+ Call call = phone.getRingingCall();
+ if (!call.getState().isRinging()) {
+ Log.w(this, "onCreateIncomingConnectionFailed, no ringing call found for failed call");
+ return;
+ }
+
+ com.android.internal.telephony.Connection originalConnection =
+ call.getState() == Call.State.WAITING
+ ? call.getLatestConnection() : call.getEarliestConnection();
+ TelephonyConnection knownConnection =
+ getConnectionForOriginalConnection(originalConnection);
+ if (knownConnection != null) {
+ Log.w(this, "onCreateIncomingConnectionFailed, original connection already registered."
+ + " Hanging it up.");
+ knownConnection.onAbort();
+ return;
+ }
+
+ TelephonyConnection connection =
+ createConnectionFor(phone, originalConnection, false /* isOutgoing */,
+ request.getAccountHandle(), request.getTelecomCallId());
+ if (connection == null) {
+ Log.w(this, "onCreateIncomingConnectionFailed, TelephonyConnection created as null, "
+ + "ignoring.");
+ return;
+ }
+
+ // We have to do all of this work because in some cases, hanging up the call maps to
+ // different underlying signaling (CDMA), which is already encapsulated in
+ // TelephonyConnection.
+ connection.onReject();
+ connection.close();
+ }
+
+ @Override
public void triggerConferenceRecalculate() {
if (mTelephonyConferenceController.shouldRecalculate()) {
mTelephonyConferenceController.recalculate();
@@ -969,7 +1180,7 @@
request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
if (phone == null) {
return Connection.createFailedConnection(
- DisconnectCauseUtil.toTelecomDisconnectCause(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(
android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
"Phone is null"));
}
@@ -1042,8 +1253,8 @@
TelephonyConnection connection =
createConnectionFor(phone, unknownConnection,
!unknownConnection.isIncoming() /* isOutgoing */,
- request.getAccountHandle(), request.getTelecomCallId(),
- request.getAddress(), videoState);
+ request.getAccountHandle(), request.getTelecomCallId()
+ );
if (connection == null) {
return Connection.createCanceledConnection();
@@ -1087,7 +1298,6 @@
@Override
public void onConnectionAdded(Connection connection) {
if (connection instanceof Holdable && !isExternalConnection(connection)) {
- connection.addConnectionListener(mConnectionListener);
mHoldTracker.addHoldable(
connection.getPhoneAccountHandle(), (Holdable) connection);
}
@@ -1218,14 +1428,12 @@
} else {
// We have run out of Phones to use. Disconnect the call and destroy the connection.
Log.i(this, "retryOutgoingOriginalConnection, no more Phones to use. Disconnecting.");
- c.setDisconnected(new DisconnectCause(DisconnectCause.ERROR));
- c.clearOriginalConnection();
- c.destroy();
+ closeOrDestroyConnection(c, new DisconnectCause(DisconnectCause.ERROR));
}
}
private void updatePhoneAccount(TelephonyConnection connection, Phone phone) {
- PhoneAccountHandle pHandle = PhoneUtils.makePstnPhoneAccountHandle(phone);
+ PhoneAccountHandle pHandle = mPhoneUtilsProxy.makePstnPhoneAccountHandle(phone);
// For ECall handling on MSIM, until the request reaches here (i.e PhoneApp), we don't know
// on which phone account ECall can be placed. After deciding, we should notify Telecom of
// the change so that the proper PhoneAccount can be displayed.
@@ -1249,6 +1457,35 @@
phone.getEmergencyNumberTracker().getEmergencyNumber(number);
if (emergencyNumber != null) {
phone.notifyOutgoingEmergencyCall(emergencyNumber);
+ if (!getAllConnections().isEmpty()) {
+ if (!shouldHoldForEmergencyCall(phone)) {
+ // If we do not support holding ongoing calls for an outgoing
+ // emergency call, disconnect the ongoing calls.
+ for (Connection c : getAllConnections()) {
+ if (!c.equals(connection)
+ && c.getState() != Connection.STATE_DISCONNECTED
+ && c instanceof TelephonyConnection) {
+ ((TelephonyConnection) c).hangup(
+ android.telephony.DisconnectCause
+ .OUTGOING_EMERGENCY_CALL_PLACED);
+ }
+ }
+ } else if (!isVideoCallHoldAllowed(phone)) {
+ // If we do not support holding ongoing video call for an outgoing
+ // emergency call, disconnect the ongoing video call.
+ for (Connection c : getAllConnections()) {
+ if (!c.equals(connection)
+ && c.getState() == Connection.STATE_ACTIVE
+ && VideoProfile.isVideo(c.getVideoState())
+ && c instanceof TelephonyConnection) {
+ ((TelephonyConnection) c).hangup(
+ android.telephony.DisconnectCause
+ .OUTGOING_EMERGENCY_CALL_PLACED);
+ break;
+ }
+ }
+ }
+ }
}
originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
.setVideoState(videoState)
@@ -1282,10 +1519,10 @@
cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
break;
}
- connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
- cause, e.getMessage(), phone.getPhoneId()));
- connection.clearOriginalConnection();
- connection.destroy();
+ connection.setTelephonyConnectionDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(cause, e.getMessage(),
+ phone.getPhoneId()));
+ connection.close();
return;
}
@@ -1306,23 +1543,45 @@
startActivity(intent);
}
Log.d(this, "placeOutgoingConnection, phone.dial returned null");
- connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
- telephonyDisconnectCause, "Connection is null", phone.getPhoneId()));
- connection.clearOriginalConnection();
- connection.destroy();
+ connection.setTelephonyConnectionDisconnected(
+ mDisconnectCauseFactory.toTelecomDisconnectCause(telephonyDisconnectCause,
+ "Connection is null", phone.getPhoneId()));
+ connection.close();
} else {
connection.setOriginalConnection(originalConnection);
}
}
+ private boolean isVideoCallHoldAllowed(Phone phone) {
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (cfgManager == null) {
+ // For some reason CarrierConfigManager is unavailable, return default
+ Log.w(this, "isVideoCallHoldAllowed: couldn't get CarrierConfigManager");
+ return true;
+ }
+ return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+ CarrierConfigManager.KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
+ }
+
+ private boolean shouldHoldForEmergencyCall(Phone phone) {
+ CarrierConfigManager cfgManager = (CarrierConfigManager)
+ phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (cfgManager == null) {
+ // For some reason CarrierConfigManager is unavailable, return default
+ Log.w(this, "shouldHoldForEmergencyCall: couldn't get CarrierConfigManager");
+ return true;
+ }
+ return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
+ CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
+ }
+
private TelephonyConnection createConnectionFor(
Phone phone,
com.android.internal.telephony.Connection originalConnection,
boolean isOutgoing,
PhoneAccountHandle phoneAccountHandle,
- String telecomCallId,
- Uri address,
- int videoState) {
+ String telecomCallId) {
TelephonyConnection returnConnection = null;
int phoneType = phone.getPhoneType();
if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
@@ -1344,21 +1603,27 @@
returnConnection.setShowPreciseFailedCause(
TelecomAccountRegistry.getInstance(this).isShowPreciseFailedCause(
phoneAccountHandle));
+ returnConnection.setTelephonyConnectionService(this);
}
return returnConnection;
}
private boolean isOriginalConnectionKnown(
com.android.internal.telephony.Connection originalConnection) {
+ return (getConnectionForOriginalConnection(originalConnection) != null);
+ }
+
+ private TelephonyConnection getConnectionForOriginalConnection(
+ com.android.internal.telephony.Connection originalConnection) {
for (Connection connection : getAllConnections()) {
if (connection instanceof TelephonyConnection) {
TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
if (telephonyConnection.getOriginalConnection() == originalConnection) {
- return true;
+ return telephonyConnection;
}
}
}
- return false;
+ return null;
}
/**
@@ -1373,7 +1638,7 @@
private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency,
@Nullable String emergencyNumberAddress) {
Phone chosenPhone = null;
- int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
+ int subId = mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(accountHandle);
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
@@ -1403,7 +1668,7 @@
return possiblyOverrideDefaultDataForEmergencyCall(phone).get(
DEFAULT_DATA_SWITCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (Exception e) {
- Log.w(this, "onCreateOutgoingConn - delayDialForDdsSwitch exception= "
+ Log.w(this, "delayDialForDdsSwitch - exception= "
+ e.getMessage());
return false;
}
@@ -1423,13 +1688,21 @@
*/
private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall(
@NonNull Phone phone) {
- TelephonyManager telephony = TelephonyManager.from(phone.getContext());
- int phoneCount = telephony.getPhoneCount();
+ int phoneCount = mTelephonyManagerProxy.getPhoneCount();
// Do not override DDS if this is a single SIM device.
if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) {
return CompletableFuture.completedFuture(Boolean.TRUE);
}
+ // Do not switch Default data if this device supports emergency SUPL on non-DDS.
+ final boolean gnssSuplRequiresDefaultData =
+ mDeviceState.isSuplDdsSwitchRequiredForEmergencyCall(this);
+ if (!gnssSuplRequiresDefaultData) {
+ Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
+ + "require DDS switch.");
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+
CarrierConfigManager cfgManager = (CarrierConfigManager)
phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
if (cfgManager == null) {
@@ -1438,34 +1711,41 @@
+ "CarrierConfigManager");
return CompletableFuture.completedFuture(Boolean.TRUE);
}
- // Only override default data if we are IN_SERVICE and on a home network. We don't want to
- // perform a DDS switch of we are on a roaming network, where SUPL may not be available.
- boolean isPhoneAvailableForEmergency = isAvailableForEmergencyCalls(phone);
+
+ // Only override default data if we are IN_SERVICE already.
+ if (!isAvailableForEmergencyCalls(phone)) {
+ Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS");
+ return CompletableFuture.completedFuture(Boolean.TRUE);
+ }
+
+ // Only override default data if we are not roaming, we do not want to switch onto a network
+ // that only supports data plane only (if we do not know).
boolean isRoaming = phone.getServiceState().getVoiceRoaming();
- if (!isPhoneAvailableForEmergency || isRoaming) {
- Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, avail = "
- + isPhoneAvailableForEmergency + ", roaming = " + isRoaming);
+ // In some roaming conditions, we know the roaming network doesn't support control plane
+ // fallback even though the home operator does. For these operators we will need to do a DDS
+ // switch anyway to make sure the SUPL request doesn't fail.
+ boolean roamingNetworkSupportsControlPlaneFallback = true;
+ String[] dataPlaneRoamPlmns = cfgManager.getConfigForSubId(phone.getSubId()).getStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY);
+ if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns).contains(
+ phone.getServiceState().getOperatorNumeric())) {
+ roamingNetworkSupportsControlPlaneFallback = false;
+ }
+ if (isRoaming && roamingNetworkSupportsControlPlaneFallback) {
+ Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed "
+ + "to support CP fallback, not switching DDS.");
return CompletableFuture.completedFuture(Boolean.TRUE);
}
-
- // Do not switch Default data if this device supports emergency SUPL on non-DDS.
- final boolean gnssSuplRequiresDefaultData = phone.getContext().getResources().getBoolean(
- R.bool.config_gnss_supl_requires_default_data_for_emergency);
- if (!gnssSuplRequiresDefaultData) {
- Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
- + "require DDS switch.");
- return CompletableFuture.completedFuture(Boolean.TRUE);
- }
-
+ // Do not try to swap default data if we support CS fallback or it is assumed that the
+ // roaming network supports control plane fallback, we do not want to introduce
+ // a lag in emergency call setup time if possible.
final boolean supportsCpFallback = cfgManager.getConfigForSubId(phone.getSubId())
.getInt(CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY)
!= CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY;
- if (supportsCpFallback) {
+ if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) {
Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier "
+ "supports CP fallback.");
- // Do not try to swap default data if we support CS fallback, do not want to introduce
- // a lag in emergency call setup time if possible.
return CompletableFuture.completedFuture(Boolean.TRUE);
}
@@ -1473,7 +1753,7 @@
// CarrierConfig default if format fails.
int extensionTime = 0;
try {
- extensionTime = Integer.valueOf(cfgManager.getConfigForSubId(phone.getSubId())
+ extensionTime = Integer.parseInt(cfgManager.getConfigForSubId(phone.getSubId())
.getString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0"));
} catch (NumberFormatException e) {
// Just use default.
@@ -1482,12 +1762,13 @@
try {
Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for "
+ extensionTime + "seconds");
- PhoneSwitcher.getInstance().overrideDefaultDataForEmergency(phone.getPhoneId(),
- extensionTime, modemResultFuture);
+ mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency(
+ phone.getPhoneId(), extensionTime, modemResultFuture);
// Catch all exceptions, we want to continue with emergency call if possible.
} catch (Exception e) {
Log.w(this, "possiblyOverrideDefaultDataForEmergencyCall: exception = "
+ e.getMessage());
+ modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE);
}
return modemResultFuture;
}
@@ -1594,6 +1875,8 @@
// 4)
// Report Slot's PIN/PUK lock status for sorting later.
int simState = mSubscriptionManagerProxy.getSimStateForSlotIdx(i);
+ // Record SimState.
+ status.simState = simState;
if (simState == TelephonyManager.SIM_STATE_PIN_REQUIRED ||
simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
status.isLocked = true;
@@ -1639,6 +1922,15 @@
if (o1.hasDialedEmergencyNumber && !o2.hasDialedEmergencyNumber) {
return 1;
}
+ // Sort by non-absent SIM.
+ if (o1.simState == TelephonyManager.SIM_STATE_ABSENT
+ && o2.simState != TelephonyManager.SIM_STATE_ABSENT) {
+ return -1;
+ }
+ if (o2.simState == TelephonyManager.SIM_STATE_ABSENT
+ && o1.simState != TelephonyManager.SIM_STATE_ABSENT) {
+ return 1;
+ }
// First start by seeing if either of the phone slots are locked. If they
// are, then sort by non-locked SIM first. If they are both locked, sort
// by capability instead.
@@ -1716,13 +2008,8 @@
return true;
}
- @Override
- public void removeConnection(Connection connection) {
- super.removeConnection(connection);
- if (connection instanceof TelephonyConnection) {
- TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
- telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
- }
+ TelephonyConnection.TelephonyConnectionListener getTelephonyConnectionListener() {
+ return mTelephonyConnectionListener;
}
/**
@@ -1782,7 +2069,8 @@
// If the CDMA conference has not been merged, add-call will not work, so fail
// this request to add a call.
- if (cdmaConf.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
+ if ((cdmaConf.getConnectionCapabilities()
+ & Connection.CAPABILITY_MERGE_CONFERENCE) != 0) {
return Connection.createFailedConnection(new DisconnectCause(
DisconnectCause.RESTRICTED,
null,
@@ -1796,13 +2084,6 @@
return null; // null means nothing went wrong, and call should continue.
}
- private boolean isTtyModeEnabled(Context context) {
- return (android.provider.Settings.Secure.getInt(
- context.getContentResolver(),
- android.provider.Settings.Secure.PREFERRED_TTY_MODE,
- TelecomManager.TTY_MODE_OFF) != TelecomManager.TTY_MODE_OFF);
- }
-
/**
* For outgoing dialed calls, potentially send a ConnectionEvent if the user is on WFC and is
* dialing an international number.
@@ -1823,7 +2104,7 @@
// are potentially placing an international call on WFC.
Log.i(this, "placeOutgoingConnection - sending international call on WFC " +
"confirmation event");
- telephonyConnection.sendConnectionEvent(
+ telephonyConnection.sendTelephonyConnectionEvent(
TelephonyManager.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC, null);
}
}
@@ -1839,4 +2120,26 @@
}
}
}
+
+ private void closeOrDestroyConnection(Connection connection, DisconnectCause cause) {
+ if (connection instanceof TelephonyConnection) {
+ TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+ telephonyConnection.setTelephonyConnectionDisconnected(cause);
+ // Close destroys the connection and notifies TelephonyConnection listeners.
+ telephonyConnection.close();
+ } else {
+ connection.setDisconnected(cause);
+ connection.destroy();
+ }
+ }
+
+ /**
+ * 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)}.
+ * @param conference The conference.
+ */
+ public void addTelephonyConference(@NonNull TelephonyConferenceBase conference) {
+ addConference(conference);
+ conference.addTelephonyConferenceListener(mTelephonyConferenceListener);
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
index 1ced34a..6c29bd2 100644
--- a/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
+++ b/src/com/android/services/telephony/TelephonyConnectionServiceProxy.java
@@ -30,7 +30,6 @@
Collection<Connection> getAllConnections();
void addConference(TelephonyConference mTelephonyConference);
void addConference(ImsConference mImsConference);
- void removeConnection(Connection connection);
void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
Connection connection);
void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
index 4ee9355..5c0416b 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
@@ -24,6 +24,7 @@
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.ArrayMap;
import android.util.Log;
@@ -103,8 +104,8 @@
}
- private final ImsMmTelManager.RegistrationCallback mRegistrationCallback =
- new ImsMmTelManager.RegistrationCallback() {
+ private final RegistrationManager.RegistrationCallback mRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
@Override
public void onRegistered(int imsRadioTech) {
diff --git a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
index 96f8bf7..f8d4487 100644
--- a/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
+++ b/testapps/TelephonyRegistryTestApp/src/com/android/phone/testapps/telephonyregistry/TelephonyRegistryTestApp.java
@@ -50,7 +50,6 @@
put(PhoneStateListener.LISTEN_DATA_CONNECTION_STATE, "DATA_CONNECTION_STATE");
put(PhoneStateListener.LISTEN_DATA_ACTIVITY, "DATA_ACTIVITY");
put(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS, "SIGNAL_STRENGTHS");
- put(PhoneStateListener.LISTEN_OTASP_CHANGED, "OTASP_CHANGED");
put(PhoneStateListener.LISTEN_CELL_INFO, "CELL_INFO");
put(PhoneStateListener.LISTEN_PRECISE_CALL_STATE, "PRECISE_CALL_STATE");
put(PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE,
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..22b40b5
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+android_test {
+ name: "TeleServiceTests",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "android.test.mock",
+ "android.test.runner",
+ "telephony-common",
+ "android.test.base",
+ "ims-common",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+
+ instrumentation_for: "TeleService",
+
+ static_libs: [
+ "androidx.test.rules",
+ "mockito-target-minus-junit4",
+ "androidx.test.espresso.core",
+ "truth-prebuilt",
+ ],
+
+ test_suites: [
+ "device-tests",
+ "mts",
+ ],
+
+}
diff --git a/tests/Android.mk b/tests/Android.mk
deleted file mode 100644
index 44bf176..0000000
--- a/tests/Android.mk
+++ /dev/null
@@ -1,41 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_PACKAGE_NAME := TeleServiceTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := telephony-common android.test.base
-
-LOCAL_INSTRUMENTATION_FOR := TeleService
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- mockito-target-minus-junit4 \
- androidx.test.espresso.core \
- truth-prebuilt
-
-LOCAL_COMPATIBILITY_SUITE := device-tests
-
-include $(BUILD_PACKAGE)
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index d30ae6b..01267d8 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -16,13 +16,10 @@
package com.android;
-import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
-
import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
@@ -33,11 +30,11 @@
*/
public class TelephonyTestBase {
- protected Context mContext;
+ protected TestContext mContext;
public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
MockitoAnnotations.initMocks(this);
+ mContext = new TestContext();
// Set up the looper if it does not exist on the test thread.
if (Looper.myLooper() == null) {
Looper.prepare();
@@ -86,4 +83,8 @@
Log.e("TelephonyTestBase", "InterruptedException while waiting: " + e);
}
}
+
+ protected TestContext getTestContext() {
+ return mContext;
+ }
}
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
new file mode 100644
index 0000000..776ec6a
--- /dev/null
+++ b/tests/src/com/android/TestContext.java
@@ -0,0 +1,122 @@
+/*
+ * 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;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContext;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class TestContext extends MockContext {
+
+ @Mock CarrierConfigManager mMockCarrierConfigManager;
+ @Mock TelecomManager mMockTelecomManager;
+ @Mock TelephonyManager mMockTelephonyManager;
+ @Mock SubscriptionManager mMockSubscriptionManager;
+
+ private PersistableBundle mCarrierConfig = new PersistableBundle();
+
+ public TestContext() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mCarrierConfig).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ @Override
+ public String getPackageName() {
+ return "com.android.phone.tests";
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return null;
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ return null;
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return null;
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, int flags) {
+ return null;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case (Context.CARRIER_CONFIG_SERVICE) : {
+ return mMockCarrierConfigManager;
+ }
+ case (Context.TELECOM_SERVICE) : {
+ return mMockTelecomManager;
+ }
+ case (Context.TELEPHONY_SERVICE) : {
+ return mMockTelephonyManager;
+ }
+ case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+ return mMockSubscriptionManager;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ if (serviceClass == CarrierConfigManager.class) {
+ return Context.CARRIER_CONFIG_SERVICE;
+ }
+ if (serviceClass == TelecomManager.class) {
+ return Context.TELECOM_SERVICE;
+ }
+ if (serviceClass == TelephonyManager.class) {
+ return Context.TELEPHONY_SERVICE;
+ }
+ if (serviceClass == SubscriptionManager.class) {
+ return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
+ }
+ return null;
+ }
+
+ public PersistableBundle getCarrierConfig() {
+ return mCarrierConfig;
+ }
+}
diff --git a/tests/src/com/android/phone/CallFeaturesSettingTest.java b/tests/src/com/android/phone/CallFeaturesSettingTest.java
index 78d68e3..8f49b97 100644
--- a/tests/src/com/android/phone/CallFeaturesSettingTest.java
+++ b/tests/src/com/android/phone/CallFeaturesSettingTest.java
@@ -21,8 +21,13 @@
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
+import android.app.KeyguardManager;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import androidx.test.rule.ActivityTestRule;
@@ -45,13 +50,16 @@
IccCard mMockIccCard;
@Rule
public ActivityTestRule<CallFeaturesSetting> mRule =
- new ActivityTestRule<>(CallFeaturesSetting.class);
+ new ActivityTestRule<>(CallFeaturesSetting.class, false, true);
private CallFeaturesSetting mActivity;
@Before
- public void setUp() {
+ public void setUp() throws Throwable {
MockitoAnnotations.initMocks(this);
mActivity = mRule.getActivity();
+ Context targetContext = InstrumentationRegistry.getTargetContext();
+ doReturn(targetContext).when(mMockPhone).getContext();
+ keepScreenOn(mRule, mActivity);
}
@FlakyTest
@@ -63,7 +71,7 @@
when(mMockIccCard.getIccFdnAvailable()).thenReturn(true);
getField("mPhone").set(mActivity, mMockPhone);
- mActivity.onResume();
+ mActivity.runOnUiThread(() -> mActivity.onResume());
// Check the FDN menu is displayed.
onView(withText(R.string.fdn)).check(matches(isDisplayed()));
@@ -77,7 +85,7 @@
when(mMockPhone.getIccCard()).thenReturn(null);
getField("mPhone").set(mActivity, mMockPhone);
- mActivity.onResume();
+ mActivity.runOnUiThread(() -> mActivity.onResume());
// Check the FDN menu is not displayed.
onView(withText(R.string.fdn)).check(doesNotExist());
@@ -92,7 +100,7 @@
when(mMockIccCard.getIccFdnAvailable()).thenReturn(false);
getField("mPhone").set(mActivity, mMockPhone);
- mActivity.onResume();
+ mActivity.runOnUiThread(() -> mActivity.onResume());
// Check the FDN menu is not displayed.
onView(withText(R.string.fdn)).check(doesNotExist());
@@ -103,4 +111,19 @@
field.setAccessible(true);
return field;
}
+
+ /**
+ * Automatically wake up device to perform tests.
+ */
+ private static void keepScreenOn(ActivityTestRule activityTestRule,
+ final CallFeaturesSetting activity) throws Throwable {
+ activityTestRule.runOnUiThread(() -> {
+ activity.setTurnScreenOn(true);
+ activity.setShowWhenLocked(true);
+ KeyguardManager keyguardManager =
+ activity.getSystemService(KeyguardManager.class);
+ keyguardManager.requestDismissKeyguard(activity, null);
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
}
diff --git a/tests/src/com/android/phone/CnapTest.java b/tests/src/com/android/phone/CnapTest.java
deleted file mode 100644
index 7161ba8..0000000
--- a/tests/src/com/android/phone/CnapTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-// Need to be in this package to access package methods.
-package com.android.phone;
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import com.android.internal.telephony.CallerInfo;
-import com.android.phone.PhoneUtils;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_ALLOWED;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_PAYPHONE;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_RESTRICTED;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_UNKNOWN;
-
-// Test suite for the Caller Name Presentation (CNAP) handling.
-// See AndroidManifest.xml how to run these tests.
-public class CnapTest extends AndroidTestCase {
- private static final String TAG = "CnapTest";
- private Context mContext;
- private CallerInfo mCallerInfo;
- // TODO: This string should be loaded from the phone package and
- // not hardcoded.
- private String mUnknown = "Unknown";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getContext();
- mCallerInfo = new CallerInfo();
- }
-
- // Checks the cnap 'ABSENT NUMBER' is mapped to the unknown presentation.
- @SmallTest
- public void testAbsentNumberIsMappedToUnknown() throws Exception {
- String num = modifyForSpecialCnapCases("ABSENT NUMBER", PRESENTATION_ALLOWED);
- assertIsUnknown(num);
- }
-
- // HELPERS
-
- /**
- * Checks the number and CallerInfo structure indicate the number
- * is unknown.
- */
- private void assertIsUnknown(String number) {
- assertEquals(mUnknown, number);
- assertEquals(PRESENTATION_UNKNOWN, mCallerInfo.numberPresentation);
- // TODO: cnapName and name presentation should be set to
- // unknown. At least I cannot see why it shouldn't be the case
- // assertEquals(mUnknown, mCallerInfo.cnapName);
- // assertEquals(PRESENTATION_UNKNOWN, mCallerInfo.namePresentation);
- }
-
- /**
- * Shorthand for PhoneUtils.modifyForSpecialCnapCases(mContext, mCallerInfo, ...)
- */
- private String modifyForSpecialCnapCases(String number, int presentation) {
- return PhoneUtils.modifyForSpecialCnapCases(
- mContext, mCallerInfo, number, presentation);
- }
-}
diff --git a/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java b/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java
index 4336a20..ede0965 100644
--- a/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java
+++ b/tests/src/com/android/services/telephony/ConferenceParticipantConnectionTest.java
@@ -16,7 +16,7 @@
package com.android.services.telephony;
-import static android.telecom.ConferenceParticipant.getParticipantAddress;
+import static com.android.ims.internal.ConferenceParticipant.getParticipantAddress;
import static org.junit.Assert.assertEquals;
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
index aa832aa..edfc34f 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -16,14 +16,13 @@
package com.android.services.telephony;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.times;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
import android.os.Looper;
import android.test.suitebuilder.annotation.SmallTest;
@@ -32,7 +31,6 @@
import org.junit.Before;
import org.junit.Test;
-
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -85,7 +83,7 @@
mControllerTest.add(mTestTelephonyConnectionA);
mTestTelephonyConnectionA.setActive();
- mTestTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
assertTrue(mTestTelephonyConnectionA.getConferenceables()
.contains(mTestTelephonyConnectionB));
@@ -125,10 +123,9 @@
mControllerTest.add(mTestTelephonyConnectionA);
mTestTelephonyConnectionA.setActive();
- mTestTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
verify(mMockTelephonyConnectionServiceProxy, times(2))
.addConference(any(ImsConference.class));
-
}
}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 568549b..7251402 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -31,11 +31,12 @@
import android.os.Looper;
import android.telecom.Call;
import android.telecom.Conference;
-import android.telecom.ConferenceParticipant;
import android.telecom.Connection;
import android.telecom.PhoneAccountHandle;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.ims.internal.ConferenceParticipant;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -97,8 +98,6 @@
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
Arrays.asList(participant1));
assertEquals(0, imsConference.getNumberOfParticipants());
- verify(mMockTelephonyConnectionServiceProxy, times(2)).removeConnection(
- any(Connection.class));
reset(mMockTelephonyConnectionServiceProxy);
// Back to 2!
@@ -111,6 +110,181 @@
}
/**
+ * Tests CEPs with disconnected participants present with disconnected state.
+ */
+ @Test
+ @SmallTest
+ public void testDisconnectParticipantViaDisconnectState() {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+
+ // Start off with 3 participants.
+ 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);
+
+ ConferenceParticipant participant3 = new ConferenceParticipant(
+ Uri.parse("tel:6505551214"),
+ "A",
+ Uri.parse("sip:6505551214@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2, participant3));
+ assertEquals(3, imsConference.getNumberOfParticipants());
+ verify(mMockTelephonyConnectionServiceProxy, times(3)).addExistingConnection(
+ any(PhoneAccountHandle.class), any(Connection.class),
+ eq(imsConference));
+
+
+ // Mark one participant as disconnected.
+ ConferenceParticipant participant3Disconnected = new ConferenceParticipant(
+ Uri.parse("tel:6505551214"),
+ "A",
+ Uri.parse("sip:6505551214@testims.com"),
+ Connection.STATE_DISCONNECTED,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2, participant3Disconnected));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+ reset(mMockTelephonyConnectionServiceProxy);
+
+ // Now remove it from another CEP update; should still be the same number of participants
+ // and no updates.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+ verify(mMockTelephonyConnectionServiceProxy, never()).addExistingConnection(
+ any(PhoneAccountHandle.class), any(Connection.class),
+ any(Conference.class));
+ }
+
+ /**
+ * Tests CEPs with removed participants.
+ */
+ @Test
+ @SmallTest
+ public void testDisconnectParticipantViaRemoval() {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+
+ // Start off with 3 participants.
+ 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);
+
+ ConferenceParticipant participant3 = new ConferenceParticipant(
+ Uri.parse("tel:6505551214"),
+ "A",
+ Uri.parse("sip:6505551214@testims.com"),
+ Connection.STATE_ACTIVE,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2, participant3));
+ assertEquals(3, imsConference.getNumberOfParticipants());
+ verify(mMockTelephonyConnectionServiceProxy, times(3)).addExistingConnection(
+ any(PhoneAccountHandle.class), any(Connection.class),
+ eq(imsConference));
+ reset(mMockTelephonyConnectionServiceProxy);
+
+ // Remove one from the CEP (don't disconnect first); should have 2 participants now.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+ verify(mMockTelephonyConnectionServiceProxy, never()).addExistingConnection(
+ any(PhoneAccountHandle.class), any(Connection.class),
+ any(Conference.class));
+ }
+
+ /**
+ * Typically when a participant disconnects from a conference it is either:
+ * 1. Removed from a subsequent CEP update.
+ * 2. Marked as disconnected in a CEP update, and then removed from another CEP update.
+ *
+ * When a participant disconnects from a conference, some carriers will mark the disconnected
+ * participant as disconnected, but fail to send another CEP update with it removed.
+ *
+ * This test verifies that we can still enter single party emulation in this case.
+ */
+ @Test
+ @SmallTest
+ public void testSinglePartyEmulationEnterOnDisconnectParticipant() {
+ when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+ .thenReturn(false);
+
+ ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+ mMockTelephonyConnectionServiceProxy, mConferenceHost,
+ null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+
+ // Setup the initial conference state with 2 participants.
+ 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());
+ verify(mMockTelephonyConnectionServiceProxy, times(2)).addExistingConnection(
+ any(PhoneAccountHandle.class), any(Connection.class),
+ eq(imsConference));
+
+ // Some carriers keep disconnected participants around in the CEP; this will cause problems
+ // when we want to enter single party conference mode. Verify that this case is handled.
+ ConferenceParticipant participant2Disconnected = new ConferenceParticipant(
+ Uri.parse("tel:6505551213"),
+ "A",
+ Uri.parse("sip:6505551213@testims.com"),
+ Connection.STATE_DISCONNECTED,
+ Call.Details.DIRECTION_INCOMING);
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2Disconnected));
+ assertEquals(0, imsConference.getNumberOfParticipants());
+ reset(mMockTelephonyConnectionServiceProxy);
+
+ // Pretend to merge someone else into the conference.
+ imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+ Arrays.asList(participant1, participant2));
+ assertEquals(2, imsConference.getNumberOfParticipants());
+ verify(mMockTelephonyConnectionServiceProxy, times(2)).addExistingConnection(
+ any(PhoneAccountHandle.class), any(Connection.class),
+ eq(imsConference));
+ }
+
+ /**
* We have seen a scenario on a carrier where a conference event package comes in just prior to
* the call disconnecting with only the conference host in it. This caused a problem because
* it triggered exiting single party conference mode (due to a bug) and caused the call to not
@@ -127,14 +301,14 @@
null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
final boolean[] isConferenceState = new boolean[1];
- Conference.Listener conferenceListener = new Conference.Listener() {
+ TelephonyConferenceBase.TelephonyConferenceListener conferenceListener =
+ new TelephonyConferenceBase.TelephonyConferenceListener() {
@Override
- public void onConferenceStateChanged(Conference c, boolean isConference) {
- super.onConferenceStateChanged(c, isConference);
- isConferenceState[0] = isConference;
+ public void onConferenceMembershipChanged(Connection connection) {
+ isConferenceState[0] = connection.getConference() != null;
}
};
- imsConference.addListener(conferenceListener);
+ imsConference.addTelephonyConferenceListener(conferenceListener);
ConferenceParticipant participant1 = new ConferenceParticipant(
Uri.parse("tel:6505551212"),
@@ -159,8 +333,6 @@
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
Arrays.asList(participant1));
assertEquals(0, imsConference.getNumberOfParticipants());
- verify(mMockTelephonyConnectionServiceProxy, times(2)).removeConnection(
- any(Connection.class));
// Emulate a pre-disconnect conference event package; there will be zero participants.
imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index 275bcc6..cfdc2fd 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -16,6 +16,13 @@
package com.android.services.telephony;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.os.Looper;
import android.telecom.Conference;
import android.telecom.Connection;
@@ -23,18 +30,9 @@
import org.junit.Before;
import org.junit.Test;
-
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.ArgumentCaptor;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.any;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
import java.util.ArrayList;
import java.util.Collection;
@@ -50,7 +48,7 @@
private TelephonyConnectionServiceProxy mMockTelephonyConnectionServiceProxy;
@Mock
- private Conference.Listener mMockListener;
+ private TelephonyConferenceBase.TelephonyConferenceListener mMockListener;
private TestTelephonyConnection mTestTelephonyConnectionA;
private TestTelephonyConnection mTestTelephonyConnectionB;
@@ -94,8 +92,8 @@
// add telephony connection A
mControllerTest.add(mTestTelephonyConnectionA);
- mTestTelephonyConnectionA.setActive();
- mTestTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionA.setTelephonyConnectionActive();
+ mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
assertTrue(mTestTelephonyConnectionA.getConferenceables()
.contains(mTestTelephonyConnectionB));
@@ -146,8 +144,8 @@
// add telephony connection A
mControllerTest.add(mTestTelephonyConnectionA);
- mTestTelephonyConnectionA.setActive();
- mTestTelephonyConnectionB.setOnHold();
+ mTestTelephonyConnectionA.setTelephonyConnectionActive();
+ mTestTelephonyConnectionB.setTelephonyConnectionOnHold();
assertTrue(mTestTelephonyConnectionA.getConferenceables()
.contains(mTestTelephonyConnectionB));
@@ -160,7 +158,7 @@
verify(mMockTelephonyConnectionServiceProxy).addConference(argumentCaptor.capture());
// add a listener to the added conference
- argumentCaptor.getValue().addListener(mMockListener);
+ argumentCaptor.getValue().addTelephonyConferenceListener(mMockListener);
verify(mMockListener, never()).onDestroyed(any(Conference.class));
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 1329a77..6e11e51 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -17,35 +17,48 @@
package com.android.services.telephony;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.Context;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
import android.test.suitebuilder.annotation.SmallTest;
-import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.TelephonyTestBase;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
import org.junit.After;
@@ -56,6 +69,8 @@
import org.mockito.Mock;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
/**
@@ -69,19 +84,72 @@
private static final int SLOT_0_PHONE_ID = 0;
private static final int SLOT_1_PHONE_ID = 1;
+ private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+ "com.android.phone.tests", TelephonyConnectionServiceTest.class.getName());
+ private static final String TEST_ACCOUNT_ID1 = "id1";
+ private static final String TEST_ACCOUNT_ID2 = "id2";
+ private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_1 = new PhoneAccountHandle(
+ TEST_COMPONENT_NAME, TEST_ACCOUNT_ID1);
+ 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");
+
@Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
@Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
@Mock TelephonyConnectionService.PhoneFactoryProxy mPhoneFactoryProxy;
+ @Mock DeviceState mDeviceState;
+ @Mock TelephonyConnectionService.PhoneSwitcherProxy mPhoneSwitcherProxy;
+ @Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
+ @Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
+ @Mock TelephonyConnectionService.HandlerFactory mHandlerFactory;
+ @Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
+ @Mock Handler mMockHandler;
+ @Mock EmergencyNumberTracker mEmergencyNumberTracker;
+ @Mock PhoneSwitcher mPhoneSwitcher;
- TelephonyConnectionService mTestConnectionService;
+ private static class TestTelephonyConnectionService extends TelephonyConnectionService {
+
+ private final Context mContext;
+
+ TestTelephonyConnectionService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onCreate() {
+ // attach test context.
+ attachBaseContext(mContext);
+ super.onCreate();
+ }
+ }
+
+ private TelephonyConnectionService mTestConnectionService;
@Before
public void setUp() throws Exception {
super.setUp();
- mTestConnectionService = new TelephonyConnectionService();
+ mTestConnectionService = new TestTelephonyConnectionService(mContext);
mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
- mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
+ // Set configurations statically
+ doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
+ mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
+ doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+ mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
+ mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
+ HandlerThread mockHandlerThread = mock(HandlerThread.class);
+ doReturn(mockHandlerThread).when(mHandlerFactory).createHandlerThread(anyString());
+ doReturn(null).when(mockHandlerThread).getLooper();
+ doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
+ mTestConnectionService.setHandlerFactory(mHandlerFactory);
+ mTestConnectionService.setDeviceState(mDeviceState);
+ doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
+ .toTelecomDisconnectCause(anyInt(), any());
+ doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
+ .toTelecomDisconnectCause(anyInt(), any(), anyInt());
+ mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
+ mTestConnectionService.onCreate();
+ mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
}
@After
@@ -278,6 +346,37 @@
/**
* Prerequisites:
+ * - MSIM Device, only slot 1 inserted and PUK locked
+ * - slot 1 has higher capabilities
+ *
+ * Result: getFirstPhoneForEmergencyCall returns the slot 1 phone because it is the only one
+ * with a SIM inserted (even if it is PUK locked)
+ */
+ @Test
+ @SmallTest
+ public void testSlot1PinLockedAndSlot0Absent() {
+ Phone slot0Phone = makeTestPhone(SLOT_0_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ setDefaultPhone(slot0Phone);
+ setupDeviceConfig(slot0Phone, slot1Phone, SLOT_0_PHONE_ID);
+ setPhoneSlotState(SLOT_0_PHONE_ID, TelephonyManager.SIM_STATE_ABSENT);
+ setPhoneSlotState(SLOT_1_PHONE_ID, TelephonyManager.SIM_STATE_PIN_REQUIRED);
+ // Slot 1 has more capabilities
+ setPhoneRadioAccessFamily(slot0Phone, RadioAccessFamily.RAF_GSM);
+ setPhoneRadioAccessFamily(slot1Phone, RadioAccessFamily.RAF_LTE);
+ // Slot 1 has SIM inserted.
+ setSlotHasIccCard(SLOT_0_PHONE_ID, false /*isInserted*/);
+ setSlotHasIccCard(SLOT_1_PHONE_ID, true /*isInserted*/);
+
+ Phone resultPhone = mTestConnectionService.getFirstPhoneForEmergencyCall();
+
+ assertEquals(slot1Phone, resultPhone);
+ }
+
+ /**
+ * Prerequisites:
* - MSIM Device, two slots with SIMs inserted
* - Slot 1 is LTE capable, Slot 0 is GSM capable
*
@@ -522,7 +621,6 @@
* called.
*/
@Test
- @FlakyTest
@SmallTest
public void testRetryOutgoingOriginalConnection_redialTempFailOneSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
@@ -531,7 +629,7 @@
List<Phone> phones = new ArrayList<>(1);
phones.add(slot0Phone);
setPhones(phones);
- c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
@@ -554,7 +652,6 @@
* not called.
*/
@Test
- @FlakyTest
@SmallTest
public void testRetryOutgoingOriginalConnection_redialPermFailOneSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
@@ -563,7 +660,7 @@
List<Phone> phones = new ArrayList<>(1);
phones.add(slot0Phone);
setPhones(phones);
- c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
@@ -588,7 +685,6 @@
* PhoneAccount.
*/
@Test
- @FlakyTest
@SmallTest
public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
@@ -597,11 +693,15 @@
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
- c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
+ doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot0Phone);
+ doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot1Phone);
mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
@@ -626,7 +726,6 @@
* PhoneAccount.
*/
@Test
- @FlakyTest
@SmallTest
public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot() {
TestTelephonyConnection c = new TestTelephonyConnection();
@@ -635,11 +734,15 @@
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
- c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
+ doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot0Phone);
+ doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot1Phone);
mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
@@ -664,7 +767,6 @@
* notified of this twice.
*/
@Test
- @FlakyTest
@SmallTest
public void testRetryOutgoingOriginalConnection_redialTempFailTwoSlot_twoFailure() {
TestTelephonyConnection c = new TestTelephonyConnection();
@@ -673,11 +775,15 @@
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
- c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
+ doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot0Phone);
+ doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot1Phone);
// First Temporary failure
mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
@@ -716,7 +822,6 @@
* notified of the change to slot 1.
*/
@Test
- @FlakyTest
@SmallTest
public void testRetryOutgoingOriginalConnection_redialPermFailTwoSlot_twoFailure() {
TestTelephonyConnection c = new TestTelephonyConnection();
@@ -725,11 +830,15 @@
Phone slot1Phone = makeTestPhone(SLOT_1_PHONE_ID, ServiceState.STATE_OUT_OF_SERVICE,
false /*isEmergencyOnly*/);
setPhonesDialConnection(slot1Phone, c.getOriginalConnection());
- c.setAddress(Uri.parse("tel:+16505551212"), TelecomManager.PRESENTATION_ALLOWED);
+ c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
List<Phone> phones = new ArrayList<>(2);
phones.add(slot0Phone);
phones.add(slot1Phone);
setPhones(phones);
+ doReturn(PHONE_ACCOUNT_HANDLE_1).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot0Phone);
+ doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
+ slot1Phone);
// First Permanent failure
mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
@@ -767,6 +876,7 @@
// registration to occur.
Phone phone = c.getPhone();
c.setOriginalConnection(c.getOriginalConnection());
+ doReturn(mContext).when(phone).getContext();
// When the registration occurs, we'll capture the handler and message so we can post our
// own messages to it.
@@ -803,6 +913,239 @@
extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
}
+ /**
+ * Test that the TelephonyConnectionService successfully performs a DDS switch before a call
+ * when we are not roaming and the carrier only supports SUPL over the data plane.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_delayDial_carrierconfig_dds() {
+ Phone testPhone = setupConnectionServiceForDelayDial();
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ // Setup test to not support SUPL on the non-DDS subscription
+ doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+ getTestContext().getCarrierConfig().putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ null);
+ testPhone.getServiceState().setRoaming(false);
+ getTestContext().getCarrierConfig().putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+ getTestContext().getCarrierConfig().putString(
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
+ delayDialRunnable.run();
+
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+ eq(150) /*extensionTime*/, any());
+ }
+
+ /**
+ * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
+ * supports control-plane fallback.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_delayDial_nocarrierconfig() {
+ Phone testPhone = setupConnectionServiceForDelayDial();
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ // Setup test to not support SUPL on the non-DDS subscription
+ doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+ getTestContext().getCarrierConfig().putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ null);
+ testPhone.getServiceState().setRoaming(false);
+ getTestContext().getCarrierConfig().putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+ getTestContext().getCarrierConfig().putString(
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+ delayDialRunnable.run();
+
+ verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+ }
+
+ /**
+ * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
+ * supports control-plane fallback.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
+ Phone testPhone = setupConnectionServiceForDelayDial();
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ // If the non-DDS supports SUPL, dont switch data
+ doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+ getTestContext().getCarrierConfig().putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ null);
+ testPhone.getServiceState().setRoaming(false);
+ getTestContext().getCarrierConfig().putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+ getTestContext().getCarrierConfig().putString(
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+ delayDialRunnable.run();
+
+ verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+ }
+
+ /**
+ * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier does
+ * not support control-plane fallback CarrierConfig while roaming.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_delayDial_roaming_nocarrierconfig() {
+ Phone testPhone = setupConnectionServiceForDelayDial();
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ // Setup test to not support SUPL on the non-DDS subscription
+ doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+ getTestContext().getCarrierConfig().putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ null);
+ testPhone.getServiceState().setRoaming(true);
+ getTestContext().getCarrierConfig().putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
+ getTestContext().getCarrierConfig().putString(
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+ delayDialRunnable.run();
+
+ verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
+ }
+
+ /**
+ * Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
+ * supports control-plane fallback CarrierConfig and the roaming partner is configured to look
+ * like a home network.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_delayDial_roamingcarrierconfig() {
+ Phone testPhone = setupConnectionServiceForDelayDial();
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ // Setup voice roaming scenario
+ String testRoamingOperator = "001001";
+ // In some roaming conditions, we are not technically "roaming"
+ testPhone.getServiceState().setRoaming(false);
+ testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+ // Setup test to not support SUPL on the non-DDS subscription
+ doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+ String[] roamingPlmns = new String[1];
+ roamingPlmns[0] = testRoamingOperator;
+ getTestContext().getCarrierConfig().putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ roamingPlmns);
+ getTestContext().getCarrierConfig().putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+ getTestContext().getCarrierConfig().putString(
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+ delayDialRunnable.run();
+
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+ eq(0) /*extensionTime*/, any());
+ }
+
+ /**
+ * Test that the TelephonyConnectionService does perform a DDS switch even though the carrier
+ * supports control-plane fallback CarrierConfig if we are roaming and the roaming partner is
+ * configured to use data plane only SUPL.
+ */
+ @Test
+ @SmallTest
+ public void testCreateOutgoingEmergencyConnection_delayDial__roaming_roamingcarrierconfig() {
+ Phone testPhone = setupConnectionServiceForDelayDial();
+ Runnable delayDialRunnable = verifyRunnablePosted();
+
+ // Setup voice roaming scenario
+ String testRoamingOperator = "001001";
+ testPhone.getServiceState().setRoaming(true);
+ testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
+ // Setup test to not support SUPL on the non-DDS subscription
+ doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+ String[] roamingPlmns = new String[1];
+ roamingPlmns[0] = testRoamingOperator;
+ getTestContext().getCarrierConfig().putStringArray(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
+ roamingPlmns);
+ getTestContext().getCarrierConfig().putInt(
+ CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
+ CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
+ getTestContext().getCarrierConfig().putString(
+ CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
+ delayDialRunnable.run();
+
+ verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
+ eq(0) /*extensionTime*/, any());
+ }
+
+ /**
+ * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number.
+ * @return the Phone associated with slot 0.
+ */
+ private Phone setupConnectionServiceForDelayDial() {
+ ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+ .setAddress(TEST_ADDRESS)
+ .build();
+ Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
+ false /*isEmergencyOnly*/);
+ Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_OUT_OF_SERVICE,
+ false /*isEmergencyOnly*/);
+ List<Phone> phones = new ArrayList<>(2);
+ doReturn(true).when(testPhone0).isRadioOn();
+ doReturn(true).when(testPhone1).isRadioOn();
+ phones.add(testPhone0);
+ phones.add(testPhone1);
+ setPhones(phones);
+ setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+ setupDeviceConfig(testPhone0, testPhone1, 1);
+ 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();
+
+ android.telecom.Connection testConnection = mTestConnectionService
+ .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+ assertNotNull("test connection was not set up correctly.", testConnection);
+
+ return testPhone0;
+ }
+
+ private Runnable verifyRunnablePosted() {
+ ArgumentCaptor<Message> runnableCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockHandler).sendMessageDelayed(runnableCaptor.capture(), anyLong());
+ assertNotNull("Invalid Message created", runnableCaptor.getValue());
+ Runnable runnable = runnableCaptor.getValue().getCallback();
+ assertNotNull("sendMessageDelayed never occurred.", runnableCaptor);
+ return runnable;
+ }
+
+ private EmergencyNumber setupEmergencyNumber(Uri address) {
+ return new EmergencyNumber(address.getSchemeSpecificPart(), "", "",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+ Collections.emptyList(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+ }
+
+ private void setupHandleToPhoneMap(PhoneAccountHandle handle, Phone phone) {
+ // use subId 0
+ when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(handle)).thenReturn(0);
+ when(mSubscriptionManagerProxy.getPhoneId(0)).thenReturn(0);
+ when(mPhoneFactoryProxy.getPhone(0)).thenReturn(phone);
+ }
+
private AsyncResult getSuppServiceNotification(int notificationType, int code) {
SuppServiceNotification notification = new SuppServiceNotification();
notification.notificationType = notificationType;
@@ -815,9 +1158,12 @@
ServiceState testServiceState = new ServiceState();
testServiceState.setState(serviceState);
testServiceState.setEmergencyOnly(isEmergencyOnly);
+ when(phone.getContext()).thenReturn(mContext);
when(phone.getServiceState()).thenReturn(testServiceState);
when(phone.getPhoneId()).thenReturn(phoneId);
when(phone.getDefaultPhone()).thenReturn(phone);
+ when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
+ when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
return phone;
}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
new file mode 100644
index 0000000..7d15680
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -0,0 +1,25 @@
+package com.android.services.telephony;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.os.Bundle;
+import android.telecom.Connection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+public class TelephonyConnectionTest {
+
+ @Test
+ public void testCodecInIms() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+ c.updateState();
+ Bundle extras = c.getExtras();
+ int codec = extras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE);
+ assertEquals(codec, Connection.AUDIO_CODEC_AMR);
+ }
+
+}
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index b6e4bf3..5b31c0f 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -19,10 +19,12 @@
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.telecom.PhoneAccountHandle;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -31,6 +33,7 @@
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -56,6 +59,9 @@
@Mock
Resources mMockResources;
+ @Mock
+ EmergencyNumberTracker mEmergencyNumberTracker;
+
private Phone mMockPhone;
private int mNotifyPhoneAccountChangedCount = 0;
private List<String> mLastConnectionEvents = new ArrayList<>();
@@ -75,11 +81,16 @@
mOriginalConnection = mock(Connection.class);
// Set up mMockRadioConnection and mMockPhone to contain an active call
when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
+ when(mOriginalConnection.getState()).thenReturn(Call.State.ACTIVE);
+ when(mMockRadioConnection.getAudioCodec()).thenReturn(
+ android.telecom.Connection.AUDIO_CODEC_AMR);
when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
when(mMockRadioConnection.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
doNothing().when(mMockRadioConnection).addListener(any(Connection.Listener.class));
doNothing().when(mMockRadioConnection).addPostDialListener(
any(Connection.PostDialListener.class));
+ when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
+ when(mMockPhone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
when(mMockPhone.getContext()).thenReturn(mMockContext);
when(mMockPhone.getCurrentSubscriberUris()).thenReturn(null);
@@ -125,6 +136,28 @@
// Do nothing since the original connection is mock object
}
+ @Override
+ public PersistableBundle getCarrierConfig() {
+ // Depends on PhoneGlobals for context in TelephonyConnection, do not implement during
+ // testing.
+ return new PersistableBundle();
+ }
+
+ @Override
+ public CharSequence getResourceText(int messageId) {
+ return "TEST";
+ }
+
+ @Override
+ public String getResourceString(int id) {
+ return "TEST";
+ }
+
+ @Override
+ void refreshConferenceSupported() {
+ // Requires ImsManager dependencies, do not implement during testing.
+ }
+
public int getNotifyPhoneAccountChangedCount() {
return mNotifyPhoneAccountChangedCount;
}