DO NOT MERGE - Restrict ability to add call based on device provision status am: 0044d82fde
am: b4b8bf90dc -s ours
* commit 'b4b8bf90dc7cf5a1ea3330dae71463379e7bdce5':
DO NOT MERGE - Restrict ability to add call based on device provision status
diff --git a/Android.mk b/Android.mk
index 395aacc..0917793 100644
--- a/Android.mk
+++ b/Android.mk
@@ -4,8 +4,6 @@
include $(CLEAR_VARS)
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := \
- guava \
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 427b248..a2c0b91 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.server.telecom"
coreApp="true"
- android:sharedUserId="android.uid.phone">
+ android:sharedUserId="android.uid.system">
<!-- Prevents the activity manager from delaying any activity-start
requests by this package, including requests immediately after
@@ -27,6 +27,7 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
@@ -38,18 +39,6 @@
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.BROADCAST_CALLLOG_INFO" />
- <!-- Protects the ability to register any PhoneAccount with a capability flags of either
- PhoneAccount#CAPABILITY_CALL_PROVIDER or PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. -->
- <permission
- android:name="com.android.server.telecom.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION"
- android:label="Register CALL_PROVIDER or SIM_SUBSCRIPTION PhoneAccount"
- android:protectionLevel="signature"/>
-
- <permission
- android:name="com.android.server.telecom.permission.REGISTER_CONNECTION_MANAGER"
- android:label="Register CONNECTION_MANAGER PhoneAccount"
- android:protectionLevel="signature"/>
-
<permission
android:name="android.permission.BROADCAST_CALLLOG_INFO"
android:label="Broadcast the call type/duration information"
@@ -64,12 +53,11 @@
can check for incompatible APIs. -->
<uses-sdk android:minSdkVersion="19" />
- <application android:name="TelecomApp"
- android:persistent="true"
- android:label="@string/telecommAppLabel"
+ <application android:label="@string/telecommAppLabel"
android:icon="@mipmap/ic_launcher_phone"
android:allowBackup="false"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:process="system">
<!-- CALL vs CALL_PRIVILEGED vs CALL_EMERGENCY
We have three different intents through which a call can be initiated each with its
@@ -90,7 +78,8 @@
<activity android:name="CallActivity"
android:theme="@style/Theme.Telecomm.Transparent"
android:permission="android.permission.CALL_PHONE"
- android:excludeFromRecents="true">
+ android:excludeFromRecents="true"
+ android:process=":ui">
<!-- CALL action intent filters for the various ways of initiating an outgoing call. -->
<intent-filter>
<action android:name="android.intent.action.CALL" />
@@ -126,7 +115,8 @@
the system from processing this intent (b/8871505). -->
<activity-alias android:name="PrivilegedCallActivity"
android:targetActivity="CallActivity"
- android:permission="android.permission.CALL_PRIVILEGED">
+ android:permission="android.permission.CALL_PRIVILEGED"
+ android:process=":ui">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.CALL_PRIVILEGED" />
<category android:name="android.intent.category.DEFAULT" />
@@ -160,7 +150,8 @@
that scheme be removed from this activity? -->
<activity-alias android:name="EmergencyCallActivity"
android:targetActivity="CallActivity"
- android:permission="android.permission.CALL_PRIVILEGED">
+ android:permission="android.permission.CALL_PRIVILEGED"
+ android:process=":ui">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.CALL_EMERGENCY" />
<category android:name="android.intent.category.DEFAULT" />
@@ -185,16 +176,8 @@
</intent-filter>
</activity-alias>
- <activity-alias android:name="IncomingCallActivity"
- android:targetActivity="CallActivity"
- android:exported="true">
- <intent-filter>
- <action android:name="android.telecom.action.INCOMING_CALL" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity-alias>
-
- <receiver android:name="TelecomBroadcastReceiver" android:exported="false">
+ <receiver android:name="TelecomBroadcastReceiver" android:exported="false"
+ android:process="system">
<intent-filter>
<action android:name="com.android.server.telecom.ACTION_CALL_BACK_FROM_NOTIFICATION" />
<action android:name="com.android.server.telecom.ACTION_CALL_BACK_FROM_NOTIFICATION" />
@@ -202,7 +185,8 @@
</intent-filter>
</receiver>
- <receiver android:name="PhoneAccountBroadcastReceiver">
+ <receiver android:name="PhoneAccountBroadcastReceiver"
+ android:process="system">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" />
@@ -211,7 +195,9 @@
<activity android:name=".RespondViaSmsSettings$Settings"
android:label="@string/respond_via_sms_setting_title"
- android:configChanges="orientation|screenSize|keyboardHidden">
+ android:configChanges="orientation|screenSize|keyboardHidden"
+ android:theme="@style/Theme.Telecom.DialerSettings"
+ android:process=":ui">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
@@ -221,19 +207,31 @@
android:configChanges="orientation|screenSize|keyboardHidden"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
- android:theme="@style/Theme.Telecomm.Transparent">
+ android:theme="@style/Theme.Telecomm.Transparent"
+ android:process=":ui">
</activity>
<receiver android:name=".CallReceiver"
- android:exported="false">
+ android:exported="true"
+ android:permission="android.permission.MODIFY_PHONE_STATE"
+ android:process="system">
</receiver>
<service android:name="BluetoothPhoneService"
- android:singleUser="true">
+ android:singleUser="true"
+ android:process="system">
<intent-filter>
<action android:name="android.bluetooth.IBluetoothHeadsetPhone" />
</intent-filter>
</service>
+ <service android:name=".TelecomService"
+ android:singleUser="true"
+ android:process="system">
+ <intent-filter>
+ <android android:name="android.telecom.ITelecomService" />
+ </intent-filter>
+ </service>
+
</application>
</manifest>
diff --git a/res/drawable-hdpi/ic_arrow_back_white_24dp.png b/res/drawable-hdpi/ic_arrow_back_white_24dp.png
new file mode 100644
index 0000000..5fa4948
--- /dev/null
+++ b/res/drawable-hdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_more_vert_white_24dp.png b/res/drawable-hdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..675f3ee
--- /dev/null
+++ b/res/drawable-hdpi/ic_more_vert_white_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_white_24dp.png b/res/drawable-mdpi/ic_arrow_back_white_24dp.png
new file mode 100644
index 0000000..9e662f6
--- /dev/null
+++ b/res/drawable-mdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_more_vert_white_24dp.png b/res/drawable-mdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..1d8ad18
--- /dev/null
+++ b/res/drawable-mdpi/ic_more_vert_white_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_white_24dp.png b/res/drawable-xhdpi/ic_arrow_back_white_24dp.png
new file mode 100644
index 0000000..addbfc8
--- /dev/null
+++ b/res/drawable-xhdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_more_vert_white_24dp.png b/res/drawable-xhdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..1b04eda
--- /dev/null
+++ b/res/drawable-xhdpi/ic_more_vert_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png b/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png
new file mode 100644
index 0000000..4057cc5
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_more_vert_white_24dp.png b/res/drawable-xxhdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..2955c02
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_more_vert_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png b/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png
new file mode 100644
index 0000000..02f2f6f
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_arrow_back_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png b/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png
new file mode 100644
index 0000000..25b6f9f
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_more_vert_white_24dp.png
Binary files differ
diff --git a/res/drawable/ic_back_arrow.xml b/res/drawable/ic_back_arrow.xml
new file mode 100644
index 0000000..cbb4c11
--- /dev/null
+++ b/res/drawable/ic_back_arrow.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_arrow_back_white_24dp"
+ android:autoMirrored="true" />
diff --git a/res/menu/respond_via_message_settings_menu.xml b/res/menu/respond_via_message_settings_menu.xml
deleted file mode 100644
index 6f5e246..0000000
--- a/res/menu/respond_via_message_settings_menu.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 Google Inc.
-
- 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.
--->
-
-<!-- Menu for Respond-via-Message settings screen. -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/respond_via_message_reset"
- android:title="@string/respond_via_sms_menu_reset_default_activity" />
-</menu>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 7decddd..aa793a1 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Redigeer vinnige antwoorde"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Vinnige antwoord"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Stel verstek program terug"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Boodskap gestuur na <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Hierdie gebruiker word nie toegelaat om nienood-foonoproepe te maak nie"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Oproep nie gestuur nie; geen geldige nommer is ingevoer nie."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Net noodoproepe word deur die toesteleienaar toegelaat"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Voer \'n geldige nommer in om \'n oproep te maak."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Oproep kan nie op die oomblik bygevoeg word nie."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Vermiste stemboodskapnommer"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Geen stemboodskapnommer is op die SIM-kaart gestoor nie."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Voeg nommer by"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d44f01b..fec8369 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ፈጣን ምላሾች አርትዕ"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ፈጣን ምላሽ"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ነባሪውን መተግበሪያ ዳግም አስጀምር"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"ለ <xliff:g id="PHONE_NUMBER">%s</xliff:g> የተላከ መልዕክት"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ይህ ተጠቃሚ የአስቸኳይ አደጋ ያልሆኑ ስልክ ጥሪዎችን እንዲያደርጉ አልተፈቀደላቸውም"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"ጥሪ አልተላከም፣ ትክክለኛ ቁጥር አልገባም።"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"የአስቸኳይ አደጋ ጥሪዎች ብቻ ናቸው በባለቤቱ የተፈቀዱት"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"አንድ ጥሪ ለማድረግ የሚሰራ ቁጥር ያስገቡ።"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ጥሪ በዚህ ጊዜ ላይ ሊታከል አይችልም።"</string>
<string name="no_vm_number" msgid="4164780423805688336">"የድምፅመልዕክት ቁጥርአመለጠ"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"በSIM ካርዱ ላይምንም የድምፅመልዕክት ቁጥር አልከተቀመጠም።"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"ቁጥር አክል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index a30f1e6..14fc640 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"تعديل الردود السريعة"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"رد سريع"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"إعادة تعيين التطبيق الافتراضي"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"تم إرسال الرسالة إلى <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"هذا المستخدم غير مسموح له بإجراء مكالمات هاتفية بخلاف مكالمات الطوارئ"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"لم يتم إرسال الاتصال، لم يتم إدخال رقم صالح."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"مسموح لمالك الجهاز بمكالمات الطوارئ فقط"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"لإجراء مكالمة، أدخل رقمًا صالحًا."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"لا يمكن إضافة مكالمة في الوقت الحالي."</string>
<string name="no_vm_number" msgid="4164780423805688336">"رقم البريد الصوتي مفقود"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"لم يتم تخزين رقم بريد صوتي على بطاقة SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"إضافة رقم"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index d3c0d96..d706029 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Редактиране на бързи отговори"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Бърз отговор"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Възст. на станд. настройки"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"До <xliff:g id="PHONE_NUMBER">%s</xliff:g> бе изпратено съобщение."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"На този потребител е разрешено да провежда само спешни обаждания"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Обаждането не е изпратено, не е въведен валиден номер."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Собственикът на устройството разрешава само спешни обаждания"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"За да извършите обаждане, въведете валиден номер."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Понастоящем обаждането не може да бъде добавено."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Липсващ номер на гласова поща"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"На SIM картата няма съхранен номер за гласова поща."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Добавяне на номер"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 9fc3317..4379a37 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"দ্রুত প্রতিক্রিয়াগুলির সম্পাদনা করুন"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"দ্রুত প্রতিক্রিয়া"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ডিফল্ট অ্যাপ্লিকেশন পুনরায় সেট করুন"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> এ বার্তা পাঠানো হয়েছে৷"</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"ফোন অ্যাকাউন্ট সেটিংস"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"আউটগোয়িং ডিফল্ট অ্যাকাউন্ট"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi এর মাধ্যমে কল করার অ্যাকাউন্ট"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"প্রতিবার জিজ্ঞাসা করুন"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi কলিং ব্যবহার করবেন না"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"জরুরী নয় এমন ফোন কল করার জন্য ব্যবহারকারী অনুমতি পাননি"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"ডিভাইসের মালিক শুধুমাত্র জরুরি কলগুলিতে অনুমতি দিয়েছেন"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"কোনো কল স্থাপন করতে, একটি বৈধ নম্বর লিখুন৷"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"এই মুহূর্তে কল যোগ করা যাবে না৷"</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"ভয়েসমেল নম্বর অনুপস্থিত"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"সিম কার্ডটিতে কোনো ভয়েসমেল নম্বর সংরক্ষিত নেই৷"</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"একটি নম্বর যোগ করুন"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index e7011f5..c2a01f5 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edita les respostes ràpides"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta ràpida"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Restableix aplic. predetermin."</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Missatge enviat a <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Aquest usuari no pot fer trucades que no siguin d\'emergència."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"No s\'ha enviat la trucada perquè no s\'ha introduït cap número vàlid."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"El propietari del dispositiu només permet les trucades d\'emergència."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Per realitzar una trucada, introdueix un número vàlid."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"En aquest moment no es pot afegir la trucada."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Falta el número de correu de veu"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"No hi ha cap número de correu de veu emmagatzemat a la targeta SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Afegeix número"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index db612a4..ff24203 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Upravit rychlé odpovědi"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Rychlá odpověď"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Resetovat výchozí aplikaci"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Zpráva byla odeslána na číslo <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Uživatel smí uskutečňovat pouze tísňová volání."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Hovor nelze spojit, nebylo zadáno platné číslo."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vlastník zařízení povolil pouze tísňová volání."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Chcete-li uskutečnit hovor, zadejte platné telefonní číslo."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hovor aktuálně nelze přidat."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Chybí číslo hlasové schránky"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Na SIM kartě není uloženo žádné číslo hlasové schránky."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Přidat číslo"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index e7ba06f..93e4b83 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Rediger hurtige svar"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hurtigt svar"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Nulstil standardapp"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Meddelelsen er sendt til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Denne bruger har kun tilladelse til at foretage nødopkald"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Opkaldet kunne ikke sendes. Der blev ikke indtastet et gyldigt nummer."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Enhedens ejer tillader kun nødopkald."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Indtast et gyldigt nummer for at foretage et opkald."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Opkaldet kan ikke tilføjes på nuværende tidspunkt."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Telefonsvarernummer mangler"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Der er ikke gemt noget telefonsvarernummer på SIM-kortet."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Tilføj nummer"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 9a5ee71..7e99980 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Kurzantworten bearbeiten"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Kurzantwort"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Standard-App zurücksetzen"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Nachricht an <xliff:g id="PHONE_NUMBER">%s</xliff:g> gesendet"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Dieser Nutzer darf abgesehen von Notrufen keine Anrufe tätigen."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Anruf nicht verbunden; keine gültige Nummer eingegeben."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Der Geräteinhaber hat nur Notrufe zugelassen."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Geben Sie eine gültige Nummer ein."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Der Anruf kann momentan nicht hinzugefügt werden."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Fehlende Mailbox-Nummer"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Auf der SIM-Karte ist keine Mailbox-Nummer gespeichert."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Nummer hinzufügen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 7bea2ac..1805be5 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Επεξεργασία γρήγορων απαντήσεων"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Γρήγορη απάντηση"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Επαναφορά προεπιλ. εφαρμογής"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Το μήνυμα εστάλη στο <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Δεν επιτρέπεται σε αυτόν το χρήστη να πραγματοποιεί τηλεφωνικές κλήσεις οι οποίες δεν είναι εκτάκτου ανάγκης"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Η κλήση δεν πραγματοποιήθηκε, δεν έγινε εισαγωγή έγκυρου αριθμού."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Από τον κάτοχο της συσκευής επιτρέπονται μόνο κλήσεις έκτακτης ανάγκης"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Για να πραγματοποιήσετε μια κλήση, εισαγάγετε έναν έγκυρο αριθμό."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Δεν είναι δυνατή η προσθήκη κλήσης αυτήν τη στιγμή."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Λείπει ο αριθμός αυτόματου τηλεφωνητή"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Δεν έχει αποθηκευτεί αριθμός για τον αυτόματο τηλεφωνητή στην κάρτα SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Προσθήκη αριθμού"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 97cc163..ae6fbb4 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit quick responses"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Quick response"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Reset default app"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Message sent to <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"This user is not allowed to make non-emergency phone calls"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Call not sent. No valid number entered."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Only emergency calls are allowed by the device owner"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"To place a call, enter a valid number."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Call cannot be added at this time."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Missing voicemail number"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"No voicemail number is stored on the SIM card."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Add number"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 97cc163..ae6fbb4 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit quick responses"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Quick response"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Reset default app"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Message sent to <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"This user is not allowed to make non-emergency phone calls"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Call not sent. No valid number entered."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Only emergency calls are allowed by the device owner"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"To place a call, enter a valid number."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Call cannot be added at this time."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Missing voicemail number"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"No voicemail number is stored on the SIM card."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Add number"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 85ee3fd..4ca4e93 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editar respuestas rápidas"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respuesta rápida"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Restablecer aplic. predeterm."</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaje enviado a <xliff:g id="PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Este usuario solo tiene permisos para hacer llamadas de emergencia."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Llamada no enviada. No se ingresó un número válido."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"El propietario del dispositivo solo permite las llamadas de emergencia."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar una llamada, ingresa un número válido."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No se puede agregar la llamada en este momento."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Falta el número de correo de voz"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"No hay un número de correo de voz almacenado en la tarjeta SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Agregar número"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 83743fb..7fc0f9c 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editar respuestas rápidas"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respuestas rápidas"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Restablecer aplicación"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaje enviado a <xliff:g id="PHONE_NUMBER">%s</xliff:g>"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Este usuario solo puede realizar llamadas de emergencia"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"No se ha enviado la llamada; el número introducido no es válido."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"El propietario del dispositivo solo permite llamadas de emergencia"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar una llamada, introduce un número válido."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No se puede añadir la llamada en este momento."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Falta el número del buzón de voz."</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"No se ha almacenado ningún número de buzón de voz en la tarjeta SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Añadir número"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 3ce3075..b101f26 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Kiirvastuste muutmine"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Kiirvastus"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Lähtesta vaikerakendus"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Sõnum on saadetud numbrile <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"See kasutaja võib teha ainult hädaabikõnesid"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Kõnet ei tehtud, sisestati kehtetu number."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Seadme omanik lubab ainult hädaabikõnesid"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Helistamiseks sisestage kehtiv number."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Kõnet ei saa praegu lisada."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Puudub kõnepostinumber"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM-kaardile pole salvestatud ühtegi kõnepostinumbrit."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Lisa number"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index b194e6e..8450649 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editatu erantzun bizkorrak"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Erantzun bizkorra"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Berrezarri aplik. lehenetsia"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mezua bidali da <xliff:g id="PHONE_NUMBER">%s</xliff:g> zenbakira."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Telefonoaren kontuaren ezarpenak"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Irteerako kontu lehenetsia"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi bidezko deiak egiteko kontua"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Galdetu beti"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Ez erabili Wi-Fi bidezko deiak"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Erabiltzaile honek ez du larrialdietakoak ez diren telefono-deiak egiteko baimenik"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Gailuaren jabeak larrialdi-deiak bakarrik egitea onartzen du"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Deitzeko, idatzi balio duen zenbaki bat."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Une honetan ezin da deirik gehitu."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"Erantzungailuaren zenbakia falta da"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"Ez da erantzungailuaren zenbakirik gorde SIM txartelean."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"Gehitu zenbakia"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a54b374..a0ba00b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ویرایش پاسخهای سریع"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"پاسخ سریع"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"بازنشانی برنامه پیشفرض"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"پیام به <xliff:g id="PHONE_NUMBER">%s</xliff:g> ارسال شد."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"این کاربر اجازه برقراری تماسهای تلفنی غیر اضطراری ندارد."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"تماس ارسال نشد، هیچ شماره معتبری وارد نشده است."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"مالک دستگاه فقط تماسهای اضطراری را مجاز کرده است"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"برای برقراری تماس، یک شماره معتبر وارد کنید."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"در این زمان نمیتوان تماسی اضافه کرد."</string>
<string name="no_vm_number" msgid="4164780423805688336">"عدم وجود شماره پست صوتی"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"شماره پست صوتی در سیم کارت ذخیره نشده است."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"افزودن شماره"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index eab9a8b..93f268e 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Muokkaa pikavastausta"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Pikavastaukset"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Palauta oletussovellus"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Viesti lähetetty numeroon <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Tämä käyttäjä saa soittaa vain hätäpuheluita"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Puhelua ei soitettu, et antanut kelvollista numeroa."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Laitteen omistaja on sallinut vain hätäpuhelut"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Soita antamalla kelvollinen numero."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Puhelua ei voi lisätä juuri nyt."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Puhelinvastaajan numero puuttuu"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM-kortille ei ole tallennettu puhelinvastaajan numeroa."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Lisää numero"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 63bd398..0d0ab25 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Modif. rép. rapides"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Réponse rapide"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Rétablir application par défaut"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Message envoyé à <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Cet utilisateur n\'est pas autorisé à faire des appels téléphoniques non urgents"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"L\'appel n\'a pas été effectué, car le numéro entré n\'est pas valide."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Seuls les appels d\'urgence sont autorisés par le propriétaire de l\'appareil"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Pour faire un appel, entrez un numéro valide."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Impossible d\'ajouter l\'appel pour le moment."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Numéro de messagerie vocale manquant"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Aucun numéro de messagerie vocale n\'est enregistré sur la carte SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Ajouter un numéro"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 2fb06f6..7983939 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Modifier les réponses rapides"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Réponse rapide"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Rétablir application par défaut"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Message envoyé à <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Cet utilisateur n\'est autorisé à effectuer que des appels d\'urgence."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"L\'appel n\'a pas été effectué. Aucun numéro valide n\'a été saisi."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Le propriétaire de l\'appareil n\'autorise que les appels d\'urgence."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Pour émettre un appel, veuillez saisir un numéro valide."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Impossible d\'ajouter un appel pour le moment."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Numéro de messagerie vocale manquant"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Aucun numéro de messagerie vocale n\'est enregistré sur la carte SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Ajouter un numéro"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index f75da28..0dba5fc 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editar respostas rápidas"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Restablecer aplicación predet."</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaxe enviada ao <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Configuración da conta do teléfono"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Conta saínte predeterminada"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Conta de chamadas wifi"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Preguntar sempre"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Non utilizar as chamadas wifi"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Este usuario non ten permiso para facer chamadas telefónicas que non sexan de emerxencia"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"O propietario do dispositivo só permite as chamadas de emerxencia"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar unha chamada, introduce un número válido."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Neste momento non se pode engadir a chamada."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"Falta o número de correo de voz"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"Non hai ningún número de correo de voz almacenado na tarxeta SIM."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"Engadir número"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index debd49b..802c206 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -30,15 +30,15 @@
<string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"मैं आपको वापस कॉल करूंगा/करूंगी."</string>
<string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"मैं आपको बाद में कॉल करूंगा/करूंगी."</string>
<string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"अभी बात नहीं हो सकती. बाद में कॉल करें?"</string>
- <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"त्वरित प्रतिसाद"</string>
- <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"त्वरित प्रतिसाद संपादित करें"</string>
+ <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"झटपट उत्तर"</string>
+ <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"झटपट उत्तर संपादित करें"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
- <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"त्वरित प्रतिसाद"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"डिफ़ॉल्ट ऐप्स रीसेट करें"</string>
+ <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"झटपट उत्तर"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> को संदेश भेजा गया."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"इस उपयोगकर्ता को गैर-आपातकालीन फ़ोन कॉल करने की अनुमति नहीं है"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"कॉल नहीं भेजा गया, कोई मान्य नंबर नहीं डाला गया था."</string>
- <string name="no_vm_number" msgid="4164780423805688336">"गुम ध्वनिमेल नंबर"</string>
- <string name="no_vm_number_msg" msgid="1300729501030053828">"सिम कार्ड पर कोई ध्वनिमेल नंबर संग्रहीत नहीं है."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"डिवाइस स्वामी द्वारा केवल आपातकालीन कॉल करने की अनुमति है"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"कॉल करने के लिए, मान्य नंबर डालें."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"इस समय कॉल नहीं जोड़ा जा सकता."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"गुम वॉयस मेल नंबर"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"सिम कार्ड पर कोई वॉयस मेल नंबर संग्रहीत नहीं है."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"नंबर जोड़ें"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index f86f4a9..af4b0c0 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Uređivanje brzih odgovora"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Brzi odgovor"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Poništavanje zadane aplikacije"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Poruka poslana na broj <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Ovaj korisnik smije uspostavljati samo hitne pozive"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Poziv nije uspostavljen, nije unesen važeći broj."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vlasnik uređaja dopušta samo hitne pozive"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Unesite važeći broj da biste uspostavili poziv."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Poziv trenutačno nije moguć."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Nedostaje broj govorne pošte"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Na SIM kartici nije spremljen broj govorne pošte."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Dodaj broj"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index cc7c77a..eb6e388 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Gyors válaszok szerkesztése"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Gyors válasz"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Alapalkalmazás visszaállítása"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Üzenet elküldve ide: <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Ez a felhasználó csak vészhelyzeti telefonhívásokat kezdeményezhet"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"A hívás indítása érvénytelen szám beírása miatt nem történt meg."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Az eszköz tulajdonosa csak a segélyhívásokat engedélyezte"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Hívásindításhoz adjon meg egy érvényes számot."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Jelenleg nem lehet videohívást hozzáadni."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Hiányzik a hangposta száma"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Nincs hangpostaszám a SIM kártyán."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Szám hozzáadása"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 8f47792..3879e04 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Խմբագրել արագ պատասխանները"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Արագ պատասխան"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Վերականգնել լռելյայն հավելվածը"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Հաղորդագրությունն ուղարկվել է <xliff:g id="PHONE_NUMBER">%s</xliff:g>-ին:"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Այս օգտվողին չի թույլատրվում կատարել ոչ արտակարգ զանգեր"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Զանգը չի կատարվել, անվավեր համար է մուտքագրված:"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Սարքի սեփականատերը թույլատրում է միայն արտակարգ իրավիճակի զանգերը"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Զանգ կատարելու համար մուտքագրեք ճիշտ համար:"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Հնարավոր չէ ևս մեկ զանգ ավելացնել այս պահին:"</string>
<string name="no_vm_number" msgid="4164780423805688336">"Բացակայում է ձայնային փոստի համարը"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM քարտում ձայնային փոստի ոչ մի համար գրանցված չէ:"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Ավելացնել համար"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 755eb9d..fac45f3 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit tanggapan cepat"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Tanggapan cepat"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Setel ulang aplikasi default"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Pesan dikirim ke <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Pengguna ini tidak diizinkan untuk membuat panggilan telepon non-darurat"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Panggilan tidak terkirim, tidak ada nomor valid yang dimasukkan."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Hanya panggilan darurat yang diizinkan oleh pemilik perangkat"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Untuk melakukan panggilan telepon, masukkan nomor yang valid."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Panggilan tidak dapat ditambahkan untuk saat ini."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Nomor kotak pesan hilang"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Tidak ada nomor kotak pesan tersimpan pada kartu SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Tambahkan nomor"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 8c37bd3..1862dac 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Breyta snarsvörum"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Snarsvar"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Endurstilla sjálfgefið forrit"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Skilaboð send til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Reikningsstillingar síma"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Sjálfgefinn sendingarreikningur"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Reikningur Wi-Fi símtala"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Spyrja í hvert skipti"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Ekki nota Wi-Fi símtöl"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Þessi notandi hefur ekki leyfi til að hringja símtöl önnur en neyðarsímtöl"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Eigandi tækisins leyfir aðeins neyðarsímtöl"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Sláðu inn gilt númer til að hringja símtal."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Ekki er hægt að bæta símtali við sem stendur."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"Talhólfsnúmer vantar"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"Ekkert talhólfsnúmer er vistað á SIM-kortinu."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"Bæta númeri við"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index ead17ae..584bbad 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Modifica risposte rapide"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Risposta rapida"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Reimposta app predefinita"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Messaggio inviato a <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Questo utente non è autorizzato a fare telefonate diverse da quelle di emergenza"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Chiamata non inviata. Nessun numero valido inserito."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Il proprietario del dispositivo consente soltanto chiamate di emergenza"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Per effettuare una chiamata, inserisci un numero valido."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Al momento non è possibile aggiungere la chiamata."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Numero segreteria mancante"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Nessun numero di segreteria presente nella SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Aggiungi numero"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 3e3c509..64e44bf 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ערוך תגובות מהירות"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"תגובה מהירה"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"אפס את האפליקציה המוגדר כברירת מחדל"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"הודעה נשלחה אל <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"משתמש זה אינו רשאי לבצע שיחות טלפון שאינן שיחות חירום"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"השיחה לא נשלחה. לא הוזן מספר חוקי."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"בעלי המכשיר מתיר לבצע שיחות חירום בלבד"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"כדי להתקשר, הזן מספר טלפון חוקי."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"לא ניתן כעת להוסיף את השיחה."</string>
<string name="no_vm_number" msgid="4164780423805688336">"חסר מספר של דואר קולי"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"בכרטיס ה-SIM לא מאוחסן מספר של דואר קולי."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"הוסף מספר"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 1caaccd..10b9087 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"クイック返信の編集"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"クイック返信"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"デフォルトのアプリをリセット"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g>にメッセージを送信しました。"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"このユーザーは緊急通報以外の通話は発信できません"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"発信できません。有効な番号が入力されていません。"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"端末の所有者に許可されているのは緊急通報のみです。"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"発信するには、有効な番号を入力してください。"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"通話は現在追加できません。"</string>
<string name="no_vm_number" msgid="4164780423805688336">"ボイスメール番号がありません"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIMカードにボイスメールの番号がありません。"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"番号を追加"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 6e7eedf..4cb2256 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"სწრაფი პასუხის რედაქტირება"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"სწრაფი პასუხი"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ნაგულისხმევი აპის ისევ დაყენება"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"შეტყობინება გაიგზავნა <xliff:g id="PHONE_NUMBER">%s</xliff:g>-თან."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ამ მომხმარებელს არ აქვს უფლება, განახორციელოს არასაგანგებო სატელეფონო ზარები."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"ვერ გაიგზავნა, ტელეფონის ნომერი სწორად არ იქნა შეყვანილი."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"მოწყობილობის მფლობელის მიერ ნებადართულია მხოლოდ საგანგებო ზარების განხორციელება"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ზარის განხორციელებისათვის, შეიყვანეთ მოქმედი ნომერი."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ამ ეტაპზე ზარის დამატება ვერ ხერხდება."</string>
<string name="no_vm_number" msgid="4164780423805688336">"ხმოვანი ფოსტის ნომერი არ არის"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM ბარათზე ხმოვანი ფოსტის ნომერი შენახული არ არის."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"ნომრის დამატება"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 921c40c..064d563 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Жылдам жауаптарды жөндеу"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Жылдам жауап"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Бастапқы қолдб-ға қайта реттеу"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Хабар <xliff:g id="PHONE_NUMBER">%s</xliff:g> нөміріне жіберілді."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Телефон есептік жазбасының параметрлері"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Әдепкі шығыс есептік жазба"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi қоңыраулар есептік жазбасы"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Әр қоңырау үшін сұрау"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi қоңырауларын пайдаланбау"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Бұл пайдаланушыға жедел емес қоңырауларды шалуға рұқсат етілмеген"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Құрылғы иесі тек жедел қоңырауларға рұқсат еткен"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Қоңырау шалу үшін жарамды нөмірді енгізіңіз."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Қоңырауды қазіргі уақытта қосу мүмкін емес."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"Дауыс хабарының нөмірі жоқ"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM картасында ешқандай дауыс хабарының нөмірі сақталмаған."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"Нөмір қосу"</string>
</resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index e392e2d..b5fb8eb 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"កែការឆ្លើយតបរហ័ស"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ឆ្លើយតបរហ័ស"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"កំណត់កម្មវិធីលំនាំដើមឡើងវិញ"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"បានផ្ញើសារទៅ <xliff:g id="PHONE_NUMBER">%s</xliff:g> ។"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"អ្នកប្រើម្នាក់នេះមិនត្រូវបានអនុញ្ញាតឲ្យបង្កើតការហៅទូរស័ព្ទពេលមិនអាសន្ន"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"មិនបានផ្ញើការហៅ បានបញ្ចូលលេខមិនត្រឹមត្រូវ។"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"ការហៅពេលមានអាសន្នដែលត្រូវបានអនុញ្ញាតដោយម្ចាស់ឧបករណ៍តែប៉ុណ្ណោះ។"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ដើម្បីធ្វើការហៅ បញ្ចូលលេខដែលមានសុពលភាព។"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"មិនអាចបន្ថែមនៅពេលនេះបានទេ។"</string>
<string name="no_vm_number" msgid="4164780423805688336">"បាត់ចំនួនសារជាសំឡេង"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"គ្មានចំនួនសារជាសំឡេងត្រូវបានរក្សាទុកនៅលើស៊ីមកាតទេ។"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"បន្ថែមលេខ"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 5109406..6887fce 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -18,10 +18,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ಫೋನ್"</string>
<string name="unknown" msgid="6878797917991465859">"ಅಜ್ಞಾತ"</string>
- <string name="notification_missedCallTitle" msgid="7554385905572364535">"ತಪ್ಪಿ ಹೋದ ಕರೆ"</string>
+ <string name="notification_missedCallTitle" msgid="7554385905572364535">"ಮಿಸ್ಡ್ ಕಾಲ್"</string>
<string name="notification_missedCallsTitle" msgid="1361677948941502522">"ತಪ್ಪಿದ ಕರೆಗಳು"</string>
<string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ತಪ್ಪಿದ ಕರೆಗಳು"</string>
- <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> ಅವರಿಂದ ತಪ್ಪಿದ ಕರೆ"</string>
+ <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> ಅವರಿಂದ ಮಿಸ್ಡ್ ಕಾಲ್"</string>
<string name="notification_missedCall_call_back" msgid="2684890353590890187">"ಮರಳಿ ಕರೆ ಮಾಡಿ"</string>
<string name="notification_missedCall_message" msgid="3049928912736917988">"ಸಂದೇಶ"</string>
<string name="accessibility_call_muted" msgid="2776111226185342220">"ಕರೆಯನ್ನು ಮ್ಯೂಟ್ ಮಾಡಲಾಗಿದೆ."</string>
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ತ್ವರಿತ ಪ್ರತಿಕ್ರಿಯೆಗಳನ್ನು ಸಂಪಾದಿಸಿ"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ತ್ವರಿತ ಪ್ರತಿಕ್ರಿಯೆ"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ಡೀಫಾಲ್ಟ್ ಅಪ್ಲಿಕೇಶನ್ ಮರುಹೊಂದಿಸಿ"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> ಗೆ ಸಂದೇಶ ಕಳುಹಿಸಲಾಗಿದೆ."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"ಫೋನ್ ಖಾತೆ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"ಡೀಫಾಲ್ಟ್ ಹೊರಹೋಗುವ ಖಾತೆ"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi ಕರೆ ಮಾಡುವ ಖಾತೆ"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಬಳಸಬೇಡಿ"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ತುರ್ತುಸ್ಥಿತಿಯಲ್ಲದ ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಲು ಈ ಬಳಕೆದಾರರನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"ಸಾಧನದ ಮಾಲೀಕರಿಂದ ತುರ್ತು ಕರೆಗಳನ್ನು ಮಾಡಲು ಮಾತ್ರ ಅವಕಾಶವಿದೆ"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ಕರೆಯನ್ನು ಮಾಡಲು, ಮಾನ್ಯವಾದ ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ಕರೆಯನ್ನು ಈ ಸಮಯದಲ್ಲಿ ಸೇರಿಸಲಾಗುವುದಿಲ್ಲ."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"ಧ್ವನಿಮೇಲ್ ಸಂಖ್ಯೆಯು ಕಾಣೆಯಾಗಿದೆ"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"ಸಿಮ್ ಕಾರ್ಡ್ನಲ್ಲಿ ಯಾವುದೇ ಧ್ವನಿಮೇಲ್ ಸಂಖ್ಯೆಯನ್ನು ಸಂಗ್ರಹಿಸಿಲ್ಲ."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಿ"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 3d67c7f..66c0131 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"빠른 응답 수정"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"빠른 응답"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"기본 앱 재설정"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g>(으)로 메시지를 보냈습니다."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"일반 전화를 걸 수 없는 사용자입니다."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"잘못된 번호를 입력했으므로 전화를 걸지 못했습니다."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"기기 소유자만 긴급 전화를 사용할 수 있습니다."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"전화를 걸려면 올바른 번호를 입력하세요."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"현재 통화를 추가할 수 없습니다."</string>
<string name="no_vm_number" msgid="4164780423805688336">"음성사서함 번호 없음"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM 카드에 저장된 음성사서함 번호가 없습니다."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"번호 추가"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 064a0d1..253d7ab 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -43,12 +43,14 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Тез жоопторду өзгөртүү"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Тез жооп"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Абалкы колдонмону өзгөртүү"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> номуруна билдирүү жөнөтүлдү."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Телефондун каттоо эсебинин жөндөөлөрү"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Демейки чыгуучу каттоо эсеби"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi чалуу үчүн каттоо эсеби"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Чалган сайын сурасын"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi чалуу колдонулбасын"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Бул колдонуучу шашылыш эмес чалууларды аткара албайт"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Түзмөк ээси шашылыш чалууларга гана уруксат берген"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Чалуу үчүн, жарактуу номер киргизиңиз."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Бул жолу чалууну кошуу мүмкүн эмес."</string>
+ <!-- no translation found for no_vm_number (4164780423805688336) -->
+ <skip />
+ <!-- no translation found for no_vm_number_msg (1300729501030053828) -->
+ <skip />
+ <!-- no translation found for add_vm_number_str (4676479471644687453) -->
+ <skip />
</resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index cac6273..a6e3ffb 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ແກ້ໄຂຂໍ້ຄວາມຕອບກັບດ່ວນ"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ຕອບກັບດ່ວນ"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ຄືນຄ່າແອັບຯເລີ່ມຕົ້ນ"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"ສົ່ງຂໍ້ຄວາມຫາ <xliff:g id="PHONE_NUMBER">%s</xliff:g> ແລ້ວ."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ຜູ່ໃຊ້ນີ້ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ໂທສຸກເສີນໄດ້"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"ບໍ່ໄດ້ໂທເທື່ອ, ເບີໂທທີ່ລະບຸບໍ່ຖືກຕ້ອງ."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"ພຽງແຕ່ການໂທສຸກເສີນທີ່ໄດ້ຮັບອະນຸຍາດຈາກເຈົ້າຂອງອຸປະກອນເທົ່ານັ້ນ"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ເພື່ອທີ່ຈະໂທ, ປ້ອນເບີໂທທີ່ໃຊ້ໄດ້ເຂົ້າໄປ."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ບໍ່ສາມາດເພີ່ມການໂທໄດ້ໃນເວລານີ້."</string>
<string name="no_vm_number" msgid="4164780423805688336">"ບໍ່ມີເບີຂໍ້ຄວາມສຽງ"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"ບໍ່ມີເບີຂໍ້ຄວາມສຽງຖືກບັນທຶກໃນ SIM card."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"ເພີ່ມໝາຍເລກ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 74213ca..6a52bb8 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Redaguoti greitus atsakus"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Greitas atsakas"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Nustat. iš naujo numat. prog."</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Pranešimas išsiųstas numeriu <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Šiam naudotojui leidžiama vykdyti tik skambučius pagalbos numeriais"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Skambutis neatliktas; įvestas netinkamas numeris."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Įrenginio savininkas leidžia skambinti tik pagalbos numeriais"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Kad galėtumėte paskambinti, įveskite tinkamą numerį."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Šiuo metu dar vieno skambučio atlikti negalima."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Trūksta balso pašto numerio"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kortelėje nėra išsaugoto balso pašto numerio."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Pridėti numerį"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 5c3fb24..0c4e2e0 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Ātro atbilžu rediģēšana"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Ātrā atbilde"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Atiestatīt noklus. lietotni"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Ziņojums nosūt. uz šādu tālr. nr.: <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Šim lietotājam nav atļauts veikt ārkārtas tālruņa zvanus."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Zvans nav nosūtīts; nav ievadīts derīgs numurs."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Ierīces īpašnieks ļauj veikt tikai ārkārtas zvanus."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Lai veiktu zvanu, ievadiet derīgu numuru."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Šobrīd nevar pievienot zvanu."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Trūkst balss pasta numura"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartē neviens balss pasta numurs nav saglabāts."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Pievienot numuru"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 52df23f..008a70f 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Уреди брзи одговори"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Брз одговор"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Ресетирај стандардна апликација"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Порака е испратена на <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Поставки на телефонска сметка"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Стандардна излезна сметка"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Сметка за повикување преку Wi-Fi"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Секогаш прашај"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Не користи повикување преку Wi-Fi"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Корисникот нема дозвола да прави телефонски повици ако не е итно"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Дозволени се само итни повици од страна на сопственикот на уредот"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"За да повикате, внесете важечки број."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Повикот не може да се додаде во моментов."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"Недостасува број на говорна пошта"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"Нема мемориран број на говорна пошта на СИМ картичката."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"Додај број"</string>
</resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 3e8d420..d3b3b1c 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ദ്രുത പ്രതികരണങ്ങൾ എഡിറ്റുചെയ്യുക"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ദ്രുത പ്രതികരണം"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"സ്ഥിര അപ്ലിക്കേഷൻ പുനഃസജ്ജമാക്കുക"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> എന്നതിലേക്ക് സന്ദേശമയച്ചു."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"ഫോൺ അക്കൗണ്ട് ക്രമീകരണങ്ങൾ"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"സ്ഥിര ഔട്ട്ഗോയിംഗ് അക്കൗണ്ട്"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi കോൾ ചെയ്യൽ അക്കൗണ്ട്"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"ഓരോ തവണയും ചോദിക്കുക"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi കോൾ ചെയ്യൽ ഉപയോഗിക്കരുത്"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"അത്യാവശ്യമല്ലാത്ത ഫോൺ കോളുകൾ ചെയ്യാൻ ഈ ഉപയോക്താവിനെ അനുവദിക്കുന്നില്ല"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"ഉപകരണ ഉടമ അടിയന്തിര കോളുകൾ മാത്രമേ അനുവദിച്ചിട്ടുള്ളൂ"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ഒരു കോൾ ചെയ്യുന്നതിന്, സാധുതയുള്ള നമ്പർ നൽകുക."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"കോൾ ഇപ്പോൾ ചേർക്കാനാകില്ല."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"വോയ്സ്മെയിൽ നമ്പർ കാണുന്നില്ല"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"സിം കാർഡിൽ വോയ്സ്മെയിൽ നമ്പറൊന്നും സംഭരിച്ചിട്ടില്ല."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"നമ്പർ ചേർക്കുക"</string>
</resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index b38d1d1..7b3c734 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Шуурхай хариунуудыг засах"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Шуурхай хариу"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Үндсэн апп-г сэргээх"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Зурвасыг <xliff:g id="PHONE_NUMBER">%s</xliff:g> руу илгээв."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Энэ хэрэглэгч нь яаралтай бус утасны дуудлага хийх эрхгүй"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Дуудлага илгээгдсэнгүй, зөв дугаар оруулаагүй байна."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Энэхүү төхөөрөмжийн эзэмшигч нь зөвхөн түргэн тусламжийн дуудлага хийх эрхтэй байна."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Дуудлага хийхийн тулд хүчин төгөлдөр дугаар оруулна уу."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Одоо дуудлага нэмэх боломжгүй."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Дуут шуудангийн дугаар байхгүй"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM карт дээр дуут шуудангийн дугаар хадгалагдаагүй байна."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Дугаар нэмэх"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index c04c823..5b802d0 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"द्रुत प्रतिसाद संपादित करा"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"द्रुत प्रतिसाद"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"डीफॉल्ट अॅप रीसेट करा"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"संदेश <xliff:g id="PHONE_NUMBER">%s</xliff:g> वर पाठविला."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"फोन खाते सेटिंग्ज"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"डीफॉल्ट केले जाणारे खाते"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi कॉल करण्याचे खाते"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"प्रत्येक वेळी विचारा"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi कॉल करणे वापरू नका"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"या वापरकर्त्यास आणीबाणी-नसलेले फोन कॉल करण्याची अनुमती नाही"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"डिव्हाइस मालकाद्वारे केवळ आणीबाणी कॉलना अनुमती आहे"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"कॉल करण्यासाठी, एक वैध नंबर प्रविष्ट करा."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"यावेळी कॉल जोडला जाऊ शकत नाही."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"व्हॉइसमेल नंबर गहाळ"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"सिम कार्डवर कोणताही व्हॉइसमेल नंबर संचयित केला नाही."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"नंबर जोडा"</string>
</resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index d0d7846..82f34a4 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit respons pantas"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respons pantas"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Tetapkan semula apl lalai"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesej dihantar ke <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Pengguna ini tidak dibenarkan membuat panggilan telefon bukan kecemasan"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Panggilan tidak dihantar, tiada nombor yang sah dimasukkan."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Hanya panggilan kecemasan dibenarkan oleh pemilik peranti"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Untuk membuat panggilan, masukkan nombor yang sah."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Panggilan tidak boleh ditambahkan pada masa ini."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Nombor mel suara tiada"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Tidak ada nombor mel suara disimpan pada kad SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Tambah nombor"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 3742912..b028ed5 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"အမြန်တုံ့ပြန်ချက်များပြင်ခြင်း"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"အမြန်တုံ့ပြန်ချက်"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ပုံသေ အပ်ပလီကေးရှင်းအား ပြန်ပြောင်းရန်"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> ထံ စာတိုပို့လိုက်ပါပြီ"</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"ဖုန်း အကောင့် ဆက်တင်များ"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"ပုံသေ အထွက် အကောင့်"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"ကြိုးမဲ့ ခေါ်ဆိုမှု အကောင့်"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"အကြိမ်တိုင်းမှာ မေးရန်"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"ကြိုးမဲ့ ခေါ်ဆိုမှုကို မသုံးပါနှင့်"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ဒီအသုံးပြုသူအား အရေးပေါ် မဟုတ်သည့် ဖုန်း ခေါ်ဆိုမှုများ လုပ်ခွင့် မပြုပါ"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"အရေးပေါ်ဖုန်းများကိုသာ ခေါ်ဆိုနိုင်ရန် စက်ကိရိယာပိုင်ရှင်က ခွင့်ပြုထား၏"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ဖုန်းခေါ်ရန်အတွက်၊ သင့်လျော်သည့်နံပါတ် ရိုက်ထည့်ပါ။"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ဗွီဒီယိုခေါ်နေစဉ် ထပ်ခေါ်မရပါ။"</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"အသံစာပို့စနစ် နံပါတ် ပျောက်နေပါသည်"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"ဆင်းမ်ကဒ်ပေါ်တွင် အသံစာပို့စနစ် နံပါတ် သိမ်းဆည်ထားခြင်း မရှိပါ"</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"နံပါတ်ထပ်ထည့်ရန်"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 36b1297..92d9061 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Rediger hurtigsvar"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hurtigsvar"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Tilbakestill standardappen"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Melding er sendt til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Denne brukeren har bare tillatelse til å ringe nødnumre"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Kunne ikke ringe. Du har ikke angitt et gyldig nummer."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Eieren av enheten tillater bare nødanrop"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Skriv inn et gyldig nummer for å plassere en samtale."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Anrop kan ikke legges til akkurat nå."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Mangler nummer til telefonsvarer"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Det er ikke lagret noe telefonsvarernummer på SIM-kortet."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Legg til nummer"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 0fe8775..cc91f33 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"द्रुत प्रतिक्रियाहरू सम्पादन गर्नुहोस्"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"द्रुत प्रतिक्रिया"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"पूर्वनिर्धारित अनुप्रयोग पुनःसेट गर्नुहोस्"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> लाई सन्देश पठाइयो।"</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"फोन खाता सेटिङहरू"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"बहिर्गमन पूर्वनिर्धारित खाता"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"वाइफाइ कल गर्ने खाता"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"प्रत्येक चोटि सोध्नुहोस्"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"वाइफाइ कल प्रयोग नगर्नुहोस्"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"यो प्रयोगकर्तालाई गैर-आकस्मिक फोन कल गर्न अनुमति छैन"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"केवल आपतकालीन कलहरू मात्र यन्त्र मालिकद्वारा अनुमति दिइएको छ"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"एक कल गर्नको लागि, एक वैध नम्बर प्रविष्ट गर्नुहोस्।"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"यस समयमा कल थप गर्न सकिँदैन।"</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"भ्वाइसमेल नम्बर हराइरहेको छ"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM कार्डमा कुनै पनि भ्वाइसमेल नम्बर भण्डारण भएको छैन।"</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"नम्बर थप्नुहोस्"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 9d5a1ee..e726f49 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Snelle reacties bewerken"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Snelle reactie"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Standaardapp opnieuw instellen"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Bericht verzonden naar <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Het is deze gebruiker niet toegestaan niet-noodoproepen te plaatsen"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Oproep niet verzonden, geen geldig nummer ingevoerd."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Alleen noodoproepen zijn toegestaan door de apparaateigenaar"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Als u wilt bellen, moet u een geldig nummer invoeren."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Oproep kan momenteel niet worden toegevoegd."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Voicemailnummer ontbreekt"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Er is geen voicemailnummer op de SIM-kaart opgeslagen."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Nummer toevoegen"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 7d392ac..9d48689 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edytuj szybkie odpowiedzi"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Szybka odpowiedź"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Resetuj domyślną aplikację"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Wiadomość wysłano na numer <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Ten użytkownik nie ma uprawnień do połączeń innych niż alarmowe"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Połączenie niezrealizowane. Nie podano poprawnego numeru."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Właściciel urządzenia zezwala tylko na połączenia alarmowe"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Aby zadzwonić, wybierz prawidłowy numer."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Nie można w tej chwili dodać połączenia."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Brakuje numeru poczty głosowej"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Na karcie SIM nie ma zapisanego numeru poczty głosowej."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Dodaj numer"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index ed953a7..2d2d4b9 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editar respostas rápidas"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Repor aplicação predefinida"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensagem enviada para <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Este utilizador não está autorizado a fazer chamadas telefónicas que não sejam de emergência"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"A chamada não foi efetuada. Não foi introduzido um número válido."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Só são permitidas chamadas de emergência pelo proprietário do dispositivo"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para telefonar, introduza um número válido."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Não é possível adicionar a chamada neste momento."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Número do correio de voz em falta"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Não existe um número de correio de voz armazenado no cartão SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Adicionar número"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f36b090..8a8b7f7 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Smartphone"</string>
+ <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefone"</string>
<string name="unknown" msgid="6878797917991465859">"Desconhecido"</string>
<string name="notification_missedCallTitle" msgid="7554385905572364535">"Chamada perdida"</string>
<string name="notification_missedCallsTitle" msgid="1361677948941502522">"Chamadas perdidas"</string>
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editar respostas rápidas"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Redefinir app padrão"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensagem enviada para <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Este usuário não tem permissão para fazer chamadas não emergenciais pelo smartphone"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Chamada não enviada: nenhum número válido foi inserido."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Apenas chamadas de emergência são permitidas pelo proprietário do dispositivo"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar uma chamada, digite um número válido."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No momento, não é possível adicionar a chamada."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Número correio de voz ausente"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Não há um número correio de voz armazenado no cartão SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Adicionar número"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 8010c9b..170d4f2 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editaţi răspunsurile rapide"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Răspuns rapid"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Resetați aplic. prestabilită"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesajul a fost trimis la <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Acest utilizator nu are permisiunea de a efectua apeluri telefonice către alte numere în afară de cele de urgență"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Apelul nu s-a trimis; niciun număr valid introdus."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Proprietarul dispozitivului permite numai apelurile de urgență"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Pentru a apela, introduceți un număr valid."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Apelul nu poate fi adăugat în acest moment."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Lipseşte numărul mesageriei vocale"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Niciun număr de mesagerie vocală nu este stocat pe cardul SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Adăugaţi numărul"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d95b887..26437e7 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Быстрые ответы"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Быстрый ответ"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Сбросить стандартные настройки"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Сообщение отправлено на номер <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Этот пользователь может звонить только в службы экстренной помощи"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Для вызова укажите действительный номер телефона."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Владелец устройства разрешил только экстренные вызовы"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Недействительный номер."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Невозможно позвонить в данный момент"</string>
<string name="no_vm_number" msgid="4164780423805688336">"Не указан номер голосовой почты"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"На SIM-карте нет ни одного номера голосовой почты."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Добавить номер"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 91f9f29..159cc40 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ක්ෂණික ප්රතිචාර සංස්කරණය කරන්න"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ක්ෂණික ප්රතිචාරය"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"සුපුරුදු යෙදුම යළි සකසන්න"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> හට පණිවිඩය යවන්න."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"දුරකථන ගිණුම සැකසීම"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"සුපුරුදු එළියට යෑමේ ගිණුම"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi ඇමතුම් ගැනීමේ ගිණුම"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"සෑම වේලාවේම අසන්න"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi ඇමතුම භාවිතා කරන්න එපා"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"හදිසි-නැති දුරකථන ඇමතුම් සිදුකිරීමට මෙම පරිශීලකයාට අවසර නොමැත"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"උපාංගයේ හිමිකරු විසින් හදිස්සි ඇමතුම් වලට පමණක් අවසර දෙයි"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ඇමතුමක් ලබාගැනීමට, වලංගු අංකයක් ලබාගන්න."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"මේ වේලාවේ ඇමතුම එකතු කළ නොහැක."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"හඬ තැපැල් අංකය නැත"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM කාඩ් පතෙහි හඬ තැපැල් අංකයක් ආචිත වී නැත."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"අංකයක් එක් කරන්න"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index f0fb553..7fb63b1 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Upraviť rýchle odpovede"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Rýchla odpoveď"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Obnoviť predvolenú aplikáciu"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Správa bola odoslaná na číslo <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Tento používateľ smie uskutočňovať iba tiesňové hovory."</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Hovor sa nepodarilo spojiť, nebolo zadané platné číslo."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vlastník zariadenia povolil iba tiesňové volania."</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ak chcete volať, zadajte platné číslo"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hovor momentálne nie je možné pridať."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Chýba číslo hlasovej schránky"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Na karte SIM nie je uložené žiadne číslo hlasovej schránky."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Pridať číslo"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index a0eb220..ec6df19 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Urejanje hitrih odgovorov"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hiter odgovor"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Ponastavi privzeto aplikacijo"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"SMS poslan na številko <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Temu uporabniku ni dovoljeno telefoniranje, razen klicev v sili"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Klic ni bil poslan, veljavna številka ni vnesena."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Lastnik naprave dovoljuje samo opravljanje klicev v sili"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Če želite opraviti klic, vnesite veljavno številko."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Klica trenutno ni mogoče dodati."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Manjkajoča številka glasovne pošte"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Na kartici SIM ni shranjena številka glasovne pošte."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Dodaj številko"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 2e49851..a93b09d 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Измена брзих одговора"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Брзи одговор"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Ресетуј подразумев. апликацију"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Порука је послата на број <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Овом кориснику није дозвољено да упућује телефонске позиве који нису хитни"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Позив није послат, није унет важећи број."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Власник уређаја је дозволио само хитне позиве"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Да бисте упутили позив, унесите важећи број."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Тренутно није могуће додати позив."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Недостаје број за говорну пошту"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Није ускладиштен ниједан број говорне поште на SIM картици."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Додај број"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 8abbe72..c25b40b 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Redigera snabbsvar"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Snabbsvar"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Återställ standardappen"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Meddelandet har skickats till <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Denna användare har inte tillåtelse att ringa icke-akuta samtal"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Samtalet har inte skickats. Du måste ange ett giltigt nummer."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Endast nödsamtal är tillåtna av enhetens ägare"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ange ett giltigt nummer om du vill ringa ett samtal."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Det går inte att lägga till samtalet just nu."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Nummer till röstbrevlåda saknas"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Det finns inget nummer till röstbrevlådan sparat på SIM-kortet."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Lägg till nummer"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6395aba..1c01fb7 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Hariri majibu ya haraka"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Majibu ya haraka"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Weka upya programu chaguo-msingi"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Ujumbe uliotumwa kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g> ."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Mtumiaji huyu haruhusiwi kupiga simu zisizo za dharura"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Simu haijatumwa, hakuna nambari sahihi iliyowekwa."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Ni simu za dharura pekee zinazoruhusiwa na mmiliki wa kifaa"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ili upige simu, weka nambari sahihi."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hangout ya video haiwezi kuongezwa kwa wakati huu."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Nambari ya sauti inayokosekana"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Hakuna nambari ya ujumbe wa sauti iliyohifadhiwa katika SIM kadi."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Ongeza nambari"</string>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 5c50ffb..98157c6 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"விரைவு பதில்களை மாற்று"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"விரைவு பதில்"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"இயல்பு பயன்பாட்டை மீட்டமை"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> க்குச் செய்தி அனுப்பப்பட்டது."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"ஃபோன் கணக்கு அமைப்புகள்"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"அழைப்பிற்கான இயல்புநிலை கணக்கு"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"வைஃபை அழைப்பிற்கான கணக்கு"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"ஒவ்வொரு முறையும் கேள்"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"வைஃபை அழைப்பைப் பயன்படுத்தாதே"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"அவசரமற்ற ஃபோன் அழைப்புகளைச் செய்ய இவருக்கு அனுமதியில்லை"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"சாதன உரிமையாளர் அவசர அழைப்புகளை மட்டுமே அனுமதித்துள்ளார்"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"அழைக்க, சரியான எண்ணை உள்ளிடவும்."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"இப்போது அழைக்க முடியாது."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"குரலஞ்சல் எண் இல்லை"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"சிம் கார்டில் குரலஞ்சலுக்கான எண் எதுவும் சேமிக்கப்படவில்லை."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"எண்ணைச் சேர்"</string>
</resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 627ed4a..31b5273 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"శీఘ్ర ప్రతిస్పందనలు సవరించండి"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"శీఘ్ర ప్రతిస్పందన"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"డిఫాల్ట్ అనువర్తనాన్ని రీసెట్ చేయి"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g>కు సందేశం పంపబడింది."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"ఫోన్ ఖాతా సెట్టింగ్లు"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"డిఫాల్ట్ అవుట్గోయింగ్ ఖాతా"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi కాలింగ్ ఖాతా"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"ప్రతిసారి అడుగు"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi కాలింగ్ను ఉపయోగించవద్దు"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ఈ వినియోగదారుకి అత్యవసరం కాని ఫోన్ కాల్లు చేయడానికి అనుమతి లేదు"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"పరికరం యజమాని అత్యవసర కాల్లను మాత్రమే అనుమతించారు"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"కాల్ చేయడానికి, చెల్లుబాటు అయ్యే నంబర్ను నమోదు చేయండి."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ఈ సమయంలో కాల్ను జోడించడం సాధ్యపడదు."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"వాయిస్ మెయిల్ నంబర్ లేదు"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"సిమ్ కార్డులో వాయిస్ మెయిల్ నంబర్ ఏదీ నిల్వ చేయబడలేదు."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"నంబర్ను జోడించు"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 78f2ee4..b936083 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"แก้ไขคำตอบด่วน"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"คำตอบด่วน"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"รีเซ็ตแอปพลิเคชันเริ่มต้น"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"ส่งข้อความไปยัง <xliff:g id="PHONE_NUMBER">%s</xliff:g> แล้ว"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"ไม่อนุญาตให้ผู้ใช้รายนี้ดำเนินการโทรที่ไม่ฉุกเฉิน"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"ไม่ได้โทรออก ไม่ได้ป้อนหมายเลขที่ถูกต้อง"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"เจ้าของอุปกรณ์อนุญาตเฉพาะหมายเลขฉุกเฉิน"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"หากต้องการโทรออก โปรดป้อนหมายเลขที่ถูกต้อง"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ไม่สามารถเพิ่มสายได้ในขณะนี้"</string>
<string name="no_vm_number" msgid="4164780423805688336">"ไม่มีหมายเลขข้อความเสียง"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"ไม่มีหมายเลขข้อความเสียงจัดเก็บอยู่ในซิมการ์ด"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"เพิ่มหมายเลข"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 0b7f481..81d05b7 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"I-edit ang mga mabilisang tugon"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Mabilisang tugon"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"I-reset ang default na app"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Naipadala ang mensahe sa <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Hindi pinapahintulutan ang user na ito na gumawa ng mga hindi pang-emergency na tawag sa telepono"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Hindi naipadala ang tawag, walang inilagay na wastong numero."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Ang mga emergency na tawag lang ang pinapayagan ng may-ari ng device"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Upang tumawag, maglagay ng wastong numero."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hindi maidadagdag ang tawag sa oras na ito."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Nawawala ang numero ng voicemail"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Walang nakaimbak na numero ng voicemail sa SIM card."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Magdagdag ng numero"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 662bee5..00169e4 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Hızlı yanıtları düzenle"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hızlı yanıt"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Varsayılan uygulamayı sıfırla"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesaj, <xliff:g id="PHONE_NUMBER">%s</xliff:g> numaralı telefona gönderildi."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Bu kullanıcının acil olmayan telefon çağrıları yapmasına izin verilmiyor"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Çağrı gönderilmedi, geçerli bir numara girilmedi."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Cihaz sahibi sadece acil durum çağrılarına izin veriyor"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Telefon etmek için geçerli bir numara girin."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Çağrı şu anda eklenemiyor."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Eksik sesli mesaj numarası"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartta depolanan sesli mesaj numarası yok."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Numara ekle"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index cb89808..e2494d2 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Редагувати швидкі відповіді"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Швидка відповідь"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Скинути програму за умовчанням"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Повідомлення надіслано на номер <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Цьому користувачу не дозволено здійснювати звичайні телефонні дзвінки"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Дзвінок не відбувся, введіть дійсний номер."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Власник пристрою дозволив лише екстрені виклики"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Щоб зателефонувати, введіть дійсний номер."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Зараз не можна почати дзвінок."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Відстун. номер голос. пошти"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"На SIM-карті немає збереж. номерів голос. пошти."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Додати номер"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index 16b8bac..c2503e3 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"فوری جوابات میں ترمیم کریں"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"فوری جواب"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"ڈیفالٹ ایپ دوبارہ ترتیب دیں"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"پیغام <xliff:g id="PHONE_NUMBER">%s</xliff:g> کو بھیج دیا گیا۔"</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"فون اکاؤنٹ کی ترتیبات"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"ڈیفالٹ آؤٹ گوئنگ اکاؤنٹ"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi کالنگ اکاؤنٹ"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"ہر بار پوچھیں"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi کالنگ کا استعمال نہ کریں"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"اس صارف کو غیر ہنگامی فون کالیں کرنے کی اجازت نہیں ہے"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"آلہ کے مالک نے صرف ہنگامی کالز کی اجازت دی ہے۔"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"کال کرنے کیلئے، ایک درست نمبر درج کریں۔"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"اس وقت کال شامل نہیں کی جا سکتی ہے۔"</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"صوتی میل نمبر درج نہیں ہے"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM کارڈ پر کوئی بھی صوتی میل نمبر اسٹور نہیں ہے۔"</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"نمبر شامل کریں"</string>
</resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 6d259b3..e2f370d 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -34,12 +34,11 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Tezkor javoblarni tahrirlash"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Tezkor javob"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Standart ilovani tiklash"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Xabar <xliff:g id="PHONE_NUMBER">%s</xliff:g>ga jo‘natildi."</string>
- <string name="phone_account_preferences_title" msgid="5042332049625236956">"Telefon hisobi sozlamalari"</string>
- <string name="default_outgoing_account_title" msgid="8261079649574578970">"Chiquvchi qo‘ng‘iroqlar uchun birlamchi hisob"</string>
- <string name="sim_call_manager_account" msgid="2559930293628077755">"Wi-Fi qo‘ng‘iroqlar uchun hisob"</string>
- <string name="account_ask_every_time" msgid="944077828070287407">"Har safar so‘ralsin"</string>
- <string name="do_not_use_sim_call_manager" msgid="5519252524007323694">"Wi-Fi qo‘ng‘iroqlardan foydalanilmasin"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Ushbu foydalanuvchiga favqulodda qo‘ng‘iroqlardan boshqa telefon qo‘ng‘iroqlarini amalga oshirish uchun ruxsat berilmagan"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Qurilma egasi faqat favqulodda qo‘ng‘iroqlarga ruxsat bergan"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Qo‘ng‘iroq qilish uchun raqamni to‘g‘ri kiriting."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hozirgi vaqtda qo‘ng‘iroq qo‘shib bo‘lmaydi."</string>
+ <string name="no_vm_number" msgid="4164780423805688336">"Javobsiz ovozli xabar raqami"</string>
+ <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartada birorta ham ovozli xabar saqlanmagan."</string>
+ <string name="add_vm_number_str" msgid="4676479471644687453">"Raqam qo‘shish"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 83d75ba..68c794e 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Chỉnh sửa trả lời nhanh"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Trả lời nhanh"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Đặt lại ứng dụng mặc định"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Đã gửi tin nhắn tới <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Người dùng này không được phép thực hiện cuộc gọi điện thoại không khẩn cấp"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Không gọi được, bạn đã nhập số không hợp lệ."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Chỉ chủ sở hữu thiết bị mới được phép thực hiện cuộc gọi khẩn cấp"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Để thực hiện cuộc gọi, hãy nhập một số hợp lệ."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Không thể thêm cuộc gọi tại thời điểm này."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Thiếu số thư thoại"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Không có số thư thoại nào được lưu trữ trên thẻ SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Thêm số điện thoại"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index e085d1e..8a62ae2 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"修改快速回复"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"快速回复"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"重置默认应用"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"讯息已发送至 <xliff:g id="PHONE_NUMBER">%s</xliff:g>。"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"此用户无权拨打非紧急呼救电话"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"拨号失败,因为输入的号码无效。"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"设备机主仅允许拨打紧急呼救电话"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"要拨打电话,请输入有效的电话号码。"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"目前无法添加通话。"</string>
<string name="no_vm_number" msgid="4164780423805688336">"缺少语音信箱号码"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM卡上未存储语音信箱号码。"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"添加号码"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index a775f59..c05d3c9 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"編輯快速回應"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"快速回應"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"重設預設應用程式"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"訊息已傳送至 <xliff:g id="PHONE_NUMBER">%s</xliff:g>。"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"這位使用者只可撥打緊急電話"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"無法傳送,輸入的號碼無效。"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"只有裝置擁有者才可撥打緊急電話"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"要撥打電話,請輸入有效的號碼。"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"目前無法新增視像通話。"</string>
<string name="no_vm_number" msgid="4164780423805688336">"未填留言信箱號碼"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM 卡中沒有儲存任何留言信箱號碼。"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"新增電話號碼"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3922f91..bb0d95e 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"編輯快速回應"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"快速回應"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"重設預設應用程式"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"訊息已傳送至 <xliff:g id="PHONE_NUMBER">%s</xliff:g>。"</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"這位使用者只能撥打緊急電話"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"輸入的號碼無效,無法撥出。"</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"裝置擁有者限定只能撥打緊急電話"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"如要撥打電話,請輸入有效的號碼。"</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"目前無法新增通話。"</string>
<string name="no_vm_number" msgid="4164780423805688336">"遺失語音信箱號碼"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"SIM 卡中未儲存語音信箱號碼。"</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"新增號碼"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index f498490..222378d 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -34,10 +34,10 @@
<string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Hlela izimpendulo ezisheshayo"</string>
<string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
<string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Izimpendulo ezisheshayo"</string>
- <string name="respond_via_sms_menu_reset_default_activity" msgid="1461742052902053466">"Setha kabusha uhlelo lokusebenza lokuzenzakalelayo"</string>
<string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Umlayezo othunyelwe ku <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
- <string name="outgoing_call_not_allowed" msgid="1434784869685645427">"Lo msebenzisi akavunyelwe ukwenza amakholi wefoni okungewona wesimo esiphuthumayo"</string>
- <string name="outgoing_call_error_no_phone_number_supplied" msgid="4987143284589568716">"Ikholi ayithunyelwe, ayikho inombolo evumelekile efakiwe."</string>
+ <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Amakholi wesimo esiphuthumayo kuphela avunyelwe ngumnikazi wedivayisi"</string>
+ <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ukuze wenze ikholi, faka inombolo evumelekile."</string>
+ <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Ikholi ayikwazi ukungezwa ngalesi sikhathi."</string>
<string name="no_vm_number" msgid="4164780423805688336">"Inombolo engekho yomyalezo wezwi"</string>
<string name="no_vm_number_msg" msgid="1300729501030053828">"Ayikho inombolo yomlayezo wezwi egcinwe ekhadini le-SIM."</string>
<string name="add_vm_number_str" msgid="4676479471644687453">"Engeza inombolo"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index e93e855..64db085 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -16,4 +16,8 @@
<resources>
<color name="theme_color">#0288d1</color>
+
+ <color name="dialer_settings_actionbar_text_color">#ffffff</color>
+ <color name="dialer_settings_actionbar_background_color">@color/theme_color</color>
+ <color name="dialer_settings_color_dark">#0277bd</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 86d803d..f7ad003 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -18,4 +18,6 @@
<!-- Dimension used to possibly down-scale high-res photo into what is suitable
for notification's large icon. -->
<dimen name="notification_icon_size">64dp</dimen>
+
+ <dimen name="dialer_settings_actionbar_elevation">2dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9ecc6bb..37e59c0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -68,9 +68,6 @@
<string name="respond_via_sms_setting_summary"></string>
<!-- Dialog title when changing a string for the "Respond via SMS" feature. [CHAR LIMIT=30] -->
<string name="respond_via_sms_edittext_dialog_title">Quick response</string>
- <!-- Menu option in "Respond via SMS" that allows user to reset the default
- activity used to handle "Respond via SMS" [CHAR LIMIT=30] -->
- <string name="respond_via_sms_menu_reset_default_activity">Reset default app</string>
<!-- "Respond via SMS": Confirmation message shown after sending
a text response. [CHAR LIMIT=40] -->
@@ -78,10 +75,13 @@
<!-- Message indicating that the user is not allowed to make non-emergency outgoing phone calls
due to a user restriction -->
- <string name="outgoing_call_not_allowed">This user is not allowed to make non-emergency phone calls</string>
+ <string name="outgoing_call_not_allowed">Only emergency calls are allowed by the device owner</string>
<!-- Call failure message displayed in an error dialog used to indicate that a phone number was not provided -->
- <string name="outgoing_call_error_no_phone_number_supplied">Call not sent, no valid number entered.</string>
+ <string name="outgoing_call_error_no_phone_number_supplied">To place a call, enter a valid number.</string>
+
+ <!-- Message shown when the user tries to make a video call when already in a video call. -->
+ <string name ="duplicate_video_call_not_allowed">Call cannot be added at this time.</string>
<!-- missing voicemail number -->
<!-- Title of the "Missing voicemail number" dialog -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6b6c1bb..f073906 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -24,4 +24,30 @@
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
+
+ <style name="Theme.Telecom.DialerSettings" parent="@android:style/Theme.Material.Light">
+ <item name="android:actionBarStyle">@style/TelecomDialerSettingsActionBarStyle</item>
+ <item name="android:actionOverflowButtonStyle">@style/TelecomDialerSettingsActionOverflowButtonStyle</item>
+ <item name="android:colorPrimaryDark">@color/dialer_settings_color_dark</item>
+ <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
+ <style name="TelecomDialerSettingsActionBarStyle" parent="android:Widget.Material.ActionBar">
+ <item name="android:background">@color/dialer_settings_actionbar_background_color</item>
+ <item name="android:titleTextStyle">@style/TelecomDialerSettingsActionBarTitleText</item>
+ <item name="android:elevation">@dimen/dialer_settings_actionbar_elevation</item>
+ <!-- Empty icon -->
+ <item name="android:icon">@android:color/transparent</item>
+ </style>
+
+ <style name="TelecomDialerSettingsActionBarTitleText"
+ parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
+ <item name="android:textColor">@color/dialer_settings_actionbar_text_color</item>
+ </style>
+
+ <style name="TelecomDialerSettingsActionOverflowButtonStyle"
+ parent="@android:style/Widget.Material.Light.ActionButton.Overflow">
+ <item name="android:src">@drawable/ic_more_vert_white_24dp</item>
+ </style>
</resources>
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 4434424..3030fea 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -196,7 +196,9 @@
}
Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri);
- ringtone.setStreamType(AudioManager.STREAM_RING);
+ if (ringtone != null) {
+ ringtone.setStreamType(AudioManager.STREAM_RING);
+ }
return ringtone;
}
}
diff --git a/src/com/android/server/telecom/BluetoothManager.java b/src/com/android/server/telecom/BluetoothManager.java
index 9b5fd26..f74349f 100644
--- a/src/com/android/server/telecom/BluetoothManager.java
+++ b/src/com/android/server/telecom/BluetoothManager.java
@@ -26,6 +26,8 @@
import android.content.IntentFilter;
import android.os.SystemClock;
+import com.android.internal.util.IndentingPrintWriter;
+
import java.util.List;
/**
@@ -244,30 +246,33 @@
mBluetoothConnectionPending = false;
}
- private void dumpBluetoothState() {
- Log.d(this, "============== dumpBluetoothState() =============");
- Log.d(this, "= isBluetoothAvailable: " + isBluetoothAvailable());
- Log.d(this, "= isBluetoothAudioConnected: " + isBluetoothAudioConnected());
- Log.d(this, "= isBluetoothAudioConnectedOrPending: " +
- isBluetoothAudioConnectedOrPending());
- Log.d(this, "=");
+ /**
+ * Dumps the state of the {@link BluetoothManager}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("isBluetoothAvailable: " + isBluetoothAvailable());
+ pw.println("isBluetoothAudioConnected: " + isBluetoothAudioConnected());
+ pw.println("isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
+
if (mBluetoothAdapter != null) {
if (mBluetoothHeadset != null) {
List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
if (deviceList.size() > 0) {
BluetoothDevice device = deviceList.get(0);
- Log.d(this, "= BluetoothHeadset.getCurrentDevice: " + device);
- Log.d(this, "= BluetoothHeadset.State: "
- + mBluetoothHeadset.getConnectionState(device));
- Log.d(this, "= BluetoothHeadset audio connected: " +
- mBluetoothHeadset.isAudioConnected(device));
+ pw.println("BluetoothHeadset.getCurrentDevice: " + device);
+ pw.println("BluetoothHeadset.State: "
+ + mBluetoothHeadset.getConnectionState(device));
+ pw.println("BluetoothHeadset audio connected: " +
+ mBluetoothHeadset.isAudioConnected(device));
}
} else {
- Log.d(this, "= mBluetoothHeadset is null");
+ pw.println("mBluetoothHeadset is null");
}
} else {
- Log.d(this, "= mBluetoothAdapter is null; device is not BT capable");
+ pw.println("mBluetoothAdapter is null; device is not BT capable");
}
}
}
diff --git a/src/com/android/server/telecom/BluetoothPhoneService.java b/src/com/android/server/telecom/BluetoothPhoneService.java
index 8bf50ae..9a0cae1 100644
--- a/src/com/android/server/telecom/BluetoothPhoneService.java
+++ b/src/com/android/server/telecom/BluetoothPhoneService.java
@@ -28,11 +28,12 @@
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.telecom.CallState;
+import android.telecom.Connection;
import android.telecom.PhoneAccount;
-import android.telecom.PhoneCapabilities;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -53,7 +54,8 @@
* Request object for performing synchronous requests to the main thread.
*/
private static class MainThreadRequest {
- Object result;
+ private static final Object RESULT_NOT_SET = new Object();
+ Object result = RESULT_NOT_SET;
int param;
MainThreadRequest(int param) {
@@ -498,7 +500,7 @@
return true;
}
} else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
- if (activeCall != null && activeCall.can(PhoneCapabilities.SWAP_CONFERENCE)) {
+ if (activeCall != null && activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
activeCall.swapConference();
return true;
} else if (ringingCall != null) {
@@ -509,13 +511,13 @@
// currently-held call.
callsManager.unholdCall(heldCall);
return true;
- } else if (activeCall != null && activeCall.can(PhoneCapabilities.HOLD)) {
+ } else if (activeCall != null && activeCall.can(Connection.CAPABILITY_HOLD)) {
callsManager.holdCall(activeCall);
return true;
}
} else if (chld == CHLD_TYPE_ADDHELDTOCONF) {
if (activeCall != null) {
- if (activeCall.can(PhoneCapabilities.MERGE_CONFERENCE)) {
+ if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
activeCall.mergeConference();
return true;
} else {
@@ -539,14 +541,19 @@
}
private <T> T sendSynchronousRequest(int message, int param) {
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ Log.w(TAG, "This method will deadlock if called from the main thread.");
+ }
+
MainThreadRequest request = new MainThreadRequest(param);
mHandler.obtainMessage(message, request).sendToTarget();
synchronized (request) {
- while (request.result == null) {
+ while (request.result == MainThreadRequest.RESULT_NOT_SET) {
try {
request.wait();
} catch (InterruptedException e) {
// Do nothing, go back and wait until the request is complete.
+ Log.e(TAG, e, "InterruptedException");
}
}
}
@@ -587,8 +594,8 @@
// Run some alternative states for Conference-level merge/swap support.
// Basically, if call supports swapping or merging at the conference-level, then we need
- // to expose the calls as having distinct states (ACTIVE vs HOLD) or the functionality
- // won't show up on the bluetooth device.
+ // to expose the calls as having distinct states (ACTIVE vs CAPABILITY_HOLD) or the
+ // functionality won't show up on the bluetooth device.
// Before doing any special logic, ensure that we are dealing with an ACTIVE call and
// that the conference itself has a notion of the current "active" child call.
@@ -597,8 +604,8 @@
// Reevaluate state if we can MERGE or if we can SWAP without previously having
// MERGED.
boolean shouldReevaluateState =
- conferenceCall.can(PhoneCapabilities.MERGE_CONFERENCE) ||
- (conferenceCall.can(PhoneCapabilities.SWAP_CONFERENCE) &&
+ conferenceCall.can(Connection.CAPABILITY_MERGE_CONFERENCE) ||
+ (conferenceCall.can(Connection.CAPABILITY_SWAP_CONFERENCE) &&
!conferenceCall.wasConferencePreviouslyMerged());
if (shouldReevaluateState) {
@@ -680,7 +687,7 @@
String ringingAddress = null;
int ringingAddressType = 128;
- if (ringingCall != null) {
+ if (ringingCall != null && ringingCall.getHandle() != null) {
ringingAddress = ringingCall.getHandle().getSchemeSpecificPart();
if (ringingAddress != null) {
ringingAddressType = PhoneNumberUtils.toaFromString(ringingAddress);
@@ -698,11 +705,11 @@
// to show "swap" and "merge" functionality.
boolean ignoreHeldCallChange = false;
if (activeCall != null && activeCall.isConference()) {
- if (activeCall.can(PhoneCapabilities.SWAP_CONFERENCE)) {
+ if (activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
// Indicate that BT device should show SWAP command by indicating that there is a
// call on hold, but only if the conference wasn't previously merged.
numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1;
- } else if (activeCall.can(PhoneCapabilities.MERGE_CONFERENCE)) {
+ } else if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
numHeldCalls = 1; // Merge is available, so expose via numHeldCalls.
}
@@ -851,8 +858,11 @@
* phone account for PhoneAccount.SCHEME_TEL.
*/
private PhoneAccount getBestPhoneAccount() {
- TelecomApp app = (TelecomApp) getApplication();
- PhoneAccountRegistrar registry = app.getPhoneAccountRegistrar();
+ PhoneAccountRegistrar registry = TelecomGlobals.getInstance().getPhoneAccountRegistrar();
+ if (registry == null) {
+ return null;
+ }
+
Call call = getCallsManager().getForegroundCall();
PhoneAccount account = null;
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index a0a8a62..8a85f2c 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -22,6 +22,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Trace;
import android.provider.ContactsContract.Contacts;
import android.telecom.CallState;
import android.telecom.DisconnectCause;
@@ -30,7 +31,6 @@
import android.telecom.ParcelableConnection;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
-import android.telecom.PhoneCapabilities;
import android.telecom.Response;
import android.telecom.StatusHints;
import android.telecom.TelecomManager;
@@ -44,7 +44,6 @@
import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
import com.android.internal.telephony.SmsApplication;
import com.android.server.telecom.ContactsAsyncHelper.OnImageLoadCompleteListener;
-
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
@@ -74,7 +73,8 @@
void onFailedUnknownCall(Call call);
void onRingbackRequested(Call call, boolean ringbackRequested);
void onPostDialWait(Call call, String remaining);
- void onCallCapabilitiesChanged(Call call);
+ void onPostDialChar(Call call, char nextChar);
+ void onConnectionCapabilitiesChanged(Call call);
void onParentChanged(Call call);
void onChildrenChanged(Call call);
void onCannedSmsResponsesLoaded(Call call);
@@ -89,6 +89,7 @@
void onConnectionManagerPhoneAccountChanged(Call call);
void onPhoneAccountChanged(Call call);
void onConferenceableCallsChanged(Call call);
+ boolean onCanceledViaNewOutgoingCallBroadcast(Call call);
}
abstract static class ListenerBase implements Listener {
@@ -109,7 +110,9 @@
@Override
public void onPostDialWait(Call call, String remaining) {}
@Override
- public void onCallCapabilitiesChanged(Call call) {}
+ public void onPostDialChar(Call call, char nextChar) {}
+ @Override
+ public void onConnectionCapabilitiesChanged(Call call) {}
@Override
public void onParentChanged(Call call) {}
@Override
@@ -138,6 +141,10 @@
public void onPhoneAccountChanged(Call call) {}
@Override
public void onConferenceableCallsChanged(Call call) {}
+ @Override
+ public boolean onCanceledViaNewOutgoingCallBroadcast(Call call) {
+ return false;
+ }
}
private static final OnQueryCompleteListener sCallerInfoQueryListener =
@@ -182,7 +189,13 @@
* The time this call was created. Beyond logging and such, may also be used for bookkeeping
* and specifically for marking certain call attempts as failed attempts.
*/
- private final long mCreationTimeMillis = System.currentTimeMillis();
+ private long mCreationTimeMillis = System.currentTimeMillis();
+
+ /** The time this call was made active. */
+ private long mConnectTimeMillis = 0;
+
+ /** The time this call was disconnected. */
+ private long mDisconnectTimeMillis = 0;
/** The gateway information associated with this call. This stores the original call handle
* that the user is attempting to connect to via the gateway, the actual handle to dial in
@@ -198,8 +211,6 @@
private final List<Call> mConferenceableCalls = new ArrayList<>();
- private long mConnectTimeMillis = 0;
-
/** The state of the call. */
private int mState;
@@ -268,7 +279,7 @@
/** Whether direct-to-voicemail query is pending. */
private boolean mDirectToVoicemailQueryPending;
- private int mCallCapabilities;
+ private int mConnectionCapabilities;
private boolean mIsConference = false;
@@ -335,6 +346,37 @@
maybeLoadCannedSmsResponses();
}
+ /**
+ * Persists the specified parameters and initializes the new instance.
+ *
+ * @param context The context.
+ * @param repository The connection service repository.
+ * @param handle The handle to dial.
+ * @param gatewayInfo Gateway information to use for the call.
+ * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
+ * This account must be one that was registered with the
+ * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
+ * @param targetPhoneAccountHandle Account information to use for the call. This account must be
+ * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
+ * @param isIncoming True if this is an incoming call.
+ * @param connectTimeMillis The connection time of the call.
+ */
+ Call(
+ Context context,
+ ConnectionServiceRepository repository,
+ Uri handle,
+ GatewayInfo gatewayInfo,
+ PhoneAccountHandle connectionManagerPhoneAccountHandle,
+ PhoneAccountHandle targetPhoneAccountHandle,
+ boolean isIncoming,
+ boolean isConference,
+ long connectTimeMillis) {
+ this(context, repository, handle, gatewayInfo, connectionManagerPhoneAccountHandle,
+ targetPhoneAccountHandle, isIncoming, isConference);
+
+ mConnectTimeMillis = connectTimeMillis;
+ }
+
void addListener(Listener listener) {
mListeners.add(listener);
}
@@ -361,13 +403,35 @@
getVideoState(),
getChildCalls().size(),
getParentCall() != null,
- PhoneCapabilities.toString(getCallCapabilities()));
+ Connection.capabilitiesToString(getConnectionCapabilities()));
}
int getState() {
return mState;
}
+ private boolean shouldContinueProcessingAfterDisconnect() {
+ // Stop processing once the call is active.
+ if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
+ return false;
+ }
+
+ // Make sure that there are additional connection services to process.
+ if (mCreateConnectionProcessor == null
+ || !mCreateConnectionProcessor.isProcessingComplete()
+ || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
+ return false;
+ }
+
+ if (mDisconnectCause == null) {
+ return false;
+ }
+
+ // Continue processing if the current attempt failed or timed out.
+ return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
+ mCreateConnectionProcessor.isCallTimedOut();
+ }
+
/**
* Sets the call state. Although there exists the notion of appropriate state transitions
* (see {@link CallState}), in practice those expectations break down when cellular systems
@@ -377,10 +441,28 @@
void setState(int newState) {
if (mState != newState) {
Log.v(this, "setState %s -> %s", mState, newState);
+
+ if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
+ Log.w(this, "continuing processing disconnected call with another service");
+ mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
+ return;
+ }
+
mState = newState;
maybeLoadCannedSmsResponses();
- if (mState == CallState.DISCONNECTED) {
+ if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
+ if (mConnectTimeMillis == 0) {
+ // We check to see if mConnectTime is already set to prevent the
+ // call from resetting active time when it goes in and out of
+ // ACTIVE/ON_HOLD
+ mConnectTimeMillis = System.currentTimeMillis();
+ }
+
+ // We're clearly not disconnected, so reset the disconnected time.
+ mDisconnectTimeMillis = 0;
+ } else if (mState == CallState.DISCONNECTED) {
+ mDisconnectTimeMillis = System.currentTimeMillis();
setLocallyDisconnecting(false);
fixParentAfterDisconnect();
}
@@ -417,8 +499,20 @@
void setHandle(Uri handle, int presentation) {
if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
- mHandle = handle;
mHandlePresentation = presentation;
+ if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
+ mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
+ mHandle = null;
+ } else {
+ mHandle = handle;
+ if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
+ && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
+ // If the number is actually empty, set it to null, unless this is a
+ // SCHEME_VOICEMAIL uri which always has an empty number.
+ mHandle = null;
+ }
+ }
+
mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(mContext,
mHandle.getSchemeSpecificPart());
startCallerInfoLookup();
@@ -533,7 +627,21 @@
* mCreationTimeMillis.
*/
long getAgeMillis() {
- return System.currentTimeMillis() - mCreationTimeMillis;
+ if (mState == CallState.DISCONNECTED &&
+ (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
+ mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
+ // Rejected and missed calls have no age. They're immortal!!
+ return 0;
+ } else if (mConnectTimeMillis == 0) {
+ // Age is measured in the amount of time the call was active. A zero connect time
+ // indicates that we never went active, so return 0 for the age.
+ return 0;
+ } else if (mDisconnectTimeMillis == 0) {
+ // We connected, but have not yet disconnected
+ return System.currentTimeMillis() - mConnectTimeMillis;
+ }
+
+ return mDisconnectTimeMillis - mConnectTimeMillis;
}
/**
@@ -544,28 +652,29 @@
return mCreationTimeMillis;
}
+ void setCreationTimeMillis(long time) {
+ mCreationTimeMillis = time;
+ }
+
long getConnectTimeMillis() {
return mConnectTimeMillis;
}
- void setConnectTimeMillis(long connectTimeMillis) {
- mConnectTimeMillis = connectTimeMillis;
+ int getConnectionCapabilities() {
+ return mConnectionCapabilities;
}
- int getCallCapabilities() {
- return mCallCapabilities;
+ void setConnectionCapabilities(int connectionCapabilities) {
+ setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
}
- void setCallCapabilities(int callCapabilities) {
- setCallCapabilities(callCapabilities, false /* forceUpdate */);
- }
-
- void setCallCapabilities(int callCapabilities, boolean forceUpdate) {
- Log.v(this, "setCallCapabilities: %s", PhoneCapabilities.toString(callCapabilities));
- if (forceUpdate || mCallCapabilities != callCapabilities) {
- mCallCapabilities = callCapabilities;
+ void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
+ Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
+ connectionCapabilities));
+ if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
+ mConnectionCapabilities = connectionCapabilities;
for (Listener l : mListeners) {
- l.onCallCapabilitiesChanged(this);
+ l.onConnectionCapabilitiesChanged(this);
}
}
}
@@ -668,12 +777,11 @@
CallIdMapper idMapper,
ParcelableConnection connection) {
Log.v(this, "handleCreateConnectionSuccessful %s", connection);
- mCreateConnectionProcessor = null;
setTargetPhoneAccount(connection.getPhoneAccount());
setHandle(connection.getHandle(), connection.getHandlePresentation());
setCallerDisplayName(
connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
- setCallCapabilities(connection.getCapabilities());
+ setConnectionCapabilities(connection.getConnectionCapabilities());
setVideoProvider(connection.getVideoProvider());
setVideoState(connection.getVideoState());
setRingbackRequested(connection.isRingbackRequested());
@@ -710,7 +818,6 @@
@Override
public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
- mCreateConnectionProcessor = null;
clearConnectionService();
setDisconnectCause(disconnectCause);
CallsManager.getInstance().markCallAsDisconnected(this, disconnectCause);
@@ -754,17 +861,21 @@
}
}
+ void disconnect() {
+ disconnect(false);
+ }
+
/**
* Attempts to disconnect the call through the connection service.
*/
- void disconnect() {
+ void disconnect(boolean wasViaNewOutgoingCallBroadcaster) {
// Track that the call is now locally disconnecting.
setLocallyDisconnecting(true);
if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT ||
mState == CallState.CONNECTING) {
Log.v(this, "Aborting call %s", this);
- abort();
+ abort(wasViaNewOutgoingCallBroadcaster);
} else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
if (mConnectionService == null) {
Log.e(this, new Exception(), "disconnect() request on a call without a"
@@ -780,11 +891,31 @@
}
}
- void abort() {
- if (mCreateConnectionProcessor != null) {
+ void abort(boolean wasViaNewOutgoingCallBroadcaster) {
+ if (mCreateConnectionProcessor != null &&
+ !mCreateConnectionProcessor.isProcessingComplete()) {
mCreateConnectionProcessor.abort();
} else if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT
|| mState == CallState.CONNECTING) {
+ if (wasViaNewOutgoingCallBroadcaster) {
+ // If the cancelation was from NEW_OUTGOING_CALL, then we do not automatically
+ // destroy the call. Instead, we announce the cancelation and CallsManager handles
+ // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
+ // then re-dial them quickly using a gateway, allowing the first call to end
+ // causes jank. This timeout allows CallsManager to transition the first call into
+ // the second call so that in-call only ever sees a single call...eliminating the
+ // jank altogether.
+ for (Listener listener : mListeners) {
+ if (listener.onCanceledViaNewOutgoingCallBroadcast(this)) {
+ // The first listener to handle this wins. A return value of true means that
+ // the listener will handle the disconnection process later and so we
+ // should not continue it here.
+ setLocallyDisconnecting(false);
+ return;
+ }
+ }
+ }
+
handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
} else {
Log.v(this, "Cannot abort a call which isn't either PRE_DIAL_WAIT or CONNECTING");
@@ -893,6 +1024,12 @@
}
}
+ void onPostDialChar(char nextChar) {
+ for (Listener l : mListeners) {
+ l.onPostDialChar(this, nextChar);
+ }
+ }
+
void postDialContinue(boolean proceed) {
mConnectionService.onPostDialContinue(this, proceed);
}
@@ -916,7 +1053,7 @@
void mergeConference() {
if (mConnectionService == null) {
Log.w(this, "merging conference calls without a connection service.");
- } else if (can(PhoneCapabilities.MERGE_CONFERENCE)) {
+ } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
mConnectionService.mergeConference(this);
mWasConferencePreviouslyMerged = true;
}
@@ -925,7 +1062,7 @@
void swapConference() {
if (mConnectionService == null) {
Log.w(this, "swapping conference calls without a connection service.");
- } else if (can(PhoneCapabilities.SWAP_CONFERENCE)) {
+ } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
mConnectionService.swapConference(this);
switch (mChildCalls.size()) {
case 1:
@@ -983,7 +1120,7 @@
}
boolean can(int capability) {
- return (mCallCapabilities & capability) == capability;
+ return (mConnectionCapabilities & capability) == capability;
}
private void addChildCall(Call call) {
@@ -1123,6 +1260,7 @@
* @param token The token used with this query.
*/
private void setCallerInfo(CallerInfo callerInfo, int token) {
+ Trace.beginSection("setCallerInfo");
Preconditions.checkNotNull(callerInfo);
if (mQueryToken == token) {
@@ -1147,6 +1285,7 @@
processDirectToVoicemail();
}
+ Trace.endSection();
}
CallerInfo getCallerInfo() {
diff --git a/src/com/android/server/telecom/CallActivity.java b/src/com/android/server/telecom/CallActivity.java
index 7022327..37e24f6 100644
--- a/src/com/android/server/telecom/CallActivity.java
+++ b/src/com/android/server/telecom/CallActivity.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.telecom.PhoneAccount;
@@ -85,8 +86,6 @@
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent);
- } else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) {
- processIncomingCallIntent(intent);
}
}
@@ -124,20 +123,7 @@
}
intent.putExtra(CallReceiver.KEY_IS_DEFAULT_DIALER, isDefaultDialer());
-
- if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
- CallReceiver.processOutgoingCallIntent(getApplicationContext(), intent);
- } else {
- sendBroadcastToReceiver(intent, false /* isIncoming */);
- }
- }
-
- private void processIncomingCallIntent(Intent intent) {
- if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
- CallReceiver.processIncomingCallIntent(intent);
- } else {
- sendBroadcastToReceiver(intent, true /* isIncoming */);
- }
+ sendBroadcastToReceiver(intent);
}
private boolean isDefaultDialer() {
@@ -166,11 +152,11 @@
/**
* Trampolines the intent to the broadcast receiver that runs only as the primary user.
*/
- private boolean sendBroadcastToReceiver(Intent intent, boolean incoming) {
- intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, incoming);
+ private boolean sendBroadcastToReceiver(Intent intent) {
+ intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, false);
intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setClass(this, CallReceiver.class);
- Log.d(this, "Sending broadcast as user to CallReceiver- isIncoming: %s", incoming);
+ Log.d(this, "Sending broadcast as user to CallReceiver");
sendBroadcastAsUser(intent, UserHandle.OWNER);
return true;
}
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index a89dcea..b555f85 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -21,6 +21,7 @@
import android.telecom.AudioState;
import android.telecom.CallState;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -43,6 +44,7 @@
private boolean mIsTonePlaying;
private boolean mWasSpeakerOn;
private int mMostRecentlyUsedMode = AudioManager.MODE_IN_CALL;
+ private Call mCallToSpeedUpMTAudio = null;
CallAudioManager(Context context, StatusBarNotifier statusBarNotifier,
WiredHeadsetManager wiredHeadsetManager) {
@@ -67,7 +69,8 @@
if (hasFocus() && getForegroundCall() == call) {
if (!call.isIncoming()) {
// Unmute new outgoing call.
- setSystemAudioState(false, mAudioState.route, mAudioState.supportedRouteMask);
+ setSystemAudioState(false, mAudioState.getRoute(),
+ mAudioState.getSupportedRouteMask());
}
}
}
@@ -92,7 +95,7 @@
@Override
public void onIncomingCallAnswered(Call call) {
- int route = mAudioState.route;
+ int route = mAudioState.getRoute();
// We do two things:
// (1) If this is the first call, then we can to turn on bluetooth if available.
@@ -103,7 +106,13 @@
route = AudioState.ROUTE_BLUETOOTH;
}
- setSystemAudioState(false /* isMute */, route, mAudioState.supportedRouteMask);
+ setSystemAudioState(false /* isMute */, route, mAudioState.getSupportedRouteMask());
+
+ if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
+ Log.v(this, "Speed up audio setup for IMS MT call.");
+ mCallToSpeedUpMTAudio = call;
+ updateAudioStreamAndMode();
+ }
}
@Override
@@ -129,21 +138,40 @@
return;
}
- int newRoute = AudioState.ROUTE_EARPIECE;
+ boolean isCurrentlyWiredHeadset = mAudioState.getRoute() == AudioState.ROUTE_WIRED_HEADSET;
+
+ int newRoute = mAudioState.getRoute(); // start out with existing route
if (newIsPluggedIn) {
newRoute = AudioState.ROUTE_WIRED_HEADSET;
- } else if (mWasSpeakerOn) {
+ } else if (isCurrentlyWiredHeadset) {
Call call = getForegroundCall();
- if (call != null && call.isAlive()) {
- // Restore the speaker state.
- newRoute = AudioState.ROUTE_SPEAKER;
+ boolean hasLiveCall = call != null && call.isAlive();
+
+ if (hasLiveCall) {
+ // In order of preference when a wireless headset is unplugged.
+ if (mWasSpeakerOn) {
+ newRoute = AudioState.ROUTE_SPEAKER;
+ } else {
+ newRoute = AudioState.ROUTE_EARPIECE;
+ }
+
+ // We don't automatically connect to bluetooth when user unplugs their wired headset
+ // and they were previously using the wired. Wired and earpiece are effectively the
+ // same choice in that they replace each other as an option when wired headsets
+ // are plugged in and out. This means that keeping it earpiece is a bit more
+ // consistent with the status quo. Bluetooth also has more danger associated with
+ // choosing it in the wrong curcumstance because bluetooth devices can be
+ // semi-public (like in a very-occupied car) where earpiece doesn't carry that risk.
}
}
- setSystemAudioState(mAudioState.isMuted, newRoute, calculateSupportedRoutes());
+
+ // We need to call this every time even if we do not change the route because the supported
+ // routes changed either to include or not include WIRED_HEADSET.
+ setSystemAudioState(mAudioState.isMuted(), newRoute, calculateSupportedRoutes());
}
void toggleMute() {
- mute(!mAudioState.isMuted);
+ mute(!mAudioState.isMuted());
}
void mute(boolean shouldMute) {
@@ -159,8 +187,9 @@
Log.v(this, "ignoring mute for emergency call");
}
- if (mAudioState.isMuted != shouldMute) {
- setSystemAudioState(shouldMute, mAudioState.route, mAudioState.supportedRouteMask);
+ if (mAudioState.isMuted() != shouldMute) {
+ setSystemAudioState(shouldMute, mAudioState.getRoute(),
+ mAudioState.getSupportedRouteMask());
}
}
@@ -178,19 +207,20 @@
Log.v(this, "setAudioRoute, route: %s", AudioState.audioRouteToString(route));
// Change ROUTE_WIRED_OR_EARPIECE to a single entry.
- int newRoute = selectWiredOrEarpiece(route, mAudioState.supportedRouteMask);
+ int newRoute = selectWiredOrEarpiece(route, mAudioState.getSupportedRouteMask());
// If route is unsupported, do nothing.
- if ((mAudioState.supportedRouteMask | newRoute) == 0) {
+ if ((mAudioState.getSupportedRouteMask() | newRoute) == 0) {
Log.wtf(this, "Asking to set to a route that is unsupported: %d", newRoute);
return;
}
- if (mAudioState.route != newRoute) {
+ if (mAudioState.getRoute() != newRoute) {
// Remember the new speaker state so it can be restored when the user plugs and unplugs
// a headset.
mWasSpeakerOn = newRoute == AudioState.ROUTE_SPEAKER;
- setSystemAudioState(mAudioState.isMuted, newRoute, mAudioState.supportedRouteMask);
+ setSystemAudioState(mAudioState.isMuted(), newRoute,
+ mAudioState.getSupportedRouteMask());
}
}
@@ -229,16 +259,16 @@
}
int supportedRoutes = calculateSupportedRoutes();
- int newRoute = mAudioState.route;
+ int newRoute = mAudioState.getRoute();
if (bluetoothManager.isBluetoothAudioConnectedOrPending()) {
newRoute = AudioState.ROUTE_BLUETOOTH;
- } else if (mAudioState.route == AudioState.ROUTE_BLUETOOTH) {
+ } else if (mAudioState.getRoute() == AudioState.ROUTE_BLUETOOTH) {
newRoute = selectWiredOrEarpiece(AudioState.ROUTE_WIRED_OR_EARPIECE, supportedRoutes);
// Do not switch to speaker when bluetooth disconnects.
mWasSpeakerOn = false;
}
- setSystemAudioState(mAudioState.isMuted, newRoute, supportedRoutes);
+ setSystemAudioState(mAudioState.isMuted(), newRoute, supportedRoutes);
}
boolean isBluetoothAudioOn() {
@@ -251,14 +281,18 @@
private void saveAudioState(AudioState audioState) {
mAudioState = audioState;
- mStatusBarNotifier.notifyMute(mAudioState.isMuted);
- mStatusBarNotifier.notifySpeakerphone(mAudioState.route == AudioState.ROUTE_SPEAKER);
+ mStatusBarNotifier.notifyMute(mAudioState.isMuted());
+ mStatusBarNotifier.notifySpeakerphone(mAudioState.getRoute() == AudioState.ROUTE_SPEAKER);
}
private void onCallUpdated(Call call) {
boolean wasNotVoiceCall = mAudioFocusStreamType != AudioManager.STREAM_VOICE_CALL;
updateAudioStreamAndMode();
+ if (call != null && call.getState() == CallState.ACTIVE &&
+ call == mCallToSpeedUpMTAudio) {
+ mCallToSpeedUpMTAudio = null;
+ }
// If we transition from not voice call to voice call, we need to set an initial state.
if (wasNotVoiceCall && mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL) {
setInitialAudioState(call, true /* force */);
@@ -283,20 +317,20 @@
Log.i(this, "changing audio state from %s to %s", oldAudioState, mAudioState);
// Mute.
- if (mAudioState.isMuted != mAudioManager.isMicrophoneMute()) {
- Log.i(this, "changing microphone mute state to: %b", mAudioState.isMuted);
- mAudioManager.setMicrophoneMute(mAudioState.isMuted);
+ if (mAudioState.isMuted() != mAudioManager.isMicrophoneMute()) {
+ Log.i(this, "changing microphone mute state to: %b", mAudioState.isMuted());
+ mAudioManager.setMicrophoneMute(mAudioState.isMuted());
}
// Audio route.
- if (mAudioState.route == AudioState.ROUTE_BLUETOOTH) {
+ if (mAudioState.getRoute() == AudioState.ROUTE_BLUETOOTH) {
turnOnSpeaker(false);
turnOnBluetooth(true);
- } else if (mAudioState.route == AudioState.ROUTE_SPEAKER) {
+ } else if (mAudioState.getRoute() == AudioState.ROUTE_SPEAKER) {
turnOnBluetooth(false);
turnOnSpeaker(true);
- } else if (mAudioState.route == AudioState.ROUTE_EARPIECE ||
- mAudioState.route == AudioState.ROUTE_WIRED_HEADSET) {
+ } else if (mAudioState.getRoute() == AudioState.ROUTE_EARPIECE ||
+ mAudioState.getRoute() == AudioState.ROUTE_WIRED_HEADSET) {
turnOnBluetooth(false);
turnOnSpeaker(false);
}
@@ -335,9 +369,21 @@
if (mIsRinging) {
requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
} else {
- Call call = getForegroundCall();
- if (call != null) {
- int mode = call.getIsVoipAudioMode() ?
+ Call foregroundCall = getForegroundCall();
+ Call waitingForAccountSelectionCall =
+ CallsManager.getInstance().getFirstCallWithState(CallState.PRE_DIAL_WAIT);
+ Call call = CallsManager.getInstance().getForegroundCall();
+ if (foregroundCall == null && call != null && call == mCallToSpeedUpMTAudio) {
+ requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL,
+ AudioManager.MODE_IN_CALL);
+ } else if (foregroundCall != null && waitingForAccountSelectionCall == null) {
+ // In the case where there is a call that is waiting for account selection,
+ // this will fall back to abandonAudioFocus() below, which temporarily exits
+ // the in-call audio mode. This is to allow TalkBack to speak the "Call with"
+ // dialog information at media volume as opposed to through the earpiece.
+ // Once exiting the "Call with" dialog, the audio focus will return to an in-call
+ // audio mode when this method (updateAudioStreamAndMode) is called again.
+ int mode = foregroundCall.getIsVoipAudioMode() ?
AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_IN_CALL;
requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL, mode);
} else if (mIsTonePlaying) {
@@ -359,7 +405,8 @@
}
private void requestAudioFocusAndSetMode(int stream, int mode) {
- Log.i(this, "requestAudioFocusAndSetMode, stream: %d -> %d", mAudioFocusStreamType, stream);
+ Log.i(this, "requestAudioFocusAndSetMode, stream: %d -> %d, mode: %d",
+ mAudioFocusStreamType, stream, mode);
Preconditions.checkState(stream != STREAM_NONE);
// Even if we already have focus, if the stream is different we update audio manager to give
@@ -380,6 +427,7 @@
Log.v(this, "abandoning audio focus");
mAudioManager.abandonAudioFocusForCall();
mAudioFocusStreamType = STREAM_NONE;
+ mCallToSpeedUpMTAudio = null;
}
}
@@ -466,7 +514,8 @@
AudioState audioState = getInitialAudioState(call);
Log.v(this, "setInitialAudioState %s, %s", audioState, call);
setSystemAudioState(
- force, audioState.isMuted, audioState.route, audioState.supportedRouteMask);
+ force, audioState.isMuted(), audioState.getRoute(),
+ audioState.getSupportedRouteMask());
}
private void updateAudioForForegroundCall() {
@@ -485,8 +534,9 @@
// We ignore any foreground call that is in the ringing state because we deal with ringing
// calls exclusively through the mIsRinging variable set by {@link Ringer}.
if (call != null && call.getState() == CallState.RINGING) {
- call = null;
+ return null;
}
+
return call;
}
@@ -498,4 +548,30 @@
private boolean hasFocus() {
return mAudioFocusStreamType != STREAM_NONE;
}
+
+ /**
+ * Dumps the state of the {@link CallAudioManager}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("mAudioState: " + mAudioState);
+ pw.println("mBluetoothManager:");
+ pw.increaseIndent();
+ mBluetoothManager.dump(pw);
+ pw.decreaseIndent();
+ if (mWiredHeadsetManager != null) {
+ pw.println("mWiredHeadsetManager:");
+ pw.increaseIndent();
+ mWiredHeadsetManager.dump(pw);
+ pw.decreaseIndent();
+ } else {
+ pw.println("mWiredHeadsetManager: null");
+ }
+ pw.println("mAudioFocusStreamType: " + mAudioFocusStreamType);
+ pw.println("mIsRinging: " + mIsRinging);
+ pw.println("mIsTonePlaying: " + mIsTonePlaying);
+ pw.println("mWasSpeakerOn: " + mWasSpeakerOn);
+ pw.println("mMostRecentlyUsedMode: " + mMostRecentlyUsedMode);
+ }
}
diff --git a/src/com/android/server/telecom/CallIdMapper.java b/src/com/android/server/telecom/CallIdMapper.java
index 40a50a5..729db0a 100644
--- a/src/com/android/server/telecom/CallIdMapper.java
+++ b/src/com/android/server/telecom/CallIdMapper.java
@@ -16,11 +16,65 @@
package com.android.server.telecom;
-import com.google.common.collect.HashBiMap;
+import android.util.ArrayMap;
+
+import java.util.Map;
/** Utility to map {@link Call} objects to unique IDs. IDs are generated when a call is added. */
class CallIdMapper {
- private final HashBiMap<String, Call> mCalls = HashBiMap.create();
+ /**
+ * A very basic bidirectional map.
+ */
+ static class BiMap<K, V> {
+ private Map<K, V> mPrimaryMap = new ArrayMap<>();
+ private Map<V, K> mSecondaryMap = new ArrayMap<>();
+
+ public boolean put(K key, V value) {
+ if (key == null || value == null || mPrimaryMap.containsKey(key) ||
+ mSecondaryMap.containsKey(value)) {
+ return false;
+ }
+
+ mPrimaryMap.put(key, value);
+ mSecondaryMap.put(value, key);
+ return true;
+ }
+
+ public boolean remove(K key) {
+ if (key == null) {
+ return false;
+ }
+ if (mPrimaryMap.containsKey(key)) {
+ V value = getValue(key);
+ mPrimaryMap.remove(key);
+ mSecondaryMap.remove(value);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean removeValue(V value) {
+ if (value == null) {
+ return false;
+ }
+ return remove(getKey(value));
+ }
+
+ public V getValue(K key) {
+ return mPrimaryMap.get(key);
+ }
+
+ public K getKey(V value) {
+ return mSecondaryMap.get(value);
+ }
+
+ public void clear() {
+ mPrimaryMap.clear();
+ mSecondaryMap.clear();
+ }
+ }
+
+ private final BiMap<String, Call> mCalls = new BiMap<>();
private final String mCallIdPrefix;
private static int sIdCount;
@@ -55,7 +109,7 @@
return;
}
ThreadUtil.checkOnMainThread();
- mCalls.inverse().remove(call);
+ mCalls.removeValue(call);
}
void removeCall(String callId) {
@@ -68,7 +122,7 @@
return null;
}
ThreadUtil.checkOnMainThread();
- return mCalls.inverse().get(call);
+ return mCalls.getKey(call);
}
Call getCall(Object objId) {
@@ -82,7 +136,7 @@
return null;
}
- return mCalls.get(callId);
+ return mCalls.getValue(callId);
}
void clear() {
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 658af10..781f2b1 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -98,10 +98,10 @@
@Override
public void onCallStateChanged(Call call, int oldState, int newState) {
+ int disconnectCause = call.getDisconnectCause().getCode();
boolean isNewlyDisconnected =
newState == CallState.DISCONNECTED || newState == CallState.ABORTED;
- boolean isCallCanceled = isNewlyDisconnected &&
- call.getDisconnectCause().getCode() == DisconnectCause.CANCELED;
+ boolean isCallCanceled = isNewlyDisconnected && disconnectCause == DisconnectCause.CANCELED;
// Log newly disconnected calls only if:
// 1) It was not in the "choose account" phase when disconnected
@@ -114,7 +114,7 @@
int type;
if (!call.isIncoming()) {
type = Calls.OUTGOING_TYPE;
- } else if (oldState == CallState.RINGING) {
+ } else if (disconnectCause == DisconnectCause.MISSED) {
type = Calls.MISSED_TYPE;
} else {
type = Calls.INCOMING_TYPE;
@@ -140,13 +140,12 @@
Log.d(TAG, "logNumber set to: %s", Log.pii(logNumber));
- final int presentation = getPresentation(call);
final PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
// TODO(vt): Once data usage is available, wire it up here.
int callFeatures = getCallFeatures(call.getVideoStateHistory());
- logCall(call.getCallerInfo(), logNumber, presentation, callLogType, callFeatures,
- accountHandle, creationTime, age, null);
+ logCall(call.getCallerInfo(), logNumber, call.getHandlePresentation(),
+ callLogType, callFeatures, accountHandle, creationTime, age, null);
}
/**
@@ -232,21 +231,6 @@
}
/**
- * Gets the presentation from the {@link Call}.
- *
- * TODO: There needs to be a way to pass information from
- * Connection.getNumberPresentation() into a {@link Call} object. Until then, always return
- * PhoneConstants.PRESENTATION_ALLOWED. On top of that, we might need to introduce
- * getNumberPresentation to the ContactInfo object as well.
- *
- * @param call The call object to retrieve caller details from.
- * @return The number presentation constant to insert into the call logs.
- */
- private int getPresentation(Call call) {
- return PhoneConstants.PRESENTATION_ALLOWED;
- }
-
- /**
* Adds the call defined by the parameters in the provided AddCallArgs to the CallLogProvider
* using an AsyncTask to avoid blocking the main thread.
*
diff --git a/src/com/android/server/telecom/CallReceiver.java b/src/com/android/server/telecom/CallReceiver.java
index 17ca3e5..bdae582 100644
--- a/src/com/android/server/telecom/CallReceiver.java
+++ b/src/com/android/server/telecom/CallReceiver.java
@@ -5,12 +5,15 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Trace;
import android.os.UserHandle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
+import android.widget.Toast;
/**
* Single point of entry for all outgoing and incoming calls. {@link CallActivity} serves as a
@@ -29,17 +32,15 @@
@Override
public void onReceive(Context context, Intent intent) {
final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false);
- final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false);
- Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall,
- isUnknownCall);
+ Log.i(this, "onReceive - isUnknownCall: %s", isUnknownCall);
+ Trace.beginSection("processNewCallCallIntent");
if (isUnknownCall) {
processUnknownCallIntent(intent);
- } else if (isIncomingCall) {
- processIncomingCallIntent(intent);
} else {
processOutgoingCallIntent(context, intent);
}
+ Trace.endSection();
}
/**
@@ -48,6 +49,10 @@
* @param intent Call intent containing data about the handle to call.
*/
static void processOutgoingCallIntent(Context context, Intent intent) {
+ if (shouldPreventDuplicateVideoCall(context, intent)) {
+ return;
+ }
+
Uri handle = intent.getData();
String scheme = handle.getScheme();
String uriString = handle.getSchemeSpecificPart();
@@ -65,7 +70,7 @@
clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
}
if (clientExtras == null) {
- clientExtras = Bundle.EMPTY;
+ clientExtras = new Bundle();
}
final boolean isDefaultDialer = intent.getBooleanExtra(KEY_IS_DEFAULT_DIALER, false);
@@ -108,7 +113,7 @@
clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
}
if (clientExtras == null) {
- clientExtras = Bundle.EMPTY;
+ clientExtras = new Bundle();
}
Log.d(TAG, "Processing incoming call from connection service [%s]",
@@ -153,4 +158,28 @@
errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivityAsUser(errorIntent, UserHandle.CURRENT);
}
+
+ /**
+ * Whether an outgoing video call should be prevented from going out. Namely, don't allow an
+ * outgoing video call if there is already an ongoing video call. Notify the user if their call
+ * is not sent.
+ *
+ * @return {@code true} if the outgoing call is a video call and should be prevented from going
+ * out, {@code false} otherwise.
+ */
+ private static boolean shouldPreventDuplicateVideoCall(Context context, Intent intent) {
+ int intentVideoState = intent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+ VideoProfile.VideoState.AUDIO_ONLY);
+ if (intentVideoState == VideoProfile.VideoState.AUDIO_ONLY
+ || !getCallsManager().hasVideoCall()) {
+ return false;
+ } else {
+ // Display an error toast to the user.
+ Toast.makeText(
+ context,
+ context.getResources().getString(R.string.duplicate_video_call_not_allowed),
+ Toast.LENGTH_LONG).show();
+ return true;
+ }
+ }
}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 4fdb8a1..3b7269d 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -17,29 +17,30 @@
package com.android.server.telecom;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Trace;
import android.provider.CallLog.Calls;
import android.provider.Settings;
import android.telecom.AudioState;
import android.telecom.CallState;
+import android.telecom.Conference;
+import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.ParcelableConference;
+import android.telecom.ParcelableConnection;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
-import android.telecom.PhoneCapabilities;
import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
+import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import com.android.internal.util.IndentingPrintWriter;
-import com.google.common.collect.ImmutableCollection;
-import com.google.common.collect.ImmutableList;
-
+import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -73,12 +74,13 @@
void onIsConferencedChanged(Call call);
void onIsVoipAudioModeChanged(Call call);
void onVideoStateChanged(Call call);
+ void onCanAddCallChanged(boolean canAddCall);
}
/**
* Singleton instance of the {@link CallsManager}, initialized from {@link TelecomService}.
*/
- private static CallsManager INSTANCE = null;
+ private static CallsManager sInstance = null;
private static final String TAG = "CallsManager";
@@ -86,17 +88,18 @@
private static final int MAXIMUM_HOLD_CALLS = 1;
private static final int MAXIMUM_RINGING_CALLS = 1;
private static final int MAXIMUM_OUTGOING_CALLS = 1;
-
- private static final int[] LIVE_CALL_STATES =
- {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE};
+ private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
private static final int[] OUTGOING_CALL_STATES =
{CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING};
+ private static final int[] LIVE_CALL_STATES =
+ {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE};
+
/**
* The main call repository. Keeps an instance of all live calls. New incoming and outgoing
* calls are added to the map and removed when the calls move to the disconnected state.
- *
+ *
* ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
* load factor before resizing, 1 means we only expect a single thread to
* access the map so make only a single shard
@@ -109,6 +112,7 @@
private final InCallController mInCallController;
private final CallAudioManager mCallAudioManager;
private final Ringer mRinger;
+ private final InCallWakeLockController mInCallWakeLockController;
// For this set initial table size to 16 because we add 13 listeners in
// the CallsManager constructor.
private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
@@ -123,6 +127,11 @@
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final MissedCallNotifier mMissedCallNotifier;
private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
+ private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
+ /* Handler tied to thread in which CallManager was initialized. */
+ private final Handler mHandler = new Handler();
+
+ private boolean mCanAddCall = true;
/**
* The call the user is currently interacting with. This is the call that should have audio
@@ -130,9 +139,11 @@
*/
private Call mForegroundCall;
+ private Runnable mStopTone;
+
/** Singleton accessor. */
static CallsManager getInstance() {
- return INSTANCE;
+ return sInstance;
}
/**
@@ -141,7 +152,7 @@
* @param instance The instance to set.
*/
static void initialize(CallsManager instance) {
- INSTANCE = instance;
+ sInstance = instance;
}
/**
@@ -166,6 +177,7 @@
mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
mConnectionServiceRepository = new ConnectionServiceRepository(mPhoneAccountRegistrar,
context);
+ mInCallWakeLockController = new InCallWakeLockController(context, this);
mListeners.add(statusBarNotifier);
mListeners.add(mCallLogManager);
@@ -257,9 +269,43 @@
}
@Override
+ public void onPostDialChar(final Call call, char nextChar) {
+ if (PhoneNumberUtils.is12Key(nextChar)) {
+ // Play tone if it is one of the dialpad digits, canceling out the previously queued
+ // up stopTone runnable since playing a new tone automatically stops the previous tone.
+ if (mStopTone != null) {
+ mHandler.removeCallbacks(mStopTone);
+ }
+
+ mDtmfLocalTonePlayer.playTone(call, nextChar);
+
+ mStopTone = new Runnable() {
+ @Override
+ public void run() {
+ // Set a timeout to stop the tone in case there isn't another tone to follow.
+ mDtmfLocalTonePlayer.stopTone(call);
+ }
+ };
+ mHandler.postDelayed(
+ mStopTone,
+ Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
+ } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
+ nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
+ // Stop the tone if a tone is playing, removing any other stopTone callbacks since
+ // the previous tone is being stopped anyway.
+ if (mStopTone != null) {
+ mHandler.removeCallbacks(mStopTone);
+ }
+ mDtmfLocalTonePlayer.stopTone(call);
+ } else {
+ Log.w(this, "onPostDialChar: invalid value %d", nextChar);
+ }
+ }
+
+ @Override
public void onParentChanged(Call call) {
// parent-child relationship affects which call should be foreground, so do an update.
- updateForegroundCall();
+ updateCallsManagerState();
for (CallsManagerListener listener : mListeners) {
listener.onIsConferencedChanged(call);
}
@@ -268,7 +314,7 @@
@Override
public void onChildrenChanged(Call call) {
// parent-child relationship affects which call should be foreground, so do an update.
- updateForegroundCall();
+ updateCallsManagerState();
for (CallsManagerListener listener : mListeners) {
listener.onIsConferencedChanged(call);
}
@@ -288,8 +334,24 @@
}
}
- ImmutableCollection<Call> getCalls() {
- return ImmutableList.copyOf(mCalls);
+ @Override
+ public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
+ mPendingCallsToDisconnect.add(call);
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mPendingCallsToDisconnect.remove(call)) {
+ Log.i(this, "Delayed disconnection of call: %s", call);
+ call.disconnect();
+ }
+ }
+ }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
+
+ return true;
+ }
+
+ Collection<Call> getCalls() {
+ return Collections.unmodifiableCollection(mCalls);
}
Call getForegroundCall() {
@@ -313,6 +375,15 @@
return false;
}
+ boolean hasVideoCall() {
+ for (Call call : mCalls) {
+ if (call.getVideoState() != VideoProfile.VideoState.AUDIO_ONLY) {
+ return true;
+ }
+ }
+ return false;
+ }
+
AudioState getAudioState() {
return mCallAudioManager.getAudioState();
}
@@ -373,13 +444,42 @@
// to the existing connection instead of trying to create a new one.
true /* isIncoming */,
false /* isConference */);
- call.setConnectTimeMillis(System.currentTimeMillis());
call.setIsUnknown(true);
call.setExtras(extras);
call.addListener(this);
call.startCreateConnection(mPhoneAccountRegistrar);
}
+ private Call getNewOutgoingCall(Uri handle) {
+ // First check to see if we can reuse any of the calls that are waiting to disconnect.
+ // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
+ Call reusedCall = null;
+ for (Call pendingCall : mPendingCallsToDisconnect) {
+ if (reusedCall == null && Objects.equals(pendingCall.getHandle(), handle)) {
+ mPendingCallsToDisconnect.remove(pendingCall);
+ Log.i(this, "Reusing disconnected call %s", pendingCall);
+ reusedCall = pendingCall;
+ } else {
+ pendingCall.disconnect();
+ }
+ }
+ if (reusedCall != null) {
+ return reusedCall;
+ }
+
+ // Create a call with original handle. The handle may be changed when the call is attached
+ // to a connection service, but in most cases will remain the same.
+ return new Call(
+ mContext,
+ mConnectionServiceRepository,
+ handle,
+ null /* gatewayInfo */,
+ null /* connectionManagerPhoneAccount */,
+ null /* phoneAccountHandle */,
+ false /* isIncoming */,
+ false /* isConference */);
+ }
+
/**
* Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
*
@@ -389,21 +489,18 @@
* @param extras The optional extras Bundle passed with the intent used for the incoming call.
*/
Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
- // Create a call with original handle. The handle may be changed when the call is attached
- // to a connection service, but in most cases will remain the same.
- Call call = new Call(
- mContext,
- mConnectionServiceRepository,
- handle,
- null /* gatewayInfo */,
- null /* connectionManagerPhoneAccount */,
- null /* phoneAccountHandle */,
- false /* isIncoming */,
- false /* isConference */);
+ Call call = getNewOutgoingCall(handle);
List<PhoneAccountHandle> accounts =
mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme());
+ Log.v(this, "startOutgoingCall found accounts = " + accounts);
+
+ if (mForegroundCall != null && mForegroundCall.getTargetPhoneAccount() != null) {
+ // If there is an ongoing call, use the same phone account to place this new call.
+ phoneAccountHandle = mForegroundCall.getTargetPhoneAccount();
+ }
+
// Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
// as if a phoneAccount was not specified (does the default behavior instead).
// Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
@@ -434,6 +531,12 @@
// a call, or cancel this call altogether.
if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) {
// just cancel at this point.
+ Log.i(this, "No remaining room for outgoing call: %s", call);
+ if (mCalls.contains(call)) {
+ // This call can already exist if it is a reused call,
+ // See {@link #getNewOutgoingCall}.
+ call.disconnect();
+ }
return null;
}
@@ -453,7 +556,9 @@
// Do not add the call if it is a potential MMI code.
if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
call.addListener(this);
- } else {
+ } else if (!mCalls.contains(call)) {
+ // We check if mCalls already contains the call because we could potentially be reusing
+ // a call which was previously added (See {@link #getNewOutgoingCall}).
addCall(call);
}
@@ -532,7 +637,8 @@
if (mForegroundCall != null && mForegroundCall != call &&
(mForegroundCall.isActive() ||
mForegroundCall.getState() == CallState.DIALING)) {
- if (0 == (mForegroundCall.getCallCapabilities() & PhoneCapabilities.HOLD)) {
+ if (0 == (mForegroundCall.getConnectionCapabilities()
+ & Connection.CAPABILITY_HOLD)) {
// This call does not support hold. If it is from a different connection
// service, then disconnect it, otherwise allow the connection service to
// figure out the right states.
@@ -540,6 +646,13 @@
mForegroundCall.disconnect();
}
} else {
+ Call heldCall = getHeldCall();
+ if (heldCall != null) {
+ Log.v(this, "Disconnecting held call %s before holding active call.",
+ heldCall);
+ heldCall.disconnect();
+ }
+
Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
mForegroundCall, call);
mForegroundCall.hold();
@@ -665,7 +778,8 @@
} else {
Log.d(this, "unholding call: (%s)", call);
for (Call c : mCalls) {
- if (c != null && c.isAlive() && c != call) {
+ // Only attempt to hold parent calls and not the individual children.
+ if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
c.hold();
}
}
@@ -743,9 +857,6 @@
}
void markCallAsActive(Call call) {
- if (call.getConnectTimeMillis() == 0) {
- call.setConnectTimeMillis(System.currentTimeMillis());
- }
setCallState(call, CallState.ACTIVE);
if (call.getStartWithSpeakerphoneOn()) {
@@ -791,7 +902,10 @@
if (service != null) {
for (Call call : mCalls) {
if (call.getConnectionService() == service) {
- markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
+ if (call.getState() != CallState.DISCONNECTED) {
+ markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
+ }
+ markCallAsRemoved(call);
}
}
}
@@ -834,13 +948,9 @@
}
/**
- * Checks to see if the specified call is the only high-level call and if so, enable the
- * "Add-call" button. We allow you to add a second call but not a third or beyond.
- *
- * @param call The call to test for add-call.
- * @return Whether the add-call feature should be enabled for the call.
+ * Returns true if telecom supports adding another top-level call.
*/
- protected boolean isAddCallCapable(Call call) {
+ boolean canAddCall() {
boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0) != 0;
if (!isDeviceProvisioned) {
@@ -848,23 +958,25 @@
return false;
}
- if (call.getParentCall() != null) {
- // Never true for child calls.
+ if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
return false;
}
- // Use canManageConference as a mechanism to check if the call is CDMA.
- // Disable "Add Call" for CDMA calls which are conference calls.
- boolean canManageConference = PhoneCapabilities.MANAGE_CONFERENCE
- == (call.getCallCapabilities() & PhoneCapabilities.MANAGE_CONFERENCE);
- if (call.isConference() && !canManageConference) {
- return false;
- }
+ int count = 0;
+ for (Call call : mCalls) {
+ if (call.isEmergencyCall()) {
+ // We never support add call if one of the calls is an emergency call.
+ return false;
+ } else if (call.getParentCall() == null) {
+ count++;
+ }
- // Loop through all the other calls and there exists a top level (has no parent) call
- // that is not the specified call, return false.
- for (Call otherCall : mCalls) {
- if (call != otherCall && otherCall.getParentCall() == null) {
+ // We do not check states for canAddCall. We treat disconnected calls the same
+ // and wait until they are removed instead. If we didn't count disconnected calls,
+ // we could put InCallServices into a state where they are showing two calls but
+ // also support add-call. Technically it's right, but overall looks better (UI-wise)
+ // and acts better if we wait until the call is removed.
+ if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
return false;
}
}
@@ -936,6 +1048,14 @@
Call createConferenceCall(
PhoneAccountHandle phoneAccount,
ParcelableConference parcelableConference) {
+
+ // If the parceled conference specifies a connect time, use it; otherwise default to 0,
+ // which is the default value for new Calls.
+ long connectTime =
+ parcelableConference.getConnectTimeMillis() ==
+ Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
+ parcelableConference.getConnectTimeMillis();
+
Call call = new Call(
mContext,
mConnectionServiceRepository,
@@ -944,13 +1064,11 @@
null /* connectionManagerPhoneAccount */,
phoneAccount,
false /* isIncoming */,
- true /* isConference */);
+ true /* isConference */,
+ connectTime);
setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()));
- if (call.getState() == CallState.ACTIVE) {
- call.setConnectTimeMillis(System.currentTimeMillis());
- }
- call.setCallCapabilities(parcelableConference.getCapabilities());
+ call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
// TODO: Move this to be a part of addCall()
call.addListener(this);
@@ -988,20 +1106,28 @@
* @param call The call to add.
*/
private void addCall(Call call) {
+ Trace.beginSection("addCall");
Log.v(this, "addCall(%s)", call);
-
call.addListener(this);
mCalls.add(call);
// TODO: Update mForegroundCall prior to invoking
// onCallAdded for calls which immediately take the foreground (like the first call).
for (CallsManagerListener listener : mListeners) {
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.beginSection(listener.getClass().toString() + " addCall");
+ }
listener.onCallAdded(call);
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.endSection();
+ }
}
- updateForegroundCall();
+ updateCallsManagerState();
+ Trace.endSection();
}
private void removeCall(Call call) {
+ Trace.beginSection("removeCall");
Log.v(this, "removeCall(%s)", call);
call.setParentCall(null); // need to clean up parent relationship before destroying.
@@ -1017,18 +1143,17 @@
// Only broadcast changes for calls that are being tracked.
if (shouldNotify) {
for (CallsManagerListener listener : mListeners) {
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
+ }
listener.onCallRemoved(call);
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.endSection();
+ }
}
- updateForegroundCall();
+ updateCallsManagerState();
}
-
- // Now that a call has been removed, other calls may gain new call capabilities (for
- // example, if only one call is left, it is now add-call capable again). Trigger the
- // recalculation of the call's current capabilities by forcing an update. (See
- // InCallController.toParcelableCall()).
- for (Call otherCall : mCalls) {
- otherCall.setCallCapabilities(otherCall.getCallCapabilities(), true /* forceUpdate */);
- }
+ Trace.endSection();
}
/**
@@ -1054,13 +1179,21 @@
// unexpected transition occurs.
call.setState(newState);
+ Trace.beginSection("onCallStateChanged");
// Only broadcast state change for calls that are being tracked.
if (mCalls.contains(call)) {
for (CallsManagerListener listener : mListeners) {
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
+ }
listener.onCallStateChanged(call, oldState, newState);
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.endSection();
+ }
}
- updateForegroundCall();
+ updateCallsManagerState();
}
+ Trace.endSection();
}
}
@@ -1068,6 +1201,7 @@
* Checks which call should be visible to the user and have audio focus.
*/
private void updateForegroundCall() {
+ Trace.beginSection("updateForegroundCall");
Call newForegroundCall = null;
for (Call call : mCalls) {
// TODO: Foreground-ness needs to be explicitly set. No call, regardless
@@ -1098,9 +1232,37 @@
mForegroundCall = newForegroundCall;
for (CallsManagerListener listener : mListeners) {
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.beginSection(listener.getClass().toString() + " updateForegroundCall");
+ }
listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.endSection();
+ }
}
}
+ Trace.endSection();
+ }
+
+ private void updateCanAddCall() {
+ boolean newCanAddCall = canAddCall();
+ if (newCanAddCall != mCanAddCall) {
+ mCanAddCall = newCanAddCall;
+ for (CallsManagerListener listener : mListeners) {
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
+ }
+ listener.onCanAddCallChanged(mCanAddCall);
+ if (Log.SYSTRACE_DEBUG) {
+ Trace.endSection();
+ }
+ }
+ }
+ }
+
+ private void updateCallsManagerState() {
+ updateForegroundCall();
+ updateCanAddCall();
}
private boolean isPotentialMMICode(Uri handle) {
@@ -1139,7 +1301,7 @@
int count = 0;
for (int state : states) {
for (Call call : mCalls) {
- if (call.getState() == state) {
+ if (call.getParentCall() == null && call.getState() == state) {
count++;
}
}
@@ -1168,6 +1330,8 @@
// NOTE: If the amount of live calls changes beyond 1, this logic will probably
// have to change.
Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES);
+ Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
+ liveCall);
if (call == liveCall) {
// If the call is already the foreground call, then we are golden.
@@ -1219,7 +1383,7 @@
}
// Try to hold the live call before attempting the new outgoing call.
- if (liveCall.can(PhoneCapabilities.HOLD)) {
+ if (liveCall.can(Connection.CAPABILITY_HOLD)) {
liveCall.hold();
return true;
}
@@ -1231,13 +1395,41 @@
}
/**
+ * Creates a new call for an existing connection.
+ *
+ * @param callId The id of the new call.
+ * @param connection The connection information.
+ * @return The new call.
+ */
+ Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
+ Call call = new Call(
+ mContext,
+ mConnectionServiceRepository,
+ connection.getHandle() /* handle */,
+ null /* gatewayInfo */,
+ null /* connectionManagerPhoneAccount */,
+ connection.getPhoneAccount(), /* targetPhoneAccountHandle */
+ false /* isIncoming */,
+ false /* isConference */);
+
+ setCallState(call, Call.getStateFromConnectionState(connection.getState()));
+ call.setConnectionCapabilities(connection.getConnectionCapabilities());
+ call.setCallerDisplayName(connection.getCallerDisplayName(),
+ connection.getCallerDisplayNamePresentation());
+
+ call.addListener(this);
+ addCall(call);
+
+ return call;
+ }
+
+ /**
* Dumps the state of the {@link CallsManager}.
*
* @param pw The {@code IndentingPrintWriter} to write the state to.
*/
public void dump(IndentingPrintWriter pw) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
- pw.increaseIndent();
if (mCalls != null) {
pw.println("mCalls: ");
pw.increaseIndent();
@@ -1246,6 +1438,34 @@
}
pw.decreaseIndent();
}
- pw.decreaseIndent();
+ pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall));
+
+ if (mCallAudioManager != null) {
+ pw.println("mCallAudioManager:");
+ pw.increaseIndent();
+ mCallAudioManager.dump(pw);
+ pw.decreaseIndent();
+ }
+
+ if (mTtyManager != null) {
+ pw.println("mTtyManager:");
+ pw.increaseIndent();
+ mTtyManager.dump(pw);
+ pw.decreaseIndent();
+ }
+
+ if (mInCallController != null) {
+ pw.println("mInCallController:");
+ pw.increaseIndent();
+ mInCallController.dump(pw);
+ pw.decreaseIndent();
+ }
+
+ if (mConnectionServiceRepository != null) {
+ pw.println("mConnectionServiceRepository:");
+ pw.increaseIndent();
+ mConnectionServiceRepository.dump(pw);
+ pw.decreaseIndent();
+ }
}
}
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index 93b2a24..ffc5947 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -72,4 +72,8 @@
@Override
public void onVideoStateChanged(Call call) {
}
+
+ @Override
+ public void onCanAddCallChanged(boolean canAddCall) {
+ }
}
diff --git a/src/com/android/server/telecom/ConnectionServiceRepository.java b/src/com/android/server/telecom/ConnectionServiceRepository.java
index e9a447d..0d73371 100644
--- a/src/com/android/server/telecom/ConnectionServiceRepository.java
+++ b/src/com/android/server/telecom/ConnectionServiceRepository.java
@@ -18,6 +18,10 @@
import android.content.ComponentName;
import android.content.Context;
+import android.os.UserHandle;
+import android.util.Pair;
+
+import com.android.internal.util.IndentingPrintWriter;
import java.util.HashMap;
@@ -26,8 +30,8 @@
*/
final class ConnectionServiceRepository
implements ServiceBinder.Listener<ConnectionServiceWrapper> {
- private final HashMap<ComponentName, ConnectionServiceWrapper> mServiceCache =
- new HashMap<ComponentName, ConnectionServiceWrapper>();
+ private final HashMap<Pair<ComponentName, UserHandle>, ConnectionServiceWrapper> mServiceCache =
+ new HashMap<>();
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final Context mContext;
@@ -36,16 +40,18 @@
mContext = context;
}
- ConnectionServiceWrapper getService(ComponentName componentName) {
- ConnectionServiceWrapper service = mServiceCache.get(componentName);
+ ConnectionServiceWrapper getService(ComponentName componentName, UserHandle userHandle) {
+ Pair<ComponentName, UserHandle> cacheKey = Pair.create(componentName, userHandle);
+ ConnectionServiceWrapper service = mServiceCache.get(cacheKey);
if (service == null) {
service = new ConnectionServiceWrapper(
componentName,
this,
mPhoneAccountRegistrar,
- mContext);
+ mContext,
+ userHandle);
service.addListener(this);
- mServiceCache.put(componentName, service);
+ mServiceCache.put(cacheKey, service);
}
return service;
}
@@ -59,4 +65,19 @@
public void onUnbind(ConnectionServiceWrapper service) {
mServiceCache.remove(service.getComponentName());
}
+
+ /**
+ * Dumps the state of the {@link ConnectionServiceRepository}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("mServiceCache:");
+ pw.increaseIndent();
+ for (Pair<ComponentName, UserHandle> cacheKey : mServiceCache.keySet()) {
+ ComponentName componentName = cacheKey.first;
+ pw.println(componentName);
+ }
+ pw.decreaseIndent();
+ }
}
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 32c6107..d470737 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -24,6 +24,7 @@
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.telecom.AudioState;
import android.telecom.Connection;
import android.telecom.ConnectionRequest;
@@ -67,7 +68,7 @@
private static final int MSG_SET_DISCONNECTED = 5;
private static final int MSG_SET_ON_HOLD = 6;
private static final int MSG_SET_RINGBACK_REQUESTED = 7;
- private static final int MSG_SET_CALL_CAPABILITIES = 8;
+ private static final int MSG_SET_CONNECTION_CAPABILITIES = 8;
private static final int MSG_SET_IS_CONFERENCED = 9;
private static final int MSG_ADD_CONFERENCE_CALL = 10;
private static final int MSG_REMOVE_CALL = 11;
@@ -80,6 +81,8 @@
private static final int MSG_SET_CALLER_DISPLAY_NAME = 18;
private static final int MSG_SET_VIDEO_STATE = 19;
private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
+ private static final int MSG_ADD_EXISTING_CONNECTION = 21;
+ private static final int MSG_ON_POST_DIAL_CHAR = 22;
private final Handler mHandler = new Handler() {
@Override
@@ -155,13 +158,13 @@
}
break;
}
- case MSG_SET_CALL_CAPABILITIES: {
+ case MSG_SET_CONNECTION_CAPABILITIES: {
call = mCallIdMapper.getCall(msg.obj);
if (call != null) {
- call.setCallCapabilities(msg.arg1);
+ call.setConnectionCapabilities(msg.arg1);
} else {
//Log.w(ConnectionServiceWrapper.this,
- // "setCallCapabilities, unknown call id: %s", msg.obj);
+ // "setConnectionCapabilities, unknown call id: %s", msg.obj);
}
break;
}
@@ -208,19 +211,26 @@
hasValidCalls = true;
}
}
- if (!hasValidCalls) {
+ // But don't bail out if the connection count is 0, because that is a valid
+ // IMS conference state.
+ if (!hasValidCalls && parcelableConference.getConnectionIds().size() > 0) {
Log.d(this, "Attempting to add a conference with no valid calls");
break;
}
// need to create a new Call
+ PhoneAccountHandle phAcc = null;
+ if (parcelableConference != null &&
+ parcelableConference.getPhoneAccount() != null) {
+ phAcc = parcelableConference.getPhoneAccount();
+ }
Call conferenceCall = mCallsManager.createConferenceCall(
- null, parcelableConference);
+ phAcc, parcelableConference);
mCallIdMapper.addCall(conferenceCall, id);
conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
- Log.d(this, "adding children to conference %s",
- parcelableConference.getConnectionIds());
+ Log.d(this, "adding children to conference %s phAcc %s",
+ parcelableConference.getConnectionIds(), phAcc);
for (String callId : parcelableConference.getConnectionIds()) {
Call childCall = mCallIdMapper.getCall(callId);
Log.d(this, "found child: %s", callId);
@@ -236,7 +246,7 @@
case MSG_REMOVE_CALL: {
call = mCallIdMapper.getCall(msg.obj);
if (call != null) {
- if (call.isActive()) {
+ if (call.isAlive()) {
mCallsManager.markCallAsDisconnected(
call, new DisconnectCause(DisconnectCause.REMOTE));
} else {
@@ -260,6 +270,21 @@
}
break;
}
+ case MSG_ON_POST_DIAL_CHAR: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ call = mCallIdMapper.getCall(args.arg1);
+ if (call != null) {
+ char nextChar = (char) args.argi1;
+ call.onPostDialChar(nextChar);
+ } else {
+ //Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
+ }
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
case MSG_QUERY_REMOTE_CALL_SERVICES: {
queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
break;
@@ -350,6 +375,19 @@
}
break;
}
+ case MSG_ADD_EXISTING_CONNECTION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String)args.arg1;
+ ParcelableConnection connection = (ParcelableConnection)args.arg2;
+ Call existingCall = mCallsManager.createCallForExistingConnection(callId,
+ connection);
+ mCallIdMapper.addCall(existingCall, callId);
+ existingCall.setConnectionService(ConnectionServiceWrapper.this);
+ } finally {
+ args.recycle();
+ }
+ }
}
}
};
@@ -445,10 +483,10 @@
}
@Override
- public void setCallCapabilities(String callId, int callCapabilities) {
- logIncoming("setCallCapabilities %s %d", callId, callCapabilities);
+ public void setConnectionCapabilities(String callId, int connectionCapabilities) {
+ logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper.isValidConferenceId(callId)) {
- mHandler.obtainMessage(MSG_SET_CALL_CAPABILITIES, callCapabilities, 0, callId)
+ mHandler.obtainMessage(MSG_SET_CONNECTION_CAPABILITIES, connectionCapabilities, 0, callId)
.sendToTarget();
} else {
Log.w(this, "ID not valid for setCallCapabilities");
@@ -458,12 +496,10 @@
@Override
public void setIsConferenced(String callId, String conferenceCallId) {
logIncoming("setIsConferenced %s %s", callId, conferenceCallId);
- if (mCallIdMapper.isValidCallId(callId)) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = callId;
- args.arg2 = conferenceCallId;
- mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
- }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = conferenceCallId;
+ mHandler.obtainMessage(MSG_SET_IS_CONFERENCED, args).sendToTarget();
}
@Override
@@ -489,6 +525,17 @@
}
@Override
+ public void onPostDialChar(String callId, char nextChar) throws RemoteException {
+ logIncoming("onPostDialChar %s %s", callId, nextChar);
+ if (mCallIdMapper.isValidCallId(callId)) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = nextChar;
+ mHandler.obtainMessage(MSG_ON_POST_DIAL_CHAR, args).sendToTarget();
+ }
+ }
+
+ @Override
public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
logIncoming("queryRemoteCSs");
mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
@@ -558,6 +605,15 @@
mHandler.obtainMessage(MSG_SET_CONFERENCEABLE_CONNECTIONS, args).sendToTarget();
}
}
+
+ @Override
+ public void addExistingConnection(String callId, ParcelableConnection connection) {
+ logIncoming("addExistingConnection %s %s", callId, connection);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = connection;
+ mHandler.obtainMessage(MSG_ADD_EXISTING_CONNECTION, args).sendToTarget();
+ }
}
private final Adapter mAdapter = new Adapter();
@@ -584,13 +640,15 @@
* @param connectionServiceRepository Connection service repository.
* @param phoneAccountRegistrar Phone account registrar
* @param context The context.
+ * @param userHandle The {@link UserHandle} to use when binding.
*/
ConnectionServiceWrapper(
ComponentName componentName,
ConnectionServiceRepository connectionServiceRepository,
PhoneAccountRegistrar phoneAccountRegistrar,
- Context context) {
- super(ConnectionService.SERVICE_INTERFACE, componentName, context);
+ Context context,
+ UserHandle userHandle) {
+ super(ConnectionService.SERVICE_INTERFACE, componentName, context, userHandle);
mConnectionServiceRepository = connectionServiceRepository;
phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {
// TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections
@@ -950,7 +1008,8 @@
PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(handle);
if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
ConnectionServiceWrapper service =
- mConnectionServiceRepository.getService(handle.getComponentName());
+ mConnectionServiceRepository.getService(handle.getComponentName(),
+ handle.getUserHandle());
if (service != null) {
simServices.add(service);
}
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index fab2679..eec1427 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -17,17 +17,24 @@
package com.android.server.telecom;
import android.content.Context;
+import android.telecom.CallState;
import android.telecom.DisconnectCause;
import android.telecom.ParcelableConnection;
import android.telecom.Phone;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
// TODO: Needed for move to system service: import com.android.internal.R;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.Objects;
/**
@@ -89,6 +96,8 @@
private DisconnectCause mLastErrorDisconnectCause;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
private final Context mContext;
+ private boolean mShouldUseConnectionManager = true;
+ private CreateConnectionTimeout mTimeout;
CreateConnectionProcessor(
Call call, ConnectionServiceRepository repository, CreateConnectionResponse response,
@@ -100,8 +109,17 @@
mContext = context;
}
+ boolean isProcessingComplete() {
+ return mResponse == null;
+ }
+
+ boolean isCallTimedOut() {
+ return mTimeout != null && mTimeout.isCallTimedOut();
+ }
+
void process() {
Log.v(this, "process");
+ clearTimeout();
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
@@ -113,6 +131,18 @@
attemptNextPhoneAccount();
}
+ boolean hasMorePhoneAccounts() {
+ return mAttemptRecordIterator.hasNext();
+ }
+
+ void continueProcessingIfPossible(CreateConnectionResponse response,
+ DisconnectCause disconnectCause) {
+ Log.v(this, "continueProcessingIfPossible");
+ mResponse = response;
+ mLastErrorDisconnectCause = disconnectCause;
+ attemptNextPhoneAccount();
+ }
+
void abort() {
Log.v(this, "abort");
@@ -120,6 +150,7 @@
// more services.
CreateConnectionResponse response = mResponse;
mResponse = null;
+ clearTimeout();
ConnectionServiceWrapper service = mCall.getConnectionService();
if (service != null) {
@@ -160,9 +191,11 @@
if (mResponse != null && attempt != null) {
Log.i(this, "Trying attempt %s", attempt);
+ PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
ConnectionServiceWrapper service =
mRepository.getService(
- attempt.connectionManagerPhoneAccount.getComponentName());
+ phoneAccount.getComponentName(),
+ phoneAccount.getUserHandle());
if (service == null) {
Log.i(this, "Found no connection service for attempt %s", attempt);
attemptNextPhoneAccount();
@@ -170,12 +203,15 @@
mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
mCall.setConnectionService(service);
+ setTimeoutIfNeeded(service, attempt);
+
Log.i(this, "Attempting to call from %s", service.getComponentName());
service.createConnection(mCall, new Response(service));
}
} else {
Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
if (mResponse != null) {
+ clearTimeout();
mResponse.handleCreateConnectionFailure(mLastErrorDisconnectCause != null ?
mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR));
mResponse = null;
@@ -184,7 +220,30 @@
}
}
+ private void setTimeoutIfNeeded(ConnectionServiceWrapper service, CallAttemptRecord attempt) {
+ clearTimeout();
+
+ CreateConnectionTimeout timeout = new CreateConnectionTimeout(
+ mContext, mPhoneAccountRegistrar, service, mCall);
+ if (timeout.isTimeoutNeededForCall(getConnectionServices(mAttemptRecords),
+ attempt.connectionManagerPhoneAccount)) {
+ mTimeout = timeout;
+ timeout.registerTimeout();
+ }
+ }
+
+ private void clearTimeout() {
+ if (mTimeout != null) {
+ mTimeout.unregisterTimeout();
+ mTimeout = null;
+ }
+ }
+
private boolean shouldSetConnectionManager() {
+ if (!mShouldUseConnectionManager) {
+ return false;
+ }
+
if (mAttemptRecords.size() == 0) {
return false;
}
@@ -223,8 +282,7 @@
CallAttemptRecord record = new CallAttemptRecord(
mPhoneAccountRegistrar.getSimCallManager(),
mAttemptRecords.get(0).targetPhoneAccount);
- Log.v(this, "setConnectionManager, changing %s -> %s",
- mAttemptRecords.get(0).targetPhoneAccount, record);
+ Log.v(this, "setConnectionManager, changing %s -> %s", mAttemptRecords.get(0), record);
mAttemptRecords.set(0, record);
} else {
Log.v(this, "setConnectionManager, not changing");
@@ -265,7 +323,7 @@
// Next, add the connection manager account as a backup if it can place emergency calls.
PhoneAccountHandle callManagerHandle = mPhoneAccountRegistrar.getSimCallManager();
- if (callManagerHandle != null) {
+ if (mShouldUseConnectionManager && callManagerHandle != null) {
PhoneAccount callManager = mPhoneAccountRegistrar
.getPhoneAccount(callManagerHandle);
if (callManager.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
@@ -284,6 +342,16 @@
}
}
+ /** Returns all connection services used by the call attempt records. */
+ private static Collection<PhoneAccountHandle> getConnectionServices(
+ List<CallAttemptRecord> records) {
+ HashSet<PhoneAccountHandle> result = new HashSet<>();
+ for (CallAttemptRecord record : records) {
+ result.add(record.connectionManagerPhoneAccount);
+ }
+ return result;
+ }
+
private class Response implements CreateConnectionResponse {
private final ConnectionServiceWrapper mService;
@@ -304,15 +372,49 @@
// in hearing about any more attempts
mResponse.handleCreateConnectionSuccess(idMapper, connection);
mResponse = null;
+ // If there's a timeout running then don't clear it. The timeout can be triggered
+ // after the call has successfully been created but before it has become active.
}
}
+ private boolean shouldFallbackToNoConnectionManager(DisconnectCause cause) {
+ PhoneAccountHandle handle = mCall.getConnectionManagerPhoneAccount();
+ if (handle == null || !handle.equals(mPhoneAccountRegistrar.getSimCallManager())) {
+ return false;
+ }
+
+ ConnectionServiceWrapper connectionManager = mCall.getConnectionService();
+ if (connectionManager == null) {
+ return false;
+ }
+
+ if (cause.getCode() == DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED) {
+ Log.d(CreateConnectionProcessor.this, "Connection manager declined to handle the "
+ + "call, falling back to not using a connection manager");
+ return true;
+ }
+
+ if (!connectionManager.isServiceValid("createConnection")) {
+ Log.d(CreateConnectionProcessor.this, "Connection manager unbound while trying "
+ + "create a connection, falling back to not using a connection manager");
+ return true;
+ }
+
+ return false;
+ }
+
@Override
public void handleCreateConnectionFailure(DisconnectCause errorDisconnectCause) {
// Failure of some sort; record the reasons for failure and try again if possible
Log.d(CreateConnectionProcessor.this, "Connection failed: (%s)", errorDisconnectCause);
mLastErrorDisconnectCause = errorDisconnectCause;
- attemptNextPhoneAccount();
+ if (shouldFallbackToNoConnectionManager(errorDisconnectCause)) {
+ mShouldUseConnectionManager = false;
+ // Restart from the beginning.
+ process();
+ } else {
+ attemptNextPhoneAccount();
+ }
}
}
}
diff --git a/src/com/android/server/telecom/CreateConnectionTimeout.java b/src/com/android/server/telecom/CreateConnectionTimeout.java
new file mode 100644
index 0000000..3f308cc
--- /dev/null
+++ b/src/com/android/server/telecom/CreateConnectionTimeout.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2015, 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.server.telecom;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.telecom.CallState;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ * Registers a timeout for a call and disconnects the call when the timeout expires.
+ */
+final class CreateConnectionTimeout extends PhoneStateListener implements Runnable {
+ private final Context mContext;
+ private final PhoneAccountRegistrar mPhoneAccountRegistrar;
+ private final ConnectionServiceWrapper mConnectionService;
+ private final Call mCall;
+ private final Handler mHandler = new Handler();
+ private boolean mIsRegistered;
+ private boolean mIsCallTimedOut;
+
+ CreateConnectionTimeout(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
+ ConnectionServiceWrapper service, Call call) {
+ mContext = context;
+ mPhoneAccountRegistrar = phoneAccountRegistrar;
+ mConnectionService = service;
+ mCall = call;
+ }
+
+ boolean isTimeoutNeededForCall(Collection<PhoneAccountHandle> accounts,
+ PhoneAccountHandle currentAccount) {
+ // Non-emergency calls timeout automatically at the radio layer. No need for a timeout here.
+ if (!TelephonyUtil.shouldProcessAsEmergency(mContext, mCall.getHandle())) {
+ return false;
+ }
+
+ // If there's no connection manager to fallback on then there's no point in having a
+ // timeout.
+ PhoneAccountHandle connectionManager = mPhoneAccountRegistrar.getSimCallManager();
+ if (!accounts.contains(connectionManager)) {
+ return false;
+ }
+
+ // No need to add a timeout if the current attempt is over the connection manager.
+ if (Objects.equals(connectionManager, currentAccount)) {
+ return false;
+ }
+
+ // To reduce the number of scenarios where a timeout is needed, only use a timeout if
+ // we're connected to Wi-Fi. This ensures that the fallback connection manager has an
+ // alternate route to place the call. TODO: remove this condition or allow connection
+ // managers to specify transports. See http://b/19199181.
+ if (!isConnectedToWifi()) {
+ return false;
+ }
+
+ Log.d(this, "isTimeoutNeededForCall, returning true");
+ return true;
+ }
+
+ void registerTimeout() {
+ Log.d(this, "registerTimeout");
+ mIsRegistered = true;
+ // First find out the cellular service state. Based on the state we decide whether a timeout
+ // will actually be enforced and if so how long it should be.
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
+ telephonyManager.listen(this, 0);
+ }
+
+ void unregisterTimeout() {
+ Log.d(this, "unregisterTimeout");
+ mIsRegistered = false;
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ boolean isCallTimedOut() {
+ return mIsCallTimedOut;
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ long timeoutLengthMillis = getTimeoutLengthMillis(serviceState);
+ if (!mIsRegistered) {
+ Log.d(this, "onServiceStateChanged, timeout no longer registered, skipping");
+ } else if (timeoutLengthMillis <= 0) {
+ Log.d(this, "onServiceStateChanged, timeout set to %d, skipping", timeoutLengthMillis);
+ } else if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
+ // If cellular service is available then don't bother with a timeout.
+ Log.d(this, "onServiceStateChanged, cellular service available, skipping");
+ } else {
+ mHandler.postDelayed(this, timeoutLengthMillis);
+ }
+ }
+
+ @Override
+ public void run() {
+ if (mIsRegistered && isCallBeingPlaced(mCall)) {
+ Log.d(this, "run, call timed out, calling disconnect");
+ mIsCallTimedOut = true;
+ mConnectionService.disconnect(mCall);
+ }
+ }
+
+ static boolean isCallBeingPlaced(Call call) {
+ int state = call.getState();
+ return state == CallState.NEW
+ || state == CallState.CONNECTING
+ || state == CallState.DIALING;
+ }
+
+ private long getTimeoutLengthMillis(ServiceState serviceState) {
+ // If the radio is off then use a longer timeout. This gives us more time to power on the
+ // radio.
+ if (serviceState.getState() == ServiceState.STATE_POWER_OFF) {
+ return Timeouts.getEmergencyCallTimeoutRadioOffMillis(
+ mContext.getContentResolver());
+ } else {
+ return Timeouts.getEmergencyCallTimeoutMillis(mContext.getContentResolver());
+ }
+ }
+
+ private boolean isConnectedToWifi() {
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ NetworkInfo ni = cm.getActiveNetworkInfo();
+ return ni != null && ni.isConnected() && ni.getType() == ConnectivityManager.TYPE_WIFI;
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/server/telecom/DtmfLocalTonePlayer.java b/src/com/android/server/telecom/DtmfLocalTonePlayer.java
index 6b4b74d..562f8d3 100644
--- a/src/com/android/server/telecom/DtmfLocalTonePlayer.java
+++ b/src/com/android/server/telecom/DtmfLocalTonePlayer.java
@@ -22,9 +22,6 @@
import android.provider.Settings;
// TODO: Needed for move to system service: import com.android.internal.R;
-import com.google.common.collect.ImmutableMap;
-
-import java.util.Map;
/**
* Plays DTMF tones locally for the caller to hear. In order to reduce (1) the amount of times we
@@ -33,22 +30,6 @@
* changes.
*/
class DtmfLocalTonePlayer extends CallsManagerListenerBase {
- private static final Map<Character, Integer> TONE_MAP =
- ImmutableMap.<Character, Integer>builder()
- .put('1', ToneGenerator.TONE_DTMF_1)
- .put('2', ToneGenerator.TONE_DTMF_2)
- .put('3', ToneGenerator.TONE_DTMF_3)
- .put('4', ToneGenerator.TONE_DTMF_4)
- .put('5', ToneGenerator.TONE_DTMF_5)
- .put('6', ToneGenerator.TONE_DTMF_6)
- .put('7', ToneGenerator.TONE_DTMF_7)
- .put('8', ToneGenerator.TONE_DTMF_8)
- .put('9', ToneGenerator.TONE_DTMF_9)
- .put('0', ToneGenerator.TONE_DTMF_0)
- .put('#', ToneGenerator.TONE_DTMF_P)
- .put('*', ToneGenerator.TONE_DTMF_S)
- .build();
-
/** Generator used to actually play the tone. */
private ToneGenerator mToneGenerator;
@@ -85,8 +66,9 @@
Log.d(this, "playTone: mToneGenerator == null, %c.", c);
} else {
Log.d(this, "starting local tone: %c.", c);
- if (TONE_MAP.containsKey(c)) {
- mToneGenerator.startTone(TONE_MAP.get(c), -1 /* toneDuration */);
+ int tone = getMappedTone(c);
+ if (tone != ToneGenerator.TONE_UNKNOWN) {
+ mToneGenerator.startTone(tone, -1 /* toneDuration */);
}
}
}
@@ -160,4 +142,15 @@
}
}
}
+
+ private static final int getMappedTone(char digit) {
+ if (digit >= '0' && digit <= '9') {
+ return ToneGenerator.TONE_DTMF_0 + digit - '0';
+ } else if (digit == '#') {
+ return ToneGenerator.TONE_DTMF_P;
+ } else if (digit == '*') {
+ return ToneGenerator.TONE_DTMF_S;
+ }
+ return ToneGenerator.TONE_UNKNOWN;
+ }
}
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index d4af791..e39b5a5 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -102,8 +102,6 @@
break;
case MSG_POST_DIAL_CONTINUE:
call = mCallIdMapper.getCall(msg.obj);
- mCallsManager.postDialContinue(call, msg.arg1 == 1);
- call = mCallIdMapper.getCall(msg.obj);
if (call != null) {
mCallsManager.postDialContinue(call, msg.arg1 == 1);
} else {
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 78553ad..eef75e3 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -28,21 +28,23 @@
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.telecom.AudioState;
import android.telecom.CallProperties;
import android.telecom.CallState;
+import android.telecom.Connection;
import android.telecom.InCallService;
import android.telecom.ParcelableCall;
-import android.telecom.PhoneCapabilities;
import android.telecom.TelecomManager;
import android.util.ArrayMap;
// TODO: Needed for move to system service: import com.android.internal.R;
import com.android.internal.telecom.IInCallService;
-import com.google.common.collect.ImmutableCollection;
+import com.android.internal.util.IndentingPrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -74,7 +76,7 @@
private final Call.Listener mCallListener = new Call.ListenerBase() {
@Override
- public void onCallCapabilitiesChanged(Call call) {
+ public void onConnectionCapabilitiesChanged(Call call) {
updateCall(call);
}
@@ -150,7 +152,7 @@
@Override
public void onCallAdded(Call call) {
if (mInCallServices.isEmpty()) {
- bind();
+ bind(call);
} else {
Log.i(this, "onCallAdded: %s", call);
// Track the call if we don't already know about it.
@@ -208,6 +210,19 @@
}
}
+ @Override
+ public void onCanAddCallChanged(boolean canAddCall) {
+ if (!mInCallServices.isEmpty()) {
+ Log.i(this, "onCanAddCallChanged : %b", canAddCall);
+ for (IInCallService inCallService : mInCallServices.values()) {
+ try {
+ inCallService.onCanAddCallChanged(canAddCall);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+ }
+
void onPostDialWait(Call call, String remaining) {
if (!mInCallServices.isEmpty()) {
Log.i(this, "Calling onPostDialWait, remaining = %s", remaining);
@@ -257,8 +272,10 @@
/**
* Binds to the in-call app if not already connected by binding directly to the saved
* component name of the {@link IInCallService} implementation.
+ *
+ * @param call The newly added call that triggered the binding to the in-call services.
*/
- private void bind() {
+ private void bind(Call call) {
ThreadUtil.checkOnMainThread();
if (mInCallServices.isEmpty()) {
PackageManager packageManager = mContext.getPackageManager();
@@ -299,8 +316,21 @@
Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
intent.setComponent(componentName);
- if (mContext.bindServiceAsUser(intent, inCallServiceConnection,
- Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
+ final int bindFlags;
+ if (mInCallComponentName.equals(componentName)) {
+ bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT;
+ if (!call.isIncoming()) {
+ intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS,
+ call.getExtras());
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+ call.getTargetPhoneAccount());
+ }
+ } else {
+ bindFlags = Context.BIND_AUTO_CREATE;
+ }
+
+ if (mContext.bindServiceAsUser(intent, inCallServiceConnection, bindFlags,
+ UserHandle.CURRENT)) {
mServiceConnections.put(componentName, inCallServiceConnection);
}
}
@@ -319,7 +349,7 @@
*/
private void onConnected(ComponentName componentName, IBinder service) {
ThreadUtil.checkOnMainThread();
-
+ Trace.beginSection("onConnected: " + componentName);
Log.i(this, "onConnected to %s", componentName);
IInCallService inCallService = IInCallService.Stub.asInterface(service);
@@ -330,11 +360,12 @@
mInCallServices.put(componentName, inCallService);
} catch (RemoteException e) {
Log.e(this, e, "Failed to set the in-call adapter.");
+ Trace.endSection();
return;
}
// Upon successful connection, send the state of the world to the service.
- ImmutableCollection<Call> calls = CallsManager.getInstance().getCalls();
+ Collection<Call> calls = CallsManager.getInstance().getCalls();
if (!calls.isEmpty()) {
Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(),
componentName);
@@ -350,9 +381,11 @@
}
}
onAudioStateChanged(null, CallsManager.getInstance().getAudioState());
+ onCanAddCallChanged(CallsManager.getInstance().canAddCall());
} else {
unbind();
}
+ Trace.endSection();
}
/**
@@ -406,7 +439,6 @@
IInCallService inCallService = entry.getValue();
ParcelableCall parcelableCall = toParcelableCall(call,
componentName.equals(mInCallComponentName) /* includeVideoProvider */);
-
Log.v(this, "updateCall %s ==> %s", call, parcelableCall);
try {
inCallService.updateCall(parcelableCall);
@@ -421,26 +453,35 @@
*
* @param call The {@link Call} to parcel.
* @param includeVideoProvider When {@code true}, the {@link IVideoProvider} is included in the
- * parcelled call. When {@code false}, the {@link IVideoProvider} is not included.
+ * parceled call. When {@code false}, the {@link IVideoProvider} is not included.
* @return The {@link ParcelableCall} containing all call information from the {@link Call}.
*/
private ParcelableCall toParcelableCall(Call call, boolean includeVideoProvider) {
String callId = mCallIdMapper.getCallId(call);
- int capabilities = call.getCallCapabilities();
- if (CallsManager.getInstance().isAddCallCapable(call)) {
- capabilities |= PhoneCapabilities.ADD_CALL;
- }
-
- // Disable mute and add call for emergency calls.
- if (call.isEmergencyCall()) {
- capabilities &= ~PhoneCapabilities.MUTE;
- capabilities &= ~PhoneCapabilities.ADD_CALL;
- }
-
- int properties = call.isConference() ? CallProperties.CONFERENCE : 0;
-
int state = call.getState();
+ int capabilities = convertConnectionToCallCapabilities(call.getConnectionCapabilities());
+
+ // If this is a single-SIM device, the "default SIM" will always be the only SIM.
+ boolean isDefaultSmsAccount =
+ CallsManager.getInstance().getPhoneAccountRegistrar().isUserSelectedSmsPhoneAccount(
+ call.getTargetPhoneAccount());
+ if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
+ capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
+ }
+
+ if (call.isEmergencyCall()) {
+ capabilities = removeCapability(
+ capabilities, android.telecom.Call.Details.CAPABILITY_MUTE);
+ }
+
+ if (state == CallState.DIALING) {
+ capabilities = removeCapability(
+ capabilities, android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL);
+ capabilities = removeCapability(
+ capabilities, android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE);
+ }
+
if (state == CallState.ABORTED) {
state = CallState.DISCONNECTED;
}
@@ -459,17 +500,18 @@
List<Call> childCalls = call.getChildCalls();
List<String> childCallIds = new ArrayList<>();
if (!childCalls.isEmpty()) {
- connectTimeMillis = Long.MAX_VALUE;
+ long childConnectTimeMillis = Long.MAX_VALUE;
for (Call child : childCalls) {
if (child.getConnectTimeMillis() > 0) {
- connectTimeMillis = Math.min(child.getConnectTimeMillis(), connectTimeMillis);
+ childConnectTimeMillis = Math.min(child.getConnectTimeMillis(),
+ childConnectTimeMillis);
}
childCallIds.add(mCallIdMapper.getCallId(child));
}
- }
- if (call.isRespondViaSmsCapable()) {
- capabilities |= PhoneCapabilities.RESPOND_VIA_TEXT;
+ if (childConnectTimeMillis != Long.MAX_VALUE) {
+ connectTimeMillis = childConnectTimeMillis;
+ }
}
Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
@@ -486,6 +528,7 @@
}
}
+ int properties = call.isConference() ? CallProperties.CONFERENCE : 0;
return new ParcelableCall(
callId,
state,
@@ -509,6 +552,63 @@
call.getExtras());
}
+ private static final int[] CONNECTION_TO_CALL_CAPABILITY = new int[] {
+ Connection.CAPABILITY_HOLD,
+ android.telecom.Call.Details.CAPABILITY_HOLD,
+
+ Connection.CAPABILITY_SUPPORT_HOLD,
+ android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD,
+
+ Connection.CAPABILITY_MERGE_CONFERENCE,
+ android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE,
+
+ Connection.CAPABILITY_SWAP_CONFERENCE,
+ android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE,
+
+ Connection.CAPABILITY_UNUSED,
+ android.telecom.Call.Details.CAPABILITY_UNUSED,
+
+ Connection.CAPABILITY_RESPOND_VIA_TEXT,
+ android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT,
+
+ Connection.CAPABILITY_MUTE,
+ android.telecom.Call.Details.CAPABILITY_MUTE,
+
+ Connection.CAPABILITY_MANAGE_CONFERENCE,
+ android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE,
+
+ Connection.CAPABILITY_SUPPORTS_VT_LOCAL,
+ android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL,
+
+ Connection.CAPABILITY_SUPPORTS_VT_REMOTE,
+ android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE,
+
+ Connection.CAPABILITY_HIGH_DEF_AUDIO,
+ android.telecom.Call.Details.CAPABILITY_HIGH_DEF_AUDIO,
+
+ Connection.CAPABILITY_VoWIFI,
+ android.telecom.Call.Details.CAPABILITY_VoWIFI,
+
+ Connection.CAPABILITY_SEPARATE_FROM_CONFERENCE,
+ android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE,
+
+ Connection.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
+ android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE,
+
+ Connection.CAPABILITY_GENERIC_CONFERENCE,
+ android.telecom.Call.Details.CAPABILITY_GENERIC_CONFERENCE
+ };
+
+ private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
+ int callCapabilities = 0;
+ for (int i = 0; i < CONNECTION_TO_CALL_CAPABILITY.length; i += 2) {
+ if ((CONNECTION_TO_CALL_CAPABILITY[i] & connectionCapabilities) != 0) {
+ callCapabilities |= CONNECTION_TO_CALL_CAPABILITY[i + 1];
+ }
+ }
+ return callCapabilities;
+ }
+
/**
* Adds the call to the list of calls tracked by the {@link InCallController}.
* @param call The call to add.
@@ -519,4 +619,32 @@
call.addListener(mCallListener);
}
}
+
+ /**
+ * Removes the specified capability from the set of capabilities bits and returns the new set.
+ */
+ private static int removeCapability(int capabilities, int capability) {
+ return capabilities & ~capability;
+ }
+
+ /**
+ * Dumps the state of the {@link InCallController}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("mInCallServices (InCalls registered):");
+ pw.increaseIndent();
+ for (ComponentName componentName : mInCallServices.keySet()) {
+ pw.println(componentName);
+ }
+ pw.decreaseIndent();
+
+ pw.println("mServiceConnections (InCalls bound):");
+ pw.increaseIndent();
+ for (ComponentName componentName : mServiceConnections.keySet()) {
+ pw.println(componentName);
+ }
+ pw.decreaseIndent();
+ }
}
diff --git a/src/com/android/server/telecom/InCallWakeLockController.java b/src/com/android/server/telecom/InCallWakeLockController.java
new file mode 100644
index 0000000..d97e171
--- /dev/null
+++ b/src/com/android/server/telecom/InCallWakeLockController.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.telecom.CallState;
+
+/**
+ * Handles acquisition and release of wake locks relating to call state.
+ */
+class InCallWakeLockController extends CallsManagerListenerBase {
+
+ private static final String TAG = "InCallWakeLockContoller";
+
+ private final Context mContext;
+ private final PowerManager.WakeLock mFullWakeLock;
+ private final CallsManager mCallsManager;
+
+ InCallWakeLockController(Context context, CallsManager callsManager) {
+ mContext = context;
+ mCallsManager = callsManager;
+
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mFullWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
+
+ callsManager.addListener(this);
+ }
+
+ @Override
+ public void onCallAdded(Call call) {
+ handleWakeLock();
+ }
+
+ @Override
+ public void onCallRemoved(Call call) {
+ handleWakeLock();
+ }
+
+ @Override
+ public void onCallStateChanged(Call call, int oldState, int newState) {
+ handleWakeLock();
+ }
+
+ private void handleWakeLock() {
+ // We grab a full lock as long as there exists a ringing call.
+ Call ringingCall = mCallsManager.getRingingCall();
+ if (ringingCall != null) {
+ mFullWakeLock.acquire();
+ Log.i(this, "Acquiring full wake lock");
+ } else if (mFullWakeLock.isHeld()) {
+ mFullWakeLock.release();
+ Log.i(this, "Releasing full wake lock");
+ }
+ }
+}
diff --git a/src/com/android/server/telecom/Log.java b/src/com/android/server/telecom/Log.java
index 6e3ff6d..3ec8267 100644
--- a/src/com/android/server/telecom/Log.java
+++ b/src/com/android/server/telecom/Log.java
@@ -34,6 +34,7 @@
private static final String TAG = "Telecom";
public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
+ public static final boolean SYSTRACE_DEBUG = false; /* STOP SHIP if true */
public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
public static final boolean INFO = isLoggable(android.util.Log.INFO);
public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 2c1ffb8..2c1e78b 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -29,6 +29,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.UserHandle;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
@@ -45,7 +46,6 @@
/**
* Creates a notification for calls that the user missed (neither answered nor rejected).
* TODO: Make TelephonyManager.clearMissedCalls call into this class.
- * STOPSHIP: Resolve b/13769374 about moving this class to InCall.
*/
class MissedCallNotifier extends CallsManagerListenerBase {
@@ -57,6 +57,14 @@
Calls.DURATION,
Calls.TYPE,
};
+
+ private static final int CALL_LOG_COLUMN_ID = 0;
+ private static final int CALL_LOG_COLUMN_NUMBER = 1;
+ private static final int CALL_LOG_COLUMN_NUMBER_PRESENTATION = 2;
+ private static final int CALL_LOG_COLUMN_DATE = 3;
+ private static final int CALL_LOG_COLUMN_DURATION = 4;
+ private static final int CALL_LOG_COLUMN_TYPE = 5;
+
private static final int MISSED_CALL_NOTIFICATION_ID = 1;
private final Context mContext;
@@ -84,18 +92,22 @@
/** Clears missed call notification and marks the call log's missed calls as read. */
void clearMissedCalls() {
- // Clear the list of new missed calls from the call log.
- ContentValues values = new ContentValues();
- values.put(Calls.NEW, 0);
- values.put(Calls.IS_READ, 1);
- StringBuilder where = new StringBuilder();
- where.append(Calls.NEW);
- where.append(" = 1 AND ");
- where.append(Calls.TYPE);
- where.append(" = ?");
- mContext.getContentResolver().update(Calls.CONTENT_URI, values, where.toString(),
- new String[]{ Integer.toString(Calls.MISSED_TYPE) });
-
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ // Clear the list of new missed calls from the call log.
+ ContentValues values = new ContentValues();
+ values.put(Calls.NEW, 0);
+ values.put(Calls.IS_READ, 1);
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.NEW);
+ where.append(" = 1 AND ");
+ where.append(Calls.TYPE);
+ where.append(" = ?");
+ mContext.getContentResolver().update(Calls.CONTENT_URI, values, where.toString(),
+ new String[]{ Integer.toString(Calls.MISSED_TYPE) });
+ }
+ });
cancelMissedCallNotification();
}
@@ -280,10 +292,10 @@
try {
while (cursor.moveToNext()) {
// Get data about the missed call from the cursor
- final String handleString = cursor.getString(
- cursor.getColumnIndexOrThrow(Calls.NUMBER));
- final int presentation = cursor.getInt(cursor.getColumnIndexOrThrow(
- Calls.NUMBER_PRESENTATION));
+ final String handleString = cursor.getString(CALL_LOG_COLUMN_NUMBER);
+ final int presentation =
+ cursor.getInt(CALL_LOG_COLUMN_NUMBER_PRESENTATION);
+ final long date = cursor.getLong(CALL_LOG_COLUMN_DATE);
final Uri handle;
if (presentation != Calls.PRESENTATION_ALLOWED
@@ -300,6 +312,7 @@
false);
call.setDisconnectCause(new DisconnectCause(DisconnectCause.MISSED));
call.setState(CallState.DISCONNECTED);
+ call.setCreationTimeMillis(date);
// Listen for the update to the caller information before posting the
// notification so that we have the contact info and photo.
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index dbdf79d..c52f2bb 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
+import android.os.Trace;
import android.os.UserHandle;
import android.telecom.GatewayInfo;
import android.telecom.PhoneAccount;
@@ -96,6 +97,7 @@
@Override
public void onReceive(Context context, Intent intent) {
+ Trace.beginSection("onReceiveNewOutgoingCallBroadcast");
Log.v(this, "onReceive: %s", intent);
// Once the NEW_OUTGOING_CALL broadcast is finished, the resultData is used as the
@@ -114,8 +116,9 @@
if (endEarly) {
if (mCall != null) {
- mCall.disconnect();
+ mCall.disconnect(true /* wasViaNewOutgoingCall */);
}
+ Trace.endSection();
return;
}
@@ -139,6 +142,7 @@
false),
mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
VideoProfile.VideoState.AUDIO_ONLY));
+ Trace.endSection();
}
}
diff --git a/src/com/android/server/telecom/PhoneAccountBroadcastReceiver.java b/src/com/android/server/telecom/PhoneAccountBroadcastReceiver.java
index 74f63cd..9634eda 100644
--- a/src/com/android/server/telecom/PhoneAccountBroadcastReceiver.java
+++ b/src/com/android/server/telecom/PhoneAccountBroadcastReceiver.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.telecom.TelecomManager;
import java.lang.String;
@@ -61,9 +62,9 @@
* @param packageName The name of the removed package.
*/
private void handlePackageRemoved(Context context, String packageName) {
- final CallsManager callsManager = CallsManager.getInstance();
- if (callsManager != null) {
- callsManager.getPhoneAccountRegistrar().clearAccounts(packageName);
+ final TelecomManager telecomManager = TelecomManager.from(context);
+ if (telecomManager != null) {
+ telecomManager.clearAccountsForPackage(packageName);
}
}
}
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 4ee2c88..eb3248e 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -17,28 +17,34 @@
package com.android.server.telecom;
import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.os.Environment;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Process;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.telecom.ConnectionService;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
-import android.content.ComponentName;
-import android.content.Context;
-import android.net.Uri;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AtomicFile;
+import android.util.Base64;
import android.util.Xml;
// TODO: Needed for move to system service: import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -47,6 +53,7 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -56,7 +63,6 @@
import java.lang.SecurityException;
import java.lang.String;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -82,7 +88,7 @@
private static final String FILE_NAME = "phone-account-registrar-state.xml";
@VisibleForTesting
- public static final int EXPECTED_STATE_VERSION = 3;
+ public static final int EXPECTED_STATE_VERSION = 5;
/** Keep in sync with the same in SipSettings.java */
private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
@@ -90,7 +96,10 @@
private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
private final AtomicFile mAtomicFile;
private final Context mContext;
+ private final UserManager mUserManager;
+ private final SubscriptionManager mSubscriptionManager;
private State mState;
+ private UserHandle mCurrentUserHandle;
@VisibleForTesting
public PhoneAccountRegistrar(Context context) {
@@ -111,41 +120,38 @@
mState = new State();
mContext = context;
+ mUserManager = UserManager.get(context);
+ mSubscriptionManager = SubscriptionManager.from(mContext);
+ mCurrentUserHandle = Process.myUserHandle();
read();
}
/**
- * Retrieves the phone account id for a given subscription id if it exists. Subscription ids
- * apply only to PSTN/SIM card phone accounts so all other accounts should not have a
- * subscription id.
- * @param subscriptionId The subscription id for which to construct the phone account id
- * @return The string representing the phone account id for the subscription id.
- */
- public String getPhoneAccountIdForSubscriptionId(long subscriptionId) {
- return String.valueOf(subscriptionId);
- }
-
- /**
* Retrieves the subscription id for a given phone account if it exists. Subscription ids
* apply only to PSTN/SIM card phone accounts so all other accounts should not have a
* subscription id.
* @param accountHandle The handle for the phone account for which to retrieve the
* subscription id.
- * @return The value of the subscription id (long) or -1 if it does not exist or is not valid.
+ * @return The value of the subscription id or -1 if it does not exist or is not valid.
*/
- public long getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
- PhoneAccount account = getPhoneAccount(accountHandle);
- if (account == null || !account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) ||
- !TextUtils.isDigitsOnly(accountHandle.getId())) {
+ public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
+ PhoneAccount account = getPhoneAccountInternal(accountHandle);
+ if (account == null
+ || !account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
+ || !TextUtils.isDigitsOnly(accountHandle.getId())
+ || !isVisibleForUser(accountHandle)) {
// Since no decimals or negative numbers can be valid subscription ids, only a string of
// numbers can be subscription id
- return -1;
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
- return Long.parseLong(accountHandle.getId());
+ return Integer.parseInt(accountHandle.getId());
}
/**
- * Retrieves the default outgoing phone account supporting the specified uriScheme.
+ * Retrieves the default outgoing phone account supporting the specified uriScheme. Note that if
+ * {@link #mCurrentUserHandle} does not have visibility into the current default, {@code null}
+ * will be returned.
+ *
* @param uriScheme The URI scheme for the outgoing call.
* @return The {@link PhoneAccountHandle} to use.
*/
@@ -155,8 +161,9 @@
if (userSelected != null) {
// If there is a default PhoneAccount, ensure it supports calls to handles with the
// specified uriScheme.
- final PhoneAccount userSelectedAccount = getPhoneAccount(userSelected);
- if (userSelectedAccount.supportsUriScheme(uriScheme)) {
+ final PhoneAccount userSelectedAccount = getPhoneAccountInternal(userSelected);
+ if (userSelectedAccount.supportsUriScheme(uriScheme)
+ && isVisibleForUser(userSelected)) {
return userSelected;
}
}
@@ -167,20 +174,29 @@
// There are no accounts, so there can be no default
return null;
case 1:
- // There is only one account, which is by definition the default
- return outgoing.get(0);
+ // There is only one account, which is by definition the default.
+ PhoneAccountHandle onlyHandle = outgoing.get(0);
+ if (isVisibleForUser(onlyHandle)) {
+ return outgoing.get(0);
+ }
+ return null;
default:
// There are multiple accounts with no selected default
return null;
}
}
+ /**
+ * @return The user-selected outgoing {@link PhoneAccount}, or null if it hasn't been set (or
+ * if it was set by another user).
+ */
PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
if (mState.defaultOutgoing != null) {
// Return the registered outgoing default iff it still exists (we keep a sticky
// default to survive account deletion and re-addition)
for (int i = 0; i < mState.accounts.size(); i++) {
- if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)) {
+ if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)
+ && isVisibleForUser(mState.defaultOutgoing)) {
return mState.defaultOutgoing;
}
}
@@ -209,13 +225,21 @@
return;
}
- if (!getPhoneAccount(accountHandle).hasCapabilities(
+ if (!getPhoneAccountInternal(accountHandle).hasCapabilities(
PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
Log.w(this, "Trying to set non-call-provider default outgoing %s",
accountHandle);
return;
}
+ if (getPhoneAccountInternal(accountHandle).hasCapabilities(
+ PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+ // If the account selected is a SIM account, propagate down to the subscription
+ // record.
+ int subId = getSubscriptionIdForPhoneAccount(accountHandle);
+ mSubscriptionManager.setDefaultVoiceSubId(subId);
+ }
+
mState.defaultOutgoing = accountHandle;
}
@@ -223,9 +247,14 @@
fireDefaultOutgoingChanged();
}
+ boolean isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle) {
+ return getSubscriptionIdForPhoneAccount(accountHandle) ==
+ SubscriptionManager.getDefaultSmsSubId();
+ }
+
public void setSimCallManager(PhoneAccountHandle callManager) {
if (callManager != null) {
- PhoneAccount callManagerAccount = getPhoneAccount(callManager);
+ PhoneAccount callManagerAccount = getPhoneAccountInternal(callManager);
if (callManagerAccount == null) {
Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
return;
@@ -243,6 +272,9 @@
fireSimCallManagerChanged();
}
+ /**
+ * @return The {@link PhoneAccount}s which are visible to {@link #mCurrentUserHandle}.
+ */
public PhoneAccountHandle getSimCallManager() {
if (mState.simCallManager != null) {
if (NO_ACCOUNT_SELECTED.equals(mState.simCallManager)) {
@@ -251,7 +283,9 @@
// Return the registered sim call manager iff it still exists (we keep a sticky
// setting to survive account deletion and re-addition)
for (int i = 0; i < mState.accounts.size(); i++) {
- if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)) {
+ if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)
+ && !resolveComponent(mState.simCallManager).isEmpty()
+ && isVisibleForUser(mState.simCallManager)) {
return mState.simCallManager;
}
}
@@ -261,19 +295,15 @@
String defaultConnectionMgr =
mContext.getResources().getString(R.string.default_connection_manager_component);
if (!TextUtils.isEmpty(defaultConnectionMgr)) {
- PackageManager pm = mContext.getPackageManager();
-
ComponentName componentName = ComponentName.unflattenFromString(defaultConnectionMgr);
- Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE);
- intent.setComponent(componentName);
-
// Make sure that the component can be resolved.
- List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 0);
+ List<ResolveInfo> resolveInfos = resolveComponent(componentName, null);
if (!resolveInfos.isEmpty()) {
// See if there is registered PhoneAccount by this component.
List<PhoneAccountHandle> handles = getAllPhoneAccountHandles();
for (PhoneAccountHandle handle : handles) {
- if (componentName.equals(handle.getComponentName())) {
+ if (componentName.equals(handle.getComponentName())
+ && isVisibleForUser(handle)) {
return handle;
}
}
@@ -289,6 +319,97 @@
}
/**
+ * A version of {@link #getPhoneAccount} which does not guard for the current user.
+ *
+ * @param handle
+ * @return
+ */
+ PhoneAccount getPhoneAccountInternal(PhoneAccountHandle handle) {
+ for (PhoneAccount m : mState.accounts) {
+ if (Objects.equals(handle, m.getAccountHandle())) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Update the current UserHandle to track when users are switched. This will allow the
+ * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything
+ * across users.
+ *
+ * @param userHandle The {@link UserHandle}, as delivered by
+ * {@link Intent#ACTION_USER_SWITCHED}.
+ */
+ public void setCurrentUserHandle(UserHandle userHandle) {
+ if (userHandle == null) {
+ Log.d(this, "setCurrentUserHandle, userHandle = null");
+ userHandle = Process.myUserHandle();
+ }
+ Log.d(this, "setCurrentUserHandle, %s", userHandle);
+ mCurrentUserHandle = userHandle;
+ }
+
+ private boolean isVisibleForUser(PhoneAccountHandle accountHandle) {
+ if (accountHandle == null) {
+ return false;
+ }
+
+ return isVisibleForUser(getPhoneAccountInternal(accountHandle));
+ }
+
+ private boolean isVisibleForUser(PhoneAccount account) {
+ if (account == null) {
+ return false;
+ }
+
+ // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and
+ // all profiles. Only Telephony and SIP accounts should have this capability.
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+ return true;
+ }
+
+ UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle();
+ if (phoneAccountUserHandle == null) {
+ return false;
+ }
+
+ if (mCurrentUserHandle == null) {
+ Log.d(this, "Current user is null; assuming true");
+ return true;
+ }
+
+ // Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure
+ // that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is
+ // fine.
+ List<UserInfo> profileUsers = mUserManager.getProfiles(mCurrentUserHandle.getIdentifier());
+
+ for (UserInfo profileInfo : profileUsers) {
+ if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List<ResolveInfo> resolveComponent(PhoneAccountHandle phoneAccountHandle) {
+ return resolveComponent(phoneAccountHandle.getComponentName(),
+ phoneAccountHandle.getUserHandle());
+ }
+
+ private List<ResolveInfo> resolveComponent(ComponentName componentName,
+ UserHandle userHandle) {
+ PackageManager pm = mContext.getPackageManager();
+ Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE);
+ intent.setComponent(componentName);
+ if (userHandle != null) {
+ return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier());
+ } else {
+ return pm.queryIntentServices(intent, 0);
+ }
+ }
+
+ /**
* Retrieves a list of all {@link PhoneAccountHandle}s registered.
*
* @return The list of {@link PhoneAccountHandle}s.
@@ -296,22 +417,21 @@
public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
List<PhoneAccountHandle> accountHandles = new ArrayList<>();
for (PhoneAccount m : mState.accounts) {
- accountHandles.add(m.getAccountHandle());
+ if (isVisibleForUser(m)) {
+ accountHandles.add(m.getAccountHandle());
+ }
}
return accountHandles;
}
public List<PhoneAccount> getAllPhoneAccounts() {
- return new ArrayList<>(mState.accounts);
- }
-
- /**
- * Determines the number of all {@link PhoneAccount}s.
- *
- * @return The total number {@link PhoneAccount}s.
- */
- public int getAllPhoneAccountsCount() {
- return mState.accounts.size();
+ List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
+ for (PhoneAccount account : mState.accounts) {
+ if (isVisibleForUser(account)) {
+ accounts.add(account);
+ }
+ }
+ return accounts;
}
/**
@@ -345,7 +465,8 @@
for (PhoneAccount m : mState.accounts) {
if (Objects.equals(
packageName,
- m.getAccountHandle().getComponentName().getPackageName())) {
+ m.getAccountHandle().getComponentName().getPackageName())
+ && isVisibleForUser(m)) {
accountHandles.add(m.getAccountHandle());
}
}
@@ -364,7 +485,8 @@
public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
for (PhoneAccount m : mState.accounts) {
- if (Objects.equals(handle, m.getAccountHandle())) {
+ if (Objects.equals(handle, m.getAccountHandle())
+ && isVisibleForUser(m)) {
return m;
}
}
@@ -392,6 +514,9 @@
* @param account The {@code PhoneAccount} to add or replace.
*/
private void addOrReplacePhoneAccount(PhoneAccount account) {
+ Log.d(this, "addOrReplacePhoneAccount(%s -> %s)",
+ account.getAccountHandle(), account);
+
mState.accounts.add(account);
// Search for duplicates and remove any that are found.
for (int i = 0; i < mState.accounts.size() - 1; i++) {
@@ -409,7 +534,8 @@
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
for (int i = 0; i < mState.accounts.size(); i++) {
- if (Objects.equals(accountHandle, mState.accounts.get(i).getAccountHandle())) {
+ PhoneAccountHandle handle = mState.accounts.get(i).getAccountHandle();
+ if (Objects.equals(accountHandle, handle)) {
mState.accounts.remove(i);
break;
}
@@ -423,15 +549,16 @@
* Un-registers all phone accounts associated with a specified package.
*
* @param packageName The package for which phone accounts will be removed.
+ * @param userHandle The {@link UserHandle} the package is running under.
*/
- public void clearAccounts(String packageName) {
+ public void clearAccounts(String packageName, UserHandle userHandle) {
boolean accountsRemoved = false;
Iterator<PhoneAccount> it = mState.accounts.iterator();
while (it.hasNext()) {
PhoneAccount phoneAccount = it.next();
- if (Objects.equals(
- packageName,
- phoneAccount.getAccountHandle().getComponentName().getPackageName())) {
+ PhoneAccountHandle handle = phoneAccount.getAccountHandle();
+ if (Objects.equals(packageName, handle.getComponentName().getPackageName())
+ && Objects.equals(userHandle, handle.getUserHandle())) {
Log.i(this, "Removing phone account " + phoneAccount.getLabel());
it.remove();
accountsRemoved = true;
@@ -445,7 +572,7 @@
}
public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
- long subId = getSubscriptionIdForPhoneAccount(accountHandle);
+ int subId = getSubscriptionIdForPhoneAccount(accountHandle);
return PhoneNumberUtils.isVoiceMailNumber(subId, number);
}
@@ -520,9 +647,23 @@
private List<PhoneAccountHandle> getPhoneAccountHandles(int flags, String uriScheme) {
List<PhoneAccountHandle> accountHandles = new ArrayList<>();
for (PhoneAccount m : mState.accounts) {
- if (m.hasCapabilities(flags) && (uriScheme == null || m.supportsUriScheme(uriScheme))) {
- accountHandles.add(m.getAccountHandle());
+ if (!m.hasCapabilities(flags)) {
+ // Account doesn't have the right capabilities; skip this one.
+ continue;
}
+ if (uriScheme != null && !m.supportsUriScheme(uriScheme)) {
+ // Account doesn't support this URI scheme; skip this one.
+ continue;
+ }
+ if (resolveComponent(m.getAccountHandle()).isEmpty()) {
+ // This component cannot be resolved anymore; skip this one.
+ continue;
+ }
+ if (!isVisibleForUser(m)) {
+ // Account is not visible for the current user; skip this one.
+ continue;
+ }
+ accountHandles.add(m.getAccountHandle());
}
return accountHandles;
}
@@ -555,6 +696,27 @@
public int versionNumber;
}
+ /**
+ * Dumps the state of the {@link CallsManager}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ if (mState != null) {
+ pw.println("xmlVersion: " + mState.versionNumber);
+ pw.println("defaultOutgoing: " + (mState.defaultOutgoing == null ? "none" :
+ mState.defaultOutgoing));
+ pw.println("simCallManager: " + (mState.simCallManager == null ? "none" :
+ mState.simCallManager));
+ pw.println("phoneAccounts:");
+ pw.increaseIndent();
+ for (PhoneAccount phoneAccount : mState.accounts) {
+ pw.println(phoneAccount);
+ }
+ pw.decreaseIndent();
+ }
+ }
+
////////////////////////////////////////////////////////////////////////////////////////////////
//
// State management
@@ -568,7 +730,7 @@
try {
XmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(new BufferedOutputStream(os), "utf-8");
- writeToXml(mState, serializer);
+ writeToXml(mState, serializer, mContext);
serializer.flush();
success = true;
} finally {
@@ -612,15 +774,29 @@
}
}
+ // Verify all of the UserHandles.
+ List<PhoneAccount> badAccounts = new ArrayList<>();
+ for (PhoneAccount phoneAccount : mState.accounts) {
+ UserHandle userHandle = phoneAccount.getAccountHandle().getUserHandle();
+ if (userHandle == null) {
+ Log.w(this, "Missing UserHandle for %s", phoneAccount);
+ badAccounts.add(phoneAccount);
+ } else if (mUserManager.getSerialNumberForUser(userHandle) == -1) {
+ Log.w(this, "User does not exist for %s", phoneAccount);
+ badAccounts.add(phoneAccount);
+ }
+ }
+ mState.accounts.removeAll(badAccounts);
+
// If an upgrade occurred, write out the changed data.
- if (versionChanged) {
+ if (versionChanged || !badAccounts.isEmpty()) {
write();
}
}
- private static void writeToXml(State state, XmlSerializer serializer)
+ private static void writeToXml(State state, XmlSerializer serializer, Context context)
throws IOException {
- sStateXml.writeToXml(state, serializer);
+ sStateXml.writeToXml(state, serializer, context);
}
private static State readFromXml(XmlPullParser parser, Context context)
@@ -642,7 +818,7 @@
/**
* Write the supplied object to XML
*/
- public abstract void writeToXml(T o, XmlSerializer serializer)
+ public abstract void writeToXml(T o, XmlSerializer serializer, Context context)
throws IOException;
/**
@@ -655,7 +831,7 @@
public abstract T readFromXml(XmlPullParser parser, int version, Context context)
throws IOException, XmlPullParserException;
- protected void writeTextSafely(String tagName, Object value, XmlSerializer serializer)
+ protected void writeTextIfNonNull(String tagName, Object value, XmlSerializer serializer)
throws IOException {
if (value != null) {
serializer.startTag(null, tagName);
@@ -690,7 +866,27 @@
serializer.attribute(null, LENGTH_ATTRIBUTE, "0");
}
serializer.endTag(null, tagName);
+ }
+ protected void writeBitmapIfNonNull(String tagName, Bitmap value, XmlSerializer serializer)
+ throws IOException {
+ if (value != null && value.getByteCount() > 0) {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ value.compress(Bitmap.CompressFormat.PNG, 100, stream);
+ byte[] imageByteArray = stream.toByteArray();
+ String text = Base64.encodeToString(imageByteArray, 0, imageByteArray.length, 0);
+
+ serializer.startTag(null, tagName);
+ serializer.text(text);
+ serializer.endTag(null, tagName);
+ }
+ }
+
+ protected void writeLong(String tagName, long value, XmlSerializer serializer)
+ throws IOException {
+ serializer.startTag(null, tagName);
+ serializer.text(Long.valueOf(value).toString());
+ serializer.endTag(null, tagName);
}
/**
@@ -723,6 +919,12 @@
return arrayEntries;
}
+
+ protected Bitmap readBitmap(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ byte[] imageByteArray = Base64.decode(parser.getText(), 0);
+ return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length);
+ }
}
@VisibleForTesting
@@ -735,7 +937,7 @@
private static final String VERSION = "version";
@Override
- public void writeToXml(State o, XmlSerializer serializer)
+ public void writeToXml(State o, XmlSerializer serializer, Context context)
throws IOException {
if (o != null) {
serializer.startTag(null, CLASS_STATE);
@@ -743,19 +945,19 @@
if (o.defaultOutgoing != null) {
serializer.startTag(null, DEFAULT_OUTGOING);
- sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer);
+ sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer, context);
serializer.endTag(null, DEFAULT_OUTGOING);
}
if (o.simCallManager != null) {
serializer.startTag(null, SIM_CALL_MANAGER);
- sPhoneAccountHandleXml.writeToXml(o.simCallManager, serializer);
+ sPhoneAccountHandleXml.writeToXml(o.simCallManager, serializer, context);
serializer.endTag(null, SIM_CALL_MANAGER);
}
serializer.startTag(null, ACCOUNTS);
for (PhoneAccount m : o.accounts) {
- sPhoneAccountXml.writeToXml(m, serializer);
+ sPhoneAccountXml.writeToXml(m, serializer, context);
}
serializer.endTag(null, ACCOUNTS);
@@ -783,6 +985,13 @@
parser.nextTag();
s.simCallManager = sPhoneAccountHandleXml.readFromXml(parser,
s.versionNumber, context);
+ if (s.simCallManager.getUserHandle() == null) {
+ // This should never happen, but handle the upgrade case.
+ s.simCallManager = new PhoneAccountHandle(
+ s.simCallManager.getComponentName(),
+ s.simCallManager.getId(),
+ Process.myUserHandle());
+ }
} else if (parser.getName().equals(ACCOUNTS)) {
int accountsDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
@@ -810,32 +1019,37 @@
private static final String SUBSCRIPTION_ADDRESS = "subscription_number";
private static final String CAPABILITIES = "capabilities";
private static final String ICON_RES_ID = "icon_res_id";
- private static final String COLOR = "color";
+ private static final String ICON_PACKAGE_NAME = "icon_package_name";
+ private static final String ICON_BITMAP = "icon_bitmap";
+ private static final String ICON_TINT = "icon_tint";
+ private static final String HIGHLIGHT_COLOR = "highlight_color";
private static final String LABEL = "label";
private static final String SHORT_DESCRIPTION = "short_description";
private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes";
- private static final String TRUE = "true";
- private static final String FALSE = "false";
@Override
- public void writeToXml(PhoneAccount o, XmlSerializer serializer)
+ public void writeToXml(PhoneAccount o, XmlSerializer serializer, Context context)
throws IOException {
if (o != null) {
serializer.startTag(null, CLASS_PHONE_ACCOUNT);
if (o.getAccountHandle() != null) {
serializer.startTag(null, ACCOUNT_HANDLE);
- sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer);
+ sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer, context);
serializer.endTag(null, ACCOUNT_HANDLE);
}
- writeTextSafely(ADDRESS, o.getAddress(), serializer);
- writeTextSafely(SUBSCRIPTION_ADDRESS, o.getSubscriptionAddress(), serializer);
- writeTextSafely(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
- writeTextSafely(ICON_RES_ID, Integer.toString(o.getIconResId()), serializer);
- writeTextSafely(COLOR, Integer.toString(o.getColor()), serializer);
- writeTextSafely(LABEL, o.getLabel(), serializer);
- writeTextSafely(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
+ writeTextIfNonNull(ADDRESS, o.getAddress(), serializer);
+ writeTextIfNonNull(SUBSCRIPTION_ADDRESS, o.getSubscriptionAddress(), serializer);
+ writeTextIfNonNull(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer);
+ writeTextIfNonNull(ICON_RES_ID, Integer.toString(o.getIconResId()), serializer);
+ writeTextIfNonNull(ICON_PACKAGE_NAME, o.getIconPackageName(), serializer);
+ writeBitmapIfNonNull(ICON_BITMAP, o.getIconBitmap(), serializer);
+ writeTextIfNonNull(ICON_TINT, Integer.toString(o.getIconTint()), serializer);
+ writeTextIfNonNull(HIGHLIGHT_COLOR,
+ Integer.toString(o.getHighlightColor()), serializer);
+ writeTextIfNonNull(LABEL, o.getLabel(), serializer);
+ writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
serializer.endTag(null, CLASS_PHONE_ACCOUNT);
@@ -850,8 +1064,11 @@
Uri address = null;
Uri subscriptionAddress = null;
int capabilities = 0;
- int iconResId = 0;
- int color = 0;
+ int iconResId = PhoneAccount.NO_RESOURCE_ID;
+ String iconPackageName = null;
+ Bitmap iconBitmap = null;
+ int iconTint = PhoneAccount.NO_ICON_TINT;
+ int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR;
String label = null;
String shortDescription = null;
List<String> supportedUriSchemes = null;
@@ -874,9 +1091,18 @@
} else if (parser.getName().equals(ICON_RES_ID)) {
parser.next();
iconResId = Integer.parseInt(parser.getText());
- } else if (parser.getName().equals(COLOR)) {
+ } else if (parser.getName().equals(ICON_PACKAGE_NAME)) {
parser.next();
- color = Integer.parseInt(parser.getText());
+ iconPackageName = parser.getText();
+ } else if (parser.getName().equals(ICON_BITMAP)) {
+ parser.next();
+ iconBitmap = readBitmap(parser);
+ } else if (parser.getName().equals(ICON_TINT)) {
+ parser.next();
+ iconTint = Integer.parseInt(parser.getText());
+ } else if (parser.getName().equals(HIGHLIGHT_COLOR)) {
+ parser.next();
+ highlightColor = Integer.parseInt(parser.getText());
} else if (parser.getName().equals(LABEL)) {
parser.next();
label = parser.getText();
@@ -909,15 +1135,28 @@
}
}
- return PhoneAccount.builder(accountHandle, label)
+ // Upgrade older phone accounts with explicit package name
+ if (version < 5) {
+ if (iconBitmap == null) {
+ iconPackageName = accountHandle.getComponentName().getPackageName();
+ }
+ }
+
+ PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, label)
.setAddress(address)
.setSubscriptionAddress(subscriptionAddress)
.setCapabilities(capabilities)
- .setIconResId(iconResId)
- .setColor(color)
.setShortDescription(shortDescription)
.setSupportedUriSchemes(supportedUriSchemes)
- .build();
+ .setHighlightColor(highlightColor);
+
+ if (iconBitmap == null) {
+ builder.setIcon(iconPackageName, iconResId, iconTint);
+ } else {
+ builder.setIcon(iconBitmap);
+ }
+
+ return builder.build();
}
return null;
}
@@ -942,19 +1181,26 @@
private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle";
private static final String COMPONENT_NAME = "component_name";
private static final String ID = "id";
+ private static final String USER_SERIAL_NUMBER = "user_serial_number";
@Override
- public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer)
+ public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer, Context context)
throws IOException {
if (o != null) {
serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
if (o.getComponentName() != null) {
- writeTextSafely(
+ writeTextIfNonNull(
COMPONENT_NAME, o.getComponentName().flattenToString(), serializer);
}
- writeTextSafely(ID, o.getId(), serializer);
+ writeTextIfNonNull(ID, o.getId(), serializer);
+
+ if (o.getUserHandle() != null && context != null) {
+ UserManager userManager = UserManager.get(context);
+ writeLong(USER_SERIAL_NUMBER,
+ userManager.getSerialNumberForUser(o.getUserHandle()), serializer);
+ }
serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE);
}
@@ -966,7 +1212,11 @@
if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) {
String componentNameString = null;
String idString = null;
+ String userSerialNumberString = null;
int outerDepth = parser.getDepth();
+
+ UserManager userManager = UserManager.get(context);
+
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (parser.getName().equals(COMPONENT_NAME)) {
parser.next();
@@ -974,12 +1224,25 @@
} else if (parser.getName().equals(ID)) {
parser.next();
idString = parser.getText();
+ } else if (parser.getName().equals(USER_SERIAL_NUMBER)) {
+ parser.next();
+ userSerialNumberString = parser.getText();
}
}
if (componentNameString != null) {
+ UserHandle userHandle = null;
+ if (userSerialNumberString != null) {
+ try {
+ long serialNumber = Long.parseLong(userSerialNumberString);
+ userHandle = userManager.getUserForSerialNumber(serialNumber);
+ } catch (NumberFormatException e) {
+ Log.e(this, e, "Could not parse UserHandle " + userSerialNumberString);
+ }
+ }
return new PhoneAccountHandle(
ComponentName.unflattenFromString(componentNameString),
- idString);
+ idString,
+ userHandle);
}
}
return null;
diff --git a/src/com/android/server/telecom/ProximitySensorManager.java b/src/com/android/server/telecom/ProximitySensorManager.java
index 289366f..5b82c43 100644
--- a/src/com/android/server/telecom/ProximitySensorManager.java
+++ b/src/com/android/server/telecom/ProximitySensorManager.java
@@ -42,7 +42,7 @@
@Override
public void onCallRemoved(Call call) {
if (CallsManager.getInstance().getCalls().isEmpty()) {
- Log.v(this, "all calls removed, resetting proximity sensor to default state");
+ Log.i(this, "All calls removed, resetting proximity sensor to default state");
turnOff(true);
}
super.onCallRemoved(call);
diff --git a/src/com/android/server/telecom/RespondViaSmsManager.java b/src/com/android/server/telecom/RespondViaSmsManager.java
index 2ac9379..ebedf9f 100644
--- a/src/com/android/server/telecom/RespondViaSmsManager.java
+++ b/src/com/android/server/telecom/RespondViaSmsManager.java
@@ -18,6 +18,7 @@
// TODO: Needed for move to system service: import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsApplication;
import android.content.ComponentName;
@@ -29,6 +30,7 @@
import android.os.Handler;
import android.os.Message;
import android.telecom.Response;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.widget.Toast;
@@ -108,7 +110,8 @@
QuickResponseUtils.maybeMigrateLegacyQuickResponses(context);
final SharedPreferences prefs = context.getSharedPreferences(
- QuickResponseUtils.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+ QuickResponseUtils.SHARED_PREFERENCES_NAME,
+ Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
final Resources res = context.getResources();
final ArrayList<String> textMessages = new ArrayList<>(
@@ -139,10 +142,13 @@
@Override
public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
- if (rejectWithMessage) {
-
+ if (rejectWithMessage && call.getHandle() != null) {
+ PhoneAccountRegistrar phoneAccountRegistrar =
+ CallsManager.getInstance().getPhoneAccountRegistrar();
+ int subId = phoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
+ call.getTargetPhoneAccount());
rejectCallWithMessage(call.getContext(), call.getHandle().getSchemeSpecificPart(),
- textMessage);
+ textMessage, subId);
}
}
@@ -175,7 +181,8 @@
/**
* Reject the call with the specified message. If message is null this call is ignored.
*/
- private void rejectCallWithMessage(Context context, String phoneNumber, String textMessage) {
+ private void rejectCallWithMessage(Context context, String phoneNumber, String textMessage,
+ int subId) {
if (textMessage != null) {
final ComponentName component =
SmsApplication.getDefaultRespondViaMessageApplication(context,
@@ -185,6 +192,9 @@
final Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null);
final Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
intent.putExtra(Intent.EXTRA_TEXT, textMessage);
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ }
SomeArgs args = SomeArgs.obtain();
args.arg1 = phoneNumber;
diff --git a/src/com/android/server/telecom/RespondViaSmsSettings.java b/src/com/android/server/telecom/RespondViaSmsSettings.java
index b537162..f5876a0 100644
--- a/src/com/android/server/telecom/RespondViaSmsSettings.java
+++ b/src/com/android/server/telecom/RespondViaSmsSettings.java
@@ -33,9 +33,6 @@
* Helper class to manage the "Respond via SMS Message" feature for incoming calls.
*/
public class RespondViaSmsSettings {
- private static final String KEY_PREFERRED_PACKAGE = "preferred_package_pref";
- private static final String KEY_INSTANT_TEXT_DEFAULT_COMPONENT = "instant_text_def_component";
-
// TODO: This class is newly copied into Telecom (com.android.server.telecom) from it previous
// location in Telephony (com.android.phone). User's preferences stored in the old location
// will be lost. We need code here to migrate KLP -> LMP settings values.
@@ -125,25 +122,10 @@
case android.R.id.home:
goUpToTopLevelSetting(this);
return true;
- case R.id.respond_via_message_reset:
- // Reset the preferences settings
- SharedPreferences prefs = getSharedPreferences(
- QuickResponseUtils.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.remove(KEY_INSTANT_TEXT_DEFAULT_COMPONENT);
- editor.apply();
-
- return true;
default:
}
return super.onOptionsItemSelected(item);
}
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.respond_via_message_settings_menu, menu);
- return super.onCreateOptionsMenu(menu);
- }
}
/**
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 5945306..6b46a71 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -252,7 +252,7 @@
private boolean shouldVibrate(Context context) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- int ringerMode = audioManager.getRingerMode();
+ int ringerMode = audioManager.getRingerModeInternal();
if (getVibrateWhenRinging(context)) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else {
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index fb747f2..9a5ad03 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -22,11 +22,12 @@
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
import com.android.internal.util.Preconditions;
-import com.google.common.base.Strings;
-
-import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.Set;
@@ -83,7 +84,15 @@
ServiceConnection connection = new ServiceBinderConnection();
Log.d(ServiceBinder.this, "Binding to service with intent: %s", serviceIntent);
- if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
+ final boolean binding;
+ if (mUserHandle != null) {
+ binding = mContext.bindServiceAsUser(serviceIntent, connection,
+ Context.BIND_AUTO_CREATE, mUserHandle);
+ } else {
+ binding = mContext.bindService(serviceIntent, connection,
+ Context.BIND_AUTO_CREATE);
+ }
+ if (!binding) {
handleFailedConnection();
return;
}
@@ -136,11 +145,14 @@
private final ComponentName mComponentName;
/** The set of callbacks waiting for notification of the binding's success or failure. */
- private final Set<BindCallback> mCallbacks = Sets.newHashSet();
+ private final Set<BindCallback> mCallbacks = new ArraySet<>();
/** Used to bind and unbind from the service. */
private ServiceConnection mServiceConnection;
+ /** {@link UserHandle} to use for binding, to support work profiles and multi-user. */
+ private UserHandle mUserHandle;
+
/** The binder provided by {@link ServiceConnection#onServiceConnected} */
private IBinder mBinder;
@@ -167,14 +179,17 @@
* @param serviceAction The intent-action used with {@link Context#bindService}.
* @param componentName The component name of the service with which to bind.
* @param context The context.
+ * @param userHandle The {@link UserHandle} to use for binding.
*/
- protected ServiceBinder(String serviceAction, ComponentName componentName, Context context) {
- Preconditions.checkState(!Strings.isNullOrEmpty(serviceAction));
+ protected ServiceBinder(String serviceAction, ComponentName componentName, Context context,
+ UserHandle userHandle) {
+ Preconditions.checkState(!TextUtils.isEmpty(serviceAction));
Preconditions.checkNotNull(componentName);
mContext = context;
mServiceAction = serviceAction;
mComponentName = componentName;
+ mUserHandle = userHandle;
}
final void incrementAssociatedCallCount() {
diff --git a/src/com/android/server/telecom/TelecomApp.java b/src/com/android/server/telecom/TelecomApp.java
deleted file mode 100644
index 4941a35..0000000
--- a/src/com/android/server/telecom/TelecomApp.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.telecom;
-
-import android.app.Application;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-
-/**
- * Top-level Application class for Telecom.
- */
-public final class TelecomApp extends Application {
- /**
- * The Telecom service implementation.
- */
- private TelecomServiceImpl mTelecomService;
-
- /**
- * Missed call notifier. Exists here so that the instance can be shared with
- * {@link TelecomBroadcastReceiver}.
- */
- private MissedCallNotifier mMissedCallNotifier;
-
- /**
- * Maintains the list of registered {@link android.telecom.PhoneAccountHandle}s.
- */
- private PhoneAccountRegistrar mPhoneAccountRegistrar;
-
- /**
- * The calls manager for the Telecom service.
- */
- private CallsManager mCallsManager;
-
- /** {@inheritDoc} */
- @Override
- public void onCreate() {
- super.onCreate();
-
- if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
- // Note: This style of initialization mimics what will be performed once Telecom is
- // moved
- // to run in the system service. The emphasis is on ensuring that initialization of all
- // telecom classes happens in one place without relying on Singleton initialization.
- mMissedCallNotifier = new MissedCallNotifier(this);
- mPhoneAccountRegistrar = new PhoneAccountRegistrar(this);
-
- mCallsManager = new CallsManager(this, mMissedCallNotifier, mPhoneAccountRegistrar);
- CallsManager.initialize(mCallsManager);
-
- mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,
- mCallsManager, this);
- ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);
-
- // Start the BluetoothPhoneService
- BluetoothPhoneService.start(this);
- }
- }
-
- MissedCallNotifier getMissedCallNotifier() {
- return mMissedCallNotifier;
- }
-
- PhoneAccountRegistrar getPhoneAccountRegistrar() {
- return mPhoneAccountRegistrar;
- }
-}
diff --git a/src/com/android/server/telecom/TelecomGlobals.java b/src/com/android/server/telecom/TelecomGlobals.java
new file mode 100644
index 0000000..cf0936c
--- /dev/null
+++ b/src/com/android/server/telecom/TelecomGlobals.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+/**
+ * Top-level Application class for Telecom.
+ */
+public final class TelecomGlobals {
+ private static final String TAG = TelecomGlobals.class.getSimpleName();
+
+ private static final IntentFilter USER_SWITCHED_FILTER =
+ new IntentFilter(Intent.ACTION_USER_SWITCHED);
+
+ private static final TelecomGlobals INSTANCE = new TelecomGlobals();
+
+ /**
+ * The Telecom service implementation.
+ */
+ private TelecomService mTelecomService;
+
+ /**
+ * Missed call notifier. Exists here so that the instance can be shared with
+ * {@link TelecomBroadcastReceiver}.
+ */
+ private MissedCallNotifier mMissedCallNotifier;
+
+ /**
+ * Maintains the list of registered {@link android.telecom.PhoneAccountHandle}s.
+ */
+ private PhoneAccountRegistrar mPhoneAccountRegistrar;
+
+ /**
+ * The calls manager for the Telecom service.
+ */
+ private CallsManager mCallsManager;
+
+ /**
+ * The application context.
+ */
+ private Context mContext;
+
+ private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ UserHandle currentUserHandle = new UserHandle(userHandleId);
+ mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
+ }
+ };
+
+ static TelecomGlobals getInstance() {
+ return INSTANCE;
+ }
+
+ void initialize(Context context) {
+ if (mContext != null) {
+ Log.e(TAG, new Exception(), "Attempting to intialize TelecomGlobals a second time.");
+ return;
+ } else {
+ Log.i(TAG, "TelecomGlobals initializing");
+ }
+ mContext = context.getApplicationContext();
+
+ mMissedCallNotifier = new MissedCallNotifier(mContext);
+ mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext);
+
+ mCallsManager = new CallsManager(mContext, mMissedCallNotifier, mPhoneAccountRegistrar);
+ CallsManager.initialize(mCallsManager);
+ Log.i(this, "CallsManager initialized");
+
+ // Start the BluetoothPhoneService
+ BluetoothPhoneService.start(mContext);
+
+ mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
+ }
+
+ MissedCallNotifier getMissedCallNotifier() {
+ return mMissedCallNotifier;
+ }
+
+ PhoneAccountRegistrar getPhoneAccountRegistrar() {
+ return mPhoneAccountRegistrar;
+ }
+
+ CallsManager getCallsManager() {
+ return mCallsManager;
+ }
+}
diff --git a/src/com/android/server/telecom/TelecomService.java b/src/com/android/server/telecom/TelecomService.java
new file mode 100644
index 0000000..77fb1d2
--- /dev/null
+++ b/src/com/android/server/telecom/TelecomService.java
@@ -0,0 +1,1029 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.Manifest;
+import android.annotation.SdkConstant;
+import android.app.AppOpsManager;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.telecom.CallState;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import android.util.EventLog;
+
+// TODO: Needed for move to system service: import com.android.internal.R;
+import com.android.internal.telecom.ITelecomService;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of the ITelecom interface.
+ */
+public class TelecomService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE = "android.telecom.ITelecomService";
+
+ /** The context. */
+ private Context mContext;
+
+ /**
+ * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
+ * request after sending. The main thread will notify the request when it is complete.
+ */
+ private static final class MainThreadRequest {
+ /** The result of the request that is run on the main thread */
+ public Object result;
+ /** Object that can be used to store non-integer arguments */
+ public Object arg;
+ }
+
+ /**
+ * A handler that processes messages on the main thread. Since many of the method calls are not
+ * thread safe this is needed to shuttle the requests from the inbound binder threads to the
+ * main thread.
+ */
+ private final class MainThreadHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.obj instanceof MainThreadRequest) {
+ MainThreadRequest request = (MainThreadRequest) msg.obj;
+ Object result = null;
+ switch (msg.what) {
+ case MSG_SILENCE_RINGER:
+ mCallsManager.getRinger().silence();
+ break;
+ case MSG_SHOW_CALL_SCREEN:
+ mCallsManager.getInCallController().bringToForeground(msg.arg1 == 1);
+ break;
+ case MSG_END_CALL:
+ result = endCallInternal();
+ break;
+ case MSG_ACCEPT_RINGING_CALL:
+ acceptRingingCallInternal();
+ break;
+ case MSG_CANCEL_MISSED_CALLS_NOTIFICATION:
+ mMissedCallNotifier.clearMissedCalls();
+ break;
+ case MSG_IS_TTY_SUPPORTED:
+ result = mCallsManager.isTtySupported();
+ break;
+ case MSG_GET_CURRENT_TTY_MODE:
+ result = mCallsManager.getCurrentTtyMode();
+ break;
+ case MSG_NEW_INCOMING_CALL:
+ if (request.arg == null || !(request.arg instanceof Intent)) {
+ Log.w(this, "Invalid new incoming call request");
+ break;
+ }
+ CallReceiver.processIncomingCallIntent((Intent) request.arg);
+ break;
+ }
+
+ if (result != null) {
+ request.result = result;
+ synchronized(request) {
+ request.notifyAll();
+ }
+ }
+ }
+ }
+ }
+
+ private static final String TAG = TelecomService.class.getSimpleName();
+
+ private static final String SERVICE_NAME = "telecom";
+
+ private static final int MSG_SILENCE_RINGER = 1;
+ private static final int MSG_SHOW_CALL_SCREEN = 2;
+ private static final int MSG_END_CALL = 3;
+ private static final int MSG_ACCEPT_RINGING_CALL = 4;
+ private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 5;
+ private static final int MSG_IS_TTY_SUPPORTED = 6;
+ private static final int MSG_GET_CURRENT_TTY_MODE = 7;
+ private static final int MSG_NEW_INCOMING_CALL = 8;
+
+ private final MainThreadHandler mMainThreadHandler = new MainThreadHandler();
+
+ private CallsManager mCallsManager;
+ private MissedCallNotifier mMissedCallNotifier;
+ private PhoneAccountRegistrar mPhoneAccountRegistrar;
+ private AppOpsManager mAppOpsManager;
+ private UserManager mUserManager;
+ private PackageManager mPackageManager;
+ private TelecomServiceImpl mServiceImpl;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Log.d(this, "onCreate");
+ mContext = this;
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mServiceImpl = new TelecomServiceImpl();
+
+ TelecomGlobals globals = TelecomGlobals.getInstance();
+ globals.initialize(this);
+
+ mMissedCallNotifier = globals.getMissedCallNotifier();
+ mPhoneAccountRegistrar = globals.getPhoneAccountRegistrar();
+ mCallsManager = globals.getCallsManager();
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mPackageManager = mContext.getPackageManager();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.d(this, "onBind");
+ return mServiceImpl;
+ }
+
+ /**
+ * Implementation of the ITelecomService interface.
+ * TODO: Reorganize this inner class to top of file.
+ */
+ class TelecomServiceImpl extends ITelecomService.Stub {
+ @Override
+ public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
+ enforceReadPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ PhoneAccountHandle defaultOutgoingPhoneAccount =
+ mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(uriScheme);
+ // Make sure that the calling user can see this phone account.
+ if (defaultOutgoingPhoneAccount != null
+ && !isVisibleToCaller(defaultOutgoingPhoneAccount)) {
+ Log.w(this, "No account found for the calling user");
+ return null;
+ }
+ return defaultOutgoingPhoneAccount;
+ } catch (Exception e) {
+ Log.e(this, e, "getDefaultOutgoingPhoneAccount");
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
+ try {
+ PhoneAccountHandle userSelectedOutgoingPhoneAccount =
+ mPhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount();
+ // Make sure that the calling user can see this phone account.
+ if (!isVisibleToCaller(userSelectedOutgoingPhoneAccount)) {
+ Log.w(this, "No account found for the calling user");
+ return null;
+ }
+ return userSelectedOutgoingPhoneAccount;
+ } catch (Exception e) {
+ Log.e(this, e, "getUserSelectedOutgoingPhoneAccount");
+ throw e;
+ }
+ }
+
+ @Override
+ public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+ enforceModifyPermission();
+
+ try {
+ mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(accountHandle);
+ } catch (Exception e) {
+ Log.e(this, e, "setUserSelectedOutgoingPhoneAccount");
+ throw e;
+ }
+ }
+
+ @Override
+ public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
+ enforceReadPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return filterForAccountsVisibleToCaller(
+ mPhoneAccountRegistrar.getCallCapablePhoneAccounts());
+ } catch (Exception e) {
+ Log.e(this, e, "getCallCapablePhoneAccounts");
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
+ enforceReadPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return filterForAccountsVisibleToCaller(
+ mPhoneAccountRegistrar.getCallCapablePhoneAccounts(uriScheme));
+ } catch (Exception e) {
+ Log.e(this, e, "getPhoneAccountsSupportingScheme %s", uriScheme);
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
+ try {
+ return filterForAccountsVisibleToCaller(
+ mPhoneAccountRegistrar.getPhoneAccountsForPackage(packageName));
+ } catch (Exception e) {
+ Log.e(this, e, "getPhoneAccountsForPackage %s", packageName);
+ throw e;
+ }
+ }
+
+ @Override
+ public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) {
+ try {
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return null;
+ }
+ return mPhoneAccountRegistrar.getPhoneAccountInternal(accountHandle);
+ } catch (Exception e) {
+ Log.e(this, e, "getPhoneAccount %s", accountHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public int getAllPhoneAccountsCount() {
+ try {
+ // This list is pre-filtered for the calling user.
+ return getAllPhoneAccounts().size();
+ } catch (Exception e) {
+ Log.e(this, e, "getAllPhoneAccountsCount");
+ throw e;
+ }
+ }
+
+ @Override
+ public List<PhoneAccount> getAllPhoneAccounts() {
+ try {
+ List<PhoneAccount> allPhoneAccounts = mPhoneAccountRegistrar.getAllPhoneAccounts();
+ List<PhoneAccount> profilePhoneAccounts = new ArrayList<>(allPhoneAccounts.size());
+ for (PhoneAccount phoneAccount : profilePhoneAccounts) {
+ if (isVisibleToCaller(phoneAccount)) {
+ profilePhoneAccounts.add(phoneAccount);
+ }
+ }
+ return profilePhoneAccounts;
+ } catch (Exception e) {
+ Log.e(this, e, "getAllPhoneAccounts");
+ throw e;
+ }
+ }
+
+ @Override
+ public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
+ try {
+ return filterForAccountsVisibleToCaller(
+ mPhoneAccountRegistrar.getAllPhoneAccountHandles());
+ } catch (Exception e) {
+ Log.e(this, e, "getAllPhoneAccounts");
+ throw e;
+ }
+ }
+
+ @Override
+ public PhoneAccountHandle getSimCallManager() {
+ try {
+ PhoneAccountHandle accountHandle = mPhoneAccountRegistrar.getSimCallManager();
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return null;
+ }
+ return accountHandle;
+ } catch (Exception e) {
+ Log.e(this, e, "getSimCallManager");
+ throw e;
+ }
+ }
+
+ @Override
+ public void setSimCallManager(PhoneAccountHandle accountHandle) {
+ enforceModifyPermission();
+
+ try {
+ mPhoneAccountRegistrar.setSimCallManager(accountHandle);
+ } catch (Exception e) {
+ Log.e(this, e, "setSimCallManager");
+ throw e;
+ }
+ }
+
+ @Override
+ public List<PhoneAccountHandle> getSimCallManagers() {
+ enforceReadPermission();
+ long token = Binder.clearCallingIdentity();
+ try {
+ return filterForAccountsVisibleToCaller(
+ mPhoneAccountRegistrar.getConnectionManagerPhoneAccounts());
+ } catch (Exception e) {
+ Log.e(this, e, "getSimCallManagers");
+ throw e;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void registerPhoneAccount(PhoneAccount account) {
+ try {
+ enforcePhoneAccountModificationForPackage(
+ account.getAccountHandle().getComponentName().getPackageName());
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
+ enforceRegisterCallProviderPermission();
+ }
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+ enforceRegisterSimSubscriptionPermission();
+ }
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
+ enforceRegisterConnectionManagerPermission();
+ }
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+ enforceRegisterMultiUser();
+ }
+ enforceUserHandleMatchesCaller(account.getAccountHandle());
+
+ mPhoneAccountRegistrar.registerPhoneAccount(account);
+ } catch (Exception e) {
+ Log.e(this, e, "registerPhoneAccount %s", account);
+ throw e;
+ }
+ }
+
+ @Override
+ public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
+ try {
+ enforcePhoneAccountModificationForPackage(
+ accountHandle.getComponentName().getPackageName());
+ enforceUserHandleMatchesCaller(accountHandle);
+ mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
+ } catch (Exception e) {
+ Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
+ throw e;
+ }
+ }
+
+ @Override
+ public void clearAccounts(String packageName) {
+ try {
+ enforcePhoneAccountModificationForPackage(packageName);
+ mPhoneAccountRegistrar.clearAccounts(packageName, Binder.getCallingUserHandle());
+ } catch (Exception e) {
+ Log.e(this, e, "clearAccounts %s", packageName);
+ throw e;
+ }
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#isVoiceMailNumber
+ */
+ @Override
+ public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
+ enforceReadPermissionOrDefaultDialer();
+ try {
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return false;
+ }
+ return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
+ } catch (Exception e) {
+ Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+ throw e;
+ }
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#hasVoiceMailNumber
+ */
+ @Override
+ public boolean hasVoiceMailNumber(PhoneAccountHandle accountHandle) {
+ enforceReadPermissionOrDefaultDialer();
+ try {
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return false;
+ }
+
+ int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle);
+ return !TextUtils.isEmpty(getTelephonyManager().getVoiceMailNumber(subId));
+ } catch (Exception e) {
+ Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+ throw e;
+ }
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#getLine1Number
+ */
+ @Override
+ public String getLine1Number(PhoneAccountHandle accountHandle) {
+ enforceReadPermissionOrDefaultDialer();
+ try {
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return null;
+ }
+ int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle);
+ return getTelephonyManager().getLine1NumberForSubscriber(subId);
+ } catch (Exception e) {
+ Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+ throw e;
+ }
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#silenceRinger
+ */
+ @Override
+ public void silenceRinger() {
+ Log.d(this, "silenceRinger");
+ enforceModifyPermission();
+ sendRequestAsync(MSG_SILENCE_RINGER, 0);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#getDefaultPhoneApp
+ */
+ @Override
+ public ComponentName getDefaultPhoneApp() {
+ Resources resources = mContext.getResources();
+ return new ComponentName(
+ resources.getString(R.string.ui_default_package),
+ resources.getString(R.string.dialer_default_class));
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#isInCall
+ */
+ @Override
+ public boolean isInCall() {
+ enforceReadPermission();
+ // Do not use sendRequest() with this method since it could cause a deadlock with
+ // audio service, which we call into from the main thread: AudioManager.setMode().
+ final int callState = mCallsManager.getCallState();
+ return callState == TelephonyManager.CALL_STATE_OFFHOOK
+ || callState == TelephonyManager.CALL_STATE_RINGING;
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#isRinging
+ */
+ @Override
+ public boolean isRinging() {
+ enforceReadPermission();
+ return mCallsManager.getCallState() == TelephonyManager.CALL_STATE_RINGING;
+ }
+
+ /**
+ * @see TelecomManager#getCallState
+ */
+ @Override
+ public int getCallState() {
+ return mCallsManager.getCallState();
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#endCall
+ */
+ @Override
+ public boolean endCall() {
+ enforceModifyPermission();
+ return (boolean) sendRequest(MSG_END_CALL);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#acceptRingingCall
+ */
+ @Override
+ public void acceptRingingCall() {
+ enforceModifyPermission();
+ sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#showInCallScreen
+ */
+ @Override
+ public void showInCallScreen(boolean showDialpad) {
+ enforceReadPermissionOrDefaultDialer();
+ sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#cancelMissedCallsNotification
+ */
+ @Override
+ public void cancelMissedCallsNotification() {
+ enforceModifyPermissionOrDefaultDialer();
+ sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#handleMmi
+ */
+ @Override
+ public boolean handlePinMmi(String dialString) {
+ enforceModifyPermissionOrDefaultDialer();
+
+ // Switch identity so that TelephonyManager checks Telecom's permissions instead.
+ long token = Binder.clearCallingIdentity();
+ boolean retval = false;
+ try {
+ retval = getTelephonyManager().handlePinMmi(dialString);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return retval;
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#handleMmi
+ */
+ @Override
+ public boolean handlePinMmiForPhoneAccount(PhoneAccountHandle accountHandle,
+ String dialString) {
+ enforceModifyPermissionOrDefaultDialer();
+
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return false;
+ }
+
+ // Switch identity so that TelephonyManager checks Telecom's permissions instead.
+ long token = Binder.clearCallingIdentity();
+ boolean retval = false;
+ try {
+ int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle);
+ retval = getTelephonyManager().handlePinMmiForSubscriber(subId, dialString);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return retval;
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#getAdnUriForPhoneAccount
+ */
+ @Override
+ public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
+ enforceModifyPermissionOrDefaultDialer();
+
+ if (!isVisibleToCaller(accountHandle)) {
+ Log.w(this, "%s is not visible for the calling user", accountHandle);
+ return null;
+ }
+
+ // Switch identity so that TelephonyManager checks Telecom's permissions instead.
+ long token = Binder.clearCallingIdentity();
+ String retval = "content://icc/adn/";
+ try {
+ long subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle);
+ retval = retval + "subId/" + subId;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ return Uri.parse(retval);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#isTtySupported
+ */
+ @Override
+ public boolean isTtySupported() {
+ enforceReadPermission();
+ return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#getCurrentTtyMode
+ */
+ @Override
+ public int getCurrentTtyMode() {
+ enforceReadPermission();
+ return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#addNewIncomingCall
+ */
+ @Override
+ public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
+ Log.i(this, "Adding new incoming call with phoneAccountHandle %s", phoneAccountHandle);
+ if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
+ // TODO(sail): Add unit tests for adding incoming calls from a SIM call manager.
+ if (isCallerSimCallManager() && TelephonyUtil.isPstnComponentName(
+ phoneAccountHandle.getComponentName())) {
+ Log.v(this, "Allowing call manager to add incoming call with PSTN handle");
+ } else {
+ mAppOpsManager.checkPackage(Binder.getCallingUid(),
+ phoneAccountHandle.getComponentName().getPackageName());
+
+ enforcePhoneAccountIsRegistered(phoneAccountHandle);
+ // Make sure it doesn't cross the UserHandle boundary
+ enforceUserHandleMatchesCaller(phoneAccountHandle);
+ }
+
+ Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, true);
+ if (extras != null) {
+ intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
+ }
+ sendRequestAsync(MSG_NEW_INCOMING_CALL, 0, intent);
+ } else {
+ Log.w(this, "Null phoneAccountHandle. Ignoring request to add new incoming call");
+ }
+ }
+
+ /**
+ * @see android.telecom.TelecomManager#addNewUnknownCall
+ */
+ @Override
+ public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
+ if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null &&
+ TelephonyUtil.isPstnComponentName(phoneAccountHandle.getComponentName())) {
+ mAppOpsManager.checkPackage(
+ Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
+
+ enforcePhoneAccountIsRegistered(phoneAccountHandle);
+ // Make sure it doesn't cross the UserHandle boundary
+ enforceUserHandleMatchesCaller(phoneAccountHandle);
+
+ Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
+ intent.setClass(mContext, CallReceiver.class);
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtras(extras);
+ intent.putExtra(CallReceiver.KEY_IS_UNKNOWN_CALL, true);
+ intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+ mContext.sendBroadcastAsUser(intent, phoneAccountHandle.getUserHandle());
+ } else {
+ Log.i(this, "Null phoneAccountHandle or not initiated by Telephony. Ignoring request"
+ + " to add new unknown call.");
+ }
+ }
+
+ /**
+ * Dumps the current state of the TelecomService. Used when generating problem reports.
+ *
+ * @param fd The file descriptor.
+ * @param writer The print writer to dump the state to.
+ * @param args Optional dump arguments.
+ */
+ @Override
+ protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ writer.println("Permission Denial: can't dump TelecomService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ Binder.getCallingUid());
+ return;
+ }
+
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ if (mCallsManager != null) {
+ pw.println("mCallsManager: ");
+ pw.increaseIndent();
+ mCallsManager.dump(pw);
+ pw.decreaseIndent();
+
+ pw.println("mPhoneAccountRegistrar: ");
+ pw.increaseIndent();
+ mPhoneAccountRegistrar.dump(pw);
+ pw.decreaseIndent();
+ }
+ }
+ }
+
+ //
+ // Supporting methods for the ITelecomService interface implementation.
+ //
+
+ private boolean isVisibleToCaller(PhoneAccountHandle accountHandle) {
+ if (accountHandle == null) {
+ return false;
+ }
+
+ return isVisibleToCaller(mPhoneAccountRegistrar.getPhoneAccountInternal(accountHandle));
+ }
+
+ private boolean isVisibleToCaller(PhoneAccount account) {
+ if (account == null) {
+ return false;
+ }
+
+ // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and
+ // all profiles. Only Telephony and SIP accounts should have this capability.
+ if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+ return true;
+ }
+
+ UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle();
+ if (phoneAccountUserHandle == null) {
+ return false;
+ }
+
+ List<UserHandle> profileUserHandles;
+ if (isCallerSystemApp()) {
+ // If the caller lives in /system/priv-app, it can see PhoneAccounts for all of the
+ // *profiles* that the calling user owns, but not for any other *users*.
+ profileUserHandles = mUserManager.getUserProfiles();
+ } else {
+ // Otherwise, it has to be owned by the current caller's profile.
+ profileUserHandles = new ArrayList<>(1);
+ profileUserHandles.add(Binder.getCallingUserHandle());
+ }
+
+ return profileUserHandles.contains(phoneAccountUserHandle);
+ }
+
+ /**
+ * Given a list of {@link PhoneAccountHandle}s, filter them to the ones that the calling
+ * user can see.
+ *
+ * @param phoneAccountHandles Unfiltered list of account handles.
+ *
+ * @return {@link PhoneAccountHandle}s visible to the calling user and its profiles.
+ */
+ private List<PhoneAccountHandle> filterForAccountsVisibleToCaller(
+ List<PhoneAccountHandle> phoneAccountHandles) {
+ List<PhoneAccountHandle> profilePhoneAccountHandles =
+ new ArrayList<>(phoneAccountHandles.size());
+ for (PhoneAccountHandle phoneAccountHandle : phoneAccountHandles) {
+ if (isVisibleToCaller(phoneAccountHandle)) {
+ profilePhoneAccountHandles.add(phoneAccountHandle);
+ }
+ }
+ return profilePhoneAccountHandles;
+ }
+
+ private boolean isCallerSystemApp() {
+ int uid = Binder.getCallingUid();
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ for (String packageName : packages) {
+ if (isPackageSystemApp(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isPackageSystemApp(String packageName) {
+ try {
+ ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ return false;
+ }
+
+ private void acceptRingingCallInternal() {
+ Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
+ if (call != null) {
+ call.answer(call.getVideoState());
+ }
+ }
+
+ private boolean endCallInternal() {
+ // Always operate on the foreground call if one exists, otherwise get the first call in
+ // priority order by call-state.
+ Call call = mCallsManager.getForegroundCall();
+ if (call == null) {
+ call = mCallsManager.getFirstCallWithState(
+ CallState.ACTIVE,
+ CallState.DIALING,
+ CallState.RINGING,
+ CallState.ON_HOLD);
+ }
+
+ if (call != null) {
+ if (call.getState() == CallState.RINGING) {
+ call.reject(false /* rejectWithMessage */, null);
+ } else {
+ call.disconnect();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ // Enforce that the PhoneAccountHandle being passed in is registered to a valid PhoneAccount.
+ private void enforcePhoneAccountIsRegistered(PhoneAccountHandle phoneAccountHandle) {
+ PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle);
+ if(phoneAccount == null) {
+ EventLog.writeEvent(0x534e4554, "26864502", Binder.getCallingUid(), "R");
+ throw new SecurityException("This PhoneAccountHandle is not registered to a valid " +
+ "PhoneAccount!");
+ }
+ }
+
+ private void enforcePhoneAccountModificationForPackage(String packageName) {
+ // TODO: Use a new telecomm permission for this instead of reusing modify.
+
+ int result = mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
+
+ // Callers with MODIFY_PHONE_STATE can use the PhoneAccount mechanism to implement
+ // built-in behavior even when PhoneAccounts are not exposed as a third-part API. They
+ // may also modify PhoneAccounts on behalf of any 'packageName'.
+
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ // Other callers are only allowed to modify PhoneAccounts if the relevant system
+ // feature is enabled ...
+ enforceConnectionServiceFeature();
+ // ... and the PhoneAccounts they refer to are for their own package.
+ enforceCallingPackage(packageName);
+ }
+ }
+
+ private void enforceReadPermissionOrDefaultDialer() {
+ if (!isDefaultDialerCalling()) {
+ enforceReadPermission();
+ }
+ }
+
+ private void enforceModifyPermissionOrDefaultDialer() {
+ if (!isDefaultDialerCalling()) {
+ enforceModifyPermission();
+ }
+ }
+
+ private void enforceCallingPackage(String packageName) {
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
+ }
+
+ private void enforceConnectionServiceFeature() {
+ enforceFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
+ }
+
+ private void enforceRegisterCallProviderPermission() {
+ enforcePermission(android.Manifest.permission.REGISTER_CALL_PROVIDER);
+ }
+
+ private void enforceRegisterSimSubscriptionPermission() {
+ enforcePermission(android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION);
+ }
+
+ private void enforceRegisterConnectionManagerPermission() {
+ enforcePermission(android.Manifest.permission.REGISTER_CONNECTION_MANAGER);
+ }
+
+ private void enforceReadPermission() {
+ enforcePermission(Manifest.permission.READ_PHONE_STATE);
+ }
+
+ private void enforceModifyPermission() {
+ enforcePermission(Manifest.permission.MODIFY_PHONE_STATE);
+ }
+
+ private void enforcePermission(String permission) {
+ mContext.enforceCallingOrSelfPermission(permission, null);
+ }
+
+ private void enforceRegisterMultiUser() {
+ if (!isCallerSystemApp()) {
+ throw new SecurityException("CAPABILITY_MULTI_USER is only available to system apps.");
+ }
+ }
+
+ private void enforceUserHandleMatchesCaller(PhoneAccountHandle accountHandle) {
+ if (!Binder.getCallingUserHandle().equals(accountHandle.getUserHandle())) {
+ throw new SecurityException("Calling UserHandle does not match PhoneAccountHandle's");
+ }
+ }
+
+ private void enforceFeature(String feature) {
+ PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(feature)) {
+ throw new UnsupportedOperationException(
+ "System does not support feature " + feature);
+ }
+ }
+
+ private boolean isCallerSimCallManager() {
+ PhoneAccountHandle accountHandle = mPhoneAccountRegistrar.getSimCallManager();
+ if (accountHandle != null) {
+ try {
+ mAppOpsManager.checkPackage(
+ Binder.getCallingUid(), accountHandle.getComponentName().getPackageName());
+ return true;
+ } catch (SecurityException e) {
+ }
+ }
+ return false;
+ }
+
+ private boolean isDefaultDialerCalling() {
+ ComponentName defaultDialerComponent = getDefaultPhoneAppInternal();
+ if (defaultDialerComponent != null) {
+ try {
+ mAppOpsManager.checkPackage(
+ Binder.getCallingUid(), defaultDialerComponent.getPackageName());
+ return true;
+ } catch (SecurityException e) {
+ Log.e(TAG, e, "Could not get default dialer.");
+ }
+ }
+ return false;
+ }
+
+ private ComponentName getDefaultPhoneAppInternal() {
+ Resources resources = mContext.getResources();
+ return new ComponentName(
+ resources.getString(R.string.ui_default_package),
+ resources.getString(R.string.dialer_default_class));
+ }
+
+ private TelephonyManager getTelephonyManager() {
+ return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ private MainThreadRequest sendRequestAsync(int command, int arg1) {
+ return sendRequestAsync(command, arg1, null);
+ }
+
+ private MainThreadRequest sendRequestAsync(int command, int arg1, Object arg) {
+ MainThreadRequest request = new MainThreadRequest();
+ request.arg = arg;
+ mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
+ return request;
+ }
+
+ /**
+ * Posts the specified command to be executed on the main thread, waits for the request to
+ * complete, and returns the result.
+ */
+ private Object sendRequest(int command) {
+ if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
+ MainThreadRequest request = new MainThreadRequest();
+ mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
+ return request.result;
+ } else {
+ MainThreadRequest request = sendRequestAsync(command, 0);
+
+ // Wait for the request to complete
+ synchronized (request) {
+ while (request.result == null) {
+ try {
+ request.wait();
+ } catch (InterruptedException e) {
+ // Do nothing, go back and wait until the request is complete
+ }
+ }
+ }
+ return request.result;
+ }
+ }
+}
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
deleted file mode 100644
index 094d8ca..0000000
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ /dev/null
@@ -1,694 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.telecom;
-
-import android.Manifest;
-import android.app.AppOpsManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.telecom.CallState;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-
-import android.util.EventLog;
-
-// TODO: Needed for move to system service: import com.android.internal.R;
-import com.android.internal.telecom.ITelecomService;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-/**
- * Implementation of the ITelecom interface.
- */
-public class TelecomServiceImpl extends ITelecomService.Stub {
- private static final String REGISTER_PROVIDER_OR_SUBSCRIPTION =
- "com.android.server.telecom.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION";
- private static final String REGISTER_CONNECTION_MANAGER =
- "com.android.server.telecom.permission.REGISTER_CONNECTION_MANAGER";
-
- /** The context. */
- private Context mContext;
-
- /** ${inheritDoc} */
- @Override
- public IBinder asBinder() {
- return super.asBinder();
- }
-
- /**
- * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
- * request after sending. The main thread will notify the request when it is complete.
- */
- private static final class MainThreadRequest {
- /** The result of the request that is run on the main thread */
- public Object result;
- }
-
- /**
- * A handler that processes messages on the main thread in the phone process. Since many
- * of the Phone calls are not thread safe this is needed to shuttle the requests from the
- * inbound binder threads to the main thread in the phone process.
- */
- private final class MainThreadHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- if (msg.obj instanceof MainThreadRequest) {
- MainThreadRequest request = (MainThreadRequest) msg.obj;
- Object result = null;
- switch (msg.what) {
- case MSG_SILENCE_RINGER:
- mCallsManager.getRinger().silence();
- break;
- case MSG_SHOW_CALL_SCREEN:
- mCallsManager.getInCallController().bringToForeground(msg.arg1 == 1);
- break;
- case MSG_END_CALL:
- result = endCallInternal();
- break;
- case MSG_ACCEPT_RINGING_CALL:
- acceptRingingCallInternal();
- break;
- case MSG_CANCEL_MISSED_CALLS_NOTIFICATION:
- mMissedCallNotifier.clearMissedCalls();
- break;
- case MSG_IS_TTY_SUPPORTED:
- result = mCallsManager.isTtySupported();
- break;
- case MSG_GET_CURRENT_TTY_MODE:
- result = mCallsManager.getCurrentTtyMode();
- break;
- }
-
- if (result != null) {
- request.result = result;
- synchronized(request) {
- request.notifyAll();
- }
- }
- }
- }
- }
-
- /** Private constructor; @see init() */
- private static final String TAG = TelecomServiceImpl.class.getSimpleName();
-
- private static final String SERVICE_NAME = "telecom";
-
- private static final int MSG_SILENCE_RINGER = 1;
- private static final int MSG_SHOW_CALL_SCREEN = 2;
- private static final int MSG_END_CALL = 3;
- private static final int MSG_ACCEPT_RINGING_CALL = 4;
- private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 5;
- private static final int MSG_IS_TTY_SUPPORTED = 6;
- private static final int MSG_GET_CURRENT_TTY_MODE = 7;
-
- /** The singleton instance. */
- private static TelecomServiceImpl sInstance;
-
- private final MainThreadHandler mMainThreadHandler = new MainThreadHandler();
- private final CallsManager mCallsManager;
- private final MissedCallNotifier mMissedCallNotifier;
- private final PhoneAccountRegistrar mPhoneAccountRegistrar;
- private final AppOpsManager mAppOpsManager;
-
- public TelecomServiceImpl(
- MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar,
- CallsManager callsManager, Context context) {
- mMissedCallNotifier = missedCallNotifier;
- mPhoneAccountRegistrar = phoneAccountRegistrar;
- mCallsManager = callsManager;
- mContext = context;
- mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- }
-
- //
- // Implementation of the ITelecomService interface.
- //
-
- @Override
- public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
- try {
- return mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(uriScheme);
- } catch (Exception e) {
- Log.e(this, e, "getDefaultOutgoingPhoneAccount");
- throw e;
- }
- }
-
- @Override
- public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
- try {
- return mPhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount();
- } catch (Exception e) {
- Log.e(this, e, "getUserSelectedOutgoingPhoneAccount");
- throw e;
- }
- }
-
- @Override
- public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
- enforceModifyPermission();
-
- try {
- mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(accountHandle);
- } catch (Exception e) {
- Log.e(this, e, "setUserSelectedOutgoingPhoneAccount");
- throw e;
- }
- }
-
- @Override
- public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
- try {
- return mPhoneAccountRegistrar.getCallCapablePhoneAccounts();
- } catch (Exception e) {
- Log.e(this, e, "getCallCapablePhoneAccounts");
- throw e;
- }
- }
-
- @Override
- public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
- try {
- return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(uriScheme);
- } catch (Exception e) {
- Log.e(this, e, "getPhoneAccountsSupportingScheme");
- throw e;
- }
- }
-
- @Override
- public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
- try {
- return mPhoneAccountRegistrar.getPhoneAccountsForPackage(packageName);
- } catch (Exception e) {
- Log.e(this, e, "getPhoneAccountsForPackage");
- throw e;
- }
- }
-
- @Override
- public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) {
- try {
- return mPhoneAccountRegistrar.getPhoneAccount(accountHandle);
- } catch (Exception e) {
- Log.e(this, e, "getPhoneAccount %s", accountHandle);
- throw e;
- }
- }
-
- @Override
- public int getAllPhoneAccountsCount() {
- try {
- return mPhoneAccountRegistrar.getAllPhoneAccountsCount();
- } catch (Exception e) {
- Log.e(this, e, "getAllPhoneAccountsCount");
- throw e;
- }
- }
-
- @Override
- public List<PhoneAccount> getAllPhoneAccounts() {
- try {
- return mPhoneAccountRegistrar.getAllPhoneAccounts();
- } catch (Exception e) {
- Log.e(this, e, "getAllPhoneAccounts");
- throw e;
- }
- }
-
- @Override
- public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
- try {
- return mPhoneAccountRegistrar.getAllPhoneAccountHandles();
- } catch (Exception e) {
- Log.e(this, e, "getAllPhoneAccounts");
- throw e;
- }
- }
-
- @Override
- public PhoneAccountHandle getSimCallManager() {
- try {
- return mPhoneAccountRegistrar.getSimCallManager();
- } catch (Exception e) {
- Log.e(this, e, "getSimCallManager");
- throw e;
- }
- }
-
- @Override
- public void setSimCallManager(PhoneAccountHandle accountHandle) {
- enforceModifyPermission();
-
- try {
- mPhoneAccountRegistrar.setSimCallManager(accountHandle);
- } catch (Exception e) {
- Log.e(this, e, "setSimCallManager");
- throw e;
- }
- }
-
- @Override
- public List<PhoneAccountHandle> getSimCallManagers() {
- try {
- return mPhoneAccountRegistrar.getConnectionManagerPhoneAccounts();
- } catch (Exception e) {
- Log.e(this, e, "getSimCallManagers");
- throw e;
- }
- }
-
- @Override
- public void registerPhoneAccount(PhoneAccount account) {
- try {
- enforcePhoneAccountModificationForPackage(
- account.getAccountHandle().getComponentName().getPackageName());
- if (account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) ||
- account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
- enforceRegisterProviderOrSubscriptionPermission();
- }
- if (account.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) {
- enforceRegisterConnectionManagerPermission();
- }
-
- mPhoneAccountRegistrar.registerPhoneAccount(account);
- } catch (Exception e) {
- Log.e(this, e, "registerPhoneAccount %s", account);
- throw e;
- }
- }
-
- @Override
- public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
- try {
- enforcePhoneAccountModificationForPackage(
- accountHandle.getComponentName().getPackageName());
- mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
- } catch (Exception e) {
- Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
- throw e;
- }
- }
-
- @Override
- public void clearAccounts(String packageName) {
- try {
- enforcePhoneAccountModificationForPackage(packageName);
- mPhoneAccountRegistrar.clearAccounts(packageName);
- } catch (Exception e) {
- Log.e(this, e, "clearAccounts %s", packageName);
- throw e;
- }
- }
-
- /**
- * @see android.telecom.TelecomManager#isVoiceMailNumber
- */
- @Override
- public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
- enforceReadPermissionOrDefaultDialer();
- try {
- return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
- } catch (Exception e) {
- Log.e(this, e, "getSubscriptionIdForPhoneAccount");
- throw e;
- }
- }
-
- /**
- * @see android.telecom.TelecomManager#silenceRinger
- */
- @Override
- public void silenceRinger() {
- Log.d(this, "silenceRinger");
- enforceModifyPermission();
- sendRequestAsync(MSG_SILENCE_RINGER, 0);
- }
-
- /**
- * @see android.telecom.TelecomManager#getDefaultPhoneApp
- */
- @Override
- public ComponentName getDefaultPhoneApp() {
- Resources resources = mContext.getResources();
- return new ComponentName(
- resources.getString(R.string.ui_default_package),
- resources.getString(R.string.dialer_default_class));
- }
-
- /**
- * @see android.telecom.TelecomManager#isInCall
- */
- @Override
- public boolean isInCall() {
- enforceReadPermission();
- // Do not use sendRequest() with this method since it could cause a deadlock with
- // audio service, which we call into from the main thread: AudioManager.setMode().
- final int callState = mCallsManager.getCallState();
- return callState == TelephonyManager.CALL_STATE_OFFHOOK
- || callState == TelephonyManager.CALL_STATE_RINGING;
- }
-
- /**
- * @see android.telecom.TelecomManager#isRinging
- */
- @Override
- public boolean isRinging() {
- enforceReadPermission();
- return mCallsManager.getCallState() == TelephonyManager.CALL_STATE_RINGING;
- }
-
- /**
- * @see TelecomManager#getCallState
- */
- @Override
- public int getCallState() {
- return mCallsManager.getCallState();
- }
-
- /**
- * @see android.telecom.TelecomManager#endCall
- */
- @Override
- public boolean endCall() {
- enforceModifyPermission();
- return (boolean) sendRequest(MSG_END_CALL);
- }
-
- /**
- * @see android.telecom.TelecomManager#acceptRingingCall
- */
- @Override
- public void acceptRingingCall() {
- enforceModifyPermission();
- sendRequestAsync(MSG_ACCEPT_RINGING_CALL, 0);
- }
-
- /**
- * @see android.telecom.TelecomManager#showInCallScreen
- */
- @Override
- public void showInCallScreen(boolean showDialpad) {
- enforceReadPermissionOrDefaultDialer();
- sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
- }
-
- /**
- * @see android.telecom.TelecomManager#cancelMissedCallsNotification
- */
- @Override
- public void cancelMissedCallsNotification() {
- enforceModifyPermissionOrDefaultDialer();
- sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
- }
-
- /**
- * @see android.telecom.TelecomManager#handleMmi
- */
- @Override
- public boolean handlePinMmi(String dialString) {
- enforceModifyPermissionOrDefaultDialer();
-
- // Switch identity so that TelephonyManager checks Telecom's permissions instead.
- long token = Binder.clearCallingIdentity();
- boolean retval = false;
- try {
- retval = getTelephonyManager().handlePinMmi(dialString);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
- return retval;
- }
-
- /**
- * @see android.telecom.TelecomManager#isTtySupported
- */
- @Override
- public boolean isTtySupported() {
- enforceReadPermission();
- return (boolean) sendRequest(MSG_IS_TTY_SUPPORTED);
- }
-
- /**
- * @see android.telecom.TelecomManager#getCurrentTtyMode
- */
- @Override
- public int getCurrentTtyMode() {
- enforceReadPermission();
- return (int) sendRequest(MSG_GET_CURRENT_TTY_MODE);
- }
-
- /**
- * @see android.telecom.TelecomManager#addNewIncomingCall
- */
- @Override
- public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
- if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
- mAppOpsManager.checkPackage(
- Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
- enforcePhoneAccountIsRegistered(phoneAccountHandle);
-
- Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
- intent.setPackage(mContext.getPackageName());
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
- if (extras != null) {
- intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
- }
-
- long token = Binder.clearCallingIdentity();
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * @see android.telecom.TelecomManager#addNewUnknownCall
- */
- @Override
- public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
- if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null &&
- TelephonyUtil.isPstnComponentName(phoneAccountHandle.getComponentName())) {
- mAppOpsManager.checkPackage(
- Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());
- enforcePhoneAccountIsRegistered(phoneAccountHandle);
-
- Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
- intent.setClass(mContext, CallReceiver.class);
- intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtras(extras);
- intent.putExtra(CallReceiver.KEY_IS_UNKNOWN_CALL, true);
- intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
- } else {
- Log.i(this, "Null phoneAccountHandle or not initiated by Telephony. Ignoring request"
- + " to add new unknown call.");
- }
- }
-
- //
- // Supporting methods for the ITelecomService interface implementation.
- //
-
- private void acceptRingingCallInternal() {
- Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
- if (call != null) {
- call.answer(call.getVideoState());
- }
- }
-
- private boolean endCallInternal() {
- // Always operate on the foreground call if one exists, otherwise get the first call in
- // priority order by call-state.
- Call call = mCallsManager.getForegroundCall();
- if (call == null) {
- call = mCallsManager.getFirstCallWithState(
- CallState.ACTIVE,
- CallState.DIALING,
- CallState.RINGING,
- CallState.ON_HOLD);
- }
-
- if (call != null) {
- if (call.getState() == CallState.RINGING) {
- call.reject(false /* rejectWithMessage */, null);
- } else {
- call.disconnect();
- }
- return true;
- }
-
- return false;
- }
-
- // Enforce that the PhoneAccountHandle being passed in is registered to a valid PhoneAccount.
- private void enforcePhoneAccountIsRegistered(PhoneAccountHandle phoneAccountHandle) {
- PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle);
- if(phoneAccount == null) {
- EventLog.writeEvent(0x534e4554, "26864502", Binder.getCallingUid(), "R");
- throw new SecurityException("This PhoneAccountHandle is not registered to a valid " +
- "PhoneAccount!");
- }
- }
-
- private void enforcePhoneAccountModificationForPackage(String packageName) {
- // TODO: Use a new telecomm permission for this instead of reusing modify.
-
- int result = mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE);
-
- // Callers with MODIFY_PHONE_STATE can use the PhoneAccount mechanism to implement
- // built-in behavior even when PhoneAccounts are not exposed as a third-part API. They
- // may also modify PhoneAccounts on behalf of any 'packageName'.
-
- if (result != PackageManager.PERMISSION_GRANTED) {
- // Other callers are only allowed to modify PhoneAccounts if the relevant system
- // feature is enabled ...
- enforceConnectionServiceFeature();
- // ... and the PhoneAccounts they refer to are for their own package.
- enforceCallingPackage(packageName);
- }
- }
-
- private void enforceReadPermissionOrDefaultDialer() {
- if (!isDefaultDialerCalling()) {
- enforceReadPermission();
- }
- }
-
- private void enforceModifyPermissionOrDefaultDialer() {
- if (!isDefaultDialerCalling()) {
- enforceModifyPermission();
- }
- }
-
- private void enforceCallingPackage(String packageName) {
- mAppOpsManager.checkPackage(Binder.getCallingUid(), packageName);
- }
-
- private void enforceConnectionServiceFeature() {
- enforceFeature(PackageManager.FEATURE_CONNECTION_SERVICE);
- }
-
- private void enforceRegisterProviderOrSubscriptionPermission() {
- enforcePermission(REGISTER_PROVIDER_OR_SUBSCRIPTION);
- }
-
- private void enforceRegisterConnectionManagerPermission() {
- enforcePermission(REGISTER_CONNECTION_MANAGER);
- }
-
- private void enforceReadPermission() {
- enforcePermission(Manifest.permission.READ_PHONE_STATE);
- }
-
- private void enforceModifyPermission() {
- enforcePermission(Manifest.permission.MODIFY_PHONE_STATE);
- }
-
- private void enforcePermission(String permission) {
- mContext.enforceCallingOrSelfPermission(permission, null);
- }
-
- private void enforceFeature(String feature) {
- PackageManager pm = mContext.getPackageManager();
- if (!pm.hasSystemFeature(feature)) {
- throw new UnsupportedOperationException(
- "System does not support feature " + feature);
- }
- }
-
- private boolean isDefaultDialerCalling() {
- ComponentName defaultDialerComponent = getDefaultPhoneApp();
- if (defaultDialerComponent != null) {
- try {
- mAppOpsManager.checkPackage(
- Binder.getCallingUid(), defaultDialerComponent.getPackageName());
- return true;
- } catch (SecurityException e) {
- Log.e(TAG, e, "Could not get default dialer.");
- }
- }
- return false;
- }
-
- private TelephonyManager getTelephonyManager() {
- return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
- }
-
- private MainThreadRequest sendRequestAsync(int command, int arg1) {
- MainThreadRequest request = new MainThreadRequest();
- mMainThreadHandler.obtainMessage(command, arg1, 0, request).sendToTarget();
- return request;
- }
-
- /**
- * Posts the specified command to be executed on the main thread, waits for the request to
- * complete, and returns the result.
- */
- private Object sendRequest(int command) {
- if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
- MainThreadRequest request = new MainThreadRequest();
- mMainThreadHandler.handleMessage(mMainThreadHandler.obtainMessage(command, request));
- return request.result;
- } else {
- MainThreadRequest request = sendRequestAsync(command, 0);
-
- // Wait for the request to complete
- synchronized (request) {
- while (request.result == null) {
- try {
- request.wait();
- } catch (InterruptedException e) {
- // Do nothing, go back and wait until the request is complete
- }
- }
- }
- return request.result;
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
- final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (mCallsManager != null) {
- pw.println("mCallsManager: ");
- pw.increaseIndent();
- mCallsManager.dump(pw);
- pw.decreaseIndent();
- }
- }
-}
diff --git a/src/com/android/server/telecom/Timeouts.java b/src/com/android/server/telecom/Timeouts.java
index 35f61ae..b5cf39a 100644
--- a/src/com/android/server/telecom/Timeouts.java
+++ b/src/com/android/server/telecom/Timeouts.java
@@ -52,4 +52,41 @@
public static long getDirectToVoicemailMillis(ContentResolver contentResolver) {
return get(contentResolver, "direct_to_voicemail_ms", 500L);
}
+
+ /**
+ * Returns the amount of time to wait before disconnecting a call that was canceled via
+ * NEW_OUTGOING_CALL broadcast. This timeout allows apps which repost the call using a gateway
+ * to reuse the existing call, preventing the call from causing a start->end->start jank in the
+ * in-call UI.
+ */
+ public static long getNewOutgoingCallCancelMillis(ContentResolver contentResolver) {
+ return get(contentResolver, "new_outgoing_call_cancel_ms", 300L);
+ }
+
+ /**
+ * Returns the amount of time to play each DTMF tone after post dial continue.
+ * This timeout allows the current tone to play for a certain amount of time before either being
+ * interrupted by the next tone or terminated.
+ */
+ public static long getDelayBetweenDtmfTonesMillis(ContentResolver contentResolver) {
+ return get(contentResolver, "delay_between_dtmf_tones_ms", 300L);
+ }
+
+ /**
+ * Returns the amount of time to wait for an emergency call to be placed before routing to
+ * a different call service. A value of 0 or less means no timeout should be used.
+ */
+ public static long getEmergencyCallTimeoutMillis(ContentResolver contentResolver) {
+ return get(contentResolver, "emergency_call_timeout_millis", 25000L /* 25 seconds */);
+ }
+
+ /**
+ * Returns the amount of time to wait for an emergency call to be placed before routing to
+ * a different call service. This timeout is used only when the radio is powered off (for
+ * example in airplane mode). A value of 0 or less means no timeout should be used.
+ */
+ public static long getEmergencyCallTimeoutRadioOffMillis(ContentResolver contentResolver) {
+ return get(contentResolver, "emergency_call_timeout_radio_off_millis",
+ 60000L /* 1 minute */);
+ }
}
diff --git a/src/com/android/server/telecom/TtyManager.java b/src/com/android/server/telecom/TtyManager.java
index b21f165..e0a9e9c 100644
--- a/src/com/android/server/telecom/TtyManager.java
+++ b/src/com/android/server/telecom/TtyManager.java
@@ -25,6 +25,8 @@
import android.provider.Settings;
import android.telecom.TelecomManager;
+import com.android.internal.util.IndentingPrintWriter;
+
// TODO: Needed for move to system service: import com.android.internal.R;
final class TtyManager implements WiredHeadsetManager.Listener {
@@ -122,4 +124,13 @@
}
}
}
+
+ /**
+ * Dumps the state of the {@link TtyManager}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("mCurrentTtyMode: " + mCurrentTtyMode);
+ }
}
diff --git a/src/com/android/server/telecom/WiredHeadsetManager.java b/src/com/android/server/telecom/WiredHeadsetManager.java
index 8ce7b61..a61dd6e 100644
--- a/src/com/android/server/telecom/WiredHeadsetManager.java
+++ b/src/com/android/server/telecom/WiredHeadsetManager.java
@@ -22,6 +22,8 @@
import android.content.IntentFilter;
import android.media.AudioManager;
+import com.android.internal.util.IndentingPrintWriter;
+
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -91,4 +93,13 @@
}
}
}
+
+ /**
+ * Dumps the state of the {@link WiredHeadsetManager}.
+ *
+ * @param pw The {@code IndentingPrintWriter} to write the state to.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("mIsPluggedIn: " + mIsPluggedIn);
+ }
}
diff --git a/tests/Android.mk b/tests/Android.mk
index b122183..f293b80 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -20,6 +20,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-ex-camera2 \
guava \
+ mockito-target \
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 9c09a40..e987dee 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -19,11 +19,11 @@
package="com.android.server.telecom.tests"
android:debuggable="true">
- <!-- Test connection service outgoing video preview. -->
<uses-permission android:name="android.permission.CAMERA" />
- <uses-permission
- android:name="com.android.server.telecom.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION" />
<uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
+ <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
+ <uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
+ <uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
<application android:label="@string/app_name"
android:debuggable="true">
@@ -72,6 +72,7 @@
<action android:name="android.telecom.testapps.ACTION_NEW_UNKNOWN_CALL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel" />
+ <data android:scheme="sip" />
</intent-filter>
</activity>
diff --git a/tests/src/com/android/server/telecom/testapps/CallServiceNotifier.java b/tests/src/com/android/server/telecom/testapps/CallServiceNotifier.java
index b82f4af..23e8222 100644
--- a/tests/src/com/android/server/telecom/testapps/CallServiceNotifier.java
+++ b/tests/src/com/android/server/telecom/testapps/CallServiceNotifier.java
@@ -24,6 +24,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
import android.net.Uri;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
@@ -100,28 +103,30 @@
telecomManager.clearAccounts();
telecomManager.registerPhoneAccount(PhoneAccount.builder(
- new PhoneAccountHandle(
- new ComponentName(context, TestConnectionService.class),
- CALL_PROVIDER_ID),
- "TelecomTestApp Call Provider")
+ new PhoneAccountHandle(
+ new ComponentName(context, TestConnectionService.class),
+ CALL_PROVIDER_ID),
+ "TelecomTestApp Call Provider")
.setAddress(Uri.parse("tel:555-TEST"))
.setSubscriptionAddress(Uri.parse("tel:555-TEST"))
.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
- .setIconResId(R.drawable.stat_sys_phone_call)
+ .setIcon(context, R.drawable.stat_sys_phone_call, Color.RED)
+ .setHighlightColor(Color.RED)
.setShortDescription("a short description for the call provider")
.setSupportedUriSchemes(Arrays.asList("tel"))
.build());
telecomManager.registerPhoneAccount(PhoneAccount.builder(
- new PhoneAccountHandle(
- new ComponentName(context, TestConnectionService.class),
- SIM_SUBSCRIPTION_ID),
- "TelecomTestApp SIM Subscription")
+ new PhoneAccountHandle(
+ new ComponentName(context, TestConnectionService.class),
+ SIM_SUBSCRIPTION_ID),
+ "TelecomTestApp SIM Subscription")
.setAddress(Uri.parse("tel:555-TSIM"))
.setSubscriptionAddress(Uri.parse("tel:555-TSIM"))
.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
- PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
- .setIconResId(R.drawable.stat_sys_phone_call)
+ PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
+ .setIcon(context, R.drawable.stat_sys_phone_call, Color.GREEN)
+ .setHighlightColor(Color.GREEN)
.setShortDescription("a short description for the sim subscription")
.build());
@@ -133,7 +138,7 @@
.setAddress(Uri.parse("tel:555-CMGR"))
.setSubscriptionAddress(Uri.parse("tel:555-CMGR"))
.setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
- .setIconResId(R.drawable.stat_sys_phone_call)
+ .setIcon(context, R.drawable.stat_sys_phone_call, Color.BLUE)
.setShortDescription("a short description for the connection manager")
.build());
}
diff --git a/tests/src/com/android/server/telecom/testapps/TestCallActivity.java b/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
index 9c9a40b..6f4ae20 100644
--- a/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
+++ b/tests/src/com/android/server/telecom/testapps/TestCallActivity.java
@@ -50,7 +50,7 @@
final Uri data = intent != null ? intent.getData() : null;
if (ACTION_NEW_INCOMING_CALL.equals(action) && data != null) {
CallNotificationReceiver.sendIncomingCallIntent(this, data, false);
- } if (ACTION_NEW_UNKNOWN_CALL.equals(action) && data != null) {
+ } else if (ACTION_NEW_UNKNOWN_CALL.equals(action) && data != null) {
CallNotificationReceiver.addNewUnknownCall(this, data, intent.getExtras());
} else {
CallServiceNotifier.getInstance().updateNotification(this);
diff --git a/tests/src/com/android/server/telecom/testapps/TestConnectionManager.java b/tests/src/com/android/server/telecom/testapps/TestConnectionManager.java
index 66b8749..a27be39 100644
--- a/tests/src/com/android/server/telecom/testapps/TestConnectionManager.java
+++ b/tests/src/com/android/server/telecom/testapps/TestConnectionManager.java
@@ -60,9 +60,9 @@
}
@Override
- public void onCallCapabilitiesChanged(RemoteConnection connection,
- int callCapabilities) {
- setCallCapabilities(callCapabilities);
+ public void onConnectionCapabilitiesChanged(RemoteConnection connection,
+ int connectionCapabilities) {
+ setConnectionCapabilities(connectionCapabilities);
}
@Override
@@ -246,8 +246,9 @@
}
@Override
- public void onCapabilitiesChanged(RemoteConference conference, int capabilities) {
- setCapabilities(capabilities);
+ public void onConnectionCapabilitiesChanged(RemoteConference conference,
+ int connectionCapabilities) {
+ setConnectionCapabilities(connectionCapabilities);
}
@Override
diff --git a/tests/src/com/android/server/telecom/testapps/TestConnectionService.java b/tests/src/com/android/server/telecom/testapps/TestConnectionService.java
index 389373f..2e01276 100644
--- a/tests/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/tests/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -27,12 +27,9 @@
import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccount;
-import android.telecom.PhoneCapabilities;
import android.telecom.ConnectionRequest;
import android.telecom.ConnectionService;
import android.telecom.PhoneAccountHandle;
-import android.telecom.RemoteConnection;
-import android.telecom.StatusHints;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.util.Log;
@@ -77,7 +74,11 @@
public TestConference(Connection a, Connection b) {
super(null);
-
+ setConnectionCapabilities(
+ Connection.CAPABILITY_SUPPORT_HOLD |
+ Connection.CAPABILITY_HOLD |
+ Connection.CAPABILITY_MUTE |
+ Connection.CAPABILITY_MANAGE_CONFERENCE);
addConnection(a);
addConnection(b);
@@ -133,13 +134,12 @@
TestConnection(boolean isIncoming) {
mIsIncoming = isIncoming;
// Assume all calls are video capable.
- int capabilities = getCallCapabilities();
- capabilities |= PhoneCapabilities.SUPPORTS_VT_LOCAL;
- capabilities |= PhoneCapabilities.ADD_CALL;
- capabilities |= PhoneCapabilities.MUTE;
- capabilities |= PhoneCapabilities.SUPPORT_HOLD;
- capabilities |= PhoneCapabilities.HOLD;
- setCallCapabilities(capabilities);
+ int capabilities = getConnectionCapabilities();
+ capabilities |= CAPABILITY_SUPPORTS_VT_LOCAL;
+ capabilities |= CAPABILITY_MUTE;
+ capabilities |= CAPABILITY_SUPPORT_HOLD;
+ capabilities |= CAPABILITY_HOLD;
+ setConnectionCapabilities(capabilities);
}
void startOutgoing() {
diff --git a/tests/src/com/android/server/telecom/testapps/TestManagedVideoProvider.java b/tests/src/com/android/server/telecom/testapps/TestManagedVideoProvider.java
index 50b77ed..649d0c0 100644
--- a/tests/src/com/android/server/telecom/testapps/TestManagedVideoProvider.java
+++ b/tests/src/com/android/server/telecom/testapps/TestManagedVideoProvider.java
@@ -111,7 +111,7 @@
}
@Override
- public void onRequestCallDataUsage() {
+ public void onRequestConnectionDataUsage() {
mRemoteVideoProvider.requestCallDataUsage();
}
diff --git a/tests/src/com/android/server/telecom/testapps/TestVideoProvider.java b/tests/src/com/android/server/telecom/testapps/TestVideoProvider.java
index 58f093a..494eacc 100644
--- a/tests/src/com/android/server/telecom/testapps/TestVideoProvider.java
+++ b/tests/src/com/android/server/telecom/testapps/TestVideoProvider.java
@@ -169,8 +169,8 @@
* Randomly reports data usage of value ranging from 10MB to 60MB.
*/
@Override
- public void onRequestCallDataUsage() {
- log("Requested call data usage");
+ public void onRequestConnectionDataUsage() {
+ log("Requested connection data usage");
int dataUsageKb = (10 *1024) + random.nextInt(50 * 1024);
changeCallDataUsage(dataUsageKb);
}
diff --git a/tests/src/com/android/server/telecom/tests/unit/InCallWakeLockControllerTest.java b/tests/src/com/android/server/telecom/tests/unit/InCallWakeLockControllerTest.java
new file mode 100644
index 0000000..5af9440
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/unit/InCallWakeLockControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.never;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.telecom.CallState;
+import android.test.AndroidTestCase;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.InCallWakeLockController;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class InCallWakeLockControllerTest extends AndroidTestCase {
+
+ @Mock Context mContext;
+ @Mock PowerManager mPowerManager;
+ @Mock PowerManager.WakeLock mWakeLock;
+ @Mock CallsManager mCallsManager;
+ @Mock Call mCall;
+
+ private InCallWakeLockController mInCallWakeLockController;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
+ when(mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "InCallWakeLockController"))
+ .thenReturn(mWakeLock);
+ mInCallWakeLockController = new InCallWakeLockController(mContext, mCallsManager);
+ }
+
+ @Override
+ public void tearDown() {
+ }
+
+ public void test_RingingCallAdded() throws Exception {
+ when(mCallsManager.getRingingCall()).thenReturn(mCall);
+ mInCallWakeLockController.onCallAdded(mCall);
+ verify(mWakeLock).acquire();
+ }
+
+ public void test_NonRingingCallAdded() throws Exception {
+ when(mCallsManager.getRingingCall()).thenReturn(null);
+ when(mWakeLock.isHeld()).thenReturn(false);
+
+ mInCallWakeLockController.onCallAdded(mCall);
+ verify(mWakeLock, never()).acquire();
+ }
+
+ public void test_RingingCallTransition() throws Exception {
+ when(mCallsManager.getRingingCall()).thenReturn(mCall);
+ mInCallWakeLockController.onCallStateChanged(mCall, CallState.NEW, CallState.RINGING);
+ verify(mWakeLock).acquire();
+ }
+
+ public void test_RingingCallRemoved() throws Exception {
+ when(mCallsManager.getRingingCall()).thenReturn(null);
+ when(mWakeLock.isHeld()).thenReturn(false);
+
+ mInCallWakeLockController.onCallRemoved(mCall);
+ verify(mWakeLock, never()).acquire();
+ }
+
+ public void test_WakeLockReleased() throws Exception {
+ when(mCallsManager.getRingingCall()).thenReturn(null);
+ when(mWakeLock.isHeld()).thenReturn(true);
+
+ mInCallWakeLockController.onCallRemoved(mCall);
+ verify(mWakeLock).release();
+ }
+}
diff --git a/tests/src/com/android/server/telecom/tests/unit/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/unit/PhoneAccountRegistrarTest.java
index cd1bbd1..e63e79f 100644
--- a/tests/src/com/android/server/telecom/tests/unit/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/unit/PhoneAccountRegistrarTest.java
@@ -16,16 +16,21 @@
package com.android.server.telecom.tests.unit;
+import android.os.UserHandle;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.telecom.Log;
import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.tests.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;
import android.content.ComponentName;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.Parcel;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.test.AndroidTestCase;
@@ -36,9 +41,11 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.util.Arrays;
public class PhoneAccountRegistrarTest extends AndroidTestCase {
+ private static final int MAX_VERSION = Integer.MAX_VALUE;
private static final String FILE_NAME = "phone-account-registrar-test.xml";
private PhoneAccountRegistrar mRegistrar;
@@ -55,12 +62,12 @@
public void testPhoneAccountHandle() throws Exception {
PhoneAccountHandle input = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), "id0");
- PhoneAccountHandle result = roundTrip(this, input,
+ PhoneAccountHandle result = roundTripXml(this, input,
PhoneAccountRegistrar.sPhoneAccountHandleXml, mContext);
assertPhoneAccountHandleEquals(input, result);
PhoneAccountHandle inputN = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), null);
- PhoneAccountHandle resultN = roundTrip(this, inputN,
+ PhoneAccountHandle resultN = roundTripXml(this, inputN,
PhoneAccountRegistrar.sPhoneAccountHandleXml, mContext);
Log.i(this, "inputN = %s, resultN = %s", inputN, resultN);
assertPhoneAccountHandleEquals(inputN, resultN);
@@ -71,14 +78,15 @@
.addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
.addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
.build();
- PhoneAccount result = roundTrip(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
+ PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
mContext);
assertPhoneAccountEquals(input, result);
}
public void testState() throws Exception {
PhoneAccountRegistrar.State input = makeQuickState();
- PhoneAccountRegistrar.State result = roundTrip(this, input, PhoneAccountRegistrar.sStateXml,
+ PhoneAccountRegistrar.State result = roundTripXml(this, input,
+ PhoneAccountRegistrar.sStateXml,
mContext);
assertStateEquals(input, result);
}
@@ -181,21 +189,52 @@
assertNull(mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL));
}
+ public void testPhoneAccountParceling() throws Exception {
+ PhoneAccountHandle handle = makeQuickAccountHandle("foo");
+ roundTripPhoneAccount(new PhoneAccount.Builder(handle, null).build());
+ roundTripPhoneAccount(new PhoneAccount.Builder(handle, "foo").build());
+ roundTripPhoneAccount(
+ new PhoneAccount.Builder(handle, "foo")
+ .setAddress(Uri.parse("tel:123456"))
+ .setCapabilities(23)
+ .setHighlightColor(0xf0f0f0)
+ .setIcon(
+ "com.android.server.telecom.tests",
+ R.drawable.stat_sys_phone_call,
+ 0xfefefe)
+ .setShortDescription("short description")
+ .setSubscriptionAddress(Uri.parse("tel:2345678"))
+ .setSupportedUriSchemes(Arrays.asList("tel", "sip"))
+ .build());
+ roundTripPhoneAccount(
+ new PhoneAccount.Builder(handle, "foo")
+ .setAddress(Uri.parse("tel:123456"))
+ .setCapabilities(23)
+ .setHighlightColor(0xf0f0f0)
+ .setIcon(
+ BitmapFactory.decodeResource(
+ getContext().getResources(),
+ R.drawable.stat_sys_phone_call))
+ .setShortDescription("short description")
+ .setSubscriptionAddress(Uri.parse("tel:2345678"))
+ .setSupportedUriSchemes(Arrays.asList("tel", "sip"))
+ .build());
+ }
+
private static PhoneAccountHandle makeQuickAccountHandle(String id) {
return new PhoneAccountHandle(
new ComponentName(
"com.android.server.telecom.tests",
"com.android.server.telecom.tests.MockConnectionService"
),
- id
- );
+ id,
+ new UserHandle(5));
}
private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
return new PhoneAccount.Builder(
makeQuickAccountHandle(id),
- "label" + idx
- );
+ "label" + idx);
}
private PhoneAccount makeQuickAccount(String id, int idx) {
@@ -203,12 +242,26 @@
.setAddress(Uri.parse("http://foo.com/" + idx))
.setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
.setCapabilities(idx)
- .setIconResId(idx)
+ .setIcon("com.android.server.telecom.tests", R.drawable.stat_sys_phone_call)
.setShortDescription("desc" + idx)
.build();
}
- private static <T> T roundTrip(
+ private static void roundTripPhoneAccount(PhoneAccount original) throws Exception {
+ PhoneAccount copy = null;
+
+ {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(original, 0);
+ parcel.setDataPosition(0);
+ copy = parcel.readParcelable(PhoneAccountRegistrarTest.class.getClassLoader());
+ parcel.recycle();
+ }
+
+ assertPhoneAccountEquals(original, copy);
+ }
+
+ private static <T> T roundTripXml(
Object self,
T input,
PhoneAccountRegistrar.XmlSerialization<T> xml,
@@ -221,7 +274,7 @@
XmlSerializer serializer = new FastXmlSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
- xml.writeToXml(input, serializer);
+ xml.writeToXml(input, serializer, context);
serializer.flush();
data = baos.toByteArray();
}
@@ -233,7 +286,7 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new BufferedInputStream(new ByteArrayInputStream(data)), null);
parser.nextTag();
- result = xml.readFromXml(parser, 0, context);
+ result = xml.readFromXml(parser, MAX_VERSION, context);
}
Log.d(self, "result = " + result);
@@ -260,13 +313,31 @@
assertEquals(a.getSubscriptionAddress(), b.getSubscriptionAddress());
assertEquals(a.getCapabilities(), b.getCapabilities());
assertEquals(a.getIconResId(), b.getIconResId());
- assertEquals(a.getColor(), b.getColor());
+ assertEquals(a.getIconPackageName(), b.getIconPackageName());
+ assertBitmapEquals(a.getIconBitmap(), b.getIconBitmap());
+ assertEquals(a.getIconTint(), b.getIconTint());
+ assertEquals(a.getHighlightColor(), b.getHighlightColor());
assertEquals(a.getLabel(), b.getLabel());
assertEquals(a.getShortDescription(), b.getShortDescription());
assertEquals(a.getSupportedUriSchemes(), b.getSupportedUriSchemes());
}
}
+ private static void assertBitmapEquals(Bitmap a, Bitmap b) {
+ if (a == null || b == null) {
+ assertEquals(null, a);
+ assertEquals(null, b);
+ } else {
+ assertEquals(a.getWidth(), b.getWidth());
+ assertEquals(a.getHeight(), b.getHeight());
+ for (int x = 0; x < a.getWidth(); x++) {
+ for (int y = 0; y < a.getHeight(); y++) {
+ assertEquals(a.getPixel(x, y), b.getPixel(x, y));
+ }
+ }
+ }
+ }
+
private static void assertStateEquals(
PhoneAccountRegistrar.State a, PhoneAccountRegistrar.State b) {
assertPhoneAccountHandleEquals(a.defaultOutgoing, b.defaultOutgoing);