Merge "use new TelephonyManager API preferred_network_type"
diff --git a/res/drawable/signal_strength_4g.xml b/res/drawable/signal_strength_4g.xml
new file mode 100644
index 0000000..9062096
--- /dev/null
+++ b/res/drawable/signal_strength_4g.xml
@@ -0,0 +1,28 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="9.208dp"
+ android:height="17dp"
+ android:viewportWidth="13.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M4.600000,7.800000l0.700000,0.000000l0.000000,1.300000L4.600000,9.100000L4.600000,11.000000L3.000000,11.000000L3.000000,9.200000L0.100000,9.200000L0.000000,8.100000L3.000000,2.500000l1.700000,0.000000L4.700000,7.800000zM1.600000,7.800000L3.000000,7.800000l0.000000,-3.000000L2.900000,5.000000L1.600000,7.800000z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.900000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S6.100000,9.000000 6.100000,7.900000L6.100000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000S8.100000,2.400000 9.000000,2.400000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000l-1.600000,0.000000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S9.500000,3.700000 9.000000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S7.700000,5.000000 7.700000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L10.099999,7.800000L9.000000,7.800000L9.000000,6.600000l2.900000,0.000000L11.900000,9.900000z"/>
+</vector>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 6a01386..fbd2255 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Stembel nie ondersteun nie"</string>
<string name="description_dial_button" msgid="7459705245418435351">"bel"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"wys belblad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Noodoproepbelblad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuele stemboodskapdiens"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Stel PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Verander PIN"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 9dceb1d..1f71f1e 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"የድምፅ ጥሪ አይታገዝም"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ደውል"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"የስልክ ሰሌዳ አሳይ"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ያስቸኳይ ጊዜ መደወያ ሰሌዳ"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ምስላዊ የድመጽ መልዕከት"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"ፒን ያዘጋጁ"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"ፒን ቀይር"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 057ab60..71006ba 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -615,6 +615,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"الاتصال الصوتي غير معتمد"</string>
<string name="description_dial_button" msgid="7459705245418435351">"طلب"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"عرض لوحة الاتصال"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"لوحة اتصال الطوارئ"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"بريد صوتي مرئي"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"تعيين رقم التعريف الشخصي"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"تغيير رقم التعريف الشخصي"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 36d80d4..2d848ae 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ভইচ কলিং কৰাৰ সুবিধা নাই"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ডায়েল"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ডায়েলপেড দেখুৱাওক"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"জৰুৰীকালীন ডায়েলপেড"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"দৃশ্যগত ভইচমেইল"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"পিন ছেট কৰক"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"পিন সলনি কৰক"</string>
@@ -619,7 +620,7 @@
<string name="sim_label_emergency_calls" msgid="4847699229529306397">"জৰুৰীকালীন কল"</string>
<string name="sim_description_emergency_calls" msgid="7535215397212301562">"জৰুৰীকালীন কল মাত্ৰ"</string>
<string name="sim_description_default" msgid="4778679519938775515">"ছিম কাৰ্ড, শ্লট: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
- <string name="accessibility_settings_activity_title" msgid="8562004288733103868">"সাধ্য় সুবিধাসমূহ"</string>
+ <string name="accessibility_settings_activity_title" msgid="8562004288733103868">"সাধ্য সুবিধাসমূহ"</string>
<string name="status_hint_label_incoming_wifi_call" msgid="5932176406432044638">"এওঁৰ পৰা ৱাই-ফাই কল আহিছে"</string>
<string name="status_hint_label_wifi_call" msgid="8900805254974653903">"ৱাই-ফাই কল"</string>
<string name="emergency_action_launch_hint" msgid="4906759256275562674">"খুলিবলৈ পুনৰাই টিপক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 84338a4..fb568e5 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Səsli zəng dəstəklənmir"</string>
<string name="description_dial_button" msgid="7459705245418435351">"Yığ"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"yığım panelini göstərin"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Təcili Zəng Paneli"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizual Səsli Mesaj"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN kodu ayarlayın"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN kodu dəyişin"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index c5ae5ad..699db97 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -609,6 +609,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Audio pozivi nisu podržani"</string>
<string name="description_dial_button" msgid="7459705245418435351">"biranje"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"prikažite numeričku tastaturu"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Numerička tastatura za hitne pozive"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizuelna govorna pošta"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Podesite PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Promenite PIN"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 06ebd0a..8434b6f 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Галасавы выклік не падтрымліваецца"</string>
<string name="description_dial_button" msgid="7459705245418435351">"набор"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"паказаць панэль набору"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Панэль набору нумара экстраннай службы"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Візуальная галасавая пошта"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Задаць PIN-код"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Змяніць PIN-код"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 9298c71..a3c2997 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Гласови обаждания не се поддържат"</string>
<string name="description_dial_button" msgid="7459705245418435351">"набиране"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"показване на клавиатурата за набиране"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Клавиатура за спешно обаждане"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Визуална гласова поща"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Задаване на ПИН"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Промяна на ПИН"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 7ba9acf..0b229b1 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ভয়েস কলিং সমর্থিত নয়"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ডায়াল করুন"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ডায়ালপ্যাড দেখুন"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"জরুরি ডায়ালপ্যাড"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ভিজ্যুয়াল ভয়েসমেল"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN সেট করুন"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"পিন পরিবর্তন করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index b3a38b3..e5c82ba 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -609,6 +609,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Glasovni pozivi nisu podržani"</string>
<string name="description_dial_button" msgid="7459705245418435351">"biraj"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"prikaži telefonsku tipkovnicu"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Telefonska tipkovnica za hitne pozive"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizuelna govorna pošta"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Postavljanje PIN-a"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Promijeni PIN"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 28da074..10424c9 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"No s\'admeten les trucades de veu"</string>
<string name="description_dial_button" msgid="7459705245418435351">"marca"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"mostra el teclat"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Teclat d\'emergència"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Bústia de veu visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Defineix el PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Canvia el PIN"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 2a981e4..e52bfb3 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Hlasové volání není podporováno"</string>
<string name="description_dial_button" msgid="7459705245418435351">"vytáčení"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"zobrazit číselník"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Tísňový číselník"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizuální hlasová schránka"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Nastavte kód PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Změnit kód PIN"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 4286b6e..6809011 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Taleopkald understøttes ikke"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ring op"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"vis numerisk tastatur"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Numerisk tastatur til nødopkald"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuel telefonsvarer"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Angiv pinkode"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Skift pinkode"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 4bc23c2..c3c6c9f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -29,7 +29,7 @@
<string name="ussdRunning" msgid="485588686340541690">"USSD-Code wird ausgeführt..."</string>
<string name="mmiCancelled" msgid="2771923949751842276">"MMI-Code abgebrochen"</string>
<string name="cancel" msgid="5044513931633602634">"Abbrechen"</string>
- <string name="enter_input" msgid="1810529547726803893">"Die USSD-Nachricht muss zwischen <xliff:g id="MIN_LEN">%d</xliff:g> und <xliff:g id="MAX_LEN">%d</xliff:g> Zeichen lang sein. Bitte versuche es erneut."</string>
+ <string name="enter_input" msgid="1810529547726803893">"Die USSD-Nachricht muss zwischen <xliff:g id="MIN_LEN">%d</xliff:g> und <xliff:g id="MAX_LEN">%d</xliff:g> Zeichen lang sein. Bitte versuche es noch einmal."</string>
<string name="manageConferenceLabel" msgid="4691922394301969053">"Telefonkonferenz verwalten"</string>
<string name="ok" msgid="3811371167865772377">"OK"</string>
<string name="audio_mode_speaker" msgid="27649582100085266">"Lautsprecher"</string>
@@ -166,7 +166,7 @@
<string name="network_query_error" msgid="8466081377231178298">"Netzwerke nicht gefunden. Bitte versuche es noch einmal."</string>
<string name="register_on_network" msgid="9055203954040805084">"Registrierung in <xliff:g id="NETWORK">%s</xliff:g> läuft..."</string>
<string name="not_allowed" msgid="5613353860205691579">"Deine SIM-Karte unterstützt keine Verbindung mit diesem Netzwerk."</string>
- <string name="connect_later" msgid="2308119155752343975">"Momentan kann keine Verbindung zu diesem Netzwerk aufgebaut werden. Bitte versuche es später erneut."</string>
+ <string name="connect_later" msgid="2308119155752343975">"Momentan kann keine Verbindung zu diesem Netzwerk aufgebaut werden. Bitte versuche es später noch einmal."</string>
<string name="registration_done" msgid="495135664535876612">"In Netzwerk registriert."</string>
<string name="already_auto" msgid="6067116884321285507">"Bereits automatisch ausgewählt."</string>
<string name="select_automatically" msgid="1046727200631770962">"Netzwerk automatisch auswählen"</string>
@@ -443,8 +443,8 @@
<string name="oldPinLabel" msgid="5287773661246368314">"Alte PIN"</string>
<string name="newPinLabel" msgid="207488227285336897">"Neue PIN"</string>
<string name="confirmPinLabel" msgid="257597715098070206">"Neue PIN bestätigen"</string>
- <string name="badPin" msgid="8955102849303984935">"Die eingegebene alte PIN ist nicht korrekt. Bitte versuche es erneut."</string>
- <string name="mismatchPin" msgid="5923253370683071889">"Die eingegebenen PIN-Codes stimmen nicht überein. Bitte versuche es erneut."</string>
+ <string name="badPin" msgid="8955102849303984935">"Die eingegebene alte PIN ist nicht korrekt. Bitte versuche es noch einmal."</string>
+ <string name="mismatchPin" msgid="5923253370683071889">"Die eingegebenen PIN-Codes stimmen nicht überein. Bitte versuche es noch einmal."</string>
<string name="invalidPin" msgid="5981171102258684792">"Gib eine 4- bis 8-stellige PIN ein."</string>
<string name="disable_sim_pin" msgid="3419351358300716472">"PIN der SIM-Karte löschen"</string>
<string name="enable_sim_pin" msgid="4845145659651484248">"PIN der SIM-Karte festlegen"</string>
@@ -458,9 +458,9 @@
<string name="oldPin2Label" msgid="8559146795026261502">"Alte PIN2"</string>
<string name="newPin2Label" msgid="4573956902204349054">"Neue PIN2"</string>
<string name="confirmPin2Label" msgid="8100319484454787708">"Neue PIN2 bestätigen"</string>
- <string name="badPuk2" msgid="7910064009531541708">"Falscher PUK2. Bitte versuche es erneut."</string>
- <string name="badPin2" msgid="6646896629970023109">"Falsche PIN2. Bitte versuche es erneut."</string>
- <string name="mismatchPin2" msgid="4177967478551851117">"PIN2-Eingaben stimmen nicht überein. Bitte versuche es erneut."</string>
+ <string name="badPuk2" msgid="7910064009531541708">"Falscher PUK2. Bitte versuche es noch einmal."</string>
+ <string name="badPin2" msgid="6646896629970023109">"Falsche PIN2. Bitte versuche es noch einmal."</string>
+ <string name="mismatchPin2" msgid="4177967478551851117">"PIN2-Eingaben stimmen nicht überein. Bitte versuche es noch einmal."</string>
<string name="invalidPin2" msgid="1757045131429105595">"Gib eine 4- bis 8-stellige PIN2 ein."</string>
<string name="invalidPuk2" msgid="7059081153334815973">"Gib einen 4- bis 8-stelligen PUK2 ein."</string>
<string name="pin2_changed" msgid="3724522579945610956">"PIN2 aktualisiert"</string>
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Sprachanruf wird nicht unterstützt."</string>
<string name="description_dial_button" msgid="7459705245418435351">"Wählen"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"Wähltasten anzeigen"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Wähltasten für Notruf"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuelle Mailbox"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN festlegen"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN ändern"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 8f2cd2d..aab5c9f 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Η φωνητική κλήση δεν υποστηρίζεται"</string>
<string name="description_dial_button" msgid="7459705245418435351">"κλήση"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"εμφάνιση πληκτρολογίου κλήσης"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Πληκτρολόγιο έκτακτης ανάγκης"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Οπτικός αυτόματος τηλεφ."</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Ορισμός PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Αλλαγή PIN"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index de82f52..321ae06 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Voice calling not supported"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dial"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"show dial pad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Emergency dial pad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Set PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Change PIN"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index de82f52..321ae06 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Voice calling not supported"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dial"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"show dial pad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Emergency dial pad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Set PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Change PIN"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index de82f52..321ae06 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Voice calling not supported"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dial"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"show dial pad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Emergency dial pad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Set PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Change PIN"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index de82f52..321ae06 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Voice calling not supported"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dial"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"show dial pad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Emergency dial pad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Set PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Change PIN"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 131c2ec..24f0370 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Voice calling not supported"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dial"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"show dialpad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Emergency Dialpad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Set PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Change PIN"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 99377da..1ec9ba4 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"No admite llamadas de voz."</string>
<string name="description_dial_button" msgid="7459705245418435351">"marcar"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"mostrar teclado"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Teclado de emergencia"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Buzón de voz visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Establecer PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Cambiar PIN"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index d811600..8544201 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Llamadas de voz no admitidas"</string>
<string name="description_dial_button" msgid="7459705245418435351">"marcar"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"mostrar teclado"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Teclado de emergencia"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Buzón de voz visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Configurar el PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Cambiar PIN"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 7df8785..fc59dd3 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Tavakõnesid ei toetata"</string>
<string name="description_dial_button" msgid="7459705245418435351">"helista"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"kuva valimisklahvistik"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Hädaabi valimisklahvistik"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuaalne kõnepost"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN-koodi määramine"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN-koodi muutmine"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 59dd389..63122d9 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Ez dira ahots-deiak onartzen"</string>
<string name="description_dial_button" msgid="7459705245418435351">"markatu"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"erakutsi markagailua"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Larrialdietarako markagailua"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Ikusizko erantzungailua"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Ezarri PIN kodea"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Aldatu PIN kodea"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 22058a4..6470c32 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"تماس صوتی پشتیبانی نمیشود"</string>
<string name="description_dial_button" msgid="7459705245418435351">"شماره گیری"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"نمایش صفحه شماره گیری"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"صفحه شمارهگیری اضطراری"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"پست صوتی تصویری"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"تنظیم پین"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"تغییر پین"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index ac5e53d..dc13269 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Äänipuheluita ei tueta"</string>
<string name="description_dial_button" msgid="7459705245418435351">"soita"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"Näytä näppäimistö"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Hätänäppäimistö"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuaalinen puhelinvastaaja"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Aseta PIN-koodi."</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Vaihda PIN-koodi"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 29155c3..5da3729 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Appels vocaux non pris en charge"</string>
<string name="description_dial_button" msgid="7459705245418435351">"composer"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"afficher le clavier numérique"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Clavier numérique d\'urgence"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Messagerie vocale visuelle"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Définir le NIP"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Modifier le NIP"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index af36849..7e59c81 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Appels vocaux non pris en charge"</string>
<string name="description_dial_button" msgid="7459705245418435351">"composer"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"afficher le clavier"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Clavier d\'appel d\'urgence"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Messagerie vocale visuelle"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Définir un code"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Modifier le code"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 2f39cbf..30bf757 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Chamadas de voz non compatibles"</string>
<string name="description_dial_button" msgid="7459705245418435351">"marcar"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"mostrar teclado de marcación"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Teclado de urxencia"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Correo de voz visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Establecer PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Cambiar PIN"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 7e5b266..61b37a2 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"વૉઇસ કૉલિંગ સમર્થિત નથી"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ડાયલ કરો"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ડાયલપૅડ બતાવો"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ઇમર્જન્સી ડાયલપૅડ"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"વિઝ્યુઅલ વૉઇસમેઇલ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN સેટ કરો"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN બદલો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 8f6a0bb..4d6fa3e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ध्वनि कॉल करना समर्थित नहीं है"</string>
<string name="description_dial_button" msgid="7459705245418435351">"डायल करें"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"डायलपैड देखें"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"आपातकालीन डायलपैड"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"विज़ुअल वॉइसमेल"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"पिन सेट करें"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"पिन बदलें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 0f97740..cac2cf1 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -609,6 +609,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Glasovni pozivi nisu podržani"</string>
<string name="description_dial_button" msgid="7459705245418435351">"biraj"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"prikaži površinu za biranje brojeva"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"površina za biranje brojeva hitne službe"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizualna govorna pošta"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Postavljanje PIN-a"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Promjena PIN-a"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 3affa74..4a2c943 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"A hanghívás nem támogatott"</string>
<string name="description_dial_button" msgid="7459705245418435351">"tárcsázás"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"tárcsázó megjelenítése"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Segélyhívás tárcsázója"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizuális hangpostaüzenet"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN-kód beállítása"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN-kód módosítása"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 93a69e1..77b3080 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Ձայնային զանգերը չեն սպասարկվում"</string>
<string name="description_dial_button" msgid="7459705245418435351">"համարհավաքել"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ցուցադրել թվաշարը"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Շտապ կանչերի թվաշար"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Վիզուալ ձայնային փոստ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Ընտրեք PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Փոխել PIN-ը"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 5dc3e35..0e6686e 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Panggilan suara tidak didukung"</string>
<string name="description_dial_button" msgid="7459705245418435351">"panggil"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"tampilkan dialpad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Tombol Nomor Darurat"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Kotak Pesan Visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Setel PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Ubah PIN"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index b7e82a1..aa8dd13 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Símtöl eru ekki studd"</string>
<string name="description_dial_button" msgid="7459705245418435351">"hringja"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"sýna símatakkaborð"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Takkaborð fyrir neyðarsíma"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Myndrænt talhólf"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Stilla PIN-númer"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Breyta PIN-númeri"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 462d2e0..8e70ef0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Chiamate vocali non supportate"</string>
<string name="description_dial_button" msgid="7459705245418435351">"componi"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"mostra tastierino"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Tastierino di emergenza"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Leggi la segreteria"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Imposta PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Cambia PIN"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index eb8e26b..f659b51 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"חיוג קולי אינו נתמך"</string>
<string name="description_dial_button" msgid="7459705245418435351">"חייג"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"הצגת לוח החיוג"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"לוח חיוג לחירום"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"דואר קולי ויזואלי"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"הגדרת קוד גישה"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"שינוי קוד הגישה"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 210a015..76d3a57 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"音声通話はサポートされていません"</string>
<string name="description_dial_button" msgid="7459705245418435351">"発信"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ダイヤルパッドを表示"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"緊急通報ダイヤルパッド"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ビジュアルボイスメール"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN の設定"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN の変更"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 74e060c..0b7454d 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ხმოვანი ზარი მხარდაჭრილი არ არის"</string>
<string name="description_dial_button" msgid="7459705245418435351">"დარეკვა"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ციფერბლატის ჩვენება"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"საგანგებო ციფერბლატი"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ვიზუალური ხმოვანი ფოსტა"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"დააყენეთ PIN-კოდი"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN-კოდის შეცვლა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index fbc32f0..a0e05ab 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Дауыс арқылы қоңырау шалу қолдауы жоқ"</string>
<string name="description_dial_button" msgid="7459705245418435351">"теру"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"теру тақтасын көрсету"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Төтенше қызмет нөмірін теру тақтасы"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Визуалды дауыс поштасы"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN кодын орнату"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN кодты өзгерту"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 67f6b9a..3c62392 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"មិនគាំទ្រការហៅជាសំឡេង"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ចុច"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"បង្ហាញបន្ទះលេខ"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"បន្ទះលេខសង្គ្រោះបន្ទាន់"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"សារជាសំឡេងអាចមើលឃើញ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"កំណត់លេខកូដ PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"ប្ដូរលេខកូដ PIN"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 599dc3f..ecbf862 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ಧ್ವನಿ ಕರೆ ಮಾಡುವಿಕೆ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ಡಯಲ್"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ಡಯಲ್ಪ್ಯಾಡ್ ತೋರಿಸಿ"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ತುರ್ತು ಡಯಲ್ಪ್ಯಾಡ್"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ದೃಶ್ಯ ಧ್ವನಿಮೇಲ್"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"ಪಿನ್ ಹೊಂದಿಸಿ"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"ಪಿನ್ ಬದಲಾಯಿಸಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 0e0f5f2..943d02b 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"음성통화 지원되지 않음"</string>
<string name="description_dial_button" msgid="7459705245418435351">"전화걸기"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"다이얼패드 표시"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"긴급 다이얼패드"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"시각적 음성사서함"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN 설정"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN 변경"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 38183a7..b399d3a 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -610,6 +610,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Үн менен чалуу колдоого алынбайт"</string>
<string name="description_dial_button" msgid="7459705245418435351">"терүү"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"номер тергичти көрсөтүү"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Шашылыш номер тергич"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Визуалдык үн почтасы"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN код коюу"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN кодду өзгөртүү"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 1d7b031..cd0bb43 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ບໍ່ຮອງຮັບການໂທດ້ວຍສຽງ"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ປຸ່ມໂທ"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ສະແດງປຸ່ມກົດ"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ແປ້ນກົດສຸກເສີນ"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ຂໍ້ຄວາມສຽງປະກອບພາບ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"ຕັ້ງລະຫັດ PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"ປ່ຽນລະຫັດ PIN"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 4399eea..00918c4 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Skambinimas balsu nepalaikomas"</string>
<string name="description_dial_button" msgid="7459705245418435351">"rinkti numerį"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"rodyti numerio rinkimo klaviatūrą"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Skambinimo pagalbos numeriu skydelis"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vaizdinis balso paštas"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN kodo nustatymas"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Keisti PIN kodą"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 30bfe91..3509996 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Balss zvani netiek atbalstīti"</string>
<string name="description_dial_button" msgid="7459705245418435351">"sastādīt numuru"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"rādīt numuru sastādīšanas tastatūru"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Ārkārtas numura sastādīšanas tastatūra"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizuālais balss pasts"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN koda iestatīšana"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Mainīt PIN"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 785e01b..99f4bfe 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Гласовното повикување не е поддржано"</string>
<string name="description_dial_button" msgid="7459705245418435351">"бирај"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"прикажи копчиња за бирање"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Копчиња за бирање за итни случаи"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Визуелна говорна пошта"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Поставете PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Променете PIN"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 2fadd76..581f3f3 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"വോയ്സ് കോൾ ചെയ്യുന്നതിനെ പിന്തുണയ്ക്കുന്നില്ല"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ഡയൽ ചെയ്യുക"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ഡയൽപാഡ് കാണിക്കുക"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"അടിയന്തര ഡയൽപാഡ്"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"വിഷ്വൽ വോയ്സ്മെയിൽ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"പിൻ സജ്ജമാക്കുക"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"പിൻ മാറ്റുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index f30a0fc..1859beb 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Дуугаар залгахыг дэмждэггүй"</string>
<string name="description_dial_button" msgid="7459705245418435351">"залгах"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"дугаар цуглуулах самбарыг харуулах"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Яаралтай тусламжийн дугаар цуглуулах самбар"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Уншиж болохуйц дуут шуудан"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN тохируулах"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN өөрчлөх"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 2f89c85..3a21b8a 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"व्हॉइस कॉलिंंग समर्थित नाही"</string>
<string name="description_dial_button" msgid="7459705245418435351">"डायल करा"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"डायलपॅड दाखवा"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"आणीबाणी डायलपॅड"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"दृश्यमान व्हॉइसमेल"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"पिन सेट करा"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"पिन बदला"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 3e9013a..194d9fe 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Panggilan suara tidak disokong"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dail"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"tunjukkan pad pendail"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Pad Pendail Kecemasan"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Mel Suara Visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Tetapkan PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Tukar PIN"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 04aa9d3..c1d3ba0 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"အသံခေါ်ဆိုမှု ထောက်ံပံ့ခြင်းမရှိပါ"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ဖုန်းခေါ်ခြင်း"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"နံပါတ်အကွက် ပြရန်"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"အရေးပေါ် နံပါတ်ကွက်"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"အမြင် အသံမေးလ်"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"ပင်နံပါတ်သတ်မှတ်ရန်"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"ပင်နံပါတ်ပြောင်းပါ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 6a9c1a1..53fa645 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Taleanrop støttes ikke"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ring"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"vis talltastatur"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Tastatur for nødanrop"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuell talepostkasse"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Angi PIN-kode"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Bytt PIN-kode"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 83d8eeb..3b157eb 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"भ्वाइस कल गर्ने समर्थित छैन"</string>
<string name="description_dial_button" msgid="7459705245418435351">"डायल"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"डायलप्याड देखाउनुहोस्"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"आपतकालीन डायलप्याड"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"भिजुअल भ्वाइस मेल"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN सेट गर्नुहोस्"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN परिवर्तन गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 739e8a4..a226839 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -542,7 +542,7 @@
<string name="hac_mode_title" msgid="8740268574688743289">"Gehoorapparaten"</string>
<string name="hac_mode_summary" msgid="6833851160514929341">"Compatibiliteit voor gehoorapparaat inschakelen"</string>
<string name="rtt_mode_title" msgid="6954652435543570121">"Realtime tekstoproep (RTT)"</string>
- <string name="rtt_mode_summary" msgid="5146344277246544580">"Berichten in een spraakoproep toestaan"</string>
+ <string name="rtt_mode_summary" msgid="5146344277246544580">"Berichten in een audiogesprek toestaan"</string>
<string name="rtt_mode_more_information" msgid="6080420780730383030">"RTT helpt bellers die doof of slechthorend zijn, een spraakbeperking hebben of meer dan alleen een stem nodig hebben.<br> <a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>>Meer informatie</a>\n <br><br> - RTT-gesprekken worden opgeslagen als berichttranscript.\n <br> - RTT is niet beschikbaar voor videogesprekken"</string>
<string-array name="tty_mode_entries">
<item msgid="512950011423868021">"TTY uit"</item>
@@ -604,9 +604,10 @@
<string name="contactPhoto" msgid="4713193418046639466">"contactfoto"</string>
<string name="goPrivate" msgid="865837794424530980">"privé"</string>
<string name="selectContact" msgid="781975788478987237">"contact selecteren"</string>
- <string name="not_voice_capable" msgid="2739898841461577811">"Spraakoproepen worden niet ondersteund"</string>
+ <string name="not_voice_capable" msgid="2739898841461577811">"Audiogesprekken worden niet ondersteund"</string>
<string name="description_dial_button" msgid="7459705245418435351">"bellen"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"toetsenblok weergeven"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Noodtoetsenblok"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuele voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Pincode instellen"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Pincode wijzigen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 560e1a3..6872f74 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ଭଏସ୍ କଲ୍ ସପୋର୍ଟ କରୁନାହିଁ"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ଡାଏଲ୍ କରନ୍ତୁ"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ଡାୟଲ୍ପ୍ୟାଡ୍ ଦେଖାନ୍ତୁ"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ଜରୁରୀକାଳୀନ ଡାୟଲ୍ପ୍ୟାଡ୍"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ଭିଜୁଆଲ୍ ଭଏସମେଲ୍"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN ସେଟ୍ କରନ୍ତୁ"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN ବଦଳାନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 84048c2..9211093 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ਵੌਇਸ ਕਾਲਿੰਗ ਸਮਰਥਿਤ ਨਹੀਂ"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ਡਾਇਲ ਕਰੋ"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ਡਾਇਲਪੈਡ ਦਿਖਾਓ"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ਸੰਕਟਕਾਲੀਨ ਡਾਇਲਪੈਡ"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ਵਿਜੁਅਲ ਵੌਇਸਮੇਲ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN ਸੈੱਟ ਕਰੋ"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN ਬਦਲੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index cba0e26..5003417 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Połączenia głosowe nie są obsługiwane"</string>
<string name="description_dial_button" msgid="7459705245418435351">"wybierz numer"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"pokaż klawiaturę"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Klawiatura alarmowa"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Wizualna poczta głosowa"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Ustaw kod PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Zmień PIN"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 90f93e0..53b7050 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Chamadas de voz não suportadas"</string>
<string name="description_dial_button" msgid="7459705245418435351">"marcar"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"mostrar o teclado"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Teclado de emergência"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Correio de voz visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Definir PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Alterar PIN"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d2674ec..b221f5b 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Chamada de voz não compatível"</string>
<string name="description_dial_button" msgid="7459705245418435351">"discar"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"Mostrar teclado"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Teclado de emergência"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Correio de voz visual"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Definir PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Alterar PIN"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index cc3ee8a..682c2a3 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -609,6 +609,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Apelarea vocală nu este acceptată"</string>
<string name="description_dial_button" msgid="7459705245418435351">"apelare"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"afișați tastatura numerică"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Tastatură pentru apeluri de urgență"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Mesagerie vocală vizuală"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Setați codul PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Schimbați codul PIN"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 0bfdf40..86eeeb0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Голосовые вызовы не поддерживаются"</string>
<string name="description_dial_button" msgid="7459705245418435351">"набор номера"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"показать кнопки"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Экстренные вызовы"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Визуальная голосовая почта"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Установка PIN-кода"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Изменение PIN-кода"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 9566074..8645166 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"හඬ ඇමතීම සඳහා සහාය නොදක්වයි"</string>
<string name="description_dial_button" msgid="7459705245418435351">"අමතන්න"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ඇමතුම් පෑඩය පෙන්වන්න"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"හදිසි ඇමතුම් පෑඩය"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"දෘශ්ය හඬ තැපෑල"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN අංකය සකසන්න"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN අංකය වෙනස් කරන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 4078707..18383ba 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Hlasové volanie nie je podporované"</string>
<string name="description_dial_button" msgid="7459705245418435351">"vytáčanie"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"zobraziť číselnú klávesnicu"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Číselná klávesnica na tiesňové volanie"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizuálna hlasová schránka"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Nastavenie kódu PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Zmena kódu PIN"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 9ccb78e..fafa670 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Glasovno klicanje ni podprto"</string>
<string name="description_dial_button" msgid="7459705245418435351">"klicanje"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"prikaži številčnico"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Tipkovnica za klic v sili"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizualno sporočilo v odzivniku"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Nastavitev kode PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Sprememba kode PIN"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 03dd296..6940af0 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Telefonata zanore nuk mbështetet"</string>
<string name="description_dial_button" msgid="7459705245418435351">"formo numrin"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"shfaq bllokun e formimit të numrit"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Blloku i formimit të numrit të urgjencës"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Posta zanore vizuale"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Konfiguro kodin PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Ndrysho kodin PIN"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f255cf0..15dcbeb 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -609,6 +609,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Аудио позиви нису подржани"</string>
<string name="description_dial_button" msgid="7459705245418435351">"бирање"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"прикажите нумеричку тастатуру"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Нумеричка тастатура за хитне позиве"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Визуелна говорна пошта"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Подесите PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Промените PIN"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index e7f8fa6..9baa614 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Röstsamtal stöds inte"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ring upp"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"visa knappsats"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Knappsats för nödsituationer"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visuell röstbrevlåda"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Ställ in pinkod"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Byt pinkod"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 710007c..5e17ba0 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Upigaji simu za sauti hauhimiliwi"</string>
<string name="description_dial_button" msgid="7459705245418435351">"piga simu"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"onyesha vitufe vya kupiga simu"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Vitufe vya kupiga simu ya Dharura"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Ujumbe wa Sauti Unaoonekana"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Weka PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Badilisha PIN"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 130cf1c..dd0667c 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"குரல் அழைப்பு ஆதரிக்கப்படவில்லை"</string>
<string name="description_dial_button" msgid="7459705245418435351">"டயல்"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"டயல்பேடைக் காட்டும் பட்டன்"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"அவசரகால டயல்பேட்"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"விஷூவல் குரலஞ்சல்"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"பின்னை அமை"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"பின்னை மாற்றவும்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 1ac83d7..dc9f0ad 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"వాయిస్ కాలింగ్కు మద్దతు లేదు"</string>
<string name="description_dial_button" msgid="7459705245418435351">"డయల్ చేయి"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"డయల్ప్యాడ్ను చూపు"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"అత్యవసర డయల్ప్యాడ్"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"దృశ్యమాన వాయిస్మెయిల్"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PINని సెట్ చేయండి"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PINను మార్చు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index fac6bbd..cad7bfb 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"ไม่สนับสนุนการโทรออกด้วยเสียง"</string>
<string name="description_dial_button" msgid="7459705245418435351">"หมุนหมายเลข"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"แสดงแป้นหมายเลข"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"แป้นหมายเลขฉุกเฉิน"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"ข้อความเสียงพร้อมภาพ"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"ตั้งค่า PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"เปลี่ยน PIN"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 87e207b..6f2374c 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Hindi suportado ang voice calling"</string>
<string name="description_dial_button" msgid="7459705245418435351">"Mag-dial"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ipakita ang dialpad"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Pang-emergency na Dialpad"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Magtakda ng PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Baguhin ang PIN"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index a408738..514efd4 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Sesli arama desteklenmiyor"</string>
<string name="description_dial_button" msgid="7459705245418435351">"çevir"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"tuş takımını gösterir"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Acil Durum Tuş Takımı"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Görsel Sesli Mesaj"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN\'i ayarlayın"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN\'i değiştirin"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 72c643f..14917e2 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Голосові дзвінки не підтримуються"</string>
<string name="description_dial_button" msgid="7459705245418435351">"дзвонити"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"показати цифрову клавіатуру"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Цифрова клавіатура для екстрених викликів"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Візуальна голосова пошта"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Установити PIN-код"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Змінити PIN-код"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index dc19c68..be97de7 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"صوتی کالنگ تعاون یافتہ نہیں ہے"</string>
<string name="description_dial_button" msgid="7459705245418435351">"ڈائل کریں"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"ڈائل پیڈ دکھائیں"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"ایمرجنسی ڈائل پیڈ"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"بصری صوتی میل"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN سیٹ کریں"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN تبدیل کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index ed25483..5566b0b 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Ovozli qo‘ng‘iroq ishlamaydi"</string>
<string name="description_dial_button" msgid="7459705245418435351">"terish"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"klaviaturani ochish"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Favqulodda chaqiruvlar"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Vizual ovozli pochta"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"PIN kodni o‘rnatish"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"PIN kodni almashtirish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 4be7f02..b08a469 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Không hỗ trợ tính năng gọi thoại"</string>
<string name="description_dial_button" msgid="7459705245418435351">"quay số"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"hiển thị bàn phím số"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Bàn phím số khẩn cấp"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Thư thoại kèm theo hình ảnh"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Đặt mã PIN"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Thay đổi mã PIN"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 856dbd3..275b418 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -607,6 +607,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"不支持语音呼叫"</string>
<string name="description_dial_button" msgid="7459705245418435351">"拨号"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"显示拨号键盘"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"紧急拨号键盘"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"可视语音邮箱"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"设置 PIN 码"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"更改 PIN 码"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 8ae9868..1d87591 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"不支援語音通話"</string>
<string name="description_dial_button" msgid="7459705245418435351">"撥號"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"顯示撥號鍵盤"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"緊急撥號盤"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Visual Voicemail"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"設定 PIN 碼"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"變更 PIN"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index eceae1e..f572b79 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -613,6 +613,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"不支援語音通話"</string>
<string name="description_dial_button" msgid="7459705245418435351">"撥號"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"顯示撥號鍵盤"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"緊急撥號鍵盤"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"具有畫面的語音信箱"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"設定語音信箱密碼"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"變更 PIN 碼"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index efb0238..5eec818 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -611,6 +611,7 @@
<string name="not_voice_capable" msgid="2739898841461577811">"Ukushayela ngezwi akusekelwe"</string>
<string name="description_dial_button" msgid="7459705245418435351">"dayela"</string>
<string name="description_dialpad_button" msgid="5220006811894230590">"bonisa iphedi yokudayela"</string>
+ <string name="pane_title_emergency_dialpad" msgid="1148505414688493134">"Iphedi yokudayela ephuthumayo"</string>
<string name="voicemail_visual_voicemail_switch_title" msgid="5012622186976275457">"Ivoyisimeyili ebonakalayo"</string>
<string name="voicemail_set_pin_dialog_title" msgid="2797924461029093837">"Setha IPHINI"</string>
<string name="voicemail_change_pin_dialog_title" msgid="6035421908626121564">"Shintsha i-PIN"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7773af6..de14389 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1074,8 +1074,8 @@
<!-- Label for the "No service" notification item, when expanded. -->
<string name="notification_network_selection_title">No service</string>
<!-- Label for the expanded "No service" notification item, including the
- operator name set by user -->
- <string name="notification_network_selection_text">Selected network (<xliff:g id="operator_name">%s</xliff:g>) unavailable</string>
+ operator name set by user. No space after "network", appended in nonempty value. -->
+ <string name="notification_network_selection_text">Selected network<xliff:g id="operator_name">%s</xliff:g> unavailable</string>
<!-- In-call screen: call failure message displayed in an error dialog. [CHAR_LIMIT=NONE] -->
<string name="incall_error_power_off" product="watch">Turn on mobile network, turn off airplane mode or turn off battery saver mode to make a call.</string>
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index bfeee7a..bdffb26 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -76,6 +76,11 @@
mReplaceInvalidCFNumber = replaceInvalidCFNumber;
}
+ void restoreCallForwardInfo(CallForwardInfo cf) {
+ handleCallForwardResult(cf);
+ updateSummaryText();
+ }
+
@Override
protected void onBindDialogView(View view) {
// default the button clicked to be the cancel button.
@@ -100,7 +105,15 @@
int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
CommandsInterface.CF_ACTION_REGISTRATION :
CommandsInterface.CF_ACTION_DISABLE;
- int time = (reason != CommandsInterface.CF_REASON_NO_REPLY) ? 0 : 20;
+ int time = 0;
+ if (reason == CommandsInterface.CF_REASON_NO_REPLY) {
+ PersistableBundle carrierConfig = PhoneGlobals.getInstance()
+ .getCarrierConfigForSubId(mPhone.getSubId());
+ if (carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true)) {
+ time = 20;
+ }
+ }
final String number = getPhoneNumber();
Log.d(LOG_TAG, "callForwardInfo=" + callForwardInfo);
@@ -137,7 +150,7 @@
}
}
- void handleCallForwardResult(CallForwardInfo cf) {
+ private void handleCallForwardResult(CallForwardInfo cf) {
callForwardInfo = cf;
Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
// In some cases, the network can send call forwarding URIs for voicemail that violate the
diff --git a/src/com/android/phone/CdmaOptions.java b/src/com/android/phone/CdmaOptions.java
index 47c0eb4..1dc4a09 100644
--- a/src/com/android/phone/CdmaOptions.java
+++ b/src/com/android/phone/CdmaOptions.java
@@ -39,6 +39,7 @@
public class CdmaOptions {
private static final String LOG_TAG = "CdmaOptions";
+ private CarrierConfigManager mCarrierConfigManager;
private CdmaSystemSelectListPreference mButtonCdmaSystemSelect;
private CdmaSubscriptionListPreference mButtonCdmaSubscription;
private RestrictedPreference mButtonAPNExpand;
@@ -59,6 +60,7 @@
mPrefFragment = prefFragment;
mPrefScreen = prefScreen;
mPrefFragment.addPreferencesFromResource(R.xml.cdma_options);
+ mCarrierConfigManager = new CarrierConfigManager(prefFragment.getContext());
// Initialize preferences.
mButtonCdmaSystemSelect = (CdmaSystemSelectListPreference) mPrefScreen
@@ -77,8 +79,7 @@
int phoneType = TelephonyManager.from(mPrefFragment.getContext())
.createForSubscriptionId(mSubId).getPhoneType();
- PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(mSubId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
// Some CDMA carriers want the APN settings.
boolean addAPNExpand = shouldAddApnExpandPreference(phoneType, carrierConfig);
boolean addCdmaSubscription =
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 5d18f8e..66701d5 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.KeyguardManager;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -31,12 +32,18 @@
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.Point;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.ToneGenerator;
+import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PersistableBundle;
+import android.os.SystemClock;
import android.provider.Settings;
import android.telecom.ParcelableCallAnalytics;
import android.telecom.PhoneAccount;
@@ -55,6 +62,7 @@
import android.text.TextWatcher;
import android.text.method.DialerKeyListener;
import android.text.style.TtsSpan;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
@@ -71,6 +79,8 @@
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.util.ViewUtil;
import com.android.phone.common.widget.ResizingTextEditText;
@@ -108,13 +118,81 @@
public class EmergencyDialer extends Activity implements View.OnClickListener,
View.OnLongClickListener, View.OnKeyListener, TextWatcher,
DialpadKeyButton.OnPressedListener, ColorExtractor.OnColorsChangedListener,
- EmergencyShortcutButton.OnConfirmClickListener {
+ EmergencyShortcutButton.OnConfirmClickListener, SensorEventListener {
+
+ private class MetricsWriter {
+ // Metrics constants indicating the entry type that user opened emergency dialer.
+ // This info is sent from system UI with EXTRA_ENTRY_TYPE. Please make them being
+ // in sync with those in com.android.systemui.util.EmergencyDialerConstants.
+ public static final int ENTRY_TYPE_UNKNOWN = 0;
+ public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+ public static final int ENTRY_TYPE_POWER_MENU = 2;
+
+ // Metrics constants indicating the UI that user made phone call.
+ public static final int CALL_SOURCE_DIALPAD = 0;
+ public static final int CALL_SOURCE_SHORTCUT = 1;
+
+ // Metrics constants indicating the phone number type of a call user made.
+ public static final int PHONE_NUMBER_TYPE_GENERAL = 0;
+ public static final int PHONE_NUMBER_TYPE_EMERGENCY = 1;
+
+ // Metrics constants indicating the actions performed by user.
+ public static final int USER_ACTION_NONE = 0x0;
+ public static final int USER_ACTION_OPEN_DIALPAD = 0x1;
+ public static final int USER_ACTION_OPEN_EMERGENCY_INFO = 0x2;
+ public static final int USER_ACTION_MAKE_CALL_VIA_DIALPAD = 0x4;
+ public static final int USER_ACTION_MAKE_CALL_VIA_SHORTCUT = 0x8;
+
+ private MetricsLogger mMetricsLogger = new MetricsLogger();
+
+ public void writeMetricsForEnter() {
+ int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+ KeyguardManager keyguard = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+ mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+ .setType(MetricsEvent.TYPE_OPEN)
+ .setSubtype(entryType)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IS_SCREEN_LOCKED,
+ keyguard.isKeyguardLocked() ? 1 : 0));
+ }
+
+ public void writeMetricsForExit() {
+ int entryType = getIntent().getIntExtra(EXTRA_ENTRY_TYPE, ENTRY_TYPE_UNKNOWN);
+ long userStayDuration = SystemClock.elapsedRealtime() - mUserEnterTimeMillis;
+ mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER)
+ .setType(MetricsEvent.TYPE_CLOSE)
+ .setSubtype(entryType)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_USER_ACTIONS, mUserActions)
+ .addTaggedData(
+ MetricsEvent.FIELD_EMERGENCY_DIALER_DURATION_MS, userStayDuration));
+ }
+
+ public void writeMetricsForMakingCall(int callSource, int phoneNumberType,
+ boolean hasShortcut) {
+ mMetricsLogger.write(new LogMaker(MetricsEvent.EMERGENCY_DIALER_MAKE_CALL)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(callSource)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_TYPE,
+ phoneNumberType)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_PHONE_NUMBER_HAS_SHORTCUT,
+ hasShortcut ? 1 : 0)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_IN_POCKET,
+ mIsProximityNear ? 1 : 0));
+ }
+ }
+
// Keys used with onSaveInstanceState().
private static final String LAST_NUMBER = "lastNumber";
// Intent action for this activity.
public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+ /**
+ * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+ * the emergency dialer.
+ */
+ public static final String EXTRA_ENTRY_TYPE =
+ "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
// List of dialer button IDs.
private static final int[] DIALER_KEYS = new int[] {
R.id.one, R.id.two, R.id.three,
@@ -212,6 +290,22 @@
private boolean mAreEmergencyDialerShortcutsEnabled;
+ private MetricsWriter mMetricsWriter;
+ private SensorManager mSensorManager;
+ private Sensor mProximitySensor;
+ private boolean mIsProximityNear = false;
+
+ /**
+ * The time, in millis, since boot when user opened emergency dialer.
+ * This is used when calculating the user stay duration for metrics data.
+ */
+ private long mUserEnterTimeMillis = 0;
+
+ /**
+ * Bit flag indicating the actions performed by user. This is used for metrics data.
+ */
+ private int mUserActions = MetricsWriter.USER_ACTION_NONE;
+
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
@@ -246,13 +340,19 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mMetricsWriter = new MetricsWriter();
+ mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+ if (mSensorManager != null) {
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ }
+
// Allow this activity to be displayed in front of the keyguard / lockscreen.
setShowWhenLocked(true);
// Allow turning screen on
setTurnScreenOn(true);
- mAreEmergencyDialerShortcutsEnabled = Settings.Global.getInt(getContentResolver(),
- Settings.Global.FASTER_EMERGENCY_PHONE_CALL_ENABLED, 0) != 0;
+ mAreEmergencyDialerShortcutsEnabled = FeatureFlagUtils
+ .isEnabled(this, FeatureFlagUtils.EMERGENCY_DIAL_SHORTCUTS);
Log.d(LOG_TAG, "Enable emergency dialer shortcut: "
+ mAreEmergencyDialerShortcutsEnabled);
@@ -484,6 +584,13 @@
public void onConfirmClick(EmergencyShortcutButton button) {
if (button == null) return;
+ mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_SHORTCUT;
+
+ // We interest on the context when user has intention to make phone call,
+ // so write metrics here for shortcut number even the call may not be created.
+ mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_SHORTCUT,
+ MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, true);
+
String phoneNumber = button.getPhoneNumber();
if (!TextUtils.isEmpty(phoneNumber)) {
@@ -517,11 +624,13 @@
return;
}
case R.id.floating_action_button_dialpad: {
+ mUserActions |= MetricsWriter.USER_ACTION_OPEN_DIALPAD;
mDigits.getText().clear();
switchView(mDialpadView, mEmergencyShortcutView, true);
return;
}
case R.id.emergency_info_button: {
+ mUserActions |= MetricsWriter.USER_ACTION_OPEN_EMERGENCY_INFO;
Intent intent = (Intent) view.getTag(R.id.tag_intent);
if (intent != null) {
startActivity(intent);
@@ -623,6 +732,11 @@
@Override
protected void onStart() {
super.onStart();
+
+ mUserEnterTimeMillis = SystemClock.elapsedRealtime();
+ mUserActions = MetricsWriter.USER_ACTION_NONE;
+ mMetricsWriter.writeMetricsForEnter();
+
// It does not support dark text theme, when emergency dialer shortcuts are enabled.
// And set background color to black.
if (mAreEmergencyDialerShortcutsEnabled) {
@@ -668,6 +782,11 @@
protected void onResume() {
super.onResume();
+ if (mProximitySensor != null) {
+ mSensorManager.registerListener(
+ this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
// retrieve the DTMF tone play back setting.
mDTMFToneEnabled = Settings.System.getInt(getContentResolver(),
Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
@@ -692,11 +811,15 @@
@Override
public void onPause() {
super.onPause();
+ if (mProximitySensor != null) {
+ mSensorManager.unregisterListener(this, mProximitySensor);
+ }
}
@Override
protected void onStop() {
super.onStop();
+ mMetricsWriter.writeMetricsForExit();
mColorExtractor.removeOnColorsChangedListener(this);
}
@@ -734,6 +857,7 @@
* place the call, but check to make sure it is a viable number.
*/
private void placeCall() {
+ mUserActions |= MetricsWriter.USER_ACTION_MAKE_CALL_VIA_DIALPAD;
mLastNumber = mDigits.getText().toString();
// Convert into emergency number according to emergency conversion map.
@@ -750,6 +874,10 @@
playTone(ToneGenerator.TONE_PROP_NACK);
return;
}
+
+ mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+ MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, isShortcutNumber(mLastNumber));
+
Bundle extras = new Bundle();
extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD);
@@ -758,6 +886,11 @@
} else {
if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
+ // We interest on the context when user has intention to make phone call,
+ // so write metrics here for non-emergency numbers even these numbers are rejected.
+ mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
+ MetricsWriter.PHONE_NUMBER_TYPE_GENERAL, false);
+
showDialog(BAD_EMERGENCY_NUMBER_DIALOG);
}
mDigits.getText().delete(0, mDigits.getText().length());
@@ -1145,4 +1278,30 @@
}
});
}
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float distance = event.values[0];
+ mIsProximityNear = (distance < mProximitySensor.getMaximumRange());
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+
+ private boolean isShortcutNumber(String number) {
+ if (TextUtils.isEmpty(number) || mEmergencyShortcutButtonList == null) {
+ return false;
+ }
+
+ boolean isShortcut = false;
+ for (EmergencyShortcutButton button : mEmergencyShortcutButtonList) {
+ if (button != null && number.equals(button.getPhoneNumber())) {
+ isShortcut = true;
+ break;
+ }
+ }
+ return isShortcut;
+ }
}
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index 275dac0..59b3794 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -19,6 +19,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
+import android.metrics.LogMaker;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -30,6 +32,9 @@
import androidx.annotation.NonNull;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
/**
* Emergency shortcut button displays a local emergency phone number information(including phone
* number, and phone type). To decrease false clicking, it need to click twice to confirm to place
@@ -61,6 +66,12 @@
private boolean mConfirmViewHiding;
+ /**
+ * The time, in millis, since boot when user taps on shortcut button to reveal confirm view.
+ * This is used for metrics when calculating the interval between reveal tap and confirm tap.
+ */
+ private long mTimeOfRevealTapInMillis = 0;
+
public EmergencyShortcutButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -185,6 +196,8 @@
switch (view.getId()) {
case R.id.emergency_call_number_info_view:
if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
+ // TalkBack itself includes a prompt to confirm click action implicitly,
+ // so we don't need an additional confirmation with second tap on button.
if (mOnConfirmClickListener != null) {
mOnConfirmClickListener.onConfirmClick(this);
}
@@ -193,6 +206,15 @@
}
break;
case R.id.emergency_call_confirm_view:
+ if (mTimeOfRevealTapInMillis != 0) {
+ long timeBetweenTwoTaps =
+ SystemClock.elapsedRealtime() - mTimeOfRevealTapInMillis;
+ // Reset reveal time to zero for next reveal-confirm taps pair.
+ mTimeOfRevealTapInMillis = 0;
+
+ writeMetricsForConfirmTap(timeBetweenTwoTaps);
+ }
+
if (mOnConfirmClickListener != null) {
mOnConfirmClickListener.onConfirmClick(this);
}
@@ -204,6 +226,7 @@
mConfirmViewHiding = false;
mConfirmView.setVisibility(View.VISIBLE);
+ mTimeOfRevealTapInMillis = SystemClock.elapsedRealtime();
int centerX = mCallNumberInfoView.getLeft() + mCallNumberInfoView.getWidth() / 2;
int centerY = mCallNumberInfoView.getTop() + mCallNumberInfoView.getHeight() / 2;
Animator reveal = ViewAnimationUtils.createCircularReveal(
@@ -240,6 +263,8 @@
@Override
public void onAnimationEnd(Animator animation) {
mConfirmView.setVisibility(INVISIBLE);
+ // Reset reveal time to zero for next reveal-confirm taps pair.
+ mTimeOfRevealTapInMillis = 0;
}
});
reveal.start();
@@ -254,4 +279,12 @@
hideSelectedButton();
}
};
+
+ private void writeMetricsForConfirmTap(long timeBetweenTwoTaps) {
+ LogMaker logContent = new LogMaker(MetricsEvent.EMERGENCY_DIALER_SHORTCUT_CONFIRM_TAP)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .addTaggedData(MetricsEvent.FIELD_EMERGENCY_DIALER_SHORTCUT_TAPS_INTERVAL,
+ timeBetweenTwoTaps);
+ MetricsLogger.action(logContent);
+ }
}
diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java
index e562e46..6d80621 100644
--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java
+++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java
@@ -31,6 +31,7 @@
private static final String KEY_TOGGLE = "toggle";
private static final String KEY_STATUS = "status";
private static final String KEY_NUMBER = "number";
+ private static final String KEY_ENABLE = "enable";
private CallForwardEditPreference mButtonCFU;
private CallForwardEditPreference mButtonCFB;
@@ -112,11 +113,12 @@
for (CallForwardEditPreference pref : mPreferences) {
Bundle bundle = mIcicle.getParcelable(pref.getKey());
pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+ pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
CallForwardInfo cf = new CallForwardInfo();
cf.number = bundle.getString(KEY_NUMBER);
cf.status = bundle.getInt(KEY_STATUS);
pref.init(this, mPhone, mReplaceInvalidCFNumbers);
- pref.handleCallForwardResult(cf);
+ pref.restoreCallForwardInfo(cf);
}
}
mFirstResume = false;
@@ -131,6 +133,7 @@
for (CallForwardEditPreference pref : mPreferences) {
Bundle bundle = new Bundle();
bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+ bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
if (pref.callForwardInfo != null) {
bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
diff --git a/src/com/android/phone/GsmUmtsOptions.java b/src/com/android/phone/GsmUmtsOptions.java
index 8b6dad3..cc07427 100644
--- a/src/com/android/phone/GsmUmtsOptions.java
+++ b/src/com/android/phone/GsmUmtsOptions.java
@@ -16,6 +16,7 @@
package com.android.phone;
+import android.content.Context;
import android.content.Intent;
import android.os.PersistableBundle;
import android.preference.Preference;
@@ -23,6 +24,7 @@
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -36,6 +38,7 @@
public class GsmUmtsOptions {
private static final String LOG_TAG = "GsmUmtsOptions";
+ private CarrierConfigManager mCarrierConfigManager;
private RestrictedPreference mButtonAPNExpand;
private Preference mCategoryAPNExpand;
Preference mCarrierSettingPref;
@@ -52,8 +55,10 @@
public GsmUmtsOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen,
final int subId, INetworkQueryService queryService) {
+ final Context context = prefFragment.getContext();
mPrefFragment = prefFragment;
mPrefScreen = prefScreen;
+ mCarrierConfigManager = new CarrierConfigManager(context);
mPrefFragment.addPreferencesFromResource(R.xml.gsm_umts_options);
mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY);
mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY);
@@ -72,16 +77,17 @@
boolean addAPNExpand = true;
boolean addNetworkOperatorsCategory = true;
boolean addCarrierSettings = true;
+ final TelephonyManager telephonyManager = TelephonyManager.from(mPrefFragment.getContext())
+ .createForSubscriptionId(subId);
Phone phone = PhoneGlobals.getPhone(subId);
if (phone == null) return;
- if (phone.getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) {
+ if (telephonyManager.getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) {
log("Not a GSM phone");
addAPNExpand = false;
mNetworkOperator.setEnabled(false);
} else {
log("Not a CDMA phone");
- PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(subId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
// Determine which options to display. For GSM these are defaulted to true in
// CarrierConfigManager, but they maybe overriden by DefaultCarrierConfigService or a
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index f7ba48c..1ebfc62 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -239,15 +239,16 @@
* doesn't set {@link CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL} to false.
*/
public static boolean hideEnhanced4gLteSettings(Context context) {
- List<SubscriptionInfo> sil =
+ final CarrierConfigManager carrierConfigManager = new CarrierConfigManager(context);
+ final List<SubscriptionInfo> sil =
SubscriptionManager.from(context).getActiveSubscriptionInfoList();
// Check all active subscriptions. We only hide the button if it's disabled for all
// active subscriptions.
if (sil != null) {
for (SubscriptionInfo subInfo : sil) {
ImsManager imsManager = ImsManager.getInstance(context, subInfo.getSimSlotIndex());
- PersistableBundle carrierConfig = PhoneGlobals.getInstance()
- .getCarrierConfigForSubId(subInfo.getSubscriptionId());
+ PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(
+ subInfo.getSubscriptionId());
if ((imsManager.isVolteEnabledByPlatform()
&& imsManager.isVolteProvisionedOnDevice())
|| carrierConfig.getBoolean(
@@ -347,6 +348,7 @@
private SubscriptionManager mSubscriptionManager;
private TelephonyManager mTelephonyManager;
+ private CarrierConfigManager mCarrierConfigManager;
private int mSubId;
//UI objects
@@ -399,6 +401,7 @@
updateEnhanced4gLteState();
updateWiFiCallState();
updateVideoCallState();
+ updatePreferredNetworkType();
}
/**
@@ -788,6 +791,7 @@
mSubscriptionManager = SubscriptionManager.from(activity);
mTelephonyManager = (TelephonyManager) activity.getSystemService(
Context.TELEPHONY_SERVICE);
+ mCarrierConfigManager = new CarrierConfigManager(getContext());
if (icicle != null) {
mExpandAdvancedFields = icicle.getBoolean(EXPAND_ADVANCED_FIELDS, false);
@@ -1055,8 +1059,7 @@
android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId,
preferredNetworkMode);
- PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(mSubId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
mIsGlobalCdma = isLteOnCdma
&& carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_CDMA_CHOICES_BOOL);
if (carrierConfig.getBoolean(
@@ -1110,6 +1113,7 @@
}
updateEnhanced4gLteState();
+ updatePreferredNetworkType();
updateCallingCategory();
// Enable link to CMAS app settings depending on the value in config.xml.
@@ -1165,8 +1169,6 @@
R.string.enhanced_4g_lte_mode_title_variant :
R.string.enhanced_4g_lte_mode_title;
- mButtonPreferredNetworkMode.setEnabled(hasActiveSubscriptions);
- mButtonEnabledNetworks.setEnabled(hasActiveSubscriptions);
mButton4glte.setTitle(enhanced4glteModeTitleId);
mLteDataServicePref.setEnabled(hasActiveSubscriptions);
Preference ps;
@@ -1203,8 +1205,7 @@
// Requires that mSubId is up to date
void updateEnabledNetworksEntries() {
final int phoneType = mTelephonyManager.getPhoneType();
- final PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(mSubId);
+ final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
final int lteForced = android.provider.Settings.Global.getInt(
getContext().getContentResolver(),
@@ -1447,8 +1448,8 @@
//normally called on the toggle click
if (!mButtonDataRoam.isChecked()) {
- PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(mSubId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(
+ mSubId);
if (carrierConfig != null && carrierConfig.getBoolean(
CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL)) {
mTelephonyManager.setDataRoamingEnabled(true);
@@ -1894,8 +1895,7 @@
return;
}
- PersistableBundle carrierConfig = PhoneGlobals.getInstance()
- .getCarrierConfigForSubId(mSubId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
if ((mImsMgr == null
|| !mImsMgr.isVolteEnabledByPlatform()
@@ -1918,8 +1918,7 @@
return;
}
- PersistableBundle carrierConfig = PhoneGlobals.getInstance()
- .getCarrierConfigForSubId(mSubId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
if (mImsMgr != null
&& mImsMgr.isVtEnabledByPlatform()
@@ -1943,6 +1942,22 @@
}
}
+ private void updatePreferredNetworkType() {
+ boolean enabled = mTelephonyManager.getCallState(
+ mSubId) == TelephonyManager.CALL_STATE_IDLE
+ && hasActiveSubscriptions();
+ Log.i(LOG_TAG, "updatePreferredNetworkType: " + enabled);
+ // TODO: Disentangle enabled networks vs preferred network mode, it looks like
+ // both buttons are shown to the user as "Preferred network type" and the options change
+ // based on what looks like World mode.
+ if (mButtonEnabledNetworks != null) {
+ mButtonEnabledNetworks.setEnabled(enabled);
+ }
+ if (mButtonPreferredNetworkMode != null) {
+ mButtonPreferredNetworkMode.setEnabled(enabled);
+ }
+ }
+
private void updateCallingCategory() {
if (mCallingCategory == null) {
return;
diff --git a/src/com/android/phone/NetworkOperatorPreference.java b/src/com/android/phone/NetworkOperatorPreference.java
index e582924..09c494c 100644
--- a/src/com/android/phone/NetworkOperatorPreference.java
+++ b/src/com/android/phone/NetworkOperatorPreference.java
@@ -43,16 +43,18 @@
private CellInfo mCellInfo;
private List<String> mForbiddenPlmns;
private int mLevel = -1;
+ private boolean mShow4GForLTE;
// The following constants are used to draw signal icon.
private static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
private static final int NO_CELL_DATA_CONNECTED_ICON = 0;
public NetworkOperatorPreference(
- CellInfo cellinfo, Context context, List<String> forbiddenPlmns) {
+ CellInfo cellinfo, Context context, List<String> forbiddenPlmns, boolean show4GForLTE) {
super(context);
mCellInfo = cellinfo;
mForbiddenPlmns = forbiddenPlmns;
+ mShow4GForLTE = show4GForLTE;
refresh();
}
@@ -85,13 +87,15 @@
updateIcon(level);
}
- private static int getIconIdForCell(CellInfo ci) {
+ private int getIconIdForCell(CellInfo ci) {
final int type = ci.getCellIdentity().getType();
switch (type) {
case CellInfo.TYPE_GSM: return R.drawable.signal_strength_g;
case CellInfo.TYPE_WCDMA: // fall through
case CellInfo.TYPE_TDSCDMA: return R.drawable.signal_strength_3g;
- case CellInfo.TYPE_LTE: return R.drawable.signal_strength_lte;
+ case CellInfo.TYPE_LTE:
+ return mShow4GForLTE
+ ? R.drawable.signal_strength_4g : R.drawable.signal_strength_lte;
case CellInfo.TYPE_CDMA: return R.drawable.signal_strength_1x;
default: return 0;
}
diff --git a/src/com/android/phone/NetworkOperators.java b/src/com/android/phone/NetworkOperators.java
index 78a2169..34c92c0 100644
--- a/src/com/android/phone/NetworkOperators.java
+++ b/src/com/android/phone/NetworkOperators.java
@@ -29,6 +29,7 @@
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
+import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -193,25 +194,12 @@
// Used by both mAutoSelect and mNetworkSelect buttons.
protected void displayNetworkSelectionFailed() {
- String status = getContext().getResources().getString(R.string.connect_later);
-
- final PhoneGlobals app = PhoneGlobals.getInstance();
- app.notificationMgr.postTransientNotification(
- NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
-
- ServiceState ss = mTelephonyManager.getServiceStateForSubscriber(mSubId);
- if (ss != null) {
- app.notificationMgr.updateNetworkSelection(ss.getState(), mSubId);
- }
+ Toast.makeText(getContext(), R.string.connect_later, Toast.LENGTH_LONG).show();
}
// Used by both mAutoSelect and mNetworkSelect buttons.
protected void displayNetworkSelectionSucceeded() {
- String status = getContext().getResources().getString(R.string.registration_done);
-
- final PhoneGlobals app = PhoneGlobals.getInstance();
- app.notificationMgr.postTransientNotification(
- NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
+ Toast.makeText(getContext(), R.string.registration_done, Toast.LENGTH_LONG).show();
}
private void selectNetworkAutomatic(boolean autoSelect) {
diff --git a/src/com/android/phone/NetworkSelectListPreference.java b/src/com/android/phone/NetworkSelectListPreference.java
index 8d947f7..a2a1080 100644
--- a/src/com/android/phone/NetworkSelectListPreference.java
+++ b/src/com/android/phone/NetworkSelectListPreference.java
@@ -404,8 +404,7 @@
final OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo);
if (DBG) logd("manually selected network: " + operatorInfo.toString());
boolean isSuccessed = mTelephonyManager.setNetworkSelectionModeManual(
- operatorInfo.getOperatorNumeric(), true /* persistSelection */);
- int mode = mTelephonyManager.getNetworkSelectionMode();
+ operatorInfo, true /* persistSelection */);
Message msg = mHandler.obtainMessage(EVENT_MANUALLY_NETWORK_SELECTION_DONE);
msg.obj = isSuccessed;
msg.sendToTarget();
diff --git a/src/com/android/phone/NetworkSelectSetting.java b/src/com/android/phone/NetworkSelectSetting.java
index 6aaaf9d..81e545e 100644
--- a/src/com/android/phone/NetworkSelectSetting.java
+++ b/src/com/android/phone/NetworkSelectSetting.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -84,6 +85,7 @@
private NetworkOperatorPreference mSelectedNetworkOperatorPreference;
private TelephonyManager mTelephonyManager;
private List<String> mForbiddenPlmns;
+ private boolean mShow4GForLTE;
private final Runnable mUpdateNetworkOperatorsRunnable = () -> {
updateNetworkOperatorsPreferenceCategory();
@@ -116,6 +118,15 @@
mStatusMessagePreference = new Preference(getContext());
mSelectedNetworkOperatorPreference = null;
mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
+ try {
+ Context con = getActivity().createPackageContext("com.android.systemui", 0);
+ int id = con.getResources().getIdentifier("config_show4GForLTE",
+ "bool", "com.android.systemui");
+ mShow4GForLTE = con.getResources().getBoolean(id);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "NameNotFoundException for show4GFotLTE");
+ mShow4GForLTE = false;
+ }
setRetainInstance(true);
}
@@ -218,7 +229,7 @@
ThreadUtils.postOnBackgroundThread(() -> {
Message msg = mHandler.obtainMessage(EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE);
msg.obj = mTelephonyManager.setNetworkSelectionModeManual(
- operatorInfo.getOperatorNumeric(), true /* persistSelection */);
+ operatorInfo, true /* persistSelection */);
msg.sendToTarget();
});
@@ -269,7 +280,6 @@
mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
} else {
if (DBG) logd("manual network selection: failed! ");
- updateNetworkSelection();
// Set summary as "Couldn't connect" to the selected network.
mSelectedNetworkOperatorPreference.setSummary(
R.string.network_could_not_connect);
@@ -394,7 +404,7 @@
for (int index = 0; index < mCellInfoList.size(); index++) {
if (!mCellInfoList.get(index).isRegistered()) {
NetworkOperatorPreference pref = new NetworkOperatorPreference(
- mCellInfoList.get(index), getContext(), mForbiddenPlmns);
+ mCellInfoList.get(index), getContext(), mForbiddenPlmns, mShow4GForLTE);
pref.setKey(CellInfoUtil.getNetworkTitle(mCellInfoList.get(index)));
pref.setOrder(index);
mNetworkOperatorsPreferences.addPreference(pref);
@@ -432,8 +442,8 @@
CellInfo cellInfo = CellInfoUtil.wrapCellInfoWithCellIdentity(cellIdentity);
if (cellInfo != null) {
if (DBG) logd("Currently registered cell: " + cellInfo.toString());
- NetworkOperatorPreference pref =
- new NetworkOperatorPreference(cellInfo, getContext(), mForbiddenPlmns);
+ NetworkOperatorPreference pref = new NetworkOperatorPreference(
+ cellInfo, getContext(), mForbiddenPlmns, mShow4GForLTE);
pref.setTitle(mTelephonyManager.getNetworkOperatorName());
pref.setSummary(R.string.network_connected);
// Update the signal strength icon, since the default signalStrength value would be
@@ -513,8 +523,8 @@
if (DBG) logd("addConnectedNetworkOperatorPreference");
// Remove the current ConnectedNetworkOperatorsPreference
removeConnectedNetworkOperatorPreference();
- final NetworkOperatorPreference pref =
- new NetworkOperatorPreference(cellInfo, getContext(), mForbiddenPlmns);
+ final NetworkOperatorPreference pref = new NetworkOperatorPreference(
+ cellInfo, getContext(), mForbiddenPlmns, mShow4GForLTE);
pref.setSummary(R.string.network_connected);
mConnectedNetworkOperatorsPreference.addPreference(pref);
PreferenceScreen preferenceScreen = getPreferenceScreen();
@@ -618,21 +628,6 @@
}
}
- /**
- * Call {@link NotificationMgr#updateNetworkSelection(int, int)} to send notification about
- * no service of user selected operator
- */
- private void updateNetworkSelection() {
- if (DBG) logd("Update notification about no service of user selected operator");
- final PhoneGlobals app = PhoneGlobals.getInstance();
- if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
- ServiceState ss = mTelephonyManager.getServiceState();
- if (ss != null) {
- app.notificationMgr.updateNetworkSelection(ss.getState(), mSubId);
- }
- }
- }
-
private void stopNetworkQuery() {
// Stop the network query process
try {
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 1b2d49e..5aa87d0 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -569,6 +569,9 @@
private void showNetworkSelection(String operator, int subId) {
if (DBG) log("showNetworkSelection(" + operator + ")...");
+ if (!TextUtils.isEmpty(operator)) {
+ operator = String.format(" (%s)", operator);
+ }
Notification.Builder builder = new Notification.Builder(mContext)
.setSmallIcon(android.R.drawable.stat_sys_warning)
.setContentTitle(mContext.getString(R.string.notification_network_selection_title))
diff --git a/src/com/android/phone/PhoneApp.java b/src/com/android/phone/PhoneApp.java
index f16d8ce..df151bf 100644
--- a/src/com/android/phone/PhoneApp.java
+++ b/src/com/android/phone/PhoneApp.java
@@ -19,14 +19,13 @@
import android.app.Application;
import android.os.UserHandle;
-import com.android.services.telephony.TelephonyGlobals;
+import com.android.services.telephony.TelecomAccountRegistry;
/**
* Top-level Application class for the Phone app.
*/
public class PhoneApp extends Application {
PhoneGlobals mPhoneGlobals;
- TelephonyGlobals mTelephonyGlobals;
public PhoneApp() {
}
@@ -39,8 +38,7 @@
mPhoneGlobals = new PhoneGlobals(this);
mPhoneGlobals.onCreate();
- mTelephonyGlobals = new TelephonyGlobals(this);
- mTelephonyGlobals.onCreate();
+ TelecomAccountRegistry.getInstance(this).setupOnBoot();
}
}
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index b1e061f..57b2906 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -41,6 +41,7 @@
import android.os.UserManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
@@ -62,6 +63,7 @@
import com.android.internal.telephony.dataconnection.DataConnectionReasons;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.anas.AlternativeNetworkAccessService;
import com.android.phone.common.CallLogAsync;
import com.android.phone.settings.SettingsConstants;
import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
@@ -333,6 +335,7 @@
phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
configLoader = CarrierConfigLoader.init(this);
+ AlternativeNetworkAccessService.initInstance(this);
// Create the CallNotifier singleton, which handles
// asynchronous events from the telephony layer (like
@@ -665,7 +668,9 @@
if (isAirplaneNewlyOn) {
// If we are trying to turn off the radio, make sure there are no active
// emergency calls. If there are, switch airplane mode back to off.
- if (PhoneUtils.isInEmergencyCall(mCM)) {
+ TelecomManager tm = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
+
+ if (tm != null && tm.isInEmergencyCall()) {
// Switch airplane mode back to off.
ConnectivityManager.from(this).setAirplaneMode(false);
Toast.makeText(this, R.string.radio_off_during_emergency_call, Toast.LENGTH_LONG)
@@ -862,6 +867,20 @@
}
/**
+ * Called when the network selection on the subscription {@code subId} is changed by the user.
+ *
+ * @param subId the subscription id.
+ */
+ public void onNetworkSelectionChanged(int subId) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ notificationMgr.updateNetworkSelection(phone.getServiceState().getState(), subId);
+ } else {
+ Log.w(LOG_TAG, "onNetworkSelectionChanged on null phone, subId: " + subId);
+ }
+ }
+
+ /**
* Dismisses the message waiting (voicemail) indicator.
*
* @param subId the subscription id we should dismiss the notification for.
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 3b4e0bb..69dd77b 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -57,6 +57,7 @@
import android.telephony.CellInfo;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoWcdma;
+import android.telephony.CellLocation;
import android.telephony.ClientRequestStats;
import android.telephony.IccOpenLogicalChannelResponse;
import android.telephony.LocationAccessPolicy;
@@ -75,6 +76,8 @@
import android.telephony.UiccSlotInfo;
import android.telephony.UssdResponse;
import android.telephony.VisualVoicemailSmsFilterSettings;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.aidl.IImsConfig;
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
@@ -82,7 +85,6 @@
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -110,6 +112,8 @@
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SmsApplication;
+import com.android.internal.telephony.SmsApplication.SmsApplicationData;
import com.android.internal.telephony.SubscriptionController;
import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.euicc.EuiccConnector;
@@ -133,6 +137,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -148,8 +153,6 @@
// Message codes used with mMainThreadHandler
private static final int CMD_HANDLE_PIN_MMI = 1;
- private static final int CMD_ANSWER_RINGING_CALL = 4;
- private static final int CMD_END_CALL = 5; // not used yet
private static final int CMD_TRANSMIT_APDU_LOGICAL_CHANNEL = 7;
private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 8;
private static final int CMD_OPEN_CHANNEL = 9;
@@ -203,6 +206,10 @@
private static final int EVENT_SET_CDMA_ROAMING_MODE_DONE = 57;
private static final int CMD_SET_CDMA_SUBSCRIPTION_MODE = 58;
private static final int EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE = 59;
+ private static final int CMD_GET_ALL_CELL_INFO = 60;
+ private static final int EVENT_GET_ALL_CELL_INFO_DONE = 61;
+ private static final int CMD_GET_CELL_LOCATION = 62;
+ private static final int EVENT_GET_CELL_LOCATION_DONE = 63;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -376,41 +383,6 @@
break;
}
- case CMD_ANSWER_RINGING_CALL:
- request = (MainThreadRequest) msg.obj;
- int answer_subId = request.subId;
- answerRingingCallInternal(answer_subId);
- request.result = ""; // dummy result for notifying the waiting thread
- // Wake up the requesting thread
- notifyRequester(request);
- break;
-
- case CMD_END_CALL:
- request = (MainThreadRequest) msg.obj;
- int end_subId = request.subId;
- final boolean hungUp;
- Phone phone = getPhone(end_subId);
- if (phone == null) {
- if (DBG) log("CMD_END_CALL: no phone for id: " + end_subId);
- break;
- }
- int phoneType = phone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- // CDMA: If the user presses the Power button we treat it as
- // ending the complete call session
- hungUp = PhoneUtils.hangupRingingAndActive(getPhone(end_subId));
- } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- // GSM: End the call as per the Phone state
- hungUp = PhoneUtils.hangup(mCM);
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
- request.result = hungUp;
- // Wake up the requesting thread
- notifyRequester(request);
- break;
-
case CMD_TRANSMIT_APDU_LOGICAL_CHANNEL:
request = (MainThreadRequest) msg.obj;
iccArgument = (IccAPDUArgument) request.argument;
@@ -795,7 +767,16 @@
break;
case EVENT_SET_NETWORK_SELECTION_MODE_MANUAL_DONE:
- handleNullReturnEvent(msg, "setNetworkSelectionModeManual");
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result = true;
+ } else {
+ request.result = false;
+ loge("setNetworkSelectionModeManual " + ar.exception);
+ }
+ notifyRequester(request);
+ mApp.onNetworkSelectionChanged(request.subId);
break;
case CMD_GET_MODEM_ACTIVITY_INFO:
@@ -1002,6 +983,47 @@
notifyRequester(request);
break;
+ case CMD_GET_ALL_CELL_INFO:
+ request = (MainThreadRequest) msg.obj;
+ Pair<Phone, WorkSource> args = (Pair<Phone, WorkSource>) request.argument;
+ onCompleted = obtainMessage(EVENT_GET_ALL_CELL_INFO_DONE, request);
+ ((Phone) args.first).getAllCellInfo(args.second, onCompleted);
+ break;
+
+ case EVENT_GET_ALL_CELL_INFO_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ request.result = (ar.exception == null) ? ar.result : new ArrayList<CellInfo>();
+ synchronized (request) {
+ request.notifyAll();
+ }
+ break;
+
+ case CMD_GET_CELL_LOCATION: {
+ request = (MainThreadRequest) msg.obj;
+ WorkSource ws = (WorkSource) request.argument;
+ Phone phone = getPhoneFromRequest(request);
+ phone.getCellLocation(ws, obtainMessage(EVENT_GET_CELL_LOCATION_DONE, request));
+ break;
+ }
+
+ case EVENT_GET_CELL_LOCATION_DONE: {
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception == null) {
+ request.result = ar.result;
+ } else {
+ Phone phone = getPhoneFromRequest(request);
+ request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)
+ ? new CdmaCellLocation() : new GsmCellLocation();
+ }
+
+ synchronized (request) {
+ request.notifyAll();
+ }
+ break;
+ }
+
default:
Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
break;
@@ -1159,9 +1181,6 @@
private Phone getPhone(int subId) {
return PhoneFactory.getPhone(mSubscriptionController.getPhoneId(subId));
}
- //
- // Implementation of the ITelephony interface.
- //
public void dial(String number) {
dialForSubscriber(getPreferredVoiceSubscription(), number);
@@ -1244,7 +1263,7 @@
* @return true is a call was ended
*/
public boolean endCall() {
- return endCallForSubscriber(getDefaultSubscription());
+ return false;
}
/**
@@ -1252,76 +1271,15 @@
* @return true is a call was ended
*/
public boolean endCallForSubscriber(int subId) {
- if (mApp.checkCallingOrSelfPermission(permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED) {
- Log.i(LOG_TAG, "endCall: called without modify phone state.");
- EventLog.writeEvent(0x534e4554, "67862398", -1, "");
- throw new SecurityException("MODIFY_PHONE_STATE permission required.");
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- return (Boolean) sendRequest(CMD_END_CALL, null, new Integer(subId));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ return false;
}
public void answerRingingCall() {
- answerRingingCallForSubscriber(getDefaultSubscription());
+ // Deprecated.
}
public void answerRingingCallForSubscriber(int subId) {
- if (DBG) log("answerRingingCall...");
- // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
- // but that can probably wait till the big TelephonyManager API overhaul.
- // For now, protect this call with the MODIFY_PHONE_STATE permission.
- enforceModifyPermission();
-
- final long identity = Binder.clearCallingIdentity();
- try {
- sendRequest(CMD_ANSWER_RINGING_CALL, null, new Integer(subId));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- /**
- * Make the actual telephony calls to implement answerRingingCall().
- * This should only be called from the main thread of the Phone app.
- * @see #answerRingingCall
- *
- * TODO: it would be nice to return true if we answered the call, or
- * false if there wasn't actually a ringing incoming call, or some
- * other error occurred. (In other words, pass back the return value
- * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
- * But that would require calling this method via sendRequest() rather
- * than sendRequestAsync(), and right now we don't actually *need* that
- * return value, so let's just return void for now.
- */
- private void answerRingingCallInternal(int subId) {
- final boolean hasRingingCall = !getPhone(subId).getRingingCall().isIdle();
- if (hasRingingCall) {
- final boolean hasActiveCall = !getPhone(subId).getForegroundCall().isIdle();
- final boolean hasHoldingCall = !getPhone(subId).getBackgroundCall().isIdle();
- if (hasActiveCall && hasHoldingCall) {
- // Both lines are in use!
- // TODO: provide a flag to let the caller specify what
- // policy to use if both lines are in use. (The current
- // behavior is hardwired to "answer incoming, end ongoing",
- // which is how the CALL button is specced to behave.)
- PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
- return;
- } else {
- // answerCall() will automatically hold the current active
- // call, if there is one.
- PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
- return;
- }
- } else {
- // No call was ringing.
- return;
- }
+ // Deprecated
}
/**
@@ -1876,12 +1834,9 @@
try {
if (DBG_LOC) log("getCellLocation: is active user");
Bundle data = new Bundle();
- Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());
- if (phone == null) {
- return null;
- }
-
- phone.getCellLocation(workSource).fillInNotifierBundle(data);
+ int subId = mSubscriptionController.getDefaultDataSubId();
+ CellLocation cl = (CellLocation) sendRequest(CMD_GET_CELL_LOCATION, workSource, subId);
+ cl.fillInNotifierBundle(data);
return data;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -2009,7 +1964,9 @@
try {
List<CellInfo> cellInfos = new ArrayList<CellInfo>();
for (Phone phone : PhoneFactory.getPhones()) {
- final List<CellInfo> info = phone.getAllCellInfo(workSource);
+ final List<CellInfo> info = (List<CellInfo>) sendRequest(
+ CMD_GET_ALL_CELL_INFO,
+ new Pair<Phone, WorkSource>(phone, workSource));
if (info != null) cellInfos.addAll(info);
}
return cellInfos;
@@ -2640,6 +2597,10 @@
@Override
public int getNetworkSelectionMode(int subId) {
+ if (!isActiveSubscription(subId)) {
+ return TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
+ }
+
return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
}
@@ -2801,6 +2762,10 @@
return mSubscriptionController.getDefaultVoiceSubId();
}
+ private boolean isActiveSubscription(int subId) {
+ return mSubscriptionController.isActiveSubId(subId);
+ }
+
/**
* @see android.telephony.TelephonyManager.WifiCallingChoices
*/
@@ -3328,6 +3293,10 @@
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "setNetworkSelectionModeAutomatic");
+ if (!isActiveSubscription(subId)) {
+ return;
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
@@ -3337,24 +3306,35 @@
}
}
- /**
- * Set the network selection mode to manual with the selected carrier.
+ /**
+ * Ask the radio to connect to the input network and change selection mode to manual.
+ *
+ * @param subId the id of the subscription.
+ * @param operatorInfo the operator information, included the PLMN, long name and short name of
+ * the operator to attach to.
+ * @param persistSelection whether the selection will persist until reboot. If true, only allows
+ * attaching to the selected PLMN until reboot; otherwise, attach to the chosen PLMN and resume
+ * normal network selection next time.
+ * @return {@code true} on success; {@code true} on any failure.
*/
@Override
- public boolean setNetworkSelectionModeManual(int subId, String operatorNumeric,
- boolean persistSelection) {
+ public boolean setNetworkSelectionModeManual(
+ int subId, OperatorInfo operatorInfo, boolean persistSelection) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "setNetworkSelectionModeManual");
+ if (!isActiveSubscription(subId)) {
+ return false;
+ }
+
final long identity = Binder.clearCallingIdentity();
try {
- OperatorInfo operator = new OperatorInfo(
- /* operatorAlphaLong */ "",
- /* operatorAlphaShort */ "",
- operatorNumeric);
- if (DBG) log("setNetworkSelectionModeManual: subId:" + subId + " operator:" + operator);
- ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operator,
+ ManualNetworkSelectionArgument arg = new ManualNetworkSelectionArgument(operatorInfo,
persistSelection);
+ if (DBG) {
+ log("setNetworkSelectionModeManual: subId: " + subId
+ + " operator: " + operatorInfo);
+ }
return (Boolean) sendRequest(CMD_SET_NETWORK_SELECTION_MODE_MANUAL, arg, subId);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -5357,4 +5337,73 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ private void ensureUserRunning(int userId) {
+ if (!mUserManager.isUserRunning(userId)) {
+ throw new IllegalStateException("User " + userId + " does not exist or not running");
+ }
+ }
+
+ /**
+ * Returns a list of SMS apps on a given user.
+ *
+ * Only the shell user (UID 2000 or 0) can call it.
+ * Target user must be running.
+ */
+ @Override
+ public String[] getSmsApps(int userId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getSmsApps");
+ ensureUserRunning(userId);
+
+ final Collection<SmsApplicationData> apps =
+ SmsApplication.getApplicationCollectionAsUser(mApp, userId);
+
+ String[] ret = new String[apps.size()];
+ int i = 0;
+ for (SmsApplicationData app : apps) {
+ ret[i++] = app.mPackageName;
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the default SMS app package name on a given user.
+ *
+ * Only the shell user (UID 2000 or 0) can call it.
+ * Target user must be running.
+ */
+ @Override
+ public String getDefaultSmsApp(int userId) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDefaultSmsApp");
+ ensureUserRunning(userId);
+
+ final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(mApp,
+ /* updateIfNeeded= */ true, userId);
+ return cn == null ? null : cn.getPackageName();
+ }
+
+ /**
+ * Set a package as the default SMS app on a given user.
+ *
+ * Only the shell user (UID 2000 or 0) can call it.
+ * Target user must be running.
+ */
+ @Override
+ public void setDefaultSmsApp(int userId, String packageName) {
+ TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDefaultSmsApp");
+ ensureUserRunning(userId);
+
+ boolean found = false;
+ for (String pkg : getSmsApps(userId)) {
+ if (TextUtils.equals(packageName, pkg)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new IllegalArgumentException("Package " + packageName + " is not an SMS app");
+ }
+
+ SmsApplication.setDefaultApplicationAsUser(packageName, mApp, userId);
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 712b8cb..9ede914 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -19,7 +19,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
-import android.bluetooth.IBluetoothHeadsetPhone;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -30,7 +29,6 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.VideoProfile;
@@ -106,9 +104,6 @@
/** Phone state changed event*/
private static final int PHONE_STATE_CHANGED = -1;
- /** check status then decide whether answerCall */
- private static final int MSG_CHECK_STATUS_ANSWERCALL = 100;
-
/** poll phone DISCONNECTING status interval */
private static final int DISCONNECTING_POLLING_INTERVAL_MS = 200;
@@ -125,15 +120,6 @@
*/
private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
- private static class FgRingCalls {
- private Call fgCall;
- private Call ringing;
- public FgRingCalls(Call fg, Call ring) {
- fgCall = fg;
- ringing = ring;
- }
- }
-
/** USSD information used to aggregate all USSD messages */
private static AlertDialog sUssdDialog = null;
private static StringBuilder sUssdMsg = new StringBuilder();
@@ -147,34 +133,6 @@
* Mute settings for each connection as needed.
*/
private static class ConnectionHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_CHECK_STATUS_ANSWERCALL:
- FgRingCalls frC = (FgRingCalls) msg.obj;
- // wait for finishing disconnecting
- // before check the ringing call state
- if ((frC.fgCall != null) &&
- (frC.fgCall.getState() == Call.State.DISCONNECTING) &&
- (msg.arg1 < DISCONNECTING_POLLING_TIMES_LIMIT)) {
- Message retryMsg =
- mConnectionHandler.obtainMessage(MSG_CHECK_STATUS_ANSWERCALL);
- retryMsg.arg1 = 1 + msg.arg1;
- retryMsg.obj = msg.obj;
- mConnectionHandler.sendMessageDelayed(retryMsg,
- DISCONNECTING_POLLING_INTERVAL_MS);
- // since hangupActiveCall() also accepts the ringing call
- // check if the ringing call was already answered or not
- // only answer it when the call still is ringing
- } else if (frC.ringing.isRinging()) {
- if (msg.arg1 == DISCONNECTING_POLLING_TIMES_LIMIT) {
- Log.e(LOG_TAG, "DISCONNECTING time out");
- }
- answerCall(frC.ringing);
- }
- break;
- }
- }
}
/**
@@ -195,294 +153,6 @@
}
/**
- * Answer the currently-ringing call.
- *
- * @return true if we answered the call, or false if there wasn't
- * actually a ringing incoming call, or some other error occurred.
- *
- * @see #answerAndEndHolding(CallManager, Call)
- * @see #answerAndEndActive(CallManager, Call)
- */
- /* package */ static boolean answerCall(Call ringingCall) {
- log("answerCall(" + ringingCall + ")...");
- final PhoneGlobals app = PhoneGlobals.getInstance();
- final CallNotifier notifier = app.notifier;
-
- final Phone phone = ringingCall.getPhone();
- final boolean phoneIsCdma = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA);
- boolean answered = false;
- IBluetoothHeadsetPhone btPhone = null;
-
- if (phoneIsCdma) {
- // Stop any signalInfo tone being played when a Call waiting gets answered
- if (ringingCall.getState() == Call.State.WAITING) {
- notifier.stopSignalInfoTone();
- }
- }
-
- if (ringingCall != null && ringingCall.isRinging()) {
- if (DBG) log("answerCall: call state = " + ringingCall.getState());
- try {
- if (phoneIsCdma) {
- if (app.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.IDLE) {
- // This is the FIRST incoming call being answered.
- // Set the Phone Call State to SINGLE_ACTIVE
- app.cdmaPhoneCallState.setCurrentCallState(
- CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE);
- } else {
- // This is the CALL WAITING call being answered.
- // Set the Phone Call State to CONF_CALL
- app.cdmaPhoneCallState.setCurrentCallState(
- CdmaPhoneCallState.PhoneCallState.CONF_CALL);
- // Enable "Add Call" option after answering a Call Waiting as the user
- // should be allowed to add another call in case one of the parties
- // drops off
- app.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true);
- }
- }
-
- final boolean isRealIncomingCall = isRealIncomingCall(ringingCall.getState());
-
- //if (DBG) log("sPhone.acceptCall");
- app.mCM.acceptCall(ringingCall);
- answered = true;
-
- setAudioMode();
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "answerCall: caught " + ex, ex);
-
- if (phoneIsCdma) {
- // restore the cdmaPhoneCallState and btPhone.cdmaSetSecondCallState:
- app.cdmaPhoneCallState.setCurrentCallState(
- app.cdmaPhoneCallState.getPreviousCallState());
- if (btPhone != null) {
- try {
- btPhone.cdmaSetSecondCallState(false);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
- }
- }
- }
- }
- }
- return answered;
- }
-
- /**
- * Hangs up all active calls.
- */
- static void hangupAllCalls(CallManager cm) {
- final Call ringing = cm.getFirstActiveRingingCall();
- final Call fg = cm.getActiveFgCall();
- final Call bg = cm.getFirstActiveBgCall();
-
- // We go in reverse order, BG->FG->RINGING because hanging up a ringing call or an active
- // call can move a bg call to a fg call which would force us to loop over each call
- // several times. This ordering works best to ensure we dont have any more calls.
- if (bg != null && !bg.isIdle()) {
- hangup(bg);
- }
- if (fg != null && !fg.isIdle()) {
- hangup(fg);
- }
- if (ringing != null && !ringing.isIdle()) {
- hangupRingingCall(fg);
- }
- }
-
- /**
- * Smart "hang up" helper method which hangs up exactly one connection,
- * based on the current Phone state, as follows:
- * <ul>
- * <li>If there's a ringing call, hang that up.
- * <li>Else if there's a foreground call, hang that up.
- * <li>Else if there's a background call, hang that up.
- * <li>Otherwise do nothing.
- * </ul>
- * @return true if we successfully hung up, or false
- * if there were no active calls at all.
- */
- static boolean hangup(CallManager cm) {
- boolean hungup = false;
- Call ringing = cm.getFirstActiveRingingCall();
- Call fg = cm.getActiveFgCall();
- Call bg = cm.getFirstActiveBgCall();
-
- if (!ringing.isIdle()) {
- log("hangup(): hanging up ringing call");
- hungup = hangupRingingCall(ringing);
- } else if (!fg.isIdle()) {
- log("hangup(): hanging up foreground call");
- hungup = hangup(fg);
- } else if (!bg.isIdle()) {
- log("hangup(): hanging up background call");
- hungup = hangup(bg);
- } else {
- // No call to hang up! This is unlikely in normal usage,
- // since the UI shouldn't be providing an "End call" button in
- // the first place. (But it *can* happen, rarely, if an
- // active call happens to disconnect on its own right when the
- // user is trying to hang up..)
- log("hangup(): no active call to hang up");
- }
- if (DBG) log("==> hungup = " + hungup);
-
- return hungup;
- }
-
- static boolean hangupRingingCall(Call ringing) {
- if (DBG) log("hangup ringing call");
- int phoneType = ringing.getPhone().getPhoneType();
- Call.State state = ringing.getState();
-
- if (state == Call.State.INCOMING) {
- // Regular incoming call (with no other active calls)
- log("hangupRingingCall(): regular incoming call: hangup()");
- return hangup(ringing);
- } else {
- // Unexpected state: the ringing call isn't INCOMING or
- // WAITING, so there's no reason to have called
- // hangupRingingCall() in the first place.
- // (Presumably the incoming call went away at the exact moment
- // we got here, so just do nothing.)
- Log.w(LOG_TAG, "hangupRingingCall: no INCOMING or WAITING call");
- return false;
- }
- }
-
- static boolean hangupActiveCall(Call foreground) {
- if (DBG) log("hangup active call");
- return hangup(foreground);
- }
-
- static boolean hangupHoldingCall(Call background) {
- if (DBG) log("hangup holding call");
- return hangup(background);
- }
-
- /**
- * Used in CDMA phones to end the complete Call session
- * @param phone the Phone object.
- * @return true if *any* call was successfully hung up
- */
- static boolean hangupRingingAndActive(Phone phone) {
- boolean hungUpRingingCall = false;
- boolean hungUpFgCall = false;
- Call ringingCall = phone.getRingingCall();
- Call fgCall = phone.getForegroundCall();
-
- // Hang up any Ringing Call
- if (!ringingCall.isIdle()) {
- log("hangupRingingAndActive: Hang up Ringing Call");
- hungUpRingingCall = hangupRingingCall(ringingCall);
- }
-
- // Hang up any Active Call
- if (!fgCall.isIdle()) {
- log("hangupRingingAndActive: Hang up Foreground Call");
- hungUpFgCall = hangupActiveCall(fgCall);
- }
-
- return hungUpRingingCall || hungUpFgCall;
- }
-
- /**
- * Trivial wrapper around Call.hangup(), except that we return a
- * boolean success code rather than throwing CallStateException on
- * failure.
- *
- * @return true if the call was successfully hung up, or false
- * if the call wasn't actually active.
- */
- static boolean hangup(Call call) {
- try {
- CallManager cm = PhoneGlobals.getInstance().mCM;
-
- if (call.getState() == Call.State.ACTIVE && cm.hasActiveBgCall()) {
- // handle foreground call hangup while there is background call
- log("- hangup(Call): hangupForegroundResumeBackground...");
- cm.hangupForegroundResumeBackground(cm.getFirstActiveBgCall());
- } else {
- log("- hangup(Call): regular hangup()...");
- call.hangup();
- }
- return true;
- } catch (CallStateException ex) {
- Log.e(LOG_TAG, "Call hangup: caught " + ex, ex);
- }
-
- return false;
- }
-
- /**
- * Trivial wrapper around Connection.hangup(), except that we silently
- * do nothing (rather than throwing CallStateException) if the
- * connection wasn't actually active.
- */
- static void hangup(Connection c) {
- try {
- if (c != null) {
- c.hangup();
- }
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "Connection hangup: caught " + ex, ex);
- }
- }
-
- static boolean answerAndEndHolding(CallManager cm, Call ringing) {
- if (DBG) log("end holding & answer waiting: 1");
- if (!hangupHoldingCall(cm.getFirstActiveBgCall())) {
- Log.e(LOG_TAG, "end holding failed!");
- return false;
- }
-
- if (DBG) log("end holding & answer waiting: 2");
- return answerCall(ringing);
-
- }
-
- /**
- * Answers the incoming call specified by "ringing", and ends the currently active phone call.
- *
- * This method is useful when's there's an incoming call which we cannot manage with the
- * current call. e.g. when you are having a phone call with CDMA network and has received
- * a SIP call, then we won't expect our telephony can manage those phone calls simultaneously.
- * Note that some types of network may allow multiple phone calls at once; GSM allows to hold
- * an ongoing phone call, so we don't need to end the active call. The caller of this method
- * needs to check if the network allows multiple phone calls or not.
- *
- * @see #answerCall(Call)
- * @see InCallScreen#internalAnswerCall()
- */
- /* package */ static boolean answerAndEndActive(CallManager cm, Call ringing) {
- if (DBG) log("answerAndEndActive()...");
-
- // Unlike the answerCall() method, we *don't* need to stop the
- // ringer or change audio modes here since the user is already
- // in-call, which means that the audio mode is already set
- // correctly, and that we wouldn't have started the ringer in the
- // first place.
-
- // hanging up the active call also accepts the waiting call
- // while active call and waiting call are from the same phone
- // i.e. both from GSM phone
- Call fgCall = cm.getActiveFgCall();
- if (!hangupActiveCall(fgCall)) {
- Log.w(LOG_TAG, "end active call failed!");
- return false;
- }
-
- mConnectionHandler.removeMessages(MSG_CHECK_STATUS_ANSWERCALL);
- Message msg = mConnectionHandler.obtainMessage(MSG_CHECK_STATUS_ANSWERCALL);
- msg.arg1 = 1;
- msg.obj = new FgRingCalls(fgCall, ringing);
- mConnectionHandler.sendMessage(msg);
-
- return true;
- }
-
- /**
* For a CDMA phone, advance the call state upon making a new
* outgoing call.
*
@@ -676,87 +346,6 @@
return builder.toString();
}
- /**
- * Wrapper function to control when to send an empty Flash command to the network.
- * Mainly needed for CDMA networks, such as scenarios when we need to send a blank flash
- * to the network prior to placing a 3-way call for it to be successful.
- */
- static void sendEmptyFlash(Phone phone) {
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- Call fgCall = phone.getForegroundCall();
- if (fgCall.getState() == Call.State.ACTIVE) {
- // Send the empty flash
- if (DBG) Log.d(LOG_TAG, "onReceive: (CDMA) sending empty flash to network");
- switchHoldingAndActive(phone.getBackgroundCall());
- }
- }
- }
-
- static void swap() {
- final PhoneGlobals mApp = PhoneGlobals.getInstance();
- if (!okToSwapCalls(mApp.mCM)) {
- // TODO: throw an error instead?
- return;
- }
-
- // Swap the fg and bg calls.
- // In the future we may provide some way for user to choose among
- // multiple background calls, for now, always act on the first background call.
- PhoneUtils.switchHoldingAndActive(mApp.mCM.getFirstActiveBgCall());
- }
-
- /**
- * @param heldCall is the background call want to be swapped
- */
- static void switchHoldingAndActive(Call heldCall) {
- log("switchHoldingAndActive()...");
- try {
- CallManager cm = PhoneGlobals.getInstance().mCM;
- if (heldCall.isIdle()) {
- // no heldCall, so it is to hold active call
- cm.switchHoldingAndActive(cm.getFgPhone().getBackgroundCall());
- } else {
- // has particular heldCall, so to switch
- cm.switchHoldingAndActive(heldCall);
- }
- setAudioMode(cm);
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "switchHoldingAndActive: caught " + ex, ex);
- }
- }
-
- static void mergeCalls() {
- mergeCalls(PhoneGlobals.getInstance().mCM);
- }
-
- static void mergeCalls(CallManager cm) {
- int phoneType = cm.getFgPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- log("mergeCalls(): CDMA...");
- PhoneGlobals app = PhoneGlobals.getInstance();
- if (app.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
- // Set the Phone Call State to conference
- app.cdmaPhoneCallState.setCurrentCallState(
- CdmaPhoneCallState.PhoneCallState.CONF_CALL);
-
- // Send flash cmd
- // TODO: Need to change the call from switchHoldingAndActive to
- // something meaningful as we are not actually trying to swap calls but
- // instead are merging two calls by sending a Flash command.
- log("- sending flash...");
- switchHoldingAndActive(cm.getFirstActiveBgCall());
- }
- } else {
- try {
- log("mergeCalls(): calling cm.conference()...");
- cm.conference(cm.getFirstActiveBgCall());
- } catch (CallStateException ex) {
- Log.w(LOG_TAG, "mergeCalls: caught " + ex, ex);
- }
- }
- }
-
static void separateCall(Connection c) {
try {
if (DBG) log("separateCall: " + toLogSafePhoneNumber(c.getAddress()));
@@ -1741,20 +1330,6 @@
return audioManager.isSpeakerphoneOn();
}
- static boolean isInEmergencyCall(CallManager cm) {
- Call fgCall = cm.getActiveFgCall();
- // isIdle includes checks for the DISCONNECTING/DISCONNECTED state.
- if(!fgCall.isIdle()) {
- for (Connection cn : fgCall.getConnections()) {
- if (PhoneNumberUtils.isLocalEmergencyNumber(PhoneGlobals.getInstance(),
- cn.getAddress())) {
- return true;
- }
- }
- }
- return false;
- }
-
/**
* Get the mute state of foreground phone, which has the current
* foreground call
diff --git a/src/com/android/phone/RoamingDialogFragment.java b/src/com/android/phone/RoamingDialogFragment.java
index d24967b..384a120 100644
--- a/src/com/android/phone/RoamingDialogFragment.java
+++ b/src/com/android/phone/RoamingDialogFragment.java
@@ -35,6 +35,7 @@
public static final String SUB_ID_KEY = "sub_id_key";
+ private CarrierConfigManager mCarrierConfigManager;
private int mSubId;
/**
@@ -52,6 +53,7 @@
super.onAttach(context);
Bundle args = getArguments();
mSubId = args.getInt(SUB_ID_KEY);
+ mCarrierConfigManager = new CarrierConfigManager(context);
// Verify host activity implemented callback interface
FragmentManager fragmentManager = getFragmentManager();
@@ -68,8 +70,7 @@
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
int title = R.string.roaming_alert_title;
- PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(mSubId);
+ PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
if (carrierConfig != null && carrierConfig.getBoolean(
CarrierConfigManager.KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL)) {
title = R.string.roaming_check_price_warning;
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 4acb46b..1a25ae3 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -18,6 +18,7 @@
import android.os.RemoteException;
import android.os.ShellCommand;
+import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.util.Log;
@@ -39,11 +40,16 @@
private static final int DEFAULT_PHONE_ID = 0;
private static final String IMS_SUBCOMMAND = "ims";
+ private static final String SMS_SUBCOMMAND = "sms";
private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
private static final String IMS_ENABLE = "enable";
private static final String IMS_DISABLE = "disable";
+ private static final String SMS_GET_APPS = "get-apps";
+ private static final String SMS_GET_DEFAULT_APP = "get-default-app";
+ private static final String SMS_SET_DEFAULT_APP = "set-default-app";
+
// Take advantage of existing methods that already contain permissions checks when possible.
private final ITelephony mInterface;
@@ -61,6 +67,9 @@
case IMS_SUBCOMMAND: {
return handleImsCommand();
}
+ case SMS_SUBCOMMAND: {
+ return handleSmsCommand();
+ }
default: {
return handleDefaultCommands(cmd);
}
@@ -75,7 +84,10 @@
pw.println(" Print this help text.");
pw.println(" ims");
pw.println(" IMS Commands.");
+ pw.println(" sms");
+ pw.println(" SMS Commands.");
onHelpIms();
+ onHelpSms();
}
private void onHelpIms() {
@@ -103,6 +115,17 @@
pw.println(" slot if none is specified.");
}
+ private void onHelpSms() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("SMS Commands:");
+ pw.println(" sms get-apps [--user USER_ID]");
+ pw.println(" Print all SMS apps on a user.");
+ pw.println(" sms get-default-app [--user USER_ID]");
+ pw.println(" Get the default SMS app.");
+ pw.println(" sms set-default-app [--user USER_ID] PACKAGE_NAME");
+ pw.println(" Set PACKAGE_NAME as the default SMS app.");
+ }
+
private int handleImsCommand() {
String arg = getNextArg();
if (arg == null) {
@@ -296,4 +319,85 @@
}
return slotId;
}
+
+ private int handleSmsCommand() {
+ String arg = getNextArg();
+ if (arg == null) {
+ onHelpSms();
+ return 0;
+ }
+
+ try {
+ switch (arg) {
+ case SMS_GET_APPS: {
+ return handleSmsGetApps();
+ }
+ case SMS_GET_DEFAULT_APP: {
+ return handleSmsGetDefaultApp();
+ }
+ case SMS_SET_DEFAULT_APP: {
+ return handleSmsSetDefaultApp();
+ }
+ default:
+ getErrPrintWriter().println("Unknown command " + arg);
+ }
+ } catch (RemoteException e) {
+ getErrPrintWriter().println("RemoteException: " + e.getMessage());
+ }
+
+ return -1;
+ }
+
+ private int maybeParseUserIdArg() {
+ int userId = UserHandle.USER_SYSTEM;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--user": {
+ try {
+ userId = Integer.parseInt(getNextArgRequired());
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Invalid user ID for --user");
+ return -1;
+ }
+ break;
+ }
+ }
+ }
+ return userId;
+ }
+
+ private int handleSmsGetApps() throws RemoteException {
+ final int userId = maybeParseUserIdArg();
+ if (userId < 0) {
+ return -1;
+ }
+
+ for (String packageName : mInterface.getSmsApps(userId)) {
+ getOutPrintWriter().println(packageName);
+ }
+ return 0;
+ }
+
+ private int handleSmsGetDefaultApp() throws RemoteException {
+ final int userId = maybeParseUserIdArg();
+ if (userId < 0) {
+ return -1;
+ }
+
+ getOutPrintWriter().println(mInterface.getDefaultSmsApp(userId));
+ return 0;
+ }
+
+ private int handleSmsSetDefaultApp() throws RemoteException {
+ final int userId = maybeParseUserIdArg();
+ if (userId < 0) {
+ return -1;
+ }
+
+ String packageName = getNextArgRequired();
+ mInterface.setDefaultSmsApp(userId, packageName);
+ getOutPrintWriter().println("SMS app set to " + mInterface.getDefaultSmsApp(userId));
+ return 0;
+ }
}
diff --git a/src/com/android/phone/anas/ANASNetworkScanCtlr.java b/src/com/android/phone/anas/ANASNetworkScanCtlr.java
new file mode 100644
index 0000000..6c75706
--- /dev/null
+++ b/src/com/android/phone/anas/ANASNetworkScanCtlr.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoLte;
+import android.telephony.NetworkScan;
+import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyScanManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Network Scan controller class which will scan for the specific bands as requested and
+ * provide results to caller when ready.
+ */
+public class ANASNetworkScanCtlr {
+ private static final String LOG_TAG = "ANASNetworkScanCtlr";
+ private static final boolean DBG = true;
+ private static final int SEARCH_PERIODICITY_SLOW = (int) TimeUnit.MINUTES.toSeconds(5);
+ private static final int SEARCH_PERIODICITY_FAST = (int) TimeUnit.MINUTES.toSeconds(1);
+ private static final int MAX_SEARCH_TIME = (int) TimeUnit.MINUTES.toSeconds(1);
+ private final Object mLock = new Object();
+
+ /* message to handle scan responses from modem */
+ private static final int MSG_SCAN_RESULTS_AVAILABLE = 1;
+ private static final int MSG_SCAN_COMPLETE = 2;
+ private static final int MSG_SCAN_ERROR = 3;
+
+ /* scan object to keep track of current scan request */
+ private NetworkScan mCurrentScan;
+ private boolean mIsScanActive;
+ private NetworkScanRequest mCurrentScanRequest;
+ private List<String> mMccMncs;
+ private TelephonyManager mTelephonyManager;
+ @VisibleForTesting
+ protected NetworkAvailableCallBack mNetworkAvailableCallBack;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SCAN_RESULTS_AVAILABLE:
+ logDebug("Msg received for scan results");
+ /* Todo: need to aggregate the results */
+ analyzeScanResults((List<CellInfo>) msg.obj);
+ break;
+ case MSG_SCAN_COMPLETE:
+ logDebug("Msg received for scan complete");
+ restartScan();
+ break;
+ case MSG_SCAN_ERROR:
+ logDebug("Msg received for scan error");
+ invalidateScanOnError((int) msg.obj);
+ break;
+ default:
+ log("invalid message");
+ break;
+ }
+ }
+ };
+
+ @VisibleForTesting
+ public TelephonyScanManager.NetworkScanCallback mNetworkScanCallback =
+ new TelephonyScanManager.NetworkScanCallback() {
+
+ @Override
+ public void onResults(List<CellInfo> results) {
+ logDebug("Total results :" + results.size());
+ for (CellInfo cellInfo : results) {
+ logDebug("cell info: " + cellInfo);
+ }
+
+ Message message = Message.obtain(mHandler, MSG_SCAN_RESULTS_AVAILABLE, results);
+ message.sendToTarget();
+ }
+
+ @Override
+ public void onComplete() {
+ logDebug("Scan completed!");
+ Message message = Message.obtain(mHandler, MSG_SCAN_COMPLETE, NetworkScan.SUCCESS);
+ message.sendToTarget();
+ }
+
+ @Override
+ public void onError(@NetworkScan.ScanErrorCode int error) {
+ logDebug("Scan error " + error);
+ Message message = Message.obtain(mHandler, MSG_SCAN_ERROR, error);
+ message.sendToTarget();
+ }
+ };
+
+ /**
+ * call back for network availability
+ */
+ public interface NetworkAvailableCallBack {
+
+ /**
+ * Returns the scan results to the user, this callback will be called multiple times.
+ */
+ void onNetworkAvailability(List<CellInfo> results);
+
+ /**
+ * on error
+ * @param error
+ */
+ void onError(int error);
+ }
+
+
+ /**
+ * analyze scan results
+ * @param results contains all available cells matching the scan request at current location.
+ */
+ public void analyzeScanResults(List<CellInfo> results) {
+ /* Inform registrants about availability of network */
+ if (results != null) {
+ List<CellInfo> filteredResults = new ArrayList<CellInfo>();
+ synchronized (mLock) {
+ for (CellInfo cellInfo : results) {
+ if (mMccMncs.contains(getMccMnc(cellInfo))) {
+ filteredResults.add(cellInfo);
+ }
+ }
+ }
+
+ if ((filteredResults.size() >= 1) && (mNetworkAvailableCallBack != null)) {
+ /* Todo: change to aggregate results on success. */
+ mNetworkAvailableCallBack.onNetworkAvailability(filteredResults);
+ }
+ }
+ }
+
+ private void invalidateScanOnError(int error) {
+ logDebug("scan invalidated on error");
+ if (mNetworkAvailableCallBack != null) {
+ mNetworkAvailableCallBack.onError(error);
+ }
+
+ synchronized (mLock) {
+ mIsScanActive = false;
+ mCurrentScan = null;
+ }
+ }
+
+ public ANASNetworkScanCtlr(Context c, TelephonyManager telephonyManager,
+ NetworkAvailableCallBack networkAvailableCallBack) {
+ init(c, telephonyManager, networkAvailableCallBack);
+ }
+
+ /**
+ * initialize Network Scan controller
+ * @param c context
+ * @param telephonyManager Telephony manager instance
+ * @param networkAvailableCallBack callback to be called when network selection is done
+ */
+ public void init(Context c, TelephonyManager telephonyManager,
+ NetworkAvailableCallBack networkAvailableCallBack) {
+ log("init called");
+ mTelephonyManager = telephonyManager;
+ mNetworkAvailableCallBack = networkAvailableCallBack;
+ }
+
+ /* get mcc mnc from cell info if the cell is for LTE */
+ private String getMccMnc(CellInfo cellInfo) {
+ if (cellInfo instanceof CellInfoLte) {
+ return ((CellInfoLte) cellInfo).getCellIdentity().getMccString()
+ + ((CellInfoLte) cellInfo).getCellIdentity().getMncString();
+ }
+
+ return null;
+ }
+
+ private NetworkScanRequest createNetworkScanRequest(List<SubscriptionInfo> subscriptionInfos,
+ int periodicity) {
+ RadioAccessSpecifier[] ras = new RadioAccessSpecifier[1];
+ int[] bands = new int[1];
+
+ /* hardcoding band for now, Todo b/113753823 */
+ bands[0] = AccessNetworkConstants.EutranBand.BAND_48;
+
+ ArrayList<String> mccMncs = new ArrayList<String>();
+ /* retrieve mcc mncs for a subscription id */
+ for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+ mccMncs.add(subscriptionInfo.getMccString() + subscriptionInfo.getMncString());
+ }
+
+ /* create network scan request */
+ ras[0] = new RadioAccessSpecifier(AccessNetworkConstants.AccessNetworkType.EUTRAN, bands,
+ null);
+ NetworkScanRequest networkScanRequest = new NetworkScanRequest(
+ NetworkScanRequest.SCAN_TYPE_PERIODIC, ras, periodicity, MAX_SEARCH_TIME, false,
+ NetworkScanRequest.MAX_INCREMENTAL_PERIODICITY_SEC, mccMncs);
+ synchronized (mLock) {
+ mMccMncs = mccMncs;
+ }
+ return networkScanRequest;
+ }
+
+ /**
+ * start high interval network scan
+ * @param subscriptionInfos list of subscriptions for which the scanning needs to be started.
+ * @return true if successfully accepted request.
+ */
+ public boolean startSlowNetworkScan(List<SubscriptionInfo> subscriptionInfos) {
+ NetworkScanRequest networkScanRequest = createNetworkScanRequest(subscriptionInfos,
+ SEARCH_PERIODICITY_SLOW);
+ return startNetworkScan(networkScanRequest);
+ }
+
+ /**
+ * start less interval network scan
+ * @param subscriptionInfos list of subscriptions for which the scanning needs to be started.
+ * @return true if successfully accepted request.
+ */
+ public boolean startFastNetworkScan(List<SubscriptionInfo> subscriptionInfos) {
+ NetworkScanRequest networkScanRequest = createNetworkScanRequest(subscriptionInfos,
+ SEARCH_PERIODICITY_FAST);
+ return startNetworkScan(networkScanRequest);
+ }
+
+
+ private boolean startNetworkScan(NetworkScanRequest networkScanRequest) {
+ NetworkScan networkScan;
+ synchronized (mLock) {
+ /* if the request is same as existing one, then make sure to not proceed */
+ if (mIsScanActive && mCurrentScanRequest.equals(networkScanRequest)) {
+ return true;
+ }
+
+ /* Need to stop current scan if we already have one */
+ stopNetworkScan();
+
+ /* start new scan */
+ networkScan = mTelephonyManager.requestNetworkScan(networkScanRequest,
+ mNetworkScanCallback);
+
+ mCurrentScan = networkScan;
+ mIsScanActive = true;
+ mCurrentScanRequest = networkScanRequest;
+ }
+
+ logDebug("startNetworkScan " + networkScanRequest);
+ return true;
+ }
+
+ private void restartScan() {
+ NetworkScan networkScan;
+ synchronized (mLock) {
+ if (mCurrentScanRequest != null) {
+ networkScan = mTelephonyManager.requestNetworkScan(mCurrentScanRequest,
+ mNetworkScanCallback);
+ mIsScanActive = true;
+ }
+ }
+ }
+
+ /**
+ * stop network scan
+ */
+ public void stopNetworkScan() {
+ logDebug("stopNetworkScan");
+ synchronized (mLock) {
+ if (mIsScanActive && mCurrentScan != null) {
+ try {
+ mCurrentScan.stopScan();
+ } catch (IllegalArgumentException iae) {
+ logDebug("Scan failed with exception " + iae);
+ }
+ mIsScanActive = false;
+ mCurrentScan = null;
+ mCurrentScanRequest = null;
+ }
+ }
+ }
+
+ private static void log(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ private static void logDebug(String msg) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ }
+}
diff --git a/src/com/android/phone/anas/ANASProfileSelector.java b/src/com/android/phone/anas/ANASProfileSelector.java
new file mode 100644
index 0000000..d347cba
--- /dev/null
+++ b/src/com/android/phone/anas/ANASProfileSelector.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.Rlog;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Profile selector class which will select the right profile based upon
+ * geographic information input and network scan results.
+ */
+public class ANASProfileSelector {
+ private static final String LOG_TAG = "ANASProfileSelector";
+ private static final boolean DBG = true;
+ private final Object mLock = new Object();
+
+ private static final int INVALID_SEQUENCE_ID = -1;
+ private static final int START_SEQUENCE_ID = 1;
+
+ /* message to indicate profile update */
+ private static final int MSG_PROFILE_UPDATE = 1;
+
+ /* message to indicate start of profile selection process */
+ private static final int MSG_START_PROFILE_SELECTION = 2;
+ private boolean mIsEnabled = false;
+
+ @VisibleForTesting
+ protected Context mContext;
+
+ @VisibleForTesting
+ protected TelephonyManager mTelephonyManager;
+
+ @VisibleForTesting
+ protected ANASNetworkScanCtlr mNetworkScanCtlr;
+
+ private SubscriptionManager mSubscriptionManager;
+ private ANASProfileSelectionCallback mProfileSelectionCallback;
+ private int mSequenceId;
+
+ /* monitor the subscription for registration */
+ private ANASServiceStateMonitor mRegMonitor;
+ public static final String ACTION_SUB_SWITCH =
+ "android.intent.action.SUBSCRIPTION_SWITCH_REPLY";
+
+ /* service monitor callback will get called for service state change on a particular subId. */
+ private ANASServiceStateMonitor.ANASServiceMonitorCallback mServiceMonitorCallback =
+ new ANASServiceStateMonitor.ANASServiceMonitorCallback() {
+ @Override
+ public void onServiceMonitorUpdate(int subId, int state) {
+ switch (state) {
+ case ANASServiceStateMonitor.EVALUATED_STATE_BAD:
+ switchPreferredData(subId);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ private SubscriptionManager.OnOpportunisticSubscriptionsChangedListener mProfileChangeListener =
+ new SubscriptionManager.OnOpportunisticSubscriptionsChangedListener() {
+ @Override
+ public void onOpportunisticSubscriptionsChanged() {
+ mHandler.sendEmptyMessage(MSG_PROFILE_UPDATE);
+ }
+ };
+
+ @VisibleForTesting
+ protected Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PROFILE_UPDATE:
+ case MSG_START_PROFILE_SELECTION:
+ logDebug("Msg received for profile update");
+ checkProfileUpdate();
+ break;
+ default:
+ log("invalid message");
+ break;
+ }
+ }
+ };
+
+ /**
+ * Broadcast receiver to receive intents
+ */
+ @VisibleForTesting
+ protected final BroadcastReceiver mProfileSelectorBroadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int sequenceId;
+ int subId;
+ String action = intent.getAction();
+ if (!mIsEnabled || action == null) {
+ return;
+ }
+
+ switch (action) {
+ case ACTION_SUB_SWITCH:
+ sequenceId = intent.getIntExtra("sequenceId", INVALID_SEQUENCE_ID);
+ subId = intent.getIntExtra("subId",
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (sequenceId != mSequenceId) {
+ return;
+ }
+
+ onSubSwitchComplete(subId);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Network scan callback handler
+ */
+ @VisibleForTesting
+ protected ANASNetworkScanCtlr.NetworkAvailableCallBack mNetworkAvailableCallBack =
+ new ANASNetworkScanCtlr.NetworkAvailableCallBack() {
+ @Override
+ public void onNetworkAvailability(List<CellInfo> results) {
+ /* sort the results according to signal strength level */
+ Collections.sort(results, new Comparator<CellInfo>() {
+ @Override
+ public int compare(CellInfo cellInfo1, CellInfo cellInfo2) {
+ return getSignalLevel(cellInfo1) - getSignalLevel(cellInfo2);
+ }
+ });
+
+ /* get subscription id for the best network scan result */
+ int subId = getSubId(getMcc(results.get(0)), getMnc(results.get(0)));
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ /* could not find any matching subscriptions */
+ return;
+ }
+
+ /* if subscription is already active, proceed to data switch */
+ if (mSubscriptionManager.isActiveSubId(subId)) {
+ /* if subscription already is data subscription,
+ complete the profile selection process */
+ /* Todo: change to getPreferredDataSubscriptionId once ready */
+ if (mSubscriptionManager.getDefaultDataSubscriptionId() == subId) {
+ mProfileSelectionCallback.onProfileSelectionDone(subId,
+ mSubscriptionManager.getDefaultSubscriptionId());
+ } else {
+ switchPreferredData(subId);
+ }
+ } else {
+ switchToSubscription(subId);
+ }
+ }
+
+ @Override
+ public void onError(int error) {
+ log("Network scan failed with error " + error);
+ }
+ };
+
+ /**
+ * interface call back to confirm profile selection
+ */
+ public interface ANASProfileSelectionCallback {
+
+ /**
+ * interface call back to confirm profile selection
+ */
+ void onProfileSelectionDone(int dataSubId, int voiceSubId);
+ }
+
+ /**
+ * ANASProfileSelector constructor
+ * @param c context
+ * @param profileSelectionCallback callback to be called once selection is done
+ */
+ public ANASProfileSelector(Context c, ANASProfileSelectionCallback profileSelectionCallback) {
+ init(c, profileSelectionCallback);
+ log("ANASProfileSelector init complete");
+ }
+
+ private int getSignalLevel(CellInfo cellInfo) {
+ if (cellInfo != null) {
+ return cellInfo.getCellSignalStrength().getLevel();
+ } else {
+ return SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ }
+ }
+
+ private String getMcc(CellInfo cellInfo) {
+ String mcc = "";
+ if (cellInfo instanceof CellInfoGsm) {
+ mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMccString();
+ } else if (cellInfo instanceof CellInfoLte) {
+ mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMccString();
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMccString();
+ }
+
+ return mcc;
+ }
+
+ private String getMnc(CellInfo cellInfo) {
+ String mnc = "";
+ if (cellInfo instanceof CellInfoGsm) {
+ mnc = ((CellInfoGsm) cellInfo).getCellIdentity().getMncString();
+ } else if (cellInfo instanceof CellInfoLte) {
+ mnc = ((CellInfoLte) cellInfo).getCellIdentity().getMncString();
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ mnc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMncString();
+ }
+
+ return mnc;
+ }
+
+ private int getSubId(String mcc, String mnc) {
+ List<SubscriptionInfo> subscriptionInfos =
+ mSubscriptionManager.getOpportunisticSubscriptions(1);
+ for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+ if (TextUtils.equals(subscriptionInfo.getMccString(), mcc)
+ && TextUtils.equals(subscriptionInfo.getMncString(), mnc)) {
+ return subscriptionInfo.getSubscriptionId();
+ }
+ }
+
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ private void switchToSubscription(int subId) {
+ Intent callbackIntent = new Intent(ACTION_SUB_SWITCH);
+ callbackIntent.setClass(mContext, ANASProfileSelector.class);
+ callbackIntent.putExtra("sequenceId", getAndUpdateToken());
+ callbackIntent.putExtra("subId", subId);
+
+ PendingIntent replyIntent = PendingIntent.getService(mContext,
+ 1, callbackIntent,
+ Intent.FILL_IN_ACTION);
+ mSubscriptionManager.switchToSubscription(subId, replyIntent);
+ }
+
+ private void switchPreferredData(int subId) {
+ mSubscriptionManager.setPreferredData(mSubscriptionManager.getSlotIndex(subId));
+ onDataSwitchComplete(subId);
+ }
+
+ private void onSubSwitchComplete(int subId) {
+ mRegMonitor.startListeningForNetworkConditionChange(subId);
+ }
+
+ private void onDataSwitchComplete(int subId) {
+ mProfileSelectionCallback.onProfileSelectionDone(subId,
+ mSubscriptionManager.getDefaultSubscriptionId());
+ }
+
+ private int getAndUpdateToken() {
+ synchronized (mLock) {
+ return mSequenceId++;
+ }
+ }
+
+ private void checkProfileUpdate() {
+ List<SubscriptionInfo> subscriptionInfos =
+ mSubscriptionManager.getOpportunisticSubscriptions(1);
+ if (subscriptionInfos == null) {
+ logDebug("received null subscription infos");
+ return;
+ }
+
+ if (subscriptionInfos.size() > 0) {
+ logDebug("opportunistic subscriptions size " + subscriptionInfos.size());
+
+ /* start scan immediately */
+ mNetworkScanCtlr.startFastNetworkScan(subscriptionInfos);
+ } else if (subscriptionInfos.size() == 0) {
+ /* check if no profile */
+ log("checkProfileUpdate 0 out");
+ mNetworkScanCtlr.stopNetworkScan();
+ }
+ }
+
+ /**
+ * start profile selection procedure
+ */
+ public void startProfileSelection() {
+ synchronized (mLock) {
+ if (!mIsEnabled) {
+ mIsEnabled = true;
+ mHandler.sendEmptyMessage(MSG_START_PROFILE_SELECTION);
+ }
+ }
+ }
+
+ /**
+ * select primary profile for data
+ */
+ public void selectPrimaryProfileForData() {
+ mSubscriptionManager.setPreferredData(mSubscriptionManager.getDefaultSubscriptionId());
+ }
+
+ /**
+ * stop profile selection procedure
+ */
+ public void stopProfileSelection() {
+ mNetworkScanCtlr.stopNetworkScan();
+ synchronized (mLock) {
+ mIsEnabled = false;
+ }
+ }
+
+ protected void init(Context c, ANASProfileSelectionCallback profileSelectionCallback) {
+ mContext = c;
+ mNetworkScanCtlr = new ANASNetworkScanCtlr(mContext, mTelephonyManager,
+ mNetworkAvailableCallBack);
+ mSequenceId = START_SEQUENCE_ID;
+ mProfileSelectionCallback = profileSelectionCallback;
+ mTelephonyManager = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mSubscriptionManager = (SubscriptionManager)
+ mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ mRegMonitor = new ANASServiceStateMonitor(mContext, mServiceMonitorCallback);
+
+ /* register for profile update events */
+ mSubscriptionManager.addOnOpportunisticSubscriptionsChangedListener(
+ AsyncTask.SERIAL_EXECUTOR, mProfileChangeListener);
+
+ /* register for subscription switch intent */
+ mContext.registerReceiver(mProfileSelectorBroadcastReceiver,
+ new IntentFilter(ACTION_SUB_SWITCH));
+ }
+
+ private void log(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ private void logDebug(String msg) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ }
+}
diff --git a/src/com/android/phone/anas/ANASServiceStateEvaluator.java b/src/com/android/phone/anas/ANASServiceStateEvaluator.java
new file mode 100644
index 0000000..23549a4
--- /dev/null
+++ b/src/com/android/phone/anas/ANASServiceStateEvaluator.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * ANASServiceStateEvaluator class which will evaluate the data service state of a subId
+ * compared to another.
+ */
+public class ANASServiceStateEvaluator {
+ private Context mContext;
+ private final Object mLock = new Object();
+ private int mOppDataSubId;
+ private int mPrimarySubId;
+ /* new opportunistic data service state */
+ private int mOppDataNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+ private int mPrimaryNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+ private boolean mIsWaitingForTimeout = false;
+
+ @VisibleForTesting
+ protected ANASServiceEvaluatorCallback mServiceEvaluatorCallback;
+
+ @VisibleForTesting
+ protected ANASServiceStateMonitor mOppDataSubMonitor;
+
+ @VisibleForTesting
+ protected ANASServiceStateMonitor mPrimarySubMonitor;
+
+ @VisibleForTesting
+ protected AlarmManager mAlarmManager;
+
+ private static final int WAIT_FOR_DATA_SERVICE_PERIOD = (int) TimeUnit.SECONDS.toMillis(10);
+ private static final String LOG_TAG = "ANASServiceStateEvaluator";
+ private static final boolean DBG = true;
+
+ /* message to indicate no data for WAIT_FOR_DATA_SERVICE_PERIOD */
+ private static final int MSG_WAIT_FOR_DATA_SERVICE_TIMOUT = 1;
+
+ /**
+ * call back to confirm service state evaluation
+ */
+ public interface ANASServiceEvaluatorCallback {
+
+ /**
+ * call back to confirm bad service
+ */
+ void onBadDataService();
+ }
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_WAIT_FOR_DATA_SERVICE_TIMOUT:
+ mIsWaitingForTimeout = false;
+ logDebug("Msg received to get data");
+ evaluateUpdatedState();
+ break;
+
+ default:
+ log("invalid message");
+ break;
+ }
+ }
+ };
+
+ public final AlarmManager.OnAlarmListener mDataServiceWaitTimer =
+ (AlarmManager.OnAlarmListener) () -> {
+ logDebug("Alarm fired");
+ mHandler.sendEmptyMessage(MSG_WAIT_FOR_DATA_SERVICE_TIMOUT);
+ };
+
+ /**
+ * set alarm to wait for data service
+ */
+ private void setDataServiceWaitAlarm() {
+ mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis()
+ + WAIT_FOR_DATA_SERVICE_PERIOD, LOG_TAG, mDataServiceWaitTimer, null);
+ }
+
+ /**
+ * stop the alarm
+ */
+ private void stopDataServiceWaitAlarm() {
+ mAlarmManager.cancel(mDataServiceWaitTimer);
+ }
+
+ private boolean evaluateIfBadOpportunisticDataService() {
+ /* if we have not received update on both subId, we can not take decision, yes */
+ log("evaluateIfBadOpportunisticDataService: mPrimaryNewState: "
+ + ANASServiceStateMonitor.getStateString(mPrimaryNewState) + " mOppDataNewState: "
+ + ANASServiceStateMonitor.getStateString(mOppDataNewState));
+
+ if ((mPrimaryNewState == ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN)
+ || (mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN)) {
+ return false;
+ }
+
+ /* Evaluate if primary subscription has good service and if
+ opportunistic data subscription is not, if yes return true.
+ */
+ switch (mPrimaryNewState) {
+ case ANASServiceStateMonitor.EVALUATED_STATE_NO_SERVICE:
+ /* no need to make any change */
+ return false;
+ case ANASServiceStateMonitor.EVALUATED_STATE_BAD:
+ if ((mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_BAD)
+ || (mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_GOOD)) {
+ return false;
+ }
+
+ break;
+ case ANASServiceStateMonitor.EVALUATED_STATE_GOOD:
+ if (mOppDataNewState == ANASServiceStateMonitor.EVALUATED_STATE_GOOD) {
+ return false;
+ }
+ break;
+ default:
+ log("invalid state");
+ break;
+ }
+
+ return true;
+ }
+
+ private void evaluateUpdatedState() {
+ logDebug("evaluateUpdatedState " + mIsWaitingForTimeout);
+ if (!mIsWaitingForTimeout && evaluateIfBadOpportunisticDataService()) {
+ mServiceEvaluatorCallback.onBadDataService();
+ }
+ }
+
+ /* service monitor callback will get called for service state change on a particular subId. */
+ ANASServiceStateMonitor.ANASServiceMonitorCallback mServiceMonitorCallback =
+ new ANASServiceStateMonitor.ANASServiceMonitorCallback() {
+ @Override
+ public void onServiceMonitorUpdate(int subId, int state) {
+ logDebug("onServiceMonitorUpdate subId: " + subId + " state: "
+ + ANASServiceStateMonitor.getStateString(state));
+ synchronized (mLock) {
+ if (mServiceEvaluatorCallback == null) {
+ return;
+ }
+
+ if (subId == mPrimarySubId) {
+ mPrimaryNewState = state;
+ } else if (subId == mOppDataSubId) {
+ mOppDataNewState = state;
+ } else {
+ logDebug("invalid sub id");
+ }
+
+ evaluateUpdatedState();
+ }
+ }
+ };
+
+ public ANASServiceStateEvaluator(Context c,
+ ANASServiceEvaluatorCallback serviceEvaluatorCallback) {
+ init(c, serviceEvaluatorCallback);
+ }
+
+ protected void init(Context c, ANASServiceEvaluatorCallback serviceEvaluatorCallback) {
+ mContext = c;
+ mServiceEvaluatorCallback = serviceEvaluatorCallback;
+ mOppDataSubMonitor = new ANASServiceStateMonitor(mContext, mServiceMonitorCallback);
+ mPrimarySubMonitor = new ANASServiceStateMonitor(mContext, mServiceMonitorCallback);
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+
+ /**
+ * start service state evaluation for dataSubId compared to voiceSubId.
+ * This API evaluates the service state conditions of dataSubId and decides whether
+ * data service is bad compared to voiceSubId
+ * @param dataSubId current data subscription id
+ * @param voiceSubId voice subscription id
+ */
+ public void startEvaluation(int dataSubId, int voiceSubId) {
+ logDebug("Start evaluation");
+ /* make sure to clean up if there is any evaluation going on. */
+ stopEvaluation();
+ setDataServiceWaitAlarm();
+ synchronized (mLock) {
+ mIsWaitingForTimeout = true;
+ mOppDataSubId = dataSubId;
+ mPrimarySubId = voiceSubId;
+ mOppDataSubMonitor.startListeningForNetworkConditionChange(dataSubId);
+ mPrimarySubMonitor.startListeningForNetworkConditionChange(voiceSubId);
+ }
+ }
+
+ /**
+ * stop service state evaluation
+ */
+ public void stopEvaluation() {
+ logDebug("Stop evaluation");
+ synchronized (mLock) {
+ mOppDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ mPrimarySubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (mIsWaitingForTimeout) {
+ stopDataServiceWaitAlarm();
+ }
+ mIsWaitingForTimeout = false;
+ mOppDataSubMonitor.stopListeningForNetworkConditionChange();
+ mPrimarySubMonitor.stopListeningForNetworkConditionChange();
+ mOppDataNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+ mPrimaryNewState = ANASServiceStateMonitor.EVALUATED_STATE_UNKNOWN;
+ }
+ }
+
+
+ private void log(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ private void logDebug(String msg) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ }
+}
diff --git a/src/com/android/phone/anas/ANASServiceStateMonitor.java b/src/com/android/phone/anas/ANASServiceStateMonitor.java
new file mode 100644
index 0000000..90a1564
--- /dev/null
+++ b/src/com/android/phone/anas/ANASServiceStateMonitor.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.telephony.PhoneStateListener;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * ANASServiceStateMonitor class which will monitor service state of a given subscription.
+ */
+public class ANASServiceStateMonitor {
+ @VisibleForTesting
+ protected Context mContext;
+
+ @VisibleForTesting
+ protected TelephonyManager mTelephonyManager;
+
+ @VisibleForTesting
+ protected ConnectivityManager mConnectivityManager;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"EVALUATED_STATE_"},
+ value = {
+ EVALUATED_STATE_UNKNOWN,
+ EVALUATED_STATE_NO_SERVICE,
+ EVALUATED_STATE_BAD,
+ EVALUATED_STATE_GOOD})
+ public @interface EvaluatedState {}
+
+ /* service states to be used while reporting onServiceMonitorUpdate */
+ public static final int EVALUATED_STATE_UNKNOWN = 0;
+
+ /* network is not available */
+ public static final int EVALUATED_STATE_NO_SERVICE = 1;
+
+ /* network is available but not good */
+ public static final int EVALUATED_STATE_BAD = 2;
+
+ /* network is available and good */
+ public static final int EVALUATED_STATE_GOOD = 3;
+
+ private static final String LOG_TAG = "ANASServiceStateMonitor";
+ private static final boolean DBG = true;
+ private ANASServiceMonitorCallback mServiceMonitorCallback;
+ private PhoneStateListener mPhoneStateListener;
+ private int mSubId;
+ private @EvaluatedState int mSignalStrengthEvaluatedState;
+ private @EvaluatedState int mServiceStateEvaluatedState;
+ private final Object mLock = new Object();
+
+ protected void init(Context c, ANASServiceMonitorCallback serviceMonitorCallback) {
+ mContext = c;
+ mTelephonyManager = TelephonyManager.from(mContext);
+ mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ mSignalStrengthEvaluatedState = EVALUATED_STATE_UNKNOWN;
+ mServiceStateEvaluatedState = EVALUATED_STATE_UNKNOWN;
+ mServiceMonitorCallback = serviceMonitorCallback;
+ logDebug("[ANASServiceStateMonitor] init by Context");
+ }
+
+ /**
+ * get the string name of a state
+ * @param state service state
+ * @return string name of a state
+ */
+ public static String getStateString(@EvaluatedState int state) {
+ switch (state) {
+ case EVALUATED_STATE_NO_SERVICE:
+ return "No Service";
+ case EVALUATED_STATE_BAD:
+ return "Bad Service";
+ case EVALUATED_STATE_GOOD:
+ return "Good Service";
+ default:
+ return "Unknown";
+ }
+ }
+
+ /**
+ * returns whether the fail reason is permanent
+ * @param failCause fail reason
+ * @return true if reason is permanent
+ */
+ @VisibleForTesting
+ public static boolean isFatalFailCause(String failCause) {
+ if (failCause == null || failCause.isEmpty()) {
+ return false;
+ }
+
+ switch (failCause) {
+ case "OPERATOR_BARRED":
+ case "USER_AUTHENTICATION":
+ case "ACTIVATION_REJECT_GGSN":
+ case "SERVICE_OPTION_NOT_SUPPORTED":
+ case "SERVICE_OPTION_NOT_SUBSCRIBED":
+ case "SERVICE_OPTION_OUT_OF_ORDER":
+ case "PROTOCOL_ERRORS":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void updateCallbackOnFinalState() {
+ int evaluatedState = EVALUATED_STATE_UNKNOWN;
+
+ logDebug("mServiceStateEvaluatedState: " + getStateString(mServiceStateEvaluatedState)
+ + " mSignalStrengthEvaluatedState: "
+ + getStateString(mSignalStrengthEvaluatedState));
+
+ /* Service state has highest priority in this validation. If no service, no need to
+ check further. */
+ if (mServiceStateEvaluatedState == EVALUATED_STATE_GOOD) {
+ evaluatedState = EVALUATED_STATE_GOOD;
+ } else if (mServiceStateEvaluatedState == EVALUATED_STATE_NO_SERVICE) {
+ evaluatedState = EVALUATED_STATE_NO_SERVICE;
+ mServiceMonitorCallback.onServiceMonitorUpdate(mSubId, EVALUATED_STATE_NO_SERVICE);
+ return;
+ }
+
+ /* use signal strength to determine service quality only, i.e is good or bad. */
+ if (evaluatedState == EVALUATED_STATE_GOOD) {
+ if (mSignalStrengthEvaluatedState == EVALUATED_STATE_BAD) {
+ evaluatedState = EVALUATED_STATE_BAD;
+ }
+ }
+
+ if (evaluatedState != EVALUATED_STATE_UNKNOWN) {
+ mServiceMonitorCallback.onServiceMonitorUpdate(mSubId, evaluatedState);
+ }
+ }
+
+ private void analyzeSignalStrengthChange(SignalStrength signalStrength) {
+ if (mServiceMonitorCallback == null) {
+ return;
+ }
+
+ if (signalStrength.getLevel() <= SignalStrength.SIGNAL_STRENGTH_POOR) {
+ mSignalStrengthEvaluatedState = EVALUATED_STATE_BAD;
+ } else {
+ mSignalStrengthEvaluatedState = EVALUATED_STATE_GOOD;
+ }
+
+ updateCallbackOnFinalState();
+ }
+
+ private void analyzeServiceStateChange(ServiceState serviceState) {
+ logDebug("analyzeServiceStateChange state:"
+ + serviceState.getDataRegState());
+ if (mServiceMonitorCallback == null) {
+ return;
+ }
+
+ if ((serviceState.getDataRegState() == ServiceState.STATE_OUT_OF_SERVICE)
+ || (serviceState.getState() == ServiceState.STATE_EMERGENCY_ONLY)) {
+ mServiceStateEvaluatedState = EVALUATED_STATE_NO_SERVICE;
+ } else if (serviceState.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+ mServiceStateEvaluatedState = EVALUATED_STATE_GOOD;
+ }
+
+ updateCallbackOnFinalState();
+ }
+
+ /**
+ * Implements phone state listener
+ */
+ @VisibleForTesting
+ public class PhoneStateListenerImpl extends PhoneStateListener {
+ PhoneStateListenerImpl(int subId) {
+ super(subId);
+ }
+
+ private boolean shouldIgnore() {
+ if (PhoneStateListenerImpl.this.mSubId != ANASServiceStateMonitor.this.mSubId) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ synchronized (mLock) {
+ if (shouldIgnore()) {
+ return;
+ }
+
+ analyzeSignalStrengthChange(signalStrength);
+ }
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ synchronized (mLock) {
+ if (shouldIgnore()) {
+ return;
+ }
+
+ analyzeServiceStateChange(serviceState);
+ }
+ }
+ };
+
+ /**
+ * get phone state listener instance
+ * @param subId subscription id
+ * @return the listener instance
+ */
+ @VisibleForTesting
+ public PhoneStateListener getPhoneStateListener(int subId) {
+ synchronized (mLock) {
+ if (mPhoneStateListener != null && subId == mSubId) {
+ return mPhoneStateListener;
+ }
+
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mSubId = subId;
+ mPhoneStateListener = (PhoneStateListener) new PhoneStateListenerImpl(subId);
+ }
+ return mPhoneStateListener;
+ }
+
+ /**
+ * call back interface
+ */
+ public interface ANASServiceMonitorCallback {
+ /**
+ * call back interface
+ */
+ void onServiceMonitorUpdate(int subId, @EvaluatedState int state);
+ }
+
+ /**
+ * request to start listening for network changes.
+ */
+ public void startListeningForNetworkConditionChange(int subId) {
+
+ logDebug("start network condition listen for " + subId);
+ /* monitor service state, signal strength and data connection state */
+ synchronized (mLock) {
+ int events = PhoneStateListener.LISTEN_SERVICE_STATE
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTH;
+ mTelephonyManager.listen(getPhoneStateListener(subId), events);
+ }
+ }
+
+ /**
+ * request to stop listening for network changes.
+ */
+ public void stopListeningForNetworkConditionChange() {
+ logDebug("stop network condition listen for " + mSubId);
+ synchronized (mLock) {
+ if (mPhoneStateListener != null) {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+ mSignalStrengthEvaluatedState = EVALUATED_STATE_UNKNOWN;
+ mServiceStateEvaluatedState = EVALUATED_STATE_UNKNOWN;
+ }
+ }
+
+ public ANASServiceStateMonitor(Context c, ANASServiceMonitorCallback serviceMonitorCallback) {
+ init(c, serviceMonitorCallback);
+ }
+
+ private static void log(String msg) {
+ Rlog.d(LOG_TAG, msg);
+ }
+
+ private static void logDebug(String msg) {
+ if (DBG) {
+ Rlog.d(LOG_TAG, msg);
+ }
+ }
+}
diff --git a/src/com/android/phone/anas/AlternativeNetworkAccessService.java b/src/com/android/phone/anas/AlternativeNetworkAccessService.java
new file mode 100644
index 0000000..b972813
--- /dev/null
+++ b/src/com/android/phone/anas/AlternativeNetworkAccessService.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.anas;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Binder;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IAnas;
+import com.android.internal.telephony.TelephonyPermissions;
+
+/**
+ * AlternativeNetworkAccessService implements ianas.
+ * It scans network and matches the results with opportunistic subscriptions.
+ * Use the same to provide user opportunistic data in areas with corresponding networks
+ */
+public class AlternativeNetworkAccessService extends IAnas.Stub {
+ private Context mContext;
+ private TelephonyManager mTelephonyManager;
+ private SubscriptionManager mSubsriptionManager;
+
+ private final Object mLock = new Object();
+ private boolean mIsEnabled;
+ private ANASProfileSelector mProfileSelector;
+ private ANASServiceStateEvaluator mServiceStateEvaluator;
+ private SharedPreferences mSharedPref;
+
+ /** The singleton instance. */
+ private static AlternativeNetworkAccessService sInstance = null;
+ private static final String TAG = "ANAS";
+ private static final String PREF_NAME = TAG;
+ private static final String PREF_ENABLED = "isEnabled";
+ private static final boolean DBG = true;
+
+ /**
+ * Profile selection callback. Will be called once Profile selector decides on
+ * the opportunistic data profile.
+ */
+ private ANASProfileSelector.ANASProfileSelectionCallback mProfileSelectionCallback =
+ new ANASProfileSelector.ANASProfileSelectionCallback() {
+
+ @Override
+ public void onProfileSelectionDone(int dataSubId, int voiceSubId) {
+ logDebug("profile selection done");
+ mProfileSelector.stopProfileSelection();
+ mServiceStateEvaluator.startEvaluation(dataSubId, voiceSubId);
+ }
+ };
+
+ /**
+ * Service state evaluator callback. Will be called once service state evaluator thinks
+ * that current opportunistic data is not providing good service.
+ */
+ private ANASServiceStateEvaluator.ANASServiceEvaluatorCallback mServiceEvaluatorCallback =
+ new ANASServiceStateEvaluator.ANASServiceEvaluatorCallback() {
+ @Override
+ public void onBadDataService() {
+ logDebug("Bad opportunistic data service");
+ mServiceStateEvaluator.stopEvaluation();
+ mProfileSelector.selectPrimaryProfileForData();
+ mProfileSelector.startProfileSelection();
+ }
+ };
+
+ /**
+ * create AlternativeNetworkAccessService instance
+ *
+ * @param c context
+ *
+ */
+ public static void initInstance(Context c) {
+ if (sInstance == null) {
+ sInstance = new AlternativeNetworkAccessService(c);
+ }
+ return;
+ }
+
+ /**
+ * get AlternativeNetworkAccessService instance
+ *
+ */
+ @VisibleForTesting
+ public static AlternativeNetworkAccessService getInstance() {
+ if (sInstance == null) {
+ Log.wtf(TAG, "getInstance null");
+ }
+ return sInstance;
+ }
+
+ /**
+ * Enable or disable Alternative Network Access service.
+ *
+ * This method should be called to enable or disable
+ * AlternativeNetworkAccess service on the device.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param enable enable(True) or disable(False)
+ * @param callingPackage caller's package name
+ * @return returns true if successfully set.
+ */
+ @Override
+ public boolean setEnable(boolean enable, String callingPackage) {
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mContext, mSubsriptionManager.getDefaultSubscriptionId(), "setEnable");
+ log("setEnable: " + enable);
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ enableAlternativeNetworkAccess(enable);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return true;
+ }
+
+ /**
+ * is Alternative Network Access service enabled
+ *
+ * This method should be called to determine if the Alternative Network Access service
+ * is enabled
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param callingPackage caller's package name
+ */
+ @Override
+ public boolean isEnabled(String callingPackage) {
+ TelephonyPermissions.enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+ mContext, mSubsriptionManager.getDefaultSubscriptionId(), "isEnabled");
+ return mIsEnabled;
+ }
+
+ /**
+ * initialize ANAS and register as service.
+ * Read persistent state to update enable state
+ * Start sub components if already enabled.
+ * @param context context instance
+ */
+ private void initializeAndRegisterAsService(Context context) {
+ mContext = context;
+ mTelephonyManager = TelephonyManager.from(mContext);
+ mServiceStateEvaluator = new ANASServiceStateEvaluator(mContext, mServiceEvaluatorCallback);
+ mProfileSelector = new ANASProfileSelector(mContext, mProfileSelectionCallback);
+ mSharedPref = mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ mSubsriptionManager = (SubscriptionManager) mContext.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+
+ /* register the service */
+ if (ServiceManager.getService("ianas") == null) {
+ ServiceManager.addService("ianas", this);
+ }
+
+ enableAlternativeNetworkAccess(getPersistentEnableState());
+ }
+
+ private AlternativeNetworkAccessService(Context c) {
+ initializeAndRegisterAsService(c);
+ log("init completed");
+ }
+
+ private boolean getPersistentEnableState() {
+ return mSharedPref.getBoolean(PREF_ENABLED, true);
+ }
+
+ private void updateEnableState(boolean enable) {
+ mIsEnabled = enable;
+ mSharedPref.edit().putBoolean(PREF_ENABLED, mIsEnabled).apply();
+ }
+
+ /**
+ * update the enable state
+ * start profile selection if enabled.
+ * @param enable enable(true) or disable(false)
+ */
+ private void enableAlternativeNetworkAccess(boolean enable) {
+ synchronized (mLock) {
+ if (mIsEnabled != enable) {
+ updateEnableState(enable);
+ if (mIsEnabled) {
+ mProfileSelector.startProfileSelection();
+ } else {
+ mProfileSelector.stopProfileSelection();
+ }
+ }
+ }
+ logDebug("service is enable state " + mIsEnabled);
+ }
+
+ private void log(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ private void logDebug(String msg) {
+ if (DBG) Rlog.d(TAG, msg);
+ }
+}
diff --git a/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java b/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
index 8187664..3ccd4a3 100644
--- a/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
+++ b/src/com/android/phone/settings/VoicemailProviderSettingsUtil.java
@@ -137,7 +137,7 @@
prefs.edit()
.putString(key + VM_NUMBER_TAG, null)
.putInt(key + FWD_SETTINGS_TAG + FWD_SETTINGS_LENGTH_TAG, 0)
- .commit();
+ .apply();
}
private static SharedPreferences getPrefs(Context context) {
diff --git a/src/com/android/phone/vvm/RemoteVvmTaskManager.java b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
index 98cb959..ec3d716 100644
--- a/src/com/android/phone/vvm/RemoteVvmTaskManager.java
+++ b/src/com/android/phone/vvm/RemoteVvmTaskManager.java
@@ -171,6 +171,13 @@
"Component " + info.getComponentInfo() + " is not a service, ignoring");
continue;
}
+
+ if (!info.serviceInfo.isEnabled()) {
+ VvmLog.i(TAG,
+ "Component " + info.getComponentInfo() + " is disabled, ignoring");
+ continue;
+ }
+
if (!android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE
.equals(info.serviceInfo.permission)) {
VvmLog.w(TAG, "package " + info.serviceInfo.packageName
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 24c3870..5d987f7 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -211,6 +211,7 @@
// 4) Add the conference to the connection service if it is new.
if (isNewlyCreated) {
Log.d(this, "Adding the conference call");
+ mConference.updateCallRadioTechAfterCreation();
mConnectionService.addConference(mConference);
}
} else if (conferenceConnections.isEmpty()) {
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index b196d57..5722834 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -640,6 +640,7 @@
setState(mConferenceHost.getState());
updateStatusHints();
+ putExtras(mConferenceHost.getExtras());
}
/**
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 642af85..6b7a002 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -64,7 +64,7 @@
* Owns all data we have registered with Telecom including handling dynamic addition and
* removal of SIMs and SIP accounts.
*/
-final class TelecomAccountRegistry {
+public final class TelecomAccountRegistry {
private static final boolean DBG = false; /* STOP SHIP if true */
// This icon is the one that is used when the Slot ID that we have for a particular SIM
@@ -659,7 +659,10 @@
mSubscriptionManager = SubscriptionManager.from(context);
}
- static synchronized final TelecomAccountRegistry getInstance(Context context) {
+ /**
+ * Get the singleton instance.
+ */
+ public static synchronized TelecomAccountRegistry getInstance(Context context) {
if (sInstance == null && context != null) {
sInstance = new TelecomAccountRegistry(context);
}
@@ -828,7 +831,7 @@
/**
* Sets up all the phone accounts for SIMs on first boot.
*/
- void setupOnBoot() {
+ public void setupOnBoot() {
// TODO: When this object "finishes" we should unregister by invoking
// SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
// This is not strictly necessary because it will be unregistered if the
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index e96815c..e9eef46 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -264,6 +264,7 @@
mTelephonyConference, connection);
mTelephonyConference.addConnection(connection);
}
+ mTelephonyConference.updateCallRadioTechAfterCreation();
mConnectionService.addConference(mTelephonyConference);
} else {
Log.d(this, "Trigger recalculate later");
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 6904874..3192c75 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -36,6 +36,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.DisconnectCause;
import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
import android.text.TextUtils;
@@ -96,6 +97,7 @@
private static final int MSG_CDMA_VOICE_PRIVACY_ON = 15;
private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
private static final int MSG_HANGUP = 17;
+ private static final int MSG_SET_CALL_RADIO_TECH = 18;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -240,6 +242,31 @@
int cause = (int) msg.obj;
hangup(cause);
break;
+
+ case MSG_SET_CALL_RADIO_TECH:
+ int vrat = (int) msg.obj;
+ // Check whether Wi-Fi call tech is changed, it means call radio tech is:
+ // a) changed from IWLAN to other value, or
+ // b) changed from other value to IWLAN.
+ //
+ // In other word, below conditions are all met:
+ // 1) {@link #getCallRadioTech} is different from new vrat
+ // 2) Current call radio technology indicates Wi-Fi call, i.e. {@link #isWifi}
+ // is true, or new vrat indicates Wi-Fi call.
+ boolean isWifiTechChange = getCallRadioTech() != vrat
+ && (isWifi() || vrat == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+
+ // Step 1) Updates call radio tech firstly, so that afterwards Wi-Fi related
+ // update actions are taken correctly.
+ setCallRadioTech(vrat);
+
+ // Step 2) Handles Wi-Fi call tech change.
+ if (isWifiTechChange) {
+ updateConnectionProperties();
+ updateStatusHints();
+ refreshDisableAddCall();
+ }
+ break;
}
}
};
@@ -421,14 +448,14 @@
}
/**
- * Used by {@link com.android.internal.telephony.Connection} to report a change in whether
- * the call is being made over a wifi network.
+ * Used by {@link com.android.internal.telephony.Connection} to report a change for
+ * the call radio technology.
*
- * @param isWifi True if call is made over wifi.
+ * @param vrat the RIL Voice Radio Technology used for current connection.
*/
@Override
- public void onWifiChanged(boolean isWifi) {
- setWifi(isWifi);
+ public void onCallRadioTechChanged(@ServiceState.RilRadioTechnology int vrat) {
+ mHandler.obtainMessage(MSG_SET_CALL_RADIO_TECH, vrat).sendToTarget();
}
/**
@@ -575,6 +602,11 @@
com.android.internal.telephony.Connection newConnection) {
setOriginalConnection(newConnection);
}
+
+ @Override
+ public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) {
+ setIsNetworkIdentifiedEmergencyCall(isEmergencyCall);
+ }
};
protected com.android.internal.telephony.Connection mOriginalConnection;
@@ -599,13 +631,6 @@
private int mOriginalConnectionCapabilities;
/**
- * Determines if the {@link TelephonyConnection} is using wifi.
- * This is used when {@link TelephonyConnection#updateConnectionProperties()} is called to
- * indicate whether a call has the {@link Connection#PROPERTY_WIFI} property.
- */
- private boolean mIsWifi;
-
- /**
* Determines the audio quality is high for the {@link TelephonyConnection}.
* This is used when {@link TelephonyConnection#updateConnectionProperties}} is called to
* indicate whether a call has the {@link Connection#PROPERTY_HIGH_DEF_AUDIO} property.
@@ -620,6 +645,15 @@
private boolean mTreatAsEmergencyCall;
/**
+ * Indicates whether the network has identified this call as an emergency call. Where
+ * {@link #mTreatAsEmergencyCall} is based on comparing dialed numbers to a list of known
+ * emergency numbers, this property is based on whether the network itself has identified the
+ * call as an emergency call (which can be the case for an incoming call from emergency
+ * services).
+ */
+ private boolean mIsNetworkIdentifiedEmergencyCall;
+
+ /**
* For video calls, indicates whether the outgoing video for the call can be paused using
* the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
*/
@@ -661,6 +695,11 @@
private boolean mIsHoldable;
/**
+ * Indicates whether TTY is enabled; used to determine whether a call is VT capable.
+ */
+ private boolean mIsTtyEnabled;
+
+ /**
* Indicates whether this call is using assisted dialing.
*/
private boolean mIsUsingAssistedDialing;
@@ -883,6 +922,13 @@
Log.v(this, "Holding active call");
try {
Phone phone = mOriginalConnection.getCall().getPhone();
+
+ // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ ImsPhone imsPhone = (ImsPhone) phone;
+ imsPhone.holdActiveCall();
+ return;
+ }
Call ringingCall = phone.getRingingCall();
// Although the method says switchHoldingAndActive, it eventually calls a RIL method
@@ -912,6 +958,13 @@
Log.v(this, "performUnhold");
if (Call.State.HOLDING == mConnectionState) {
try {
+ Phone phone = mOriginalConnection.getCall().getPhone();
+ // New behavior for IMS -- don't use the clunky switchHoldingAndActive logic.
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ ImsPhone imsPhone = (ImsPhone) phone;
+ imsPhone.unholdHeldCall();
+ return;
+ }
// Here's the deal--Telephony hold/unhold is weird because whenever there exists
// more than one call, one of them must always be active. In other words, if you
// have an active call and holding call, and you put the active call on hold, it
@@ -1016,7 +1069,7 @@
newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
hasHighDefAudioProperty());
- newProperties = changeBitmask(newProperties, PROPERTY_WIFI, mIsWifi);
+ newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi());
newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
isExternalConnection());
newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
@@ -1024,6 +1077,8 @@
newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
mIsUsingAssistedDialing);
newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
+ newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
+ isNetworkIdentifiedEmergencyCall());
if (getConnectionProperties() != newProperties) {
setConnectionProperties(newProperties);
@@ -1103,12 +1158,14 @@
// Set video state and capabilities
setVideoState(mOriginalConnection.getVideoState());
setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
- setWifi(mOriginalConnection.isWifi());
+ setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
setVideoProvider(mOriginalConnection.getVideoProvider());
setAudioQuality(mOriginalConnection.getAudioQuality());
setTechnologyTypeExtra();
+ setCallRadioTech(mOriginalConnection.getCallRadioTech());
+
// Post update of extras to the handler; extras are updated via the handler to ensure thread
// safety. The Extras Bundle is cloned in case the original extras are modified while they
// are being added to mOriginalConnectionExtras in updateExtras.
@@ -1225,7 +1282,7 @@
if (isCurrentVideoCall) {
return true;
- } else if (wasVideoCall && mIsWifi && !isVowifiEnabled) {
+ } else if (wasVideoCall && isWifi() && !isVowifiEnabled) {
return true;
}
return false;
@@ -1260,7 +1317,7 @@
return false;
}
- if (mIsWifi && !canWifiCallsBeHdAudio) {
+ if (isWifi() && !canWifiCallsBeHdAudio) {
return false;
}
@@ -1810,28 +1867,40 @@
capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
+ boolean isLocalVideoSupported = can(mOriginalConnectionCapabilities,
+ Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL) && !mIsTtyEnabled;
capabilities = changeBitmask(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
- can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+ isLocalVideoSupported);
return capabilities;
}
/**
- * Sets whether the call is using wifi. Used when rebuilding the capabilities to set or unset
- * the {@link Connection#PROPERTY_WIFI} property.
- */
- public void setWifi(boolean isWifi) {
- mIsWifi = isWifi;
- updateConnectionProperties();
- updateStatusHints();
- refreshDisableAddCall();
- }
-
- /**
* Whether the call is using wifi.
*/
boolean isWifi() {
- return mIsWifi;
+ return getCallRadioTech() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+ }
+
+ /**
+ * Sets whether this call has been identified by the network as an emergency call.
+ * @param isNetworkIdentifiedEmergencyCall {@code true} if the network has identified this call
+ * as an emergency call, {@code false} otherwise.
+ */
+ public void setIsNetworkIdentifiedEmergencyCall(boolean isNetworkIdentifiedEmergencyCall) {
+ Log.d(this, "setIsNetworkIdentifiedEmergencyCall; callId=%s, "
+ + "isNetworkIdentifiedEmergencyCall=%b", getTelecomCallId(),
+ isNetworkIdentifiedEmergencyCall);
+ mIsNetworkIdentifiedEmergencyCall = isNetworkIdentifiedEmergencyCall;
+ updateConnectionProperties();
+ }
+
+ /**
+ * @return {@code true} if the network has identified this call as an emergency call,
+ * {@code false} otherwise.
+ */
+ public boolean isNetworkIdentifiedEmergencyCall() {
+ return mIsNetworkIdentifiedEmergencyCall;
}
/**
@@ -1939,6 +2008,15 @@
}
/**
+ * Sets whether TTY is enabled or not.
+ * @param isTtyEnabled
+ */
+ public void setTtyEnabled(boolean isTtyEnabled) {
+ mIsTtyEnabled = isTtyEnabled;
+ updateConnectionCapabilities();
+ }
+
+ /**
* Whether the original connection is an IMS connection.
* @return {@code True} if the original connection is an IMS connection, {@code false}
* otherwise.
@@ -2011,7 +2089,7 @@
}
private void updateStatusHints() {
- if (mIsWifi && getPhone() != null) {
+ if (isWifi() && getPhone() != null) {
int labelId = isValidRingingCall()
? R.string.status_hint_label_incoming_wifi_call
: R.string.status_hint_label_wifi_call;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 195194c..3ec00b9 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -17,9 +17,11 @@
package com.android.services.telephony;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
@@ -119,6 +121,23 @@
}
};
+ private final BroadcastReceiver mTtyBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.v(this, "onReceive, action: %s", action);
+ if (action.equals(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED)) {
+ int newPreferredTtyMode = intent.getIntExtra(
+ TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
+
+ boolean isTtyNowEnabled = newPreferredTtyMode != TelecomManager.TTY_MODE_OFF;
+ if (isTtyNowEnabled != mIsTtyEnabled) {
+ handleTtyModeChange(isTtyNowEnabled);
+ }
+ }
+ }
+ };
+
private final TelephonyConferenceController mTelephonyConferenceController =
new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
private final CdmaConferenceController mCdmaConferenceController =
@@ -131,6 +150,7 @@
private RadioOnHelper mRadioOnHelper;
private EmergencyTonePlayer mEmergencyTonePlayer;
private HoldTracker mHoldTracker;
+ private boolean mIsTtyEnabled;
// Contains one TelephonyConnection that has placed a call and a memory of which Phones it has
// already tried to connect with. There should be only one TelephonyConnection trying to place a
@@ -263,6 +283,17 @@
mEmergencyTonePlayer = new EmergencyTonePlayer(this);
TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
mHoldTracker = new HoldTracker();
+ mIsTtyEnabled = isTtyModeEnabled(getApplicationContext());
+
+ IntentFilter intentFilter = new IntentFilter(
+ TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
+ registerReceiver(mTtyBroadcastReceiver, intentFilter);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ unregisterReceiver(mTtyBroadcastReceiver);
+ return super.onUnbind(intent);
}
@Override
@@ -633,8 +664,9 @@
}
final Context context = getApplicationContext();
- if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled(context) &&
- !isEmergencyNumber) {
+ final boolean isTtyModeEnabled = isTtyModeEnabled(context);
+ if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled
+ && !isEmergencyNumber) {
return Connection.createFailedConnection(DisconnectCauseUtil.toTelecomDisconnectCause(
android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,
null, phone.getPhoneId()));
@@ -670,7 +702,7 @@
connection.setInitializing();
connection.setVideoState(request.getVideoState());
connection.setRttTextStream(request.getRttTextStream());
-
+ connection.setTtyEnabled(isTtyModeEnabled);
return connection;
}
@@ -1005,7 +1037,9 @@
}
Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
- Phone phoneUsed = c.getPhone();
+ // Need to refer default phone considering ImsPhone because
+ // cachedPhones is a list that contains default phones.
+ Phone phoneUsed = c.getPhone().getDefaultPhone();
if (phoneUsed == null) {
return;
}
@@ -1453,4 +1487,15 @@
}
}
}
+
+ private void handleTtyModeChange(boolean isTtyEnabled) {
+ Log.i(this, "handleTtyModeChange; isTtyEnabled=%b", isTtyEnabled);
+ mIsTtyEnabled = isTtyEnabled;
+ for (Connection connection : getAllConnections()) {
+ if (connection instanceof TelephonyConnection) {
+ TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+ telephonyConnection.setTtyEnabled(isTtyEnabled);
+ }
+ }
+ }
}
diff --git a/src/com/android/services/telephony/TelephonyGlobals.java b/src/com/android/services/telephony/TelephonyGlobals.java
deleted file mode 100644
index 02ef639..0000000
--- a/src/com/android/services/telephony/TelephonyGlobals.java
+++ /dev/null
@@ -1,70 +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.services.telephony;
-
-import android.content.Context;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Singleton entry point for the telephony-services app. Initializes ongoing systems relating to
- * PSTN calls. This is started when the device starts and will be restarted automatically
- * if it goes away for any reason (e.g., crashes).
- * This is separate from the actual Application class because we only support one instance of this
- * app - running as the default user. {@link com.android.phone.PhoneApp} determines whether or not
- * we are running as the default user and if we are, then initializes and runs this class's
- * {@link #onCreate}.
- */
-public class TelephonyGlobals {
- private static TelephonyGlobals sInstance;
-
- /** The application context. */
- private final Context mContext;
-
- // For supporting MSIM phone, change Phone and TtyManager as 1 to 1
- private List<TtyManager> mTtyManagers = new ArrayList<>();
-
- /**
- * Persists the specified parameters.
- *
- * @param context The application context.
- */
- public TelephonyGlobals(Context context) {
- mContext = context.getApplicationContext();
- }
-
- public static synchronized TelephonyGlobals getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new TelephonyGlobals(context);
- }
- return sInstance;
- }
-
- public void onCreate() {
- // Make this work with Multi-SIM devices
- Phone[] phones = PhoneFactory.getPhones();
- for (Phone phone : phones) {
- mTtyManagers.add(new TtyManager(mContext, phone));
- }
-
- TelecomAccountRegistry.getInstance(mContext).setupOnBoot();
- }
-}
diff --git a/src/com/android/services/telephony/TtyManager.java b/src/com/android/services/telephony/TtyManager.java
deleted file mode 100644
index 3389ce8..0000000
--- a/src/com/android/services/telephony/TtyManager.java
+++ /dev/null
@@ -1,153 +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.services.telephony;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Settings;
-import android.telecom.TelecomManager;
-
-import com.android.internal.telephony.Phone;
-
-final class TtyManager {
- private final static int MSG_SET_TTY_MODE_RESPONSE = 1;
- private final static int MSG_GET_TTY_MODE_RESPONSE = 2;
-
- private final TtyBroadcastReceiver mReceiver = new TtyBroadcastReceiver();
- private final Phone mPhone;
- private int mTtyMode;
- private int mUiTtyMode = -1;
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SET_TTY_MODE_RESPONSE: {
- Log.v(TtyManager.this, "got setTtyMode response");
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- Log.d(TtyManager.this, "setTTYMode exception: %s", ar.exception);
- }
- mPhone.queryTTYMode(obtainMessage(MSG_GET_TTY_MODE_RESPONSE));
- break;
- }
- case MSG_GET_TTY_MODE_RESPONSE: {
- Log.v(TtyManager.this, "got queryTTYMode response");
- AsyncResult ar = (AsyncResult) msg.obj;
- if (ar.exception != null) {
- Log.d(TtyManager.this, "queryTTYMode exception: %s", ar.exception);
- } else {
- int ttyMode = phoneModeToTelecomMode(((int[]) ar.result)[0]);
- if (ttyMode != mTtyMode) {
- Log.d(TtyManager.this, "setting TTY mode failed, attempted %d, got: %d",
- mTtyMode, ttyMode);
- } else {
- Log.d(TtyManager.this, "setting TTY mode to %d succeeded", ttyMode);
- }
- }
- break;
- }
- }
- }
- };
-
- TtyManager(Context context, Phone phone) {
- mPhone = phone;
-
- IntentFilter intentFilter = new IntentFilter(
- TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
- intentFilter.addAction(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
- context.registerReceiver(mReceiver, intentFilter);
-
- int ttyMode = TelecomManager.TTY_MODE_OFF;
- TelecomManager telecomManager = TelecomManager.from(context);
- if (telecomManager != null) {
- ttyMode = telecomManager.getCurrentTtyMode();
- }
- updateTtyMode(ttyMode);
- //Get preferred TTY mode from data base as UI Tty mode is always user preferred Tty mode.
- ttyMode = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF);
- updateUiTtyMode(ttyMode);
- }
-
- private void updateTtyMode(int ttyMode) {
- Log.v(this, "updateTtyMode %d -> %d", mTtyMode, ttyMode);
- mTtyMode = ttyMode;
- mPhone.setTTYMode(telecomModeToPhoneMode(ttyMode),
- mHandler.obtainMessage(MSG_SET_TTY_MODE_RESPONSE));
- }
-
- private void updateUiTtyMode(int ttyMode) {
- Log.i(this, "updateUiTtyMode %d -> %d", mUiTtyMode, ttyMode);
- if(mUiTtyMode != ttyMode) {
- mUiTtyMode = ttyMode;
- mPhone.setUiTTYMode(telecomModeToPhoneMode(ttyMode), null);
- } else {
- Log.i(this, "ui tty mode didnt change");
- }
- }
-
- private final class TtyBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- Log.v(TtyManager.this, "onReceive, action: %s", action);
- if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
- int ttyMode = intent.getIntExtra(
- TelecomManager.EXTRA_CURRENT_TTY_MODE, TelecomManager.TTY_MODE_OFF);
- updateTtyMode(ttyMode);
- } else if (action.equals(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED)) {
- int newPreferredTtyMode = intent.getIntExtra(
- TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
- updateUiTtyMode(newPreferredTtyMode);
- }
- }
- }
-
- private static int telecomModeToPhoneMode(int telecomMode) {
- switch (telecomMode) {
- // AT command only has 0 and 1, so mapping VCO
- // and HCO to FULL
- case TelecomManager.TTY_MODE_FULL:
- case TelecomManager.TTY_MODE_VCO:
- case TelecomManager.TTY_MODE_HCO:
- return Phone.TTY_MODE_FULL;
- default:
- return Phone.TTY_MODE_OFF;
- }
- }
-
- private static int phoneModeToTelecomMode(int phoneMode) {
- switch (phoneMode) {
- case Phone.TTY_MODE_FULL:
- return TelecomManager.TTY_MODE_FULL;
- case Phone.TTY_MODE_VCO:
- return TelecomManager.TTY_MODE_VCO;
- case Phone.TTY_MODE_HCO:
- return TelecomManager.TTY_MODE_HCO;
- case Phone.TTY_MODE_OFF:
- default:
- return TelecomManager.TTY_MODE_OFF;
- }
- }
-}
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 3bd2716..a18adb8 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -21,7 +21,6 @@
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
@@ -817,6 +816,7 @@
testServiceState.setEmergencyOnly(isEmergencyOnly);
when(phone.getServiceState()).thenReturn(testServiceState);
when(phone.getPhoneId()).thenReturn(phoneId);
+ when(phone.getDefaultPhone()).thenReturn(phone);
return phone;
}
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 9040257..39e4579 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -74,6 +74,7 @@
any(Connection.PostDialListener.class));
when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
when(mMockPhone.getContext()).thenReturn(null);
+ when(mMockPhone.getDefaultPhone()).thenReturn(mMockPhone);
when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
when(mMockCall.getPhone()).thenReturn(mMockPhone);
}
@@ -107,6 +108,11 @@
mLastConnectionEventExtras.add(extras);
}
+ @Override
+ void clearOriginalConnection() {
+ // Do nothing since the original connection is mock object
+ }
+
public int getNotifyPhoneAccountChangedCount() {
return mNotifyPhoneAccountChangedCount;
}