Fix security vulnerability of TelecomManager#getPhoneAccountsForPackage am: f3f2d7c2dc am: 21bc22a06e am: 0b9f87a4e3

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telecomm/+/12141784

Change-Id: Id134980b9b5b7b884792fac38274589c6ab2144d
diff --git a/Android.mk b/Android.mk
index 79ef194..4e5eeff 100644
--- a/Android.mk
+++ b/Android.mk
@@ -7,12 +7,14 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-proto-files-under, proto)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_USE_AAPT2 := true
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/proto/
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
 
 LOCAL_PACKAGE_NAME := Telecom
+LOCAL_PRIVATE_PLATFORM_APIS := true
 
 LOCAL_CERTIFICATE := platform
 LOCAL_PRIVILEGED_MODULE := true
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4f1cb8b..588e5c3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -36,8 +36,11 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <!-- Required to determine source of ongoing audio recordings. -->
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CALL_LOG" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.STOP_APP_SWITCHES" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
@@ -99,6 +102,15 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+
+        <activity android:name=".settings.CallBlockDisabledActivity"
+                android:configChanges="keyboardHidden|orientation|screenSize"
+                android:excludeFromRecents="true"
+                android:launchMode="singleInstance"
+                android:theme="@style/Theme.Telecomm.Transparent"
+                android:process=":ui">
+        </activity>
+
         <!-- Activity that starts the outgoing call process by listening to CALL intent which
              contain contact information in the intent's data. CallActivity handles any data
              URL with the schemes "tel", "sip", and "voicemail". It also handles URLs linked to
diff --git a/proguard.flags b/proguard.flags
index 4e0c310..7ecfc3f 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,8 +1,29 @@
 -verbose
 -keep @com.android.internal.annotations.VisibleForTesting class *
+-keep public class * extends android.widget.ListView {
+    public *;
+}
 -keep class com.android.server.telecom.TelecomSystem {
   *;
 }
 -keep class android.telecom.Log {
   *;
 }
+
+# Keep classes, annotations and members used by Lifecycle. Remove this once aapt2 is enabled
+-keepattributes *Annotation*
+
+-keepclassmembers enum android.arch.lifecycle.Lifecycle$Event {
+    <fields>;
+}
+
+-keep class * implements android.arch.lifecycle.LifecycleObserver {
+}
+
+-keep class * implements android.arch.lifecycle.GeneratedAdapter {
+    <init>(...);
+}
+
+-keepclassmembers class ** {
+    @android.arch.lifecycle.OnLifecycleEvent *;
+}
diff --git a/proto/telecom.proto b/proto/telecom.proto
index 9fd64db..2f4fae8 100644
--- a/proto/telecom.proto
+++ b/proto/telecom.proto
@@ -13,6 +13,9 @@
 
   // Timing information for the logging sessions
   repeated LogSessionTiming session_timings = 2;
+
+  // Hardware revision (EVT, DVT, PVT etc.)
+  optional string hardware_revision = 3;
 }
 
 message LogSessionTiming {
@@ -145,6 +148,7 @@
     BLOCK_CHECK_FINISHED_TIMING = 9;
     FILTERING_COMPLETED_TIMING = 10;
     FILTERING_TIMED_OUT_TIMING = 11;
+    START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING = 12;
   }
 
   // The name of the event timing.
@@ -230,6 +234,18 @@
     CONNECTION_MANAGER_NOT_SUPPORTED = 10;
   }
 
+  // The source where user initiated this call.
+  enum CallSource {
+    // Call source is not specified.
+    CALL_SOURCE_UNSPECIFIED = 0;
+
+    // Dialpad at emergency dialer.
+    CALL_SOURCE_EMERGENCY_DIALPAD = 1;
+
+    // Shortcut button at emergency dialer.
+    CALL_SOURCE_EMERGENCY_SHORTCUT = 2;
+  }
+
   // Start time of the connection.
   // Rounded to the nearest 5 minute interval.
   optional int64 start_time_5min = 1;
@@ -283,4 +299,7 @@
   // A bitmask of the properties that were set at any point during the call.
   // Bits are defined by android.telecom.Connection.PROPERTY_* constants.
   optional int32 connection_properties = 17;
+
+  // Call source.
+  optional CallSource call_source = 18;
 }
diff --git a/res/raw/record.ogg b/res/raw/record.ogg
new file mode 100644
index 0000000..a023e6d
--- /dev/null
+++ b/res/raw/record.ogg
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ccc7e28..8b488c2 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Oproep kan nie gemaak word nie weens \'n oproep in \'n ander program."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Inkomende oproepe"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Gemiste oproepe"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Oproepblokkering"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"As jy hierdie oproep maak, sal dit jou <xliff:g id="OTHER_APP">%1$s</xliff:g>-oproep beëindig."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Oproepblokkering"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nommers nie in Kontakte nie"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokkeer nommers wat nie in jou Kontakte gelys is nie"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privaat"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokkeer bellers wat nie hulle nommer bekendmaak nie"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefoonhokkies"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokkeer oproepe vanaf telefoonhokkies"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Onbekend"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokkeer oproepe vanaf onbekende bellers"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Oproepblokkering"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Oproepblokkering is gedeaktiveer"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Noodoproep gemaak"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Oproepblokkering is gedeaktiveer sodat noodeenhede jou kan kontak."</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 478fce5..6331a39 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"በሌላ መተግበሪያ ውስጥ ባለ ጥሪ ምክንያት ጥሪ መደረግ አይችልም።"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ገቢ ጥሪዎች"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"ያመለጡ ጥሪዎች"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ጥሪን ማገድ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ይህን ጥሪ ማድረግ የ<xliff:g id="OTHER_APP">%1$s</xliff:g> ጥሪዎን ያቋርጣል።"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ጥሪን ማገድ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"ቁጥሮች በእውቂያዎች ውስጥ የሉም"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"በእውቂያዎችዎ ውስጥ ያልተዘረዘሩ ቁጥሮችን አግድ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"የግል"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ቁጥራቸውን የማይገልጹ ደዋዮችን አግድ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"የክፍያ ስልክ"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"የክፍያ ስልኮች የሚመጡ ጥሪዎችን አግድ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ያልታወቀ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ካልታወቁ ደዋዮች የሚመጡ ጥሪዎችን አግድ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ጥሪን ማገድ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ጥሪ ማገድ ተሰናክሏል"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"የአደጋ ጊዜ ጥሪ ተደርጓል"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"የአደጋ ጊዜ ምላሽ ሰጪዎች እርስዎን ለማግኘት እንዲችሉ ጥሪ ማገድ ተሰናክሏል።"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 9684b94..16321c2 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -27,11 +27,11 @@
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"معاودة الاتصال"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"رسالة"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"تم كتم صوت المكالمة."</string>
-    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"تم تمكين مكبر صوت الهاتف."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"لا يمكنني التحدث الآن. ماذا هناك؟"</string>
+    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"تم تفعيل مكبر صوت الهاتف."</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"لا يمكنني التحدث الآن. ما الأمر؟"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"سأعاود الاتصال بك."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"سأتصل بك لاحقًا."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"لا يمكنني التحدث الآن. اتصل بي لاحقًا."</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"لا يمكنني التحدث الآن. اتصل لاحقًا."</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"الردود السريعة"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"تعديل الردود السريعة"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -45,12 +45,12 @@
     <string name="no_vm_number" msgid="4164780423805688336">"رقم البريد الصوتي مفقود"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"‏لم يتم تخزين رقم بريد صوتي على شريحة SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"إضافة رقم"</string>
-    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"هل تريد تعيين <xliff:g id="NEW_APP">%s</xliff:g> باعتباره تطبيق الهاتف الافتراضي؟"</string>
-    <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"تعيين كافتراضي"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"هل تريد تعيين <xliff:g id="NEW_APP">%s</xliff:g> كتطبيق الهاتف التلقائي؟"</string>
+    <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"التحديد كتطبيق تلقائي"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"إلغاء"</string>
-    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"سيتمكن <xliff:g id="NEW_APP">%s</xliff:g> من إجراء المكالمات والتحكم في كل جوانبها. يمكن فقط تعيين التطبيقات التي تثق بها باعتبارها تطبيق الهاتف الافتراضي."</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"سيتمكن <xliff:g id="NEW_APP">%s</xliff:g> من إجراء المكالمات والتحكم في كل جوانبها. يمكن فقط تعيين التطبيقات التي تثق بها كتطبيق الهاتف التلقائي."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"الأرقام المحظورة"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"لن تتلقى أي مكالمات أو مراسلات نصية من الأرقام المحظورة."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"لن تتلقّى أي مكالمات أو مراسلات نصية من الأرقام المحظورة."</string>
     <string name="block_number" msgid="1101252256321306179">"إضافة رقم"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"هل تريد إلغاء حظر <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>؟"</string>
     <string name="unblock_button" msgid="3078048901972674170">"إلغاء الحظر"</string>
@@ -61,8 +61,8 @@
     <string name="delete_icon_description" msgid="8903995728252556724">"إلغاء الحظر"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"تم إيقاف الحظر مؤقتًا"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"بعد الاتصال الهاتفي أو إرسال رسالة نصية إلى رقم طوارئ، يتم إيقاف تشغيل الحظر لضمان تمكن خدمات الطوارئ من الاتصال بك."</string>
-    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"إعادة تمكين الآن"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> محظور"</string>
+    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"إعادة تفعيل الآن"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"تم حظر <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"تم إلغاء حظر <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"غير قادر على حظر رقم الطوارئ."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"تم حظر <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> بالفعل."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"يتعذر إجراء المكالمة نتيجة لوجود مكالمة في تطبيق آخر."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"المكالمات الواردة"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"المكالمات الفائتة"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"حظر المكالمات"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"يؤدي إجراء هذه المكالمة إلى إنهاء مكالمة <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"حظر المكالمات"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"الأرقام غير المسردة في \"جهات الاتصال\""</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"حظر المكالمات عير المسردة في \"جهات الاتصال\""</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"خاص"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"حظر المتصلين الذين لا يكشفون رقمهم"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"هاتف مدفوع"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"حظر المكالمات من الهواتف المدفوعة"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"غير معروف"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"حظر المكالمات من المتّصلين مجهولي الهوية"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"حظر المكالمات"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"تم إيقاف حظر المكالمات"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"تم إجراء مكالمة طوارئ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"تم إيقاف حظر المكالمات للسماح لمسؤولي استجابة الطوارئ بالاتصال بك."</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
new file mode 100644
index 0000000..a8a1b2e
--- /dev/null
+++ b/res/values-as/strings.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="telecommAppLabel" product="default" msgid="382363169988504520">"কল পৰিচালনা"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"ফ’ন"</string>
+    <string name="unknown" msgid="6878797917991465859">"অজ্ঞাত"</string>
+    <string name="notification_missedCallTitle" msgid="7554385905572364535">"মিছ্ড কল"</string>
+    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"মিছ কৰা কৰ্মস্থানৰ কল"</string>
+    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"মিছ্ড কল"</string>
+    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>টা মিছ্ড কল"</string>
+    <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>ৰ পৰা মিছ্ড কল"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"কলবেক কৰক"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"বাৰ্তা"</string>
+    <string name="accessibility_call_muted" msgid="2776111226185342220">"কল মিউট কৰা হৈছে।"</string>
+    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"স্পীকাৰফ\'ন সক্ষম কৰা হৈছে।"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"এতিয়া কথা পাতিব নোৱাৰোঁ। কি খবৰ?"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"মই আপোনাক লগে লগে কলবেক কৰি আছোঁ।"</string>
+    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"মই আপোনাক পিছত কল কৰিম।"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"এতিয়া কথা পাতিব নোৱাৰোঁ। মোক পিছত কল কৰিবনে?"</string>
+    <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"ক্ষীপ্ৰ উত্তৰসমূহ"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ক্ষীপ্ৰ উত্তৰসমূহ সম্পাদনা কৰক"</string>
+    <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
+    <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"তাৎক্ষণিক উত্তৰ"</string>
+    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g>লৈ বাৰ্তা পঠিওৱা হ’ল।"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"কলিং একাউণ্ট"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"কেৱল জৰুৰীকালীন কল কৰাৰ হে অনুমতি আছে।"</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"ফ\'ন অনুমতিটোৰ অবিহনে এই এপ্লিকেশ্বনটোৱে কোনো বহিৰ্গামী কল কৰিব নোৱাৰে।"</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"কল কৰিবৰ কাৰণে এটা মান্য নম্বৰ দিয়ক।"</string>
+    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"এই মুহূৰ্তত কল যোগ কৰিব নোৱাৰি।"</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"ভইচমেইল নম্বৰ নাই"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"ছিম কাৰ্ডত কোনো ভইচমেইল নম্বৰ সঞ্চিত কৰি থোৱা হোৱা নাই।"</string>
+    <string name="add_vm_number_str" msgid="4676479471644687453">"নম্বৰ যোগ কৰক"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"<xliff:g id="NEW_APP">%s</xliff:g>ক আপোনাৰ ডিফ\'ল্ট ফ\'ন এপ্ হিচাপে চিহ্নিত কৰেনে?"</string>
+    <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"ডিফ\'ল্ট ছেট কৰক"</string>
+    <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"বাতিল কৰক"</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g>এ কল কৰা লগতে কলৰ সকলো দিশ নিয়ন্ত্ৰণ কৰিবলৈ সক্ষম হ\'ব। কেৱল আপুনি সম্পূৰ্ণৰূপে বিশ্বাস কৰা এপক হে আপোনাৰ ডিফ\'ল্ট ফ\'ন এপ্ হিচাপে চিহ্নিত কৰা উচিত।"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"অৱৰোধ কৰা নম্বৰসমূহ"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"আপুনি অৱৰোধ কৰা নম্বৰসমূহৰ পৰা আৰু কল বা বাৰ্তা লাভ নকৰে।"</string>
+    <string name="block_number" msgid="1101252256321306179">"নম্বৰ যোগ কৰক"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>ক অৱৰোধৰ পৰা আঁতৰাইনে?"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"অৱৰোধৰ পৰা আঁতৰাওক"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"এই নম্বৰৰ পৰা কল আৰু পাঠ বাৰ্তা অৱৰোধ কৰক"</string>
+    <string name="add_blocked_number_hint" msgid="6847675097085433553">"ফ\'ন নম্বৰ"</string>
+    <string name="block_button" msgid="8822290682524373357">"অৱৰোধ কৰক"</string>
+    <string name="non_primary_user" msgid="5180129233352533459">"কেৱল ডিভাইচটোৰ গৰাকীয়েহে অৱৰোধ কৰা নম্বৰসমূহ চাব আৰু পৰিচালনা কৰিব পাৰে।"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"অৱৰোধৰ পৰা আঁতৰাওক"</string>
+    <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"সাময়িকভাৱে অৱৰোধৰ সুবিধা বন্ধ কৰি থোৱা হৈছে"</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"আপুনি জৰুৰীকালীন নম্বৰ এটা ডায়েল কৰাৰ পিছত বা সেই নম্বৰটোলৈ পাঠ বাৰ্তা পঠিওৱাৰ পিছত নম্বৰটো অৱৰোধৰ পৰা আঁতৰোৱা হয় যাতে জৰুৰীকালীন সেৱাসমূহে আপোনাৰ সৈতে যোগাযোগ কৰিব পাৰে।"</string>
+    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"এতিয়াই পুনঃসক্ষম কৰক"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> অৱৰোধ কৰা হৈছে"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> অৱৰোধৰ পৰা আঁতৰ কৰা হৈছে"</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"জৰুৰীকালীন নম্বৰ অৱৰোধ কৰিব পৰা নাই।"</string>
+    <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>ক ইতিমধ্যে অৱৰোধ কৰা হৈছে।"</string>
+    <string name="toast_personal_call_msg" msgid="5115361633476779723">"কলটো কৰিবলৈ ব্যক্তিগত ডায়েলাৰৰ ব্যৱহাৰ কৰা হৈছে"</string>
+    <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_VIA">%1$s</xliff:g> <xliff:g id="CALL_FROM">%2$s</xliff:g>ৰ পৰা অহা কল"</string>
+    <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_VIA">%1$s</xliff:g> <xliff:g id="CALL_FROM">%2$s</xliff:g>ৰ পৰা অহা ভিডিঅ\' কল"</string>
+    <string name="answering_ends_other_call" msgid="8282145910153766401">"উত্তৰ দিলে <xliff:g id="CALL_VIA">%1$s</xliff:g> কলটোৰ অন্ত পৰিব"</string>
+    <string name="answering_ends_other_calls" msgid="1198589551399049197">"উত্তৰ দিলে <xliff:g id="CALL_VIA">%1$s</xliff:g> কলকেইটাৰ অন্ত পৰিব"</string>
+    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"উত্তৰ দিলে আপোনাৰ বৰ্তমান চলি থকা <xliff:g id="CALL_VIA">%1$s</xliff:g> ভিডিঅ\' কলটোৰ অন্ত পৰিব"</string>
+    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"উত্তৰ দিলে আপোনাৰ বৰ্তমান চলি থকা কলটোৰ অন্ত পৰিব"</string>
+    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"উত্তৰ দিলে আপোনাৰ বৰ্তমান চলি থকা কলসমূহৰ অন্ত পৰিব"</string>
+    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"উত্তৰ দিলে আপোনাৰ বৰ্তমান চলি থকা ভিডিঅ\' কলটোৰ অন্ত পৰিব"</string>
+    <string name="answer_incoming_call" msgid="4140530013111794587">"উত্তৰ"</string>
+    <string name="decline_incoming_call" msgid="806026168661598368">"প্ৰত্যাখ্যান কৰক"</string>
+    <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"আপোনাৰ <xliff:g id="OTHER_CALL">%1$s</xliff:g> কল চলি থকাৰ কাৰণে বেলেগ কল কৰিব নোৱাৰি।"</string>
+    <string name="cant_call_due_to_ongoing_calls" msgid="1380804892363503856">"আপোনাৰ <xliff:g id="OTHER_CALL">%1$s</xliff:g> কলকেইটা চলি থকাৰ কাৰণে বেলেগ কল কৰিব নোৱাৰি।"</string>
+    <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"অইন এটা এপত কল চলি থকাৰ কাৰণে বেলেগ কল কৰিব নোৱাৰি।"</string>
+    <string name="notification_channel_incoming_call" msgid="3513761697082968084">"অন্তৰ্গামী কল"</string>
+    <string name="notification_channel_missed_call" msgid="8727062678632713146">"মিছ্ড কল"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"কল অৱৰোধ"</string>
+    <string name="alert_outgoing_call" msgid="982908156825958001">"এই কলটো কৰিলে আপোনাৰ <xliff:g id="OTHER_APP">%1$s</xliff:g> কলটোৰ অন্ত পৰিব।"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"কল অৱৰোধ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"আপোনাৰ সর্ম্পকসূচীত নথকা"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"আপোনাৰ সর্ম্পকসূচীত নথকা নম্বৰ অৱৰোধ কৰক"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ব্য়ক্তিগত"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"যিসকল কল কৰোঁতাই তেওঁলোকৰ নম্বৰ প্ৰকাশ নকৰে তেওঁলোকক অৱৰোধ কৰক"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"পে\'ফ\'ন"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"পে\'ফ\'নৰ পৰা অহা কল অৱৰোধ কৰক"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"অজ্ঞাত"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"অচিনাক্ত কল কৰোঁতাৰ পৰা অহা কল অৱৰোধ কৰক"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"কল অৱৰোধ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"কল অৱৰোধ সুবিধাটো অক্ষম কৰি থোৱা আছে"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"জৰুৰীকালীন কল ম\'ড"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"আপোনাক যাতে জৰুৰীকালীন সেৱা প্ৰদানকাৰীসকলে যোগাযোগ কৰিব পাৰে তাৰ বাবে কল অৱৰোধ সুবিধাটো অক্ষম কৰি থোৱা হৈছে।"</string>
+</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index ac3bf76..fd42626 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -31,9 +31,9 @@
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"İndi danışmaq olmur. Nə olub?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Özüm zəng edəcəm."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Özüm sonra zəng edəcəm."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"İndi danışa bilmirəm. Sonra zəng edin."</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Danışa bilmirəm. Sonra zəngləşərik."</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Tez cavablar"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Tez cavablara düzəliş edin"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Hazır cavablara düzəliş edin"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Tez cavab"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesaj <xliff:g id="PHONE_NUMBER">%s</xliff:g> nömrəsinə göndərildi."</string>
@@ -54,7 +54,7 @@
     <string name="block_number" msgid="1101252256321306179">"Nömrə əlavə edin"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> nömrəsi blokdan çıxarılsın?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Blokdan çıxar"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Zəngləri və mətnləri buradan blok edin"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bu nömrədən olan zəngləri və mətnləri bloklayın"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Telefon nömrəsi"</string>
     <string name="block_button" msgid="8822290682524373357">"Blok edin"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Yalnız cihaz sahibi blok edilmiş nömrələrə baxa və idarə edə bilər."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Başqa bir tətbiqdəki zəng səbəbilə çağrı edilə bilməz."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Gələn zənglər"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Buraxılmış zənglər"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Zəngi Bloklama"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Bu zəngin yerləşdirilməsi <xliff:g id="OTHER_APP">%1$s</xliff:g> zəngini sonlandıracaq."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Zəngi Bloklama"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Kontaktda olmayan nömrələr"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Kontaktda göstərilməyən nömrələri blok edin"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Şəxsi"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Nömrələrini bildirməyən şəxslərin zənglərini blok edin"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefon ödənişi"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Ödənişli telefon zənglərini blok edin"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Naməlum"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Naməlum şəxslərdən gələn zəngləri blok edin"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Zəngi Bloklama"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Zəngi Bloklama deaktivdir"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Təcili zəng edildi"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Təcili zənglərə cavab verənlərin Sizinlə əlaqə saxlamalarına icazə vermək üçün Zəngi Bloklama deaktiv edilib."</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index ef4025b..0730093 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -51,7 +51,7 @@
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> će moći da upućuje pozive i kontroliše sve njihove aspekte. Aplikaciju podesite kao podrazumevanu za telefoniranje samo ako je smatrate pouzdanom."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Blokirani brojevi"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Nećete primati pozive ni SMS-ove sa blokiranih brojeva."</string>
-    <string name="block_number" msgid="1101252256321306179">"Dodaj broj"</string>
+    <string name="block_number" msgid="1101252256321306179">"Dodajte broj"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Želite li da deblokirate <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Deblokiraj"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokirajte pozive i SMS-ove od"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Ne možete da uputite poziv zbog poziva u drugoj aplikaciji."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Dolazni pozivi"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Propušteni pozivi"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokiranje poziva"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ako uputite ovaj poziv, završićete <xliff:g id="OTHER_APP">%1$s</xliff:g> poziv."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokiranje poziva"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Brojevi koji nisu u kontaktima"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokirajte brojeve koji vam nisu u kontaktima"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Skriveni brojevi"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokirajte pozivaoce koji skrivaju broj"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefonska govornica"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokirajte pozive sa telefonskih govornica"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Nepoznati brojevi"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokirajte pozive neidentifikovanih pozivalaca"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokiranje poziva"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokiranje poziva je onemogućeno"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Upućen je hitni poziv"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokiranje poziva je onemogućeno da bi hitne službe mogle da vas kontaktiraju."</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 9950e14..0cfa989 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Выклік немагчыма выканаць, бо ідзе выклік у іншай праграме."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Уваходныя выклікі"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Прапушчаныя выклікі"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Блакіраванне выклікаў"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Калі зрабіць гэты выклік, ваш выклік праз праграму <xliff:g id="OTHER_APP">%1$s</xliff:g> скончыцца."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Блакіраванне выклікаў"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Нумары не ў спісе кантактаў"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Блакіраваць нумары, якіх няма ў спісе вашых кантактаў"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Схаваныя нумары"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Блакіраваць абанентаў, якія не раскрываюць свой нумар"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Таксафон"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Блакіраваць выклікі з таксафонаў"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Невядомыя нумары"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Блакіраваць выклікі ад неапазнаных абанентаў"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Блакіраванне выклікаў"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Блакіраванне выклікаў адключана"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Зроблены экстранны выклік"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Блакіраванне выклікаў было адключана, каб дазволіць аварыйнай брыгадзе звязацца з вамі."</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index babb59c..102fb6f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Не можете да се обадите заради обаждане в друго приложение."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Входящи обаждания"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Пропуснати обаждания"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Блокиране на обажданията"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ако извършите това обаждане, обаждането ви през <xliff:g id="OTHER_APP">%1$s</xliff:g> ще прекъсне."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Блокиране на обажданията"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Номера, които не са в контактите"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Блокиране на номерата, които не са в контактите ви"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Скрити"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Блокиране на обаждащите се, които крият номера си"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Импулсни телефони"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Блокиране на обаждания от импулсни телефони"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Неизвестни"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Блокиране на обажданията от неидентифицирани обаждащи се"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Блокиране на обажданията"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Блокирането на обажданията е деактивирано"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Извършено бе спешно обаждане"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Блокирането на обажданията е деактивирано, за да могат службите за спешни случаи да се свържат с вас."</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 00c5312..5f5a8bb 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -25,8 +25,8 @@
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>টি মিসড কল"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> এর থেকে মিসড কল"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"কল ব্যাক করুন"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"বার্তা"</string>
-    <string name="accessibility_call_muted" msgid="2776111226185342220">"কল নিঃশব্দ করা আছে৷"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"মেসেজ"</string>
+    <string name="accessibility_call_muted" msgid="2776111226185342220">"কল মিউট করা আছে৷"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"স্পীকারফোন সক্ষম করা আছে৷"</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"এখন কথা বলতে পারছি না৷ কি খবর?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"আমি আপনাকে কিছুক্ষণ পরেই কল করছি৷"</string>
@@ -49,21 +49,21 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"ডিফল্ট হিসাবে সেট করুন"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"বাতিল করুন"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> কল করতে এবং কলের সমগ্র বৈশিষ্ট্য নিয়ন্ত্রণ করতে সক্ষম হবে৷ শুধুমাত্র আপনি যে অ্যাপ্সকে বিশ্বাস করেন সেগুলিকেই ডিফল্ট ফোন অ্যাপ হিসাবে সেট করা উচিৎ৷"</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"অবরুদ্ধ নম্বরগুলি"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"অবরুদ্ধ নম্বরগুলি থেকে আপনি কল বা এসএমএস পাবেন না।"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"ব্লক করা নম্বরগুলি"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ব্লক করা নম্বরগুলি থেকে আপনি কল বা এসএমএস পাবেন না।"</string>
     <string name="block_number" msgid="1101252256321306179">"একটি নম্বর যোগ করুন"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> অবরোধ মুক্ত করবেন?"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> আনব্লক করবেন?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"অবরোধ মুক্ত করুন"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"এর থেকে কল এবং এসএমএস অবরোধ করুন"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"এর থেকে কল এবং এসএমএস ব্লক করুন"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"ফোন নম্বর"</string>
-    <string name="block_button" msgid="8822290682524373357">"অবরোধ করুন"</string>
+    <string name="block_button" msgid="8822290682524373357">"ব্লক করুন"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"শুধুমাত্র ডিভাইসের মালিক এই অবরুদ্ধ নম্বরগুলিকে দেখতে এবং পরিচালনা করতে পারেন৷"</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"অবরোধ মুক্ত করুন"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"অবরুদ্ধ করা সাময়িকভাবে বন্ধ আছে"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"আপনি কোনো জরুরি নম্বরে ডায়াল করার বা এসএমএস পাঠানোর পরে, জরুরি পরিষেবাগুলি যাতে আপনাকে কল করতে পারে সেই বিষয়টি নিশ্চিত করতে অবরুদ্ধ করার ব্যবস্থাটিকে বন্ধ করা হয়৷"</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"এখনই পুনরায় সক্ষম করুন"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> অবরোধ করা হয়েছে"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> অবরোধ মুক্ত করা হয়েছে"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> আনব্লক করা হয়েছে"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"জরুরি নম্বর অবরোধ করতে অক্ষম৷"</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ইতিমধ্যেই অবরোধ করা রয়েছে৷"</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"কল করার জন্য ব্যক্তিগত ডায়ালার ব্যবহার করা হচ্ছে"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"অন্য একটি অ্যাপের কলের কারণে কলটি করা যাবে না।"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ইনকামিং কল"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"মিস করা কল"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"কল ব্লক করা"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"এই কলটির উত্তর দেওয়া হলে তা আপনার <xliff:g id="OTHER_APP">%1$s</xliff:g> কলটি কেটে যাবে৷"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"কল ব্লক করা"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"পরিচিতিতে নেই এমন নম্বর"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"আপনার পরিচিতিতে নেই এমন নম্বর ব্লক করুন"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ব্যক্তিগত"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"কলার আইডিতে নম্বর দেখা যায়না এমন কল ব্লক করুন"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"পাবলিক ফোন"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"পাবলিক ফোন থেকে করা কল ব্লক করুন"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"অজানা"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"অচেনা নম্বর থেকে করা কল ব্লক করুন"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"কল ব্লক করা"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"কল ব্লক করার বৈশিষ্ট্য বন্ধ করা হয়েছে"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"জরুরি অবস্থার কল করা হয়েছে"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"কল ব্লক করার বৈশিষ্ট্য বন্ধ করা হয়েছে যাতে জরুরি অবস্থার সাহায্যকারী ব্যক্তি আপনার সাথে যোগাযোগ করতে পারেন।"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 569e738..44ee8e4 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -31,7 +31,7 @@
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Ne mogu sada pričati. O čemu se radi?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Nazvat ću te uskoro."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Nazvat ću te kasnije."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Ne mogu pričati. Nazovi me kasnije."</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Ne mogu sada pričati. Nazovi me kasnije."</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Brzi odgovori"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Uredi brze odgovore"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -54,13 +54,13 @@
     <string name="block_number" msgid="1101252256321306179">"Dodaj broj"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Deblokirati <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Deblokiraj"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokiraj pozive i poruke od"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokirajte pozive i SMS-ove od"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Broj telefona"</string>
     <string name="block_button" msgid="8822290682524373357">"Blokiraj"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Samo vlasnik uređaja može pregledati i upravljati blokiranim brojevima."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"Deblokiraj"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Blokiranje je privremeno isključeno"</string>
-    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Nakon što pozovete ili pošaljete poruku na broj za hitne slučajeve, blokiranje se isključuje da bi vas hitna služba mogla kontaktirati."</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Nakon što pozovete ili pošaljete poruku na broj za hitne slučajeve, blokiranje se isključuje da bi vas hitne službe mogle kontaktirati."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Ponovo omogući sada"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> je blokiran"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> je deblokiran"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Pozivanje nije moguće zbog poziva u drugoj aplikaciji."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Dolazni pozivi"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Propušteni pozivi"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokiranje poziva"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Upućivanje ovog poziva će prekinuti poziv: <xliff:g id="OTHER_APP">%1$s</xliff:g>"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokiranje poziva"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Brojevi koji nisu u Kontaktima"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokirajte brojeve koji se ne nalaze u vašim Kontaktima"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privatan"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokirajte pozivaoce koji ne otkrivaju svoj broj"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefonska govornica"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokirajte pozive s telefonskih govornica"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Nepoznato"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokirajte pozive neidentificiranih pozivalaca"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokiranje poziva"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokiranje poziva je onemogućeno"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Upućen je hitni poziv"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokiranje poziva je onemogućeno kako bi se omogućilo osobama koje reagiraju u hitnim slučajevima da vas kontaktiraju."</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 3776db9..2673eb1 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -31,7 +31,7 @@
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Ara no puc parlar. Què passa?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Et truco de seguida."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Et truco més tard."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Ara no puc parlar. Truques després?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"No puc parlar. Em truques després?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Respostes ràpides"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edita les respostes ràpides"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"No es pot trucar perquè ja hi ha una trucada en curs en una altra aplicació."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Trucades entrants"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Trucades perdudes"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bloqueig de trucades"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"En fer aquesta trucada, finalitzarà la de l\'aplicació <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bloqueig de trucades"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Números que no surten a Contactes"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloqueja els números que no surten a Contactes"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privades"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloqueja les trucades que tinguin el número ocult"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telèfons públics"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloqueja les trucades de telèfons públics"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Desconeguts"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloqueja les trucades de números no identificats"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bloqueig de trucades"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"El bloqueig de trucades s\'ha desactivat"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"S\'ha fet una trucada d\'emergència"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"El bloqueig de trucades s\'ha desactivat perquè els serveis d\'emergència puguin contactar amb tu."</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 2a9ac60..76c40e6 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Hovor není možné provést kvůli hovoru v jiné aplikaci."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Příchozí hovory"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Zmeškané hovory"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokování hovorů"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Uskutečněním tohoto hovoru ukončíte hovor <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokování hovorů"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Čísla, která nejsou v kontaktech"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokovat čísla, která nemáte v kontaktech"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Ze skrytých čísel"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokovat volající, kteří skrývají své číslo"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Z veřejných telefonů"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokovat hovory z veřejných telefonů"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Z nerozpoznaných čísel"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokovat hovory od nerozpoznaných volajících"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokování hovorů"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokování hovorů bylo vypnuto"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Uskutečněno tísňové volání"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokování hovorů bylo vypnuto, aby vás mohli kontaktovat pracovníci tísňových služeb."</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 2e0ed39..1ba9a08 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -36,11 +36,11 @@
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Rediger hurtige svar"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hurtigt svar"</string>
-    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Meddelelsen er sendt til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
+    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Beskeden er sendt til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
     <string name="enable_account_preference_title" msgid="2021848090086481720">"Opkaldskonti"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Kun nødopkald er tilladt."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Denne app kan ikke foretage udgående opkald uden opkaldstilladelse."</string>
-    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Indtast et gyldigt nummer for at foretage et opkald."</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Angiv et gyldigt nummer for at foretage et opkald."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Opkaldet kan ikke tilføjes på nuværende tidspunkt."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Telefonsvarernummer mangler"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Der er ikke gemt noget telefonsvarernummer på SIM-kortet."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Opkaldet kan ikke foretages på grund et opkald i en anden app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Indgående opkald"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Ubesvarede opkald"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Opkaldsblokering"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Hvis du foretager dette opkald, afsluttes dit opkald i <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Opkaldsblokering"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numre er ikke i Kontakter"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloker numre, som ikke er i dine kontakter"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privat"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloker opkald fra personer, der ringer fra hemmeligt nummer"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Mønttelefon"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloker opkald fra mønttelefoner"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Ukendt"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloker opkald fra numre uden opkalds-id"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Opkaldsblokering"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Opkaldsblokering er deaktiveret"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Foretagne nødopkald"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Opkaldsblokering er blevet deaktiveret for at give nødnumre mulighed for at kontakte dig."</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index f0a4b75..d34d905 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Dieser Anruf kann aufgrund eines Anrufs in einer anderen App nicht getätigt werden."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Eingehende Anrufe"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Entgangene Anrufe"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Anrufblockierung"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Durch diesen Anruf wird der Anruf in <xliff:g id="OTHER_APP">%1$s</xliff:g> beendet."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Anrufblockierung"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nicht in den Kontakten gespeicherte Telefonnummern"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Telefonnummern, die nicht in den Kontakten gespeichert sind, werden blockiert"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privat"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Anrufer mit unterdrückter Nummer werden blockiert"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Öffentliche Telefone"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Anrufe von öffentlichen Telefonen blockieren"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Unbekannt"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Anrufe von nicht identifizierten Anrufern blockieren"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Anrufblockierung"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Anrufblockierung deaktiviert"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Notruf abgesetzt"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Die Anrufblockierung wurde deaktiviert, damit Ersthelfer und Rettungskräfte dich kontaktieren können."</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 5dd11d5..cb21b12 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Δεν είναι δυνατή η πραγματοποίηση της κλήσης, λόγω κάποιας κλήσης μέσω άλλης εφαρμογής."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Εισερχόμενες κλήσεις"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Αναπάντητες κλήσεις"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Φραγή κλήσεων"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Εάν πραγματοποιήσετε αυτήν την κλήση, η κλήση σας μέσω <xliff:g id="OTHER_APP">%1$s</xliff:g> θα τερματιστεί."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Φραγή κλήσεων"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Αριθμοί που δεν βρίσκονται στις Επαφές"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Αποκλεισμός αριθμών που δεν περιλαμβάνονται στις επαφές σας"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Με απόκρυψη"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Αποκλεισμός καλούντων που δεν αποκαλύπτουν τον αριθμό τους"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Καρτοτηλέφωνο"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Αποκλεισμός κλήσεων από καρτοτηλέφωνα"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Άγνωστος"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Αποκλεισμός κλήσεων από αγνώστους"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Φραγή κλήσεων"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Φραγή κλήσεων απενεργοποιημένη"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Πραγματοποιήθηκε κλήση έκτακτης ανάγκης"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Η φραγή κλήσεων έχει απενεργοποιηθεί, ώστε να επιτρέπεται σε άτομα που ανταποκρίνονται σε έκτακτες ανάγκες να επικοινωνούν μαζί σας."</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 7d344f1..d62ac55 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Call cannot be placed due to a call in another app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Incoming calls"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Missed calls"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Call Blocking"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Placing this call will end your <xliff:g id="OTHER_APP">%1$s</xliff:g> call."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Call Blocking"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numbers not in Contacts"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Block numbers that are not listed in your Contacts"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Private"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Block callers who do not disclose their number"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Phonebox"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Block calls from pay phones"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Unknown"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Block calls from unidentified callers"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Call Blocking"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Call Blocking disabled"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Emergency call made"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Call Blocking has been disabled to allow emergency responders to contact you."</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 7d344f1..d62ac55 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Call cannot be placed due to a call in another app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Incoming calls"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Missed calls"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Call Blocking"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Placing this call will end your <xliff:g id="OTHER_APP">%1$s</xliff:g> call."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Call Blocking"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numbers not in Contacts"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Block numbers that are not listed in your Contacts"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Private"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Block callers who do not disclose their number"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Phonebox"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Block calls from pay phones"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Unknown"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Block calls from unidentified callers"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Call Blocking"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Call Blocking disabled"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Emergency call made"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Call Blocking has been disabled to allow emergency responders to contact you."</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 7d344f1..d62ac55 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Call cannot be placed due to a call in another app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Incoming calls"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Missed calls"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Call Blocking"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Placing this call will end your <xliff:g id="OTHER_APP">%1$s</xliff:g> call."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Call Blocking"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numbers not in Contacts"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Block numbers that are not listed in your Contacts"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Private"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Block callers who do not disclose their number"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Phonebox"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Block calls from pay phones"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Unknown"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Block calls from unidentified callers"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Call Blocking"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Call Blocking disabled"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Emergency call made"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Call Blocking has been disabled to allow emergency responders to contact you."</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 7d344f1..d62ac55 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Call cannot be placed due to a call in another app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Incoming calls"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Missed calls"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Call Blocking"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Placing this call will end your <xliff:g id="OTHER_APP">%1$s</xliff:g> call."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Call Blocking"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numbers not in Contacts"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Block numbers that are not listed in your Contacts"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Private"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Block callers who do not disclose their number"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Phonebox"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Block calls from pay phones"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Unknown"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Block calls from unidentified callers"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Call Blocking"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Call Blocking disabled"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Emergency call made"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Call Blocking has been disabled to allow emergency responders to contact you."</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 26daa02..7a44902 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -16,71 +16,85 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="382363169988504520">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎Call Management‎‏‎‎‏‎"</string>
-    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎Phone‎‏‎‎‏‎"</string>
-    <string name="unknown" msgid="6878797917991465859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎Unknown‎‏‎‎‏‎"</string>
-    <string name="notification_missedCallTitle" msgid="7554385905572364535">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎Missed call‎‏‎‎‏‎"</string>
-    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎Missed work call‎‏‎‎‏‎"</string>
-    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎Missed calls‎‏‎‎‏‎"</string>
-    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>‎‏‎‎‏‏‏‎ missed calls‎‏‎‎‏‎"</string>
-    <string name="notification_missedCallTicker" msgid="504686252427747209">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎Missed call from ‎‏‎‎‏‏‎<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎Call back‎‏‎‎‏‎"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎Message‎‏‎‎‏‎"</string>
-    <string name="accessibility_call_muted" msgid="2776111226185342220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎Call muted.‎‏‎‎‏‎"</string>
-    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎Speakerphone enabled.‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎Can\'t talk now. What\'s up?‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎I\'ll call you right back.‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎I\'ll call you later.‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎Can\'t talk now. Call me later?‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎Quick responses‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎Edit quick responses‎‏‎‎‏‎"</string>
+    <string name="telecommAppLabel" product="default" msgid="382363169988504520">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‎‎‎Call Management‎‏‎‎‏‎"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎Phone‎‏‎‎‏‎"</string>
+    <string name="unknown" msgid="6878797917991465859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎Unknown‎‏‎‎‏‎"</string>
+    <string name="notification_missedCallTitle" msgid="7554385905572364535">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎Missed call‎‏‎‎‏‎"</string>
+    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎Missed work call‎‏‎‎‏‎"</string>
+    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎Missed calls‎‏‎‎‏‎"</string>
+    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>‎‏‎‎‏‏‏‎ missed calls‎‏‎‎‏‎"</string>
+    <string name="notification_missedCallTicker" msgid="504686252427747209">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎Missed call from ‎‏‎‎‏‏‎<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎Call back‎‏‎‎‏‎"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎Message‎‏‎‎‏‎"</string>
+    <string name="accessibility_call_muted" msgid="2776111226185342220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎Call muted.‎‏‎‎‏‎"</string>
+    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎Speakerphone enabled.‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎Can\'t talk now. What\'s up?‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎I\'ll call you right back.‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‎I\'ll call you later.‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎Can\'t talk now. Call me later?‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎Quick responses‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎Edit quick responses‎‏‎‎‏‎"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
-    <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎Quick response‎‏‎‎‏‎"</string>
-    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎Message sent to ‎‏‎‎‏‏‎<xliff:g id="PHONE_NUMBER">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
-    <string name="enable_account_preference_title" msgid="2021848090086481720">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎Calling accounts‎‏‎‎‏‎"</string>
-    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎Only emergency calls are allowed.‎‏‎‎‏‎"</string>
-    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎This application cannot make outgoing calls without the Phone permission.‎‏‎‎‏‎"</string>
-    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎To place a call, enter a valid number.‎‏‎‎‏‎"</string>
-    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎Call cannot be added at this time.‎‏‎‎‏‎"</string>
-    <string name="no_vm_number" msgid="4164780423805688336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎Missing voicemail number‎‏‎‎‏‎"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎No voicemail number is stored on the SIM card.‎‏‎‎‏‎"</string>
-    <string name="add_vm_number_str" msgid="4676479471644687453">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎Add number‎‏‎‎‏‎"</string>
-    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎Make ‎‏‎‎‏‏‎<xliff:g id="NEW_APP">%s</xliff:g>‎‏‎‎‏‏‏‎ your default Phone app?‎‏‎‎‏‎"</string>
-    <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎Set Default‎‏‎‎‏‎"</string>
-    <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎Cancel‎‏‎‎‏‎"</string>
-    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="NEW_APP">%s</xliff:g>‎‏‎‎‏‏‏‎ will be able to place and control all aspects of calls. Only apps you trust should be set as the default Phone app.‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎Blocked numbers‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎You won\'t receive calls or texts from blocked numbers.‎‏‎‎‏‎"</string>
-    <string name="block_number" msgid="1101252256321306179">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎Add a number‎‏‎‎‏‎"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎Unblock ‎‏‎‎‏‏‎<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎Unblock‎‏‎‎‏‎"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎Block calls and texts from‎‏‎‎‏‎"</string>
-    <string name="add_blocked_number_hint" msgid="6847675097085433553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎Phone number‎‏‎‎‏‎"</string>
-    <string name="block_button" msgid="8822290682524373357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎Block‎‏‎‎‏‎"</string>
-    <string name="non_primary_user" msgid="5180129233352533459">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎Only the device owner can view and manage blocked numbers.‎‏‎‎‏‎"</string>
-    <string name="delete_icon_description" msgid="8903995728252556724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎Unblock‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎Blocking temporarily off‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎After you dial or text an emergency number, blocking is turned off to ensure that emergency services can contact you.‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎Re-enable now‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ blocked‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ unblocked‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎Unable to block emergency number.‎‏‎‎‏‎"</string>
-    <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is already blocked.‎‏‎‎‏‎"</string>
-    <string name="toast_personal_call_msg" msgid="5115361633476779723">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎Using the personal dialer to make the call‎‏‎‎‏‎"</string>
-    <string name="notification_incoming_call" msgid="7713197997773986670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call from ‎‏‎‎‏‏‎<xliff:g id="CALL_FROM">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="notification_incoming_video_call" msgid="6638486071698373893">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ video call from ‎‏‎‎‏‏‎<xliff:g id="CALL_FROM">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="answering_ends_other_call" msgid="8282145910153766401">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎Answering will end your ‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call‎‏‎‎‏‎"</string>
-    <string name="answering_ends_other_calls" msgid="1198589551399049197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎Answering will end your ‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ calls‎‏‎‎‏‎"</string>
-    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎Answering will end your ‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ video call‎‏‎‎‏‎"</string>
-    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎Answering will end your ongoing call‎‏‎‎‏‎"</string>
-    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎Answering will end your ongoing calls‎‏‎‎‏‎"</string>
-    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‎Answering will end your ongoing video call‎‏‎‎‏‎"</string>
-    <string name="answer_incoming_call" msgid="4140530013111794587">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎Answer‎‏‎‎‏‎"</string>
-    <string name="decline_incoming_call" msgid="806026168661598368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎Decline‎‏‎‎‏‎"</string>
-    <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎Call cannot be placed due to your ‎‏‎‎‏‏‎<xliff:g id="OTHER_CALL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call.‎‏‎‎‏‎"</string>
-    <string name="cant_call_due_to_ongoing_calls" msgid="1380804892363503856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎Call cannot be placed due to your ‎‏‎‎‏‏‎<xliff:g id="OTHER_CALL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ calls.‎‏‎‎‏‎"</string>
-    <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎Call cannot be placed due to a call in another app.‎‏‎‎‏‎"</string>
-    <string name="notification_channel_incoming_call" msgid="3513761697082968084">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‎Incoming calls‎‏‎‎‏‎"</string>
-    <string name="notification_channel_missed_call" msgid="8727062678632713146">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎Missed calls‎‏‎‎‏‎"</string>
-    <string name="alert_outgoing_call" msgid="982908156825958001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎Placing this call will end your ‎‏‎‎‏‏‎<xliff:g id="OTHER_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call.‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‎Quick response‎‏‎‎‏‎"</string>
+    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‎Message sent to ‎‏‎‎‏‏‎<xliff:g id="PHONE_NUMBER">%s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎Calling accounts‎‏‎‎‏‎"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎Only emergency calls are allowed.‎‏‎‎‏‎"</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎This application cannot make outgoing calls without the Phone permission.‎‏‎‎‏‎"</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎To place a call, enter a valid number.‎‏‎‎‏‎"</string>
+    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎Call cannot be added at this time.‎‏‎‎‏‎"</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎Missing voicemail number‎‏‎‎‏‎"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎No voicemail number is stored on the SIM card.‎‏‎‎‏‎"</string>
+    <string name="add_vm_number_str" msgid="4676479471644687453">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎Add number‎‏‎‎‏‎"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‎Make ‎‏‎‎‏‏‎<xliff:g id="NEW_APP">%s</xliff:g>‎‏‎‎‏‏‏‎ your default Phone app?‎‏‎‎‏‎"</string>
+    <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎Set Default‎‏‎‎‏‎"</string>
+    <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎Cancel‎‏‎‎‏‎"</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="NEW_APP">%s</xliff:g>‎‏‎‎‏‏‏‎ will be able to place and control all aspects of calls. Only apps you trust should be set as the default Phone app.‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎Blocked numbers‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎You won\'t receive calls or texts from blocked numbers.‎‏‎‎‏‎"</string>
+    <string name="block_number" msgid="1101252256321306179">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎Add a number‎‏‎‎‏‎"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎Unblock ‎‏‎‎‏‏‎<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎Unblock‎‏‎‎‏‎"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎Block calls and texts from‎‏‎‎‏‎"</string>
+    <string name="add_blocked_number_hint" msgid="6847675097085433553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎Phone number‎‏‎‎‏‎"</string>
+    <string name="block_button" msgid="8822290682524373357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎Block‎‏‎‎‏‎"</string>
+    <string name="non_primary_user" msgid="5180129233352533459">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‏‎Only the device owner can view and manage blocked numbers.‎‏‎‎‏‎"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎Unblock‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎Blocking temporarily off‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎After you dial or text an emergency number, blocking is turned off to ensure that emergency services can contact you.‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎Re-enable now‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ blocked‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ unblocked‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎Unable to block emergency number.‎‏‎‎‏‎"</string>
+    <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is already blocked.‎‏‎‎‏‎"</string>
+    <string name="toast_personal_call_msg" msgid="5115361633476779723">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎Using the personal dialer to make the call‎‏‎‎‏‎"</string>
+    <string name="notification_incoming_call" msgid="7713197997773986670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call from ‎‏‎‎‏‏‎<xliff:g id="CALL_FROM">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="notification_incoming_video_call" msgid="6638486071698373893">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ video call from ‎‏‎‎‏‏‎<xliff:g id="CALL_FROM">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="answering_ends_other_call" msgid="8282145910153766401">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎Answering will end your ‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call‎‏‎‎‏‎"</string>
+    <string name="answering_ends_other_calls" msgid="1198589551399049197">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎Answering will end your ‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ calls‎‏‎‎‏‎"</string>
+    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎Answering will end your ‎‏‎‎‏‏‎<xliff:g id="CALL_VIA">%1$s</xliff:g>‎‏‎‎‏‏‏‎ video call‎‏‎‎‏‎"</string>
+    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎Answering will end your ongoing call‎‏‎‎‏‎"</string>
+    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‎Answering will end your ongoing calls‎‏‎‎‏‎"</string>
+    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‎Answering will end your ongoing video call‎‏‎‎‏‎"</string>
+    <string name="answer_incoming_call" msgid="4140530013111794587">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎Answer‎‏‎‎‏‎"</string>
+    <string name="decline_incoming_call" msgid="806026168661598368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎Decline‎‏‎‎‏‎"</string>
+    <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎Call cannot be placed due to your ‎‏‎‎‏‏‎<xliff:g id="OTHER_CALL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call.‎‏‎‎‏‎"</string>
+    <string name="cant_call_due_to_ongoing_calls" msgid="1380804892363503856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎Call cannot be placed due to your ‎‏‎‎‏‏‎<xliff:g id="OTHER_CALL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ calls.‎‏‎‎‏‎"</string>
+    <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎Call cannot be placed due to a call in another app.‎‏‎‎‏‎"</string>
+    <string name="notification_channel_incoming_call" msgid="3513761697082968084">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‎Incoming calls‎‏‎‎‏‎"</string>
+    <string name="notification_channel_missed_call" msgid="8727062678632713146">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎Missed calls‎‏‎‎‏‎"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎Call Blocking‎‏‎‎‏‎"</string>
+    <string name="alert_outgoing_call" msgid="982908156825958001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‏‎Placing this call will end your ‎‏‎‎‏‏‎<xliff:g id="OTHER_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ call.‎‏‎‎‏‎"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‎‏‎Call Blocking‎‏‎‎‏‎"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎Numbers not in Contacts‎‏‎‎‏‎"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎Block numbers that are not listed in your Contacts‎‏‎‎‏‎"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎Private‎‏‎‎‏‎"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‎Block callers that do not disclose their number‎‏‎‎‏‎"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎Pay phone‎‏‎‎‏‎"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎Block calls from pay phones‎‏‎‎‏‎"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎Unknown‎‏‎‎‏‎"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎Block calls from unidentified callers‎‏‎‎‏‎"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎Call Blocking‎‏‎‎‏‎"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎Call Blocking disabled‎‏‎‎‏‎"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎Emergency call made‎‏‎‎‏‎"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎Call Blocking has been disabled to allow emergency responders to contact you.‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index fc35dcf..309e825 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -52,7 +52,7 @@
     <string name="blocked_numbers" msgid="2751843139572970579">"Números bloqueados"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"No recibirás llamadas ni mensajes de texto de los números bloqueados."</string>
     <string name="block_number" msgid="1101252256321306179">"Agregar un número"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"¿Quieres desbloquear el <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"¿Quieres desbloquear el número <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Desbloquear"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear llamadas y mensajes de texto de"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Número de teléfono"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"No se puede realizar la llamada porque hay una llamada en curso en otra app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Llamadas entrantes"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Llamadas perdidas"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bloqueo de llamadas"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Si realizas esta llamada, finalizará la de <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bloqueo de llamadas"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Números que no están en Contactos"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquear números que no estén en tus Contactos"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privado"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquear llamadas de emisores con números ocultos"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Teléfono público"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquear llamadas provenientes de teléfonos públicos"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Desconocido"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquear llamadas de emisores desconocidos"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bloqueo de llamadas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Se inhabilitó el bloqueo de llamadas"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Se realizó una llamada de emergencia"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Se inhabilitó el bloqueo de llamadas para permitir que los servicios de emergencia se comuniquen contigo."</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 3c08971..f3d17f5 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -50,11 +50,11 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Cancelar"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> podrá llamar y controlar todo lo relacionado con las llamadas. Te aconsejamos que solo configures una aplicación como predeterminada si es de confianza."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Números bloqueados"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"No recibirás llamadas ni SMS de los números bloqueados."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"No recibirás llamadas ni mensajes de los números bloqueados."</string>
     <string name="block_number" msgid="1101252256321306179">"Añadir un número"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"¿Desbloquear el número <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Desbloquear"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear llamadas y mensajes de texto del número"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear llamadas y mensajes de texto del"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Número de teléfono"</string>
     <string name="block_button" msgid="8822290682524373357">"Bloquear"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Solo el propietario del dispositivo puede ver y administrar los números bloqueados."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"No puedes llamar porque tienes una llamada en curso en otra aplicación."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Llamadas entrantes"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Llamadas perdidas"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bloqueo de llamadas"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Si haces esta llamada, se finalizará la de <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bloqueo de llamadas"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Números que no están en Contactos"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquea los números que no estén en Contactos"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privada"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquea las llamadas que tengan el número oculto"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Teléfono público"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquea las llamadas de teléfonos públicos"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Desconocidos"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquea las llamadas de números desconocidos"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bloqueo de llamadas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Se ha inhabilitado el bloqueo de llamadas"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Se ha hecho una llamada de emergencia"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Se ha inhabilitado el bloqueo de llamadas para que los servicios de emergencia puedan ponerse en contacto contigo."</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 70a4cf4..f94ac85 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Kõnet ei saa teise rakenduse kõne tõttu teha."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Sissetulevad kõned"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Vastamata kõned"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Kõnede blokeerimine"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Selle kõne tegemisel lõpetatakse pooleliolev kõne rakenduses <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Kõnede blokeerimine"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numbrid, mis ei ole kontaktide hulgas"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokeeritud numbreid ei lisata teie kontaktide hulka"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privaatne"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Nende helistajate blokeerimine, kes ei avalda oma numbrit"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefoniautomaat"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Kõnede blokeerimine telefoniautomaatidest"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Tundmatud"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Kõnede blokeerimine tuvastamata helistajatelt"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Kõnede blokeerimine"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Kõnede blokeerimine on keelatud"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Tehti hädaabikõne"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Kõnede blokeerimine on keelatud, et lubada hädaabiteenustel teiega ühendust võtta."</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 529c845..df8b015 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -51,8 +51,8 @@
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"Deien aspektu guztiak erabili eta kontrolatu ahal izango ditu <xliff:g id="NEW_APP">%s</xliff:g> aplikazioak. Fidagarriak diren aplikazioak bakarrik erabili beharko lirateke Telefonoa aplikazio lehenetsi gisa."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Blokeatutako zenbakiak"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Ez duzu jasoko deirik edo testu-mezurik blokeatutako zenbakietatik."</string>
-    <string name="block_number" msgid="1101252256321306179">"Gehitu zenbakia"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> desblokeatu?"</string>
+    <string name="block_number" msgid="1101252256321306179">"Gehitu zenbaki bat"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> desblokeatu nahi duzu?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Desblokeatu"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokeatu zenbaki honetatik jasotzen diren deiak eta testu-mezuak:"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Telefono-zenbakia"</string>
@@ -64,7 +64,7 @@
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Gaitu berriro"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"Blokeatu da <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"Desblokeatu da <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>"</string>
-    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Ezin da blokeatu larrialdi-zenbakia."</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Ezin da blokeatu larrialdietarako zenbakia."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> blokeatuta dago dagoeneko."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Telefono pertsonala erabiltzen ari zara deia egiteko"</string>
     <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_VIA">%1$s</xliff:g> deia (deitzailea: <xliff:g id="CALL_FROM">%2$s</xliff:g>)"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Ezin da egin deia, beste dei bat abian delako beste aplikazio batean."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Jasotako deiak"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Dei galduak"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Deiak blokeatzeko aukera"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Dei hau egiten baduzu, amaitu egingo da <xliff:g id="OTHER_APP">%1$s</xliff:g> aplikazioko deia."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Deiak blokeatzeko aukera"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Kontaktuak zerbitzuan ez dauden zenbakiak"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokeatu Kontaktuak zerbitzuan zerrendatuta ez dauden zenbakiak"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Pribatua"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokeatu zenbakia erakusten ez duten deitzaileak"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefono publikoa"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokeatu telefono publikoen deiak"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Ezezaguna"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokeatu identifikatu gabeko deitzaileen deiak"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Deiak blokeatzeko aukera"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Desgaitu da deiak blokeatzeko aukera"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Larrialdi-deia egin da"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Desgaitu da deiak blokeatzeko aukera, larrialdietako zerbitzuak zurekin harremanetan jarri ahal daitezen."</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 80754b8..b3f8d1e 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"به دلیل تماسی در برنامه دیگر، نمی‌توان تماسی برقرار کرد."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"تماس‌های ورودی"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"تماس‌های بی‌پاسخ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"مسدود کردن تماس"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"اگر این تماس را برقرار کنید، تماس <xliff:g id="OTHER_APP">%1$s</xliff:g> شما قطع می‌شود."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"مسدود کردن تماس"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"شماره‌هایی که در «مخاطبین» نیستند"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"مسدود کردن شماره‌هایی که در «مخاطبین» شما نیستند"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"خصوصی"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"مسدود کردن تماس‌گیرندگانی که شماره‌شان را افشا نمی‌کنند"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"تلفن عمومی"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"مسدود کردن تماس‌ها از تلفن‌های عمومی"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ناشناس"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"مسدودن تماس‌های تماس‌گیرندگان ناشناس"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"مسدود کردن تماس"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"مسدود کردن تماس غیرفعال شد"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"تماس اضطراری برقرار شد"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"مسدود کردن تماس غیرفعال شده است تا پاسخ‌دهندگان اضطراری بتوانند با شما تماس بگیرند."</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index d65e584..73cb390 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Puhelua ei voi soittaa, koska toisessa sovelluksessa on puhelu käynnissä."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Saapuvat puhelut"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Vastaamattomat puhelut"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Puhelujen esto"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Tämän puhelun soittaminen päättää puhelun sovelluksessa <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Puhelujen esto"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numerot eivät ole yhteystiedoissa"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Toiminto estää numerot, jotka eivät ole yhteystiedoissasi"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Yksityinen"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Estä soittajat, jotka ovat estäneet oman numeronsa näkymisen"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Yleisöpuhelin"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Estä yleisöpuhelimista soitetut puhelut"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Tuntematon"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Estä tuntemattomien soittajien puhelut"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Puhelujen esto"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Puhelujen esto poistettu käytöstä"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Hätäpuhelu soitettu"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Puhelujen esto on poistettu käytöstä, jotta pelastusviranomaiset voivat soittaa puhelimeesi."</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 7cba712..b90b2ff 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Impossible de faire l\'appel en raison d\'un appel dans une autre application."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Appels entrants"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Appels manqués"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blocage des appels"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Si vous passez cet appel, vous mettrez fin à l\'appel <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blocage des appels"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numéros non répertoriés dans les contacts"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquer les numéros non répertoriés dans vos contacts"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privé"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquer les appelants qui ne divulguent pas leur numéro"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Téléphone public"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquer les appels provenant de téléphones publics"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Inconnu"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquer les appels provenant d\'appelants non identifiés"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blocage des appels"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blocage des appels désactivé"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Appel d\'urgence effectué"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Le blocage des appels a été désactivé pour permettre aux intervenants d\'urgence de communiquer avec vous."</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 12701d8..f7a5a00 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -28,10 +28,10 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Message"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Son coupé"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Haut-parleur activé"</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Peux pas parler. Quoi de neuf ?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Je ne peux pas répondre. Ça va ?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Je te rappelle tout de suite."</string>
-    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Je t\'appellerai plus tard."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Peux pas parler. On se rappelle ?"</string>
+    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Je t\'appelle plus tard."</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Peux pas répondre. On se rappelle ?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Réponses rapides"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Modifier les réponses rapides"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -54,7 +54,7 @@
     <string name="block_number" msgid="1101252256321306179">"Ajouter un numéro"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Débloquer <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Débloquer"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquer les appels et les SMS provenant du numéro :"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquer les appels et les SMS provenant du :"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Numéro de téléphone"</string>
     <string name="block_button" msgid="8822290682524373357">"Bloquer"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Seul le propriétaire de l\'appareil peut afficher et gérer les numéros bloqués."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Vous ne pouvez pas passer cet appel, car vous avez une communication en cours dans une autre application."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Appels entrants"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Appels manqués"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blocage d\'appels"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Si vous passez cet appel, vous mettrez fin à celui qui est en cours dans l\'application <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blocage d\'appels"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numéros non répertoriés dans Contacts"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquez les numéros qui ne figurent pas dans votre annuaire Contacts"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Appels masqués"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquez les appelants qui masquent leur numéro"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Cabines téléphoniques"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquez les appels provenant de cabines téléphoniques"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Numéros inconnus"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquez les appels provenant de personnes non identifiées"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blocage d\'appels"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blocage d\'appels désactivé"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Appel d\'urgence"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Le blocage d\'appels a été désactivé pour que les services d\'urgence puissent vous contacter."</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 7217797..1821f57 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -38,7 +38,7 @@
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaxe enviada ao <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
     <string name="enable_account_preference_title" msgid="2021848090086481720">"Contas de chamadas"</string>
-    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Só se permiten chamadas de urxencia."</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Só se permiten chamadas de emerxencia."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Esta aplicación non pode facer chamadas saíntes sen permiso do teléfono."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar unha chamada, introduce un número válido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Neste momento non se pode engadir a chamada."</string>
@@ -50,7 +50,7 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Cancelar"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> poderá facer chamadas e controlar todo o referente a estas. Só deberías establecer como aplicación de teléfono predeterminada aplicacións nas que confíes."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Números bloqueados"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Non recibirás chamadas nin mensaxes de texto desde números bloqueados."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Non recibirás chamadas nin mensaxes de texto dos números bloqueados."</string>
     <string name="block_number" msgid="1101252256321306179">"Engadir un número"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Queres desbloquear o <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Desbloquear"</string>
@@ -60,11 +60,11 @@
     <string name="non_primary_user" msgid="5180129233352533459">"Só o propietario do dispositivo pode ver e xestionar os números bloqueados."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"Desbloquear"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"O bloqueo desactivouse temporalmente"</string>
-    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Despois de facer unha chamada ou enviar unha mensaxe a un número de urxencia, desactívase o bloqueo para garantir que os servizos de urxencia poidan poñerse en contacto contigo."</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Despois de facer unha chamada ou enviar unha mensaxe a un número de emerxencia, desactívase o bloqueo para garantir que os servizos de emerxencia poidan poñerse en contacto contigo."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Volver activar agora"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"Bloqueouse o <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"Desbloqueouse o <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>"</string>
-    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Non se pode bloquear o número de urxencia."</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Non se pode bloquear o número de emerxencia."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"O <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> xa está bloqueado."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Usando o marcador persoal para facer a chamada"</string>
     <string name="notification_incoming_call" msgid="7713197997773986670">"Chamada de <xliff:g id="CALL_FROM">%2$s</xliff:g> a través de <xliff:g id="CALL_VIA">%1$s</xliff:g>"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Non se pode realizar a chamada porque hai chamadas en curso noutra aplicación."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Chamadas entrantes"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Chamadas perdidas"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bloqueo de chamadas"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ao facer esta chamada, finalizarase o túa chamada de <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bloqueo de chamadas"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Números que non aparezan nos contactos"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquea números que non aparezan entre os teus contactos"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privado"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquea os emisores de chamada que non revelen os seus números"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Teléfono público"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquea as chamadas de teléfonos públicos"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Descoñecido"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquea as chamadas de emisores non identificados"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bloqueo de chamadas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Desactivouse o bloqueo de chamadas"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Realizouse unha chamada de emerxencia"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Desactivouse o bloqueo de chamadas parar permitir que os servizos de emerxencias se poidan poñer en contacto contigo."</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index daa3945..d229a2f 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -50,11 +50,11 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"રદ કરો"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g>, કૉલ્સ કરવામાં અને કૉલ્સના તમામ પાસાઓને નિયંત્રિત કરવામાં સમર્થ હશે. તમે વિશ્વાસ કરો છો તે જ ઍપ્લિકેશનોને ડિફૉલ્ટ ફોન ઍપ્લિકેશન તરીકે સેટ કરો."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"અવરોધિત નંબરો"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"તમે અવરોધિત નંબરોથી કૉલ્સ અથવા ટેક્સ્ટ પ્રાપ્ત કરશો નહીં."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"બ્લૉક કરેલા નંબર પરથી કૉલ અથવા ટેક્સ્ટ તમને આવશે નહિ."</string>
     <string name="block_number" msgid="1101252256321306179">"એક નંબર ઉમેરો"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ને અનાવરોધિત કરીએ?"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ને બ્લૉક કરીએ?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"અનાવરોધિત કરો"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"આ નંબરના કૉલ્સ અને ટેક્સ્ટને અવરોધિત કરો"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"આ નંબરના કૉલ અને ટેક્સ્ટને અવરોધિત કરો"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"ફોન નંબર"</string>
     <string name="block_button" msgid="8822290682524373357">"અવરોધિત કરો"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"ફક્ત ઉપકરણના માલિક અવરોધિત નંબરોને જોઈ અને સંચાલિત કરી શકે છે."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"અન્ય ઍપ્લિકેશનમાં કૉલને કારણે કૉલ કરી શકતાં નથી."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ઇનકમિંગ કૉલ"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"છૂટેલા કૉલ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"કૉલ બ્લૉક કરી રહ્યાં છીએ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"આ કૉલ કરવાથી તમારો <xliff:g id="OTHER_APP">%1$s</xliff:g> કૉલ સમાપ્ત થઈ જશે."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"કૉલ બ્લૉક કરી રહ્યાં છીએ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"સંપર્કોમાં આ નંબર નથી"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"તમારા સંપર્કોમાં જે નંબર સૂચિબદ્ધ ન હોય તેને બ્લૉક કરો"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ખાનગી"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"જે કૉલર પોતાનો નંબર ન બતાવે તેમને બ્લૉક કરો"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"પે ફોન"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"પે ફોન પરના કૉલ બ્લૉક કરો"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"અજાણ્યા"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"અજાણ્યા કૉલરના કૉલ બ્લૉક કરો"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"કૉલ બ્લૉક કરી રહ્યાં છીએ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"કૉલ બ્લૉક કરવાનું બંધ કરવામાં આવ્યું છે"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"કટોકટીનો કૉલ કર્યો"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"કટોકટીમાં પ્રતિસાદ કરનારાઓ તમારો સંપર્ક કરી શકે તે માટે કૉલ બ્લૉક કરવાનું બંધ કરવામાં આવ્યું છે."</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 2c4dd3c..4bf7696 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -20,20 +20,20 @@
     <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"फ़ोन"</string>
     <string name="unknown" msgid="6878797917991465859">"अज्ञात"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"छूटी कॉल"</string>
-    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"कार्यस्थल का छूटा हुआ कॉल"</string>
+    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"दफ़्तर का छूटा हुआ कॉल"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"छूटी कॉल"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> छूटी कॉल"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> की कॉल छूटी"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"वापस कॉल करें"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"संदेश"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"मैसेज"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"कॉल म्‍यूट की गई."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"स्‍पीकरफ़ोन सक्षम."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"अभी बात नहीं हो सकती. क्‍या चल रहा है?"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"मैं आपको वापस कॉल करूंगा/करूंगी."</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"अभी बात नहीं हो सकती. क्‍या हो रहा है?"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"मैं जल्दी ही कॉल करता हूं/करती हूं."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"मैं आपको बाद में कॉल करूंगा/करूंगी."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"अभी बात नहीं हो सकती. बाद में कॉल करें?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"अभी बात नहीं हो सकती. मुझे बाद में कॉल करेंगे?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"झटपट उत्तर"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"झटपट उत्तर संपादित करें"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"पहले से तैयार जवाब में बदलाव करें"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"झटपट उत्तर"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> को संदेश भेजा गया."</string>
@@ -50,31 +50,31 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"अभी नहीं"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> कॉल करने और कॉल से संबंधित सभी पहलुओं को नियंत्रित कर पाएगा. केवल उन्हीं ऐप्लिकेशन को डिफ़ॉल्ट फ़ोन ऐप्लिकेशन के रूप में सेट करना चाहिए जिन पर आप विश्वास करते हैं."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"अवरोधित नंबर"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"आपको अवरुद्ध किए गए नंबर से कॉल या लेख संदेश नहीं मिलेंगे."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ब्लॉक किए गए नंबर से आपको कॉल या मैसेज नहीं मिलेंगे."</string>
     <string name="block_number" msgid="1101252256321306179">"नंबर जोड़ें"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> को अनवरोधित करें?"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> को अनब्लॉक करें?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"अनवरोधित करें"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"इसके कॉल और लेख अवरुद्ध करें"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"इसके कॉल और मैसेज रोकें"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"फ़ोन नंबर"</string>
-    <string name="block_button" msgid="8822290682524373357">"अवरुद्ध करें"</string>
-    <string name="non_primary_user" msgid="5180129233352533459">"केवल डिवाइस स्वामी अवरुद्ध किए गए नंबर देख और प्रबंधित कर सकते हैं."</string>
+    <string name="block_button" msgid="8822290682524373357">"ब्लॉक करें"</string>
+    <string name="non_primary_user" msgid="5180129233352533459">"सिर्फ़ डिवाइस के मालिक रोके गए नंबर देख और प्रबंधित कर सकते हैं."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"अनवरोधित करें"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"अवरोधन अस्थायी रूप से बंद है"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"आपातकालीन नंबर डायल करने या उस पर लेख संदेश भेजने के बाद, अवरोधन बंद हो जाता है ताकि आपातकालीन सेवाएं आपसे संपर्क कर सकें."</string>
-    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"अभी पुन: सक्षम करें"</string>
+    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"अभी फिर से सक्षम करें"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> अवरोधित है"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> अनवरोधित है"</string>
-    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"आपातकालीन नंबर अवरुद्ध करने में असमर्थ."</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"आपातकालीन नंबर ब्लॉक करने में असमर्थ."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> पहले से अवरोधित है."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"कॉल करने के लिए व्यक्तिगत डायलर का उपयोग करना"</string>
     <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_FROM">%2$s</xliff:g> की ओर से <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल"</string>
     <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_FROM">%2$s</xliff:g> की ओर से <xliff:g id="CALL_VIA">%1$s</xliff:g> वीडियो कॉल"</string>
-    <string name="answering_ends_other_call" msgid="8282145910153766401">"उत्तर देने से आपका <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल समाप्त हो जाएगा"</string>
-    <string name="answering_ends_other_calls" msgid="1198589551399049197">"उत्तर देने से आपके <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल समाप्त हो जाएंगे"</string>
-    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"उत्तर देने से आपका <xliff:g id="CALL_VIA">%1$s</xliff:g> वीडियो कॉल समाप्त हो जाएगा"</string>
-    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"उत्तर देने से आपका जारी कॉल समाप्त हो जाएगा"</string>
-    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"उत्तर देने से आपके जारी कॉल समाप्त हो जाएंगे"</string>
-    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"उत्तर देने से आपका जारी वीडियो कॉल समाप्त हो जाएगा"</string>
+    <string name="answering_ends_other_call" msgid="8282145910153766401">"उत्तर देने से आपका <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल खत्म हो जाएगा"</string>
+    <string name="answering_ends_other_calls" msgid="1198589551399049197">"उत्तर देने से आपके <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल खत्म हो जाएंगे"</string>
+    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"उत्तर देने से आपका <xliff:g id="CALL_VIA">%1$s</xliff:g> वीडियो कॉल खत्म हो जाएगा"</string>
+    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"उत्तर देने से आपका जारी कॉल खत्म हो जाएगा"</string>
+    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"उत्तर देने से आपके जारी कॉल खत्म हो जाएंगे"</string>
+    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"उत्तर देने से आपका जारी वीडियो कॉल खत्म हो जाएगा"</string>
     <string name="answer_incoming_call" msgid="4140530013111794587">"उत्तर दें"</string>
     <string name="decline_incoming_call" msgid="806026168661598368">"अस्वीकार करें"</string>
     <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"आपके <xliff:g id="OTHER_CALL">%1$s</xliff:g> कॉल के कारण कॉल नहीं लगाया जा सकता."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"किसी दूसरे ऐप्लिकेशन में कॉल के कारण कॉल नहीं लगाया जा सकता."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"इनकमिंग कॉल"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"छूटे कॉल"</string>
-    <string name="alert_outgoing_call" msgid="982908156825958001">"इस कॉल को करने से आपका <xliff:g id="OTHER_APP">%1$s</xliff:g> कॉल समाप्त हो जाएगा."</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"कॉल पर रोक"</string>
+    <string name="alert_outgoing_call" msgid="982908156825958001">"इस कॉल को करने से आपका <xliff:g id="OTHER_APP">%1$s</xliff:g> कॉल खत्म हो जाएगा."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"कॉल पर रोक"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"वे नंबर जो संपर्कों में नहीं हैं"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"उन नंबरों पर रोक लगाएं, जो आपके संपर्क में मौजूद नहीं हैं"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"निजी"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"अपना नंबर ज़ाहिर न करने वाले कॉलर पर रोक लगाएं"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"पे फ़ोन"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"पे फ़ोन से आने वाले कॉल पर रोक लगाएं"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"अज्ञात"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"अनजान कॉलर के कॉल पर रोक लगाएं"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"कॉल पर रोक"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"कॉल पर रोक लगाने की सुविधा बंद है"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"आपातकालीन कॉल किया गया"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"आपातकालीन सहायता कर्मचारी आपसे संपर्क कर सकें, इसलिए कॉल पर रोक लगाने की सुविधा बंद कर दी गई है."</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 6889507..1f7978e 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -51,10 +51,10 @@
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> će moći uspostaviti poziv i upravljati svim njegovim aspektima. Kao zadanu aplikaciju telefona postavite samo aplikaciju koju smatrate pouzdanom."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Blokirani brojevi"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Nećete primati ni pozive ni poruke s blokiranih brojeva."</string>
-    <string name="block_number" msgid="1101252256321306179">"Dodaj broj"</string>
+    <string name="block_number" msgid="1101252256321306179">"Dodajte broj"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Želite li deblokirati <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Deblokiraj"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokirajte pozive i poruke koje upućuje"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokiraj pozive i poruke koje upućuje"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Telefonski broj"</string>
     <string name="block_button" msgid="8822290682524373357">"Blokiraj"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Samo vlasnik uređaja može pregledavati i kontrolirati blokirane brojeve."</string>
@@ -62,7 +62,7 @@
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Blokiranje je privremeno isključeno"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Nakon što nazovete broj hitne službe ili pošaljete poruku na njega, blokada će se isključiti kako bi vam se hitna služba mogla javiti."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Ponovno omogući sada"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"Broj <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> je blokiran"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"Broj <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> blokiran je"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"Broj <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> deblokiran je"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Broj hitne službe ne može se blokirati."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"Broj <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> već je blokiran."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Poziv se ne može uspostaviti zbog poziva u drugoj aplikaciji."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Dolazni pozivi"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Propušteni pozivi"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokiranje poziva"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Upućivanjem ovog poziva prekinut ćete poziv u aplikaciji <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokiranje poziva"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Brojevi koji nisu u kontaktima"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokirajte brojeve koje nemate u kontaktima"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privatno"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokirajte pozivatelje koji ne žele otkriti svoj broj"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefonska govornica"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokirajte pozive s telefonskih govornica"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Nepoznato"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokirajte pozive nepoznatih pozivatelja"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokiranje poziva"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokiranje poziva je onemogućeno"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Hitni je poziv upućen"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokiranje poziva onemogućeno je da bi vas mogli kontaktirati djelatnici hitnih službi."</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 26d0c42..effd923 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Egy másik alkalmazásban folytatott hívás miatt nem indítható hívás."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Beérkező hívások"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Nem fogadott hívások"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Hívásletiltás"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ha hívást indít, azzal megszakítja a(z) <xliff:g id="OTHER_APP">%1$s</xliff:g>-hívást."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Hívásletiltás"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"A névjegyek között nem szereplő számok"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"A névjegyek között nem szereplő számok letiltása"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privát"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"A privát számról hívók letiltása"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Nyilvános telefon"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"A nyilvános telefonokról bejövő hívások letiltása"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Ismeretlen"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"A nem azonosított hívók letiltása"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Hívásletiltás"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Hívásletiltás kikapcsolva"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Segélyhívás indítva"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"A hívásletiltás ki van kapcsolva, hogy a segélyszolgálatok kapcsolatba léphessenek Önnel."</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index c768e8d..8dfdf11 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -25,7 +25,7 @@
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> բաց թողնված զանգ"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Բաց թողնված զանգ <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>-ից"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Հետ զանգել"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"Ուղարկել հաղորդագրություն"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"Գրել"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Զանգը խլացված է:"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Բարձրախոսը միացված է:"</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Հիմա չեմ կարող խոսել: Ի՞նչ կա:"</string>
@@ -52,18 +52,18 @@
     <string name="blocked_numbers" msgid="2751843139572970579">"Արգելափակված համարներ"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Արգելափակված համարներից զանգեր և SMS-ներ չեք ստանա:"</string>
     <string name="block_number" msgid="1101252256321306179">"Ավելացնել համար"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"Արգելաբացե՞լ <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> համարը:"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"Արգելաբացել"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"Արգելահանե՞լ <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> համարը:"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"Արգելահանել"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Արգելափակել զանգերն ու հաղորդագրությունները այս համարից"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Հեռախոսահամարը"</string>
     <string name="block_button" msgid="8822290682524373357">"Արգելափակել"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Միայն սարքի սեփականատերը կարող է դիտել և կառավարել արգելափակված համարները:"</string>
-    <string name="delete_icon_description" msgid="8903995728252556724">"Արգելաբացել"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"Արգելահանել"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Արգելափակումը ժամանակավորապես անջատվել է"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Արտակարգ իրավիճակի հեռախոսահամար հավաքելուց հետո հեռախոսի արգելափակումը կանջատվի, որպեսզի արտակարգ իրավիճակների ծառայությունները կարողանան կապվել ձեզ հետ:"</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Նորից միացնել"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> համարն արգելափակվեց"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> արգելաբացվեց"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> արգելահանվեց"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Արտակարգ իրավիճակների հեռախոսահամարը հնարավոր չէ արգելափակել:"</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> համարն արդեն արգելափակված է:"</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Զանգելու նպատակով անհատական համարհավաքիչի օգտագործում"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Զանգը հնարավոր չէ կատարել՝ մեկ այլ հավելվածի ընթացիկ զանգի պատճառով:"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Մուտքային զանգեր"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Բաց թողնված զանգեր"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Զանգերի արգելափակում"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Այս զանգը կատարելու դեպքում <xliff:g id="OTHER_APP">%1$s</xliff:g>-ի ընթացիկ զանգը կընդհատվի"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Զանգերի արգելափակում"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Կոնտակտներում չներառված համարներ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Արգելափակել համարները, որոնք ձեր կոնտակտներում չկան"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Փակ համարներ"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Արգելափակել փակ համարներից եկող զանգերը"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Ավտոմատ հեռախոսներ"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Արգելափակել ավտոմատ հեռախոսներից եկող զանգերը"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Անհայտ համարներ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Արգելափակել անհայտ համարներից եկող զանգերը"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Զանգերի արգելափակում"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Զանգերի արգելափակումն անջատած է"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Կատարվեց շտապ կանչ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Զանգերի արգելափակումն անջատվել է, որպեսզի արտակարգ ծառայությունները կարողանան ձեզ զանգել:"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 5fdf63b..8450eff 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -63,7 +63,7 @@
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Setelah Anda menelepon atau mengirim SMS ke nomor gawat darurat, blokir akan dinonaktifkan agar layanan gawat darurat tersebut dapat menghubungi Anda."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Aktifkan lagi sekarang"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> diblokir"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> dibebaskan"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> dibatalkan blokirnya"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Tidak dapat memblokir nomor darurat."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> sudah diblokir."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Menggunakan telepon pribadi untuk melakukan panggilan"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Panggilan tidak dapat dilakukan karena adanya panggilan di aplikasi lain."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Panggilan masuk"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Panggilan tak terjawab"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Pemblokiran Panggilan"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Melakukan panggilan ini akan mengakhiri panggilan <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Pemblokiran Panggilan"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nomor yang tidak ada di Kontak"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokir nomor yang tidak tercantum di Kontak Anda"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Pribadi"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokir penelepon yang menyembunyikan nomornya"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telepon umum"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokir panggilan dari telepon umum"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Tidak tahu"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokir panggilan dari penelepon yang tidak dikenal"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Pemblokiran Panggilan"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Pemblokiran Panggilan dinonaktifkan"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Panggilan darurat dibuat"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Pemblokiran Panggilan dinonaktifkan untuk mengizinkan penjawab darurat menghubungi Anda."</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 743aa5a..c41f657 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Ekki er hægt að hringja sökum símtals í öðru forriti."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Móttekin símtöl"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Ósvöruð símtöl"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Lokað fyrir símtöl"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ef þú hringir mun þessu símtali í <xliff:g id="OTHER_APP">%1$s</xliff:g> ljúka."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Lokað fyrir símtöl"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Númer sem eru ekki í tengiliðum"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Loka fyrir númer sem eru ekki á tengiliðalista"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Lokað"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Loka á hringjendur sem birta ekki símanúmer"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Almenningssími"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Loka fyrir símtöl úr almenningssímum"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Óþekkt"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Loka fyrir símtöl frá óþekktum hringjendum"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Lokað fyrir símtöl"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Ekki er lokað fyrir símtöl"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Neyðarsímtal var hringt"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Slökkt hefur verið á „Lokað fyrir símtöl“ svo neyðarþjónustuaðilar geti haft samband við þig."</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index af92640..e5e63cc 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Impossibile effettuare la chiamata a causa di una chiamata in un\'altra app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Chiamate in arrivo"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Chiamate perse"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blocco delle chiamate"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Se effettui questa chiamata, la chiamata di <xliff:g id="OTHER_APP">%1$s</xliff:g> verrà terminata."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blocco delle chiamate"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numeri esterni ai Contatti"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blocca i numeri non elencati nei Contatti"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privato"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blocca i chiamanti che nascondono il loro numero"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Cabina telefonica"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blocca le chiamate dalle cabine telefoniche"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Sconosciuto"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blocca le chiamate da chiamanti non identificati"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blocco delle chiamate"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blocco delle chiamate disattivato"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Chiamata di emergenza effettuata"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Il blocco delle chiamate è stato disattivato per consentire ai servizi di emergenza di contattarti."</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a68ceee..dcab617 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -25,13 +25,13 @@
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> שיחות שלא נענו"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"שיחה שלא נענתה מאת <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"התקשר חזרה"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"שלח הודעה"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"שליחת הודעה"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"שיחה מושתקת."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"רמקול מופעל."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"לא נוח לי עכשיו. מה קורה?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"תיכף אחזור אליך."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"אני אתקשר אליך יותר מאוחר."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"לא נוח לי עכשיו. תתקשר מאוחר יותר?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"לא נוח לי עכשיו. נדבר אחר כך?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"תגובות מהירות"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"תגובות מהירות"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -39,7 +39,7 @@
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"הודעה נשלחה אל <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
     <string name="enable_account_preference_title" msgid="2021848090086481720">"חשבונות לביצוע שיחות"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"ניתן לבצע רק שיחות חירום."</string>
-    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"לא ניתן לבצע שיחות יוצאות באמצעות האפליקציה הזו ללא ההרשאה \'טלפון\'."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"לא ניתן להוציא שיחות באמצעות האפליקציה הזו ללא ההרשאה \'טלפון\'."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"כדי להתקשר, הזן מספר טלפון חוקי."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"לא ניתן כעת להוסיף את השיחה."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"חסר מספר של דואר קולי"</string>
@@ -53,10 +53,10 @@
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"לא יגיעו אליך שיחות או הודעות טקסט מהמספרים החסומים."</string>
     <string name="block_number" msgid="1101252256321306179">"מספר חדש"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"האם לבטל את חסימת המספר <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"בטל חסימה"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"חסום שיחות והודעות טקסט מ-"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"ביטול חסימה"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"חסימת שיחות והודעות טקסט מ-"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"מספר טלפון"</string>
-    <string name="block_button" msgid="8822290682524373357">"חסום"</string>
+    <string name="block_button" msgid="8822290682524373357">"חסימה"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"רק בעל המכשיר יכול להציג ולנהל מספרים חסומים."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"ביטול חסימה"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"החסימה הושבתה זמנית"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"אי אפשר להתקשר בגלל שיש שיחה באפליקציה אחרת."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"שיחות נכנסות"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"שיחות שלא נענו"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"חסימת שיחות"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ביצוע השיחה הזו יסיים את השיחה ב-<xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"חסימת שיחות"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"מספרים שאינם באנשי הקשר"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"חסימת שיחות ממספרים שאינם ברשימה של אנשי הקשר"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"מספרים פרטיים"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"חסימת שיחות מאנשים שלא חושפים את המספר שלהם"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"טלפונים ציבוריים"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"חסימת שיחות מטלפונים ציבוריים"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"מספרים לא ידועים"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"חסימת שיחות ממספרים לא מזוהים"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"חסימת שיחות"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"חסימת השיחות הושבתה"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"בוצעה שיחת חירום"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"חסימת השיחות הושבתה כדי לאפשר לצוותי עזרה ראשונה להתקשר אליך."</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index a7fcb0e..22c2073 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -57,7 +57,7 @@
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"次の発信元からの通話とテキスト メッセージをブロック"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"電話番号"</string>
     <string name="block_button" msgid="8822290682524373357">"ブロック"</string>
-    <string name="non_primary_user" msgid="5180129233352533459">"ブロックした番号を表示、管理できるのは端末の所有者のみです。"</string>
+    <string name="non_primary_user" msgid="5180129233352533459">"ブロックした番号を表示、管理できるのはデバイスの所有者のみです。"</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"ブロックを解除"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"ブロックが一時的に OFF になっています"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"緊急通報番号に電話をかけたり、テキスト メッセージを送信したりすると、ブロックが OFF になり、緊急通報受理機関からの通話を着信できるようになります。"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"別のアプリで通話中のため、この通話を発信することはできません。"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"着信"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"不在着信"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"着信のブロック"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"この通話を発信すると、<xliff:g id="OTHER_APP">%1$s</xliff:g> の通話が終了します。"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"着信のブロック"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"連絡帳にない番号"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"連絡帳にリストされていない番号をブロック"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"非通知"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"番号非通知の発信者をブロック"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"公衆電話"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"公衆電話からの着信をブロック"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"不明"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"不明な発信者からの着信をブロック"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"着信のブロック"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"着信のブロックを無効にしました"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"緊急通報"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"着信のブロックを無効して、救急隊員などがあなたに連絡できるようにしました。"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 34dc303..1a39384 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ზარი ვერ ხორციელდება ზარის გამო სხვა აპში."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"შემომავალი ზარები"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"გამოტოვებული ზარები"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ზარების დაბლოკვა"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ამ ზარის განხორციელება თქვენს <xliff:g id="OTHER_APP">%1$s</xliff:g> ზარს დაასრულებს."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ზარების დაბლოკვა"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"ნომრები არ არის კონტაქტებში"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ნომრების დაბლოკვა, რომლებიც არ არის შეტანილი თქვენს კონტაქტებში"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"პირადი"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ზარების დაბლოკვა, რომლებიც არ ამხელს ნომერს"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"საზოგადოებრივი ტელეფონი"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ზარების დაბლოკვა საზოგადოებრივი ტელეფონებიდან"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"უცნობი"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ზარების დაბლოკვა ამოუცნობი აბონენტებისგან"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ზარების დაბლოკვა"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ზარების დაბლოკვა გათიშულია"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"საგანგებო ზარი შესრულდა"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ზარების დაბლოკვა გათიშულია, რათა საგანგებო სიტუაციებში მოპასუხეებმა თქვენთან დაკავშირება შეძლონ"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index aa98d40..50bb0f2 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -38,7 +38,7 @@
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Жылдам жауап"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Хабар <xliff:g id="PHONE_NUMBER">%s</xliff:g> нөміріне жіберілді."</string>
     <string name="enable_account_preference_title" msgid="2021848090086481720">"Қоңырау шалу есептік жазбалары"</string>
-    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Тек төтенше қоңырауларға рұқсат етілген."</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Құтқару қызметіне ғана қоңырау шалуға рұқсат етілген."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"\"Телефон\" рұқсатынсыз бұл қолданба шығыс қоңырауларды соға алмайды."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Қоңырау шалу үшін жарамды нөмірді енгізіңіз."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Қоңырауды қазіргі уақытта қосу мүмкін емес."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Қоңырау шалу мүмкін емес, себебі басқа қолданбадан қоңырау шалынуда."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Кіріс қоңыраулары"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Қабылданбаған қоңыраулар"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Қоңырауды бөгеу"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Жаңа қоңырау шалу <xliff:g id="OTHER_APP">%1$s</xliff:g> қоңырауын тоқтатады."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Қоңырауды бөгеу"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Контактілерде жоқ нөмірлер"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Контактілерде жоқ нөмірлерді бөгеу"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Жеке"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Нөмірі жасырын қоңырауларды бөгеу"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Ақылы телефон"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Ақылы телефон қоңырауларын бөгеу"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Белгісіз"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Белгісіз қоңырауларды бөгеу"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Қоңырауды бөгеу"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Қоңырау бөгеу функциясы өшірулі"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Төтенше жағдай қоңырауы шалынды"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Төтенше жағдай қызметтері сізге хабарласа алуы үшін, қоңырау бөгеу функциясы өшірілді."</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index e9f3cff..827b02d 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="telecommAppLabel" product="default" msgid="382363169988504520">"គ្រប់គ្រងការហៅ"</string>
-    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"ទូរស័ព្ទ"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"ទូរសព្ទ"</string>
     <string name="unknown" msgid="6878797917991465859">"មិន​ស្គាល់"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"ខកខាន​ទទួល"</string>
     <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"បានខកខានការហៅចូលពីកន្លែងការងារ"</string>
@@ -28,7 +28,7 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"សារ"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"ការ​ហៅ​បិទ​សំឡេង។"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"បាន​បើក​អូប៉ាល័រ​ទូរស័ព្ទ។"</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"មិន​អាច​និយាយ​បាន​ឥឡូវ​នេះ។ មានការអីដែរ?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"មិន​អាច​និយាយ​បានទេ ​ឥឡូវ​នេះ។ មានការអីដែរ?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"ខ្ញុំ​នឹង​ហៅ​​​​ទៅ​អ្នក​​វិញ។"</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"ខ្ញុំ​នឹង​ហៅ​ទៅ​អ្នក​នៅ​ពេល​ក្រោយ។"</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"មិន​អាច​និយាយ​បាន​ទេ​ឥឡូវ​នេះ។ ហៅ​​​​មក​ខ្ញុំ​ពេល​ក្រោយ?"</string>
@@ -49,21 +49,21 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"កំណត់​លំនាំដើម"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"បោះបង់"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> នឹង​អាច​ដាក់ចុះ និង​ត្រួតពិនិត្យ​​ទិដ្ឋភាព​ការ​ហៅ​ទាំងអស់។ មាន​តែ​កម្មវិធី​ដែល​អ្នក​ទុកចិត្ត​ប៉ុណ្ណោះ​​អាច​​ត្រូវ​បាន​​កំណត់​ជា​កម្មវិធី​ទូរសព្ទ​លំនាំដើម។"</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"លេខដែល​បានរារាំង"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"អ្នក​នឹង​មិន​ទទួល​បាន​ការ​ហៅទូរសព្ទ ឬ​សារ​​ពី​លេខ​ដែល​បាន​​រារាំង​ឡើយ។"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"លេខដែល​បានទប់ស្កាត់"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"អ្នក​នឹង​មិន​ទទួល​បាន​ការ​ហៅទូរសព្ទ ឬ​សារ​​ពី​លេខ​ដែល​បានទប់ស្កាត់ឡើយ។"</string>
     <string name="block_number" msgid="1101252256321306179">"បញ្ចូល​លេខ"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"ឈប់រារាំង <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ឬ?"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"ឈប់រារាំង"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"រារាំងការហៅ និងការផ្ញើសារពី"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"ឈប់ទប់ស្កាត់ <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ឬ?"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"ឈប់ទប់ស្កាត់"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ទប់ស្កាត់ការហៅ និងការផ្ញើសារពី"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"លេខទូរសព្ទ"</string>
-    <string name="block_button" msgid="8822290682524373357">"រារាំង"</string>
+    <string name="block_button" msgid="8822290682524373357">"ទប់ស្កាត់"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"មានតែម្ចាស់ឧបករណ៍តែប៉ុណ្ណោះដែលអាចមើល និងគ្រប់គ្រងបញ្ជីរារាំងបាន"</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"ឈប់​ទប់ស្កាត់"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"បាន​បិទ​ការ​ទប់ស្កាត់​​​ជា​បណ្ដោះអាសន្ន"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"បន្ទាប់​ពី​អ្នក​ចុច​ហៅ ឬ​ផ្ញើ​សារ​លេខ​​អាសន្ន ការ​ទប់ស្កាត់​​ត្រូវ​បាន​បិទ​​ដើម្បី​​ប្រាកដ​ថា​សេវាកម្ម​អាសន្ន​អាច​ទាក់ទង​អ្នក​បាន។"</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"បើក​ដំណើរការ​ឡើងវិញ​ឥឡូវនេះ"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"បាន​ទប់ស្កាត់ <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"បាន​អនុញ្ញាត <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"បានឈប់​ទប់ស្កាត់ <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"មិន​អាច​​​ទប់ស្កាត់​លេខ​បន្ទាន់​បាន​ទេ។"</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ត្រូវ​បាន​ទប់ស្កាត់​រួច​ហើយ។"</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"កំពុងប្រើកម្មវិធីហៅផ្ទាល់ខ្លួនដើម្បីធ្វើការហៅទូរស័ព្ទ"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ការ​ហៅ​មិន​អាច​ធ្វើ​បាន​ទេ ដោយ​សារ​មាន​ការហៅ​មួយ​នៅ​ក្នុង​កម្មវិធី​ផ្សេង។"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ការ​ហៅ​ចូល"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"ការ​ហៅ​ដែល​មិន​បាន​ទទួល"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ការទប់ស្កាត់ការហៅ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ការ​ហៅ​ទូរសព្ទ​នេះ នឹង​បញ្ចប់​ការហៅ <xliff:g id="OTHER_APP">%1$s</xliff:g> របស់​អ្នក។"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ការទប់ស្កាត់ការហៅ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"លេខ​មិន​នៅ​ក្នុង​ទំនាក់ទំនង"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ទប់ស្កាត់​លេខ​ដែល​មិនស្ថិត​​នៅ​ក្នុង​បញ្ជី​ទំនាក់ទំនង​របស់​អ្នក"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ឯកជន"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ទប់ស្កាត់​​អ្នកហៅទូរសព្ទ​​ដែល​មិន​ឲ្យ​ឃើញ​លេខ​របស់​ពួកគេ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"ទូរសព្ទ​ដែល​បង់ប្រាក់"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ទប់ស្កាត់​ការហៅ​ពី​ទូរសព្ទ​ដែល​បង់ប្រាក់"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"មិនស្គាល់"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ទប់ស្កាត់​ការហៅ​ទូរសព្ទពី​អ្នក​ហៅ​ដែល​មិន​ស្គាល់អត្តសញ្ញាណ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ការទប់ស្កាត់​ការហៅ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"បាន​បិទ​ការទប់ស្កាត់​ការហៅ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"បាន​ធ្វើការហៅ​បន្ទាន់"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ការទប់ស្កាត់​ការហៅ​ត្រូវបាន​បិទ ដើម្បី​អនុញ្ញាត​ឲ្យ​អ្នក​ឆ្លើយតប​បន្ទាន់​អាច​ទាក់ទង​អ្នក​បាន។"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 9dd0f09..159091a 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -56,7 +56,7 @@
     <string name="unblock_button" msgid="3078048901972674170">"ನಿರ್ಬಂಧ ತೆಗೆಯಿರಿ"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ಇದರಿಂದ ಬರುವ ಕರೆಗಳು ಮತ್ತು ಪಠ್ಯ ಸಂದೇಶಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"ಫೋನ್ ಸಂಖ್ಯೆ"</string>
-    <string name="block_button" msgid="8822290682524373357">"ನಿರ್ಬಂಧಿಸು"</string>
+    <string name="block_button" msgid="8822290682524373357">"ನಿರ್ಬಂಧಿಸಿ"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"ಸಾಧನ ಮಾಲೀಕರು ಮಾತ್ರ ನಿರ್ಬಂಧಿಸಿದ ಸಂಖ್ಯೆಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"ನಿರ್ಬಂಧ ತೆಗೆಯಿರಿ"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"ನಿರ್ಬಂಧಿಸುವಿಕೆ ತಾತ್ಕಾಲಿಕವಾಗಿ ಆಫ್ ಮಾಡಲಾಗಿದೆ"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ಬೇರೊಂದು ಅಪ್ಲಿಕೇಶನ್‍ನಲ್ಲಿ ಕರೆಯಲ್ಲಿರುವುದರಿಂದ ಕರೆ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ಒಳಬರುವ ಕರೆಗಳು"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"ಮಿಸ್ಡ್‌ ಕರೆಗಳು"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ಈ ಕರೆಯನ್ನು ಮಾಡುವುದರಿಂದ ನಿಮ್ಮ <xliff:g id="OTHER_APP">%1$s</xliff:g> ಕರೆಯು ಅಂತ್ಯಗೊಳ್ಳುತ್ತದೆ."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"ಸಂಖ್ಯೆಗಳು ಸಂಪರ್ಕದಲ್ಲಿಲ್ಲ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ನಿಮ್ಮ ಸಂಪರ್ಕಗಳಲ್ಲಿ ಪಟ್ಟಿ ಮಾಡಿರದ ಸಂಪರ್ಕಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ಖಾಸಗಿ"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ತಮ್ಮ ಸಂಖ್ಯೆಯನ್ನು ಬಹಿರಂಗಪಡಿಸದ ಕರೆ ಮಾಡುವವರನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"ಪಾವತಿ ಫೋನ್"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ಪಾವತಿ ಫೋನ್‌ಗಳಿಂದ ಕರೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ಅಪರಿಚಿತ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ಗುರುತಿಸದ ಕರೆದಾರರಿಂದ ಕರೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಿ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ಕರೆ ನಿರ್ಬಂಧಿಸುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"ತುರ್ತು ಕರೆ ಮಾಡಲಾಗಿದೆ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ನಿಮ್ಮನ್ನು ಸಂಪರ್ಕಿಸುವುದಕ್ಕಾಗಿ ತುರ್ತಾಗಿ ಪ್ರತಿಕ್ರಿಯಿಸುವವರ ಸಂಖ್ಯೆಯನ್ನು ನಿರ್ಬಂಧಿಸುವುದನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 33e1c40..dd3658c 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -50,7 +50,7 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"취소"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g>은(는) 모든 전화를 걸고 제어할 수 있습니다. 신뢰할 수 있는 앱만 기본 전화 앱으로 설정하세요."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"차단된 번호"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"차단한 번호에서 걸려오는 전화나 문자를 더 이상 받지 않습니다."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"차단한 번호에서 걸려오는 전화나 문자는 더 이상 수신되지 않습니다."</string>
     <string name="block_number" msgid="1101252256321306179">"번호 추가"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>번을 차단 해제하시겠습니까?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"차단 해제"</string>
@@ -62,8 +62,8 @@
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"차단 기능이 일시적으로 중지됨"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"긴급 전화번호로 전화를 걸거나 문자를 보내면 긴급 서비스를 사용할 수 있도록 차단 기능이 중지됩니다."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"지금 다시 사용 설정"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>번은 차단되었습니다."</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>번은 차단 해제되었습니다."</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>번이 차단되었습니다."</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>번이 차단 해제되었습니다."</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"긴급 전화번호를 차단할 수 없습니다."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>번은 이미 차단되었습니다."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"전화를 걸 때 개인 다이얼러 사용"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"다른 앱에서 통화 중이므로 전화를 걸 수 없습니다."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"수신 전화"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"부재중 전화"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"통화 차단"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"이 전화를 걸면 현재 <xliff:g id="OTHER_APP">%1$s</xliff:g>에서 진행 중인 통화가 종료됩니다."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"통화 차단"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"연락처에 없는 번호"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"연락처에 없는 번호 차단"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"익명"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"전화번호를 공개하지 않는 발신자 차단"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"공중전화"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"공중전화에서 걸려 온 통화 차단"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"알 수 없는 발신자"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"신원을 확인할 수 없는 발신자로부터 걸려 온 통화 차단"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"통화 차단"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"통화 차단이 사용 중지됨"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"긴급 통화가 사용됨"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"응급 구조 요원이 연락할 수 있도록 통화 차단이 사용 중지되었습니다."</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b973486..760fdb1 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Башка колдонмодо чалып жатасыз, ошондуктан чала албайсыз."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Кирүүчү чалуулар"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Кабыл алынбаган чалуулар"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Чалууну бөгөттөө"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Эгер чалып баштасаңыз, <xliff:g id="OTHER_APP">%1$s</xliff:g> чалууңуз аяктайт."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Чалууну бөгөттөө"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Номерлер Байланыштар тизмесинде жок"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Байланыштар тизмеңизде болбогон номерлерди бөгөттөңүз"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Купуя"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Номерин көрсөтпөгөн чалуучуларды бөгөттөө"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Телефон-автомат"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Телефон-автоматтардан келген чалууларды бөгөттөө"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Белгисиз"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Белгисиз чалуучуларды бөгөттөө"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Чалууну бөгөттөө"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Чалууну бөгөттөө өчүрүлдү"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Шашылыш чалуу аткарылды"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Өзгөчө кырдаалдардагы кызматчылар сиз менен байланышуусу үчүн чалууну бөгөттөө функциясы өчүрүлгөн."</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 0b7a3b6..4c7ec9e 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ບໍ່ສາມາດໂທອອກໄດ້ເນື່ອງຈາກສາຍໃນແອັບອື່ນ."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ສາຍໂທເຂົ້າ"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"ສາຍບໍ່ໄດ້ຮັບ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ການບລັອກສາຍ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ການໂທສາຍນີ້ຈະເປັນການສິ້ນສຸດສາຍ <xliff:g id="OTHER_APP">%1$s</xliff:g> ຂອງທ່ານ."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ການບລັອກສາຍ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"ເບີໂທບໍ່ໄດ້ຢູ່ໃນລາຍຊື່ຜູ້ຕິດຕໍ່"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ບລັອກເບີໂທທີ່ບໍ່ຢູ່ໃນລາຍຊື່ຜູ້ຕິດຕໍ່ຂອງທ່ານ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ສ່ວນຕົວ"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ບລັອກຜູ້ໂທທີ່ບໍ່ສະແດງເບີໂທຂອງເຂົາເຈົ້າ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"ຕູ້ໂທ"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ບລັອກສາຍຈາກຕູ້ໂທ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ບໍ່ຮູ້ຈັກ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ບລັອກສາຍຈາກຜູ້ໂທທີ່ລະບຸຕົວຕົນບໍ່ໄດ້"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ການບລັອກສາຍ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ປິດການບລັອກສາຍແລ້ວ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"ໂທສຸກເສີນແລ້ວ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ປິດການບລັອກສາຍແລ້ວເພື່ອອະນຸຍາດໃຫ້ສາຍສຸກເສີນສາມາດຕິດຕໍ່ຫາທ່ານໄດ້."</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 45f125c..4891114 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Negalima skambinti dėl skambučio kitoje programoje."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Gaunamieji skambučiai"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Praleisti skambučiai"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Skambučių blokavimas"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Atliekant šį skambutį bus užbaigtas „<xliff:g id="OTHER_APP">%1$s</xliff:g>“ skambutis."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Skambučių blokavimas"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numerių nėra Kontaktuose"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokuoti Kontaktuose nepateiktus numerius"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privatus"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokuoti skambintojus, neatskleidžiančius savo numerių"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Taksofonas"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokuoti skambučius iš taksofonų"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Nežinomas"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokuoti skambučius nuo nenustatytų skambintojų"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Skambučių blokavimas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Skambučių blokavimas išjungtas"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Atliktas skambutis pagalbos numeriu"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Skambučių blokavimas išjungtas, kad pagalbos numeriu atsiliepusiems žmonėms būtų leidžiama su jumis susisiekti."</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index db710d5..8b8d36a 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Nevar veikt zvanu citā lietotnē notiekoša zvana dēļ."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Ienākošie zvani"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Neatbildētie zvani"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Zvanu bloķēšana"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Veicot šo zvanu, tiks beigts zvans lietotnē <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Zvanu bloķēšana"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numuri, kas nav ietverti kontaktpersonu sarakstā"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloķēt numurus, kas nav ietverti kontaktpersonu sarakstā"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privāts"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloķēt zvanītājus, kas neatklāj savu numuru"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Taksofons"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloķēt zvanus no taksofoniem"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Nezināms"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloķēt zvanus no neidentificētiem zvanītājiem"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Zvanu bloķēšana"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Zvanu bloķēšana atspējota"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Ārkārtas zvans ir veikts"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Zvanu bloķēšana ir atspējota, lai ļautu ar jums sazināties avārijas dienestu darbiniekiem."</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index a8debe9..6fcfa2a 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -52,7 +52,7 @@
     <string name="blocked_numbers" msgid="2751843139572970579">"Блокирани броеви"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Нема да добивате повици или SMS од блокирани броеви."</string>
     <string name="block_number" msgid="1101252256321306179">"Додај број"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"Одблокирајте го <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"Да се одблокира <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Одблокирај"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Блокирај повици и пораки од"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Телефонски број"</string>
@@ -63,7 +63,7 @@
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Откако ќе повикате или ќе испратите SMS на број за итни случаи, блокирањето се исклучува за да може да ве контактираат службите за итни случаи."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Овозможи сега повторно"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> е блокиран"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> е деблокиран"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> е одблокиран"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Бројот за итни случаи не може да се блокира."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> е веќе блокиран."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Користење на личниот бирач за остварување повик"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Не може да се воспостави повик поради вашиот повик на друга апликација."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Дојдовни повици"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Пропуштени повици"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Блокирање повици"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ако се воспостави повиков, вашиот повик на <xliff:g id="OTHER_APP">%1$s</xliff:g> ќе заврши."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Блокирање повици"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Броеви што не се наведени во „Контакти“"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Блокирани броеви што не се наведени во вашите „Контакти“"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Приватно"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Блокирај повикувачи со сокриен број"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Телефонска говорница"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Блокирај повици од телефонски говорници"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Непознато"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Блокирај повици од неидентификувани повикувачи"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Блокирање повици"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Блокирањето повици е оневозможено"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Воспоставен е итен повик"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Блокирањето повици е оневозможено за да им се овозможи на лицата од службите за итни случаи да контактираат со вас."</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index d604690..6547a3c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -49,20 +49,20 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"ഡിഫോൾട്ട് ഫോൺ ആപ്പ് സജ്ജമാക്കുക"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"റദ്ദാക്കുക"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> ആപ്പിന് എല്ലാ തരത്തിലുമുള്ള കോളുകൾ ചെയ്യാനും നിയന്ത്രിക്കാനുമാവും. നിങ്ങൾക്ക് വിശ്വാസമുള്ള ആപ്‌സിനെ മാത്രമേ ഡിഫോൾട്ട് ഫോൺ ആപ്പായി സജ്ജമാക്കാവൂ."</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"ബ്ലോക്കുചെയ്ത നമ്പറുകൾ"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ബ്ലോക്കുചെയ്‌ത നമ്പറുകളിൽ നിന്ന് നിങ്ങൾക്ക് കോളുകളോ സന്ദേശങ്ങളോ ലഭിക്കില്ല."</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"ബ്ലോക്ക് ചെയ്‍ത നമ്പറുകൾ"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ബ്ലോക്ക് ചെയ്‌ത നമ്പറുകളിൽ നിന്ന് നിങ്ങൾക്ക് കോളുകളോ സന്ദേശങ്ങളോ ലഭിക്കില്ല."</string>
     <string name="block_number" msgid="1101252256321306179">"ഒരു നമ്പർ ചേർക്കുക"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> അൺബ്ലോക്കുചെയ്യണോ?"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"അൺബ്ലോക്കുചെയ്യുക"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ഇനിപ്പറയുന്നതിൽ നിന്നുള്ള കോളുകളും ടെക്സ്റ്റുകളും ബ്ലോക്കുചെയ്യുക"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"അൺബ്ലോക്ക് ചെയ്യുക"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ഈ നമ്പറിൽ നിന്നുള്ള കോളുകളും ടെക്സ്റ്റുകളും ബ്ലോക്ക് ചെയ്യുക"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"ഫോൺ നമ്പർ"</string>
     <string name="block_button" msgid="8822290682524373357">"ബ്ലോക്കുചെയ്യുക"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"ബ്ലോക്കുചെയ്ത നമ്പറുകൾ ഉപകരണ ഉടമയ്ക്ക് മാത്രമേ കാണാനും മാനേജുചെയ്യാനും കഴിയൂ."</string>
-    <string name="delete_icon_description" msgid="8903995728252556724">"അൺബ്ലോക്കുചെയ്യുക"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"അൺബ്ലോക്ക് ചെയ്യുക"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"ബ്ലോക്കുചെയ്യൽ താൽക്കാലികമായി ഓഫാണ്"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"നിങ്ങൾ ഒരു എമർജൻസി നമ്പർ ഡയൽ ചെയ്‌ത് കഴിയുമ്പോഴോ അതിലേക്ക് സന്ദേശമയച്ചുകഴിയുമ്പോഴോ, എമർജൻസി സേവനങ്ങൾ നിങ്ങളിലേക്ക് എത്തുമെന്ന് ഉറപ്പാക്കാൻ കോൾ ബ്ലോക്കിംഗ് ഓഫാക്കും."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"ഇപ്പോൾ വീണ്ടും പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ബ്ലോക്കുചെയ്‌തു"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ബ്ലോക്ക് ചെയ്‌തു"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> അൺബ്ലോക്കുചെയ്‌തു"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"അടിയന്തര നമ്പർ ബ്ലോക്കുചെയ്യാനാകുന്നില്ല."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> മുമ്പേതന്നെ ബ്ലോക്കുചെയ്‌തതാണ്."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"മറ്റൊരു ആപ്പിലുള്ള കോൾ കാരണം കോൾ ചെയ്യാനായില്ല."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ഇൻകമിംഗ് കോളുകൾ"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"മിസ്‌ഡ് കോളുകൾ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"കോൾ ബ്ലോക്ക് ചെയ്യൽ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ഈ കോൾ ചെയ്യുന്നത് നിങ്ങളുടെ <xliff:g id="OTHER_APP">%1$s</xliff:g> കോൾ അവസാനിക്കാനിടയാക്കും."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"കോൾ ബ്ലോക്ക് ചെയ്യൽ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"കോൺടാക്‌റ്റുകളിൽ ഇല്ലാത്ത നമ്പറുകൾ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"നിങ്ങളുടെ കോൺടാക്റ്റുകളിൽ ലിസ്‌റ്റ് ചെയ്യാത്ത നമ്പറുകൾ ബ്ലോക്ക് ചെയ്യുക"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"സ്വകാര്യം"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"വിളിക്കുന്നവരിൽ നമ്പർ വെളിപ്പെടുത്താത്തവരെ ബ്ലോക്ക് ചെയ്യുക"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"പേ ഫോൺ"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"പേ ഫോണുകളിൽ നിന്നുള്ള കോളുകൾ ബ്ലോക്ക് ചെയ്യുക"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"അജ്ഞാതം"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"തിരിച്ചറിയാത്ത കോളർമാരിൽ നിന്നുള്ള കോളുകൾ ബ്ലോക്ക് ചെയ്യുക"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"കോൾ ബ്ലോക്ക് ചെയ്യൽ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"കോൾ ബ്ലോക്ക് ചെയ്യൽ പ്രവർത്തനരഹിതമാക്കി"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"അടിയന്തര കോൾ ചെയ്തു"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"അടിയന്തരമായി ബന്ധപ്പെടുന്നവരെ അനുവദിക്കാനായി കോൾ ബ്ലോക്ക് ചെയ്യൽ പ്രവർത്തനരഹിതമാക്കി."</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 82b4ebd..342680b 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -50,7 +50,7 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Цуцлах"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> бүх төрлийн дуудлага хийх, хянах боломжтой болно. Зөвхөн өөрийн итгэдэг апп-г өгөгдмөл Утасны апп-р тохируулах нь зүйтэй."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Блоклосон дугаар"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Та хориглосон дугаараас дуудлага, мессеж хүлээн авахгүй."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Та блоклосон дугаараас дуудлага, мессеж хүлээн авахгүй."</string>
     <string name="block_number" msgid="1101252256321306179">"Дугаар нэмэх"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>-г блокоос гаргах уу?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Блокоос гаргах"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Өөр апп доторх дуудлагаас шалтгаалан дуудлага хийх боломжгүй байна."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Ирж буй дуудлага"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Аваагүй дуудлага"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Дуудлага хориглох"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Энэ дуудлагыг хийснээр таны <xliff:g id="OTHER_APP">%1$s</xliff:g> дуудлагыг дуусгана."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Дуудлага хориглох"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Харилцагчид дотор байхгүй дугаарууд"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Өөрийн Харилцагчид дотор байхгүй дугааруудыг хориглох"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Хувийн"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Дугаараа нууцалсан дуудлагыг хориглох"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Төлбөртэй утас"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Төлбөртэй утаснаас залгасан дуудлагуудыг хориглох"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Тодорхойгүй"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Тодорхойгүй дугаараас ирсэн дуудлагуудыг блоклох"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Дуудлага хориглох"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Дуудлага хориглохыг идэвхгүй болгосон"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Яаралтай тусламжийн дуудлага хийсэн"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Яаралтай тусламжийнханд тантай холбогдохыг зөвшөөрөхийн тулд дуудлага хориглохыг идэвхгүй болгосон."</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 9f2991a..ebb08de 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -25,7 +25,7 @@
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> सुटलेले कॉल"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> कडील सुटलेला कॉल"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"पुन्हा कॉल करा"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"संदेश"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"मेसेज"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"कॉल नि.शब्‍द केला."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"स्‍पीकरफोन सक्षम केला."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"आत्ता बोलू शकत नाही. काय चालले आहे?"</string>
@@ -36,45 +36,45 @@
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"द्रुत प्रतिसाद संपादित करा"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"द्रुत प्रतिसाद"</string>
-    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"संदेश <xliff:g id="PHONE_NUMBER">%s</xliff:g> वर पाठविला."</string>
+    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"मेसेज <xliff:g id="PHONE_NUMBER">%s</xliff:g> वर पाठविला."</string>
     <string name="enable_account_preference_title" msgid="2021848090086481720">"कॉल करण्याची खाती"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"फक्त आणीबाणी कॉल करण्याची परवानगी आहे."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"हा अॅप्लिकेशन फोन परवानगी शिवाय कॉल करू शकत नाही."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"कॉल करण्यासाठी, एक वैध नंबर एंटर करा."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"यावेळी कॉल जोडला जाऊ शकत नाही."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"व्हॉइसमेल नंबर गहाळ"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"सिम कार्डवर कोणताही व्हॉइसमेल नंबर संचयित केला नाही."</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"सिम कार्डवर कोणताही व्हॉइसमेल नंबर स्टोअर केला नाही."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"नंबर जोडा"</string>
-    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"<xliff:g id="NEW_APP">%s</xliff:g> ला आपला डीफॉल्ट अॅप बनवायचा?"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"<xliff:g id="NEW_APP">%s</xliff:g> ला तुमचा डीफॉल्ट अ‍ॅप बनवायचा?"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"डीफॉल्ट म्हणून सेट करा"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"रद्द करा"</string>
-    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> कॉल करण्यात आणि त्याचे सर्व पैलू नियंत्रित करण्‍यात सक्षम असेल. ज्या अॅप्सवर आपला विश्वास आहे फक्त त्यांंनाच आपला डीफॉल्ट फोन अॅप म्हणून सेट करावे."</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"अवरोधित केलेले नंबर"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"आपल्याला अवरोधित नंबरवरून कॉल किंवा मजकूर प्राप्त होणार नाहीत."</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> कॉल करण्यात आणि त्याचे सर्व पैलू नियंत्रित करण्‍यात सक्षम असेल. ज्या अ‍ॅप्सवर तुमचा विश्वास आहे फक्त त्यांंनाच तुमचा डीफॉल्ट फोन अ‍ॅप म्हणून सेट करावे."</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"ब्लॉक केलेले नंबर"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ब्लॉक केलेल्या नंबरवरुन तुम्हाला कॉल किंवा एसएमएस येणार नाहीत."</string>
     <string name="block_number" msgid="1101252256321306179">"एक नंबर जोडा"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> अनावरोधित करायचा?"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"अनावरोधित करा"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"यावरील कॉल आणि मजकूर अवरोधित करा"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> अनब्लॉक करायचा?"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"ब्लॉक करा"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"यावरील कॉल आणि एसएमएस ब्लॉक करा"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"फोन नंबर"</string>
-    <string name="block_button" msgid="8822290682524373357">"अवरोधित करा"</string>
+    <string name="block_button" msgid="8822290682524373357">"ब्लॉक करा"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"फक्त डिव्हाइस मालक अवरोधित केलेले नंबर पाहू आणि व्यवस्थापित करू शकतो."</string>
-    <string name="delete_icon_description" msgid="8903995728252556724">"अनावरोधित करा"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"ब्लॉक करा"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"अवरोधित करणे तात्पुरते बंद आहे"</string>
-    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"आपण एखादा आणीबाणी नंबर डायल केला किंवा त्यावर मजकूर पाठविल्यानंतर, आणीबाणी सेवा आपल्याशी संपर्क साधू शकतात हे सुनिश्चित करण्यासाठी अवरोधित करणे बंद करते."</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"तुम्ही एखादा आणीबाणी नंबर डायल केला किंवा त्यावर मजकूर पाठविल्यानंतर, आणीबाणी सेवा आपल्याशी संपर्क साधू शकतात हे सुनिश्चित करण्यासाठी अवरोधित करणे बंद करते."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"आता पुन्हा-सक्षम करा"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> अवरोधित केला"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> अनावरोधित केला"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> अनब्लॉक केला"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"आणीबाणी नंबर अवरोधित करण्यात अक्षम."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> आधीपासून अवरोधित केला आहे."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"कॉल करण्यासाठी वैयक्तिक डायलर वापरणे"</string>
     <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_FROM">%2$s</xliff:g> कडील <xliff:g id="CALL_VIA">%1$s</xliff:g> मधील कॉल"</string>
     <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_FROM">%2$s</xliff:g> कडील <xliff:g id="CALL_VIA">%1$s</xliff:g> मधील व्हिडिओ कॉल"</string>
-    <string name="answering_ends_other_call" msgid="8282145910153766401">"उत्तर देण्यामुळे आपला <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल समाप्त होईल"</string>
-    <string name="answering_ends_other_calls" msgid="1198589551399049197">"उत्तर देण्यामुळे आपले <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल समाप्त होतील"</string>
-    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"उत्तर देण्यामुळे आपला <xliff:g id="CALL_VIA">%1$s</xliff:g> व्हिडिओ कॉल समाप्त होईल"</string>
-    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"उत्तर देण्यामुळे आपला सुरु असलेला कॉल समाप्त होईल"</string>
-    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"उत्तर देण्यामुळे आपले सुरु असलेले कॉल समाप्त होतील"</string>
-    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"उत्तर देण्यामुळे आपला सुरु असलेला व्हिडिओ कॉल समाप्त होईल"</string>
+    <string name="answering_ends_other_call" msgid="8282145910153766401">"उत्तर देण्यामुळे तुमचा <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल समाप्त होईल"</string>
+    <string name="answering_ends_other_calls" msgid="1198589551399049197">"उत्तर देण्यामुळे तुमचे <xliff:g id="CALL_VIA">%1$s</xliff:g> कॉल समाप्त होतील"</string>
+    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"उत्तर देण्यामुळे तुमचा <xliff:g id="CALL_VIA">%1$s</xliff:g> व्हिडिओ कॉल समाप्त होईल"</string>
+    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"उत्तर देण्यामुळे तुमचा सुरु असलेला कॉल समाप्त होईल"</string>
+    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"उत्तर देण्यामुळे तुमचे सुरु असलेले कॉल समाप्त होतील"</string>
+    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"उत्तर देण्यामुळे तुमचा सुरु असलेला व्हिडिओ कॉल समाप्त होईल"</string>
     <string name="answer_incoming_call" msgid="4140530013111794587">"उत्तर द्या"</string>
     <string name="decline_incoming_call" msgid="806026168661598368">"नकार द्या"</string>
     <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"आपल्या <xliff:g id="OTHER_CALL">%1$s</xliff:g> कॉलमुळे कॉल केला जाऊ शकत नाही."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"दुसर्‍या अॅपमधील कॉलमुळे कॉल केला जाऊ शकत नाही."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"येणारे कॉल"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"सुटलेले कॉल"</string>
-    <string name="alert_outgoing_call" msgid="982908156825958001">"हा कॉल केल्याने आपला <xliff:g id="OTHER_APP">%1$s</xliff:g> कॉल समाप्त होईल."</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"कॉल ब्‍लॉक करणे"</string>
+    <string name="alert_outgoing_call" msgid="982908156825958001">"हा कॉल केल्याने तुमचा <xliff:g id="OTHER_APP">%1$s</xliff:g> कॉल समाप्त होईल."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"कॉल ब्‍लॉक करणे"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"संपर्कांमध्‍ये क्रमांक नाहीत"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"तुमच्‍या संपर्कांच्‍या सूचीमध्‍ये नसलेले क्रमांक ब्‍लॉक करा"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"खाजगी"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"अशा कॉलरना ब्‍लॉक करा, जे त्‍यांचे क्रमांक उघड करत नाहीत"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"पे फोन"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"पे फोनवरून येणारे कॉल ब्‍लॉक करा"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"अज्ञात"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"अनोळखी कॉलरकडून येणारे कॉल ब्‍लॉक करा"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"कॉल ब्‍लॉक करणे"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"कॉल ब्‍लॉक करणे बंद केले"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"आणीबाणी कॉल केला"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"आणीबाणीत प्रतिसाद देणार्‍यांना तुमच्‍याशी संपर्क साधण्‍याची अनुमती देण्‍यासाठी कॉल ब्‍लॉक करणे बंद केले आहे."</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 8c59a02..ce0e2d1 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -29,7 +29,7 @@
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Panggilan diredam."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Telefon pembesar suara didayakan."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Sedang sibuk. Ada apa?"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Saya akn segera hubungi awak nanti."</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Saya akan hubungi awak semula."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Saya akan hubungi awak kemudian."</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Sedang sibuk. Telefon saya nanti?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Respons pantas"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Panggilan tidak dapat dibuat disebabkan panggilan dalam apl lain."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Panggilan masuk"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Panggilan tidak dijawab"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Sekatan Panggilan"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Membuat panggilan ini akan menamatkan panggilan <xliff:g id="OTHER_APP">%1$s</xliff:g> anda."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Sekatan Panggilan"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nombor bukan dalam Kenalan"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Sekat nombor yang tidak disenaraikan dalam Kenalan anda"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Persendirian"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Sekat pemanggil yang tidak mendedahkan nombor mereka"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefon awam"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Sekat panggilan daripada telefon awam"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Tidak diketahui"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Sekat panggilan daripada pemanggil yang tidak dikenal pasti"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Sekatan Panggilan"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Sekatan Panggilan dilumpuhkan"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Panggilan kecemasan dibuat"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Sekatan Panggilan telah dilumpuhkan untuk membolehkan pasukan bantuan kecemasan menghubungi anda."</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 165dd0e..fe9f33e 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="telecommAppLabel" product="default" msgid="382363169988504520">"ခေါ်ဆိုမှုစီမံခန့်ခွဲရေး"</string>
     <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"ဖုန်း"</string>
-    <string name="unknown" msgid="6878797917991465859">"အကြောင်းအရာ မသိရှိ"</string>
+    <string name="unknown" msgid="6878797917991465859">"မသိပါ"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"လွဲသွားသော ဖုန်းခေါ်မှု"</string>
     <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"လွတ်သွားသည့် အလုပ်ဆိုင်ရာ ခ​ေါ်ဆိုမှု"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"လွဲသွားသော ဖုန်းခေါ်မှုများ"</string>
@@ -29,8 +29,8 @@
     <string name="accessibility_call_muted" msgid="2776111226185342220">"နားထောင်ရုံသာ (စကားပြောပိတ်ထားသည်)"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"စပီကာဖုန်း သုံးလို့ရသည်"</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"အခုပြောလို့မရဘူး။ အကြောင်းထူးရှိလား။"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"ပြန်ခေါ်လိုက်မယ်နော်"</string>
-    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"နောက်မှ ပြန်ခေါ်လိုက်မယ်"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"အခုပဲ ပြန်ခေါ်လိုက်မယ်။"</string>
+    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"နောက်မှ ပြန်ခေါ်လိုက်မယ်။"</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"အခုပြောလို့မရဘူး။ ပြန်ခေါ်ပါလား။"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"အမြန်တုံ့ပြန်ချက်များ"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"အမြန်တုံ့ပြန်ချက်များပြင်ခြင်း"</string>
@@ -42,19 +42,19 @@
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"ဤအပ္ပလီကေးရှင်းသည် ဖုန်းခွင့်ပြုချက်မရှိဘဲ အထွက်ခေါ်ဆိုမှု ပြုလုပ်၍မရပါ။"</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ဖုန်းခေါ်ရန်အတွက်၊ သင့်လျော်သည့်နံပါတ် ရိုက်ထည့်ပါ။"</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ဗွီဒီယိုခေါ်နေစဉ် ထပ်ခေါ်မရပါ။"</string>
-    <string name="no_vm_number" msgid="4164780423805688336">"အသံစာပို့စနစ် နံပါတ် ပျောက်နေပါသည်"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"ဆင်းမ်ကဒ်ပေါ်တွင် အသံစာပို့စနစ် နံပါတ် သိမ်းဆည်ထားခြင်း မရှိပါ"</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"အသံမေးလ် နံပါတ် ပျောက်နေပါသည်"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"ဆင်းမ်ကဒ်ပေါ်တွင် အသံမေးလ် နံပါတ် သိမ်းဆည်ထားခြင်း မရှိပါ"</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"နံပါတ်ထပ်ထည့်ရန်"</string>
     <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"<xliff:g id="NEW_APP">%s</xliff:g> ကို သင့်ဖုန်း၏မူရင်းအက်ပ်အဖြစ် ထားမလား။"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"မူရင်း သတ်မှတ်ရန်"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"မလုပ်တော့"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> သည် ခေါ်ဆိုမှုများကို ဘက်စုံပြုလုပ်ထိန်းချုပ်သွားနိုင်မည်ဖြစ်သည်။ သင်ယုံကြည်သော အက်ပ်များကိုသာ မူရင်း Phone အက်ပ်အဖြစ် သတ်မှတ်သင့်ပါသည်။"</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"ပိတ်ထားသည့် နံပါတ်များ"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"ပိတ်ဆို့နံပါတ်များ"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"ပိတ်ထားသော နံပါတ်များမှ ဖုန်းခေါ်ခြင်း (သို့) စာသားပို့ခြင်းတို့ကို သင်လက်ခံရရှိမည် မဟုတ်ပါ။"</string>
     <string name="block_number" msgid="1101252256321306179">"နံပါတ်တစ်ခု ထည့်ပါ"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ကို ပိတ်ဆို့မှုပြန်ဖွင့်မလား။"</string>
     <string name="unblock_button" msgid="3078048901972674170">"ပိတ်ဆို့မှုပြန်ဖွင့်ပါ"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ဖော်ပြပါပုဂ္ဂိုလ်ထံမှ စာနှင့် ခေါ်ဆိုမှုများကို ပိတ်ဆို့ပါ"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ဤနံပါတ်မှ ခေါ်ဆိုမှုနှင့် စာများကို ပိတ်ဆို့ပါ"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"ဖုန်းနံပါတ်"</string>
     <string name="block_button" msgid="8822290682524373357">"ပိတ်ဆို့ပါ"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"ပိတ်ဆို့ထားသည့် နံပါတ်များကို စက်ပစ္စည်းပိုင်ရှင်သာလျှင် ကြည့်ရှု၍ စီမံခန့်ခွဲနိုင်ပါသည်။"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"အခြားအက်ပ်သုံးပြီးပြောနေသည့်အတွက် အထွက်ခေါ်ဆိုမှုကို မပြုလုပ်နိုင်ပါ။"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"အဝင်ဖုန်းခေါ်ဆိုမှုများ"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"လွတ်သွားသော ဖုန်းခေါ်ဆိုမှုများ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ခေါ်ဆိုမှု ပိတ်ခြင်း"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ဤခေါ်ဆိုမှု ပြုလုပ်ပါက <xliff:g id="OTHER_APP">%1$s</xliff:g> သုံးပြီးပြောနေခြင်းကို ဖြတ်ပစ်ပါမည်။"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ခေါ်ဆိုမှု ပိတ်ခြင်း"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"\'အဆက်အသွယ်များ\' ထဲတွင် မရှိသော နံပါတ်များ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"သင်၏ \'အဆက်အသွယ်များ\' ထဲတွင် မပါဝင်သော နံပါတ်များကို ပိတ်ပါ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"သီးသန့်"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"နံပါတ်မပြသသော ခေါ်ဆိုမှုများကို ပိတ်ပါ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"အများသုံးဖုန်း"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"အများသုံးဖုန်းများမှ ခေါ်ဆိုမှုများကို ပိတ်ပါ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"အမည်မသိ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"အမည်မသိသော ခေါ်ဆိုသူများကို ပိတ်ပါ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ခေါ်ဆိုမှု ပိတ်ခြင်း"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"\'ခေါ်ဆိုမှု ပိတ်ခြင်း\' ကို ရပ်ထားပါသည်"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"အရေးပေါ် ခေါ်ဆိုမှု ပြုလုပ်ထားပါသည်"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"အရေးပေါ်တုံ့ပြန်သူများက သင့်အား ဆက်သွယ်နိုင်စေရန် \'ခေါ်ဆိုမှု ပိတ်ခြင်း\' ကို ရပ်ထားပါသည်။"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index cd5b1cd..6e63c1e 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Kan ikke ringe ut på grunn av en samtale i en annen app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Innkommende anrop"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Tapte anrop"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Anropsblokkering"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Samtalen din i <xliff:g id="OTHER_APP">%1$s</xliff:g> avsluttes hvis du foretar dette anropet."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Anropsblokkering"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numre som ikke står i Kontakter"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokkér numre som ikke står i kontaktene mine"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privat"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokkér oppringere som ikke viser telefonnummeret sitt"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefonkiosk"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokkér anrop fra telefonkiosker"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Ukjent"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokkér anrop fra oppringere som ikke er identifisert"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Anropsblokkering"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Anropsblokkering er slått av"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Nødanrop utført"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Anropsblokkering er slått av for å gjøre det mulig for nødtjenester å kontakte deg."</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 7f15ebe..155ae7e 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -40,7 +40,7 @@
     <string name="enable_account_preference_title" msgid="2021848090086481720">"कलिङ खाताहरू"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"आपतकालीन कलहरूलाई मात्र अनुमति दिइएको छ।"</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"यो अनुप्रयोगले फोनको अनुमति बिना बहिर्गमन कलहरू गर्न सक्दैन।"</string>
-    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"एक कल गर्नको लागि, एक वैध नम्बर प्रविष्ट गर्नुहोस्।"</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"एक कल गर्नको लागि, एक वैध नम्बर प्रविष्टि गर्नुहोस्।"</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"यस समयमा कल थप गर्न सकिँदैन।"</string>
     <string name="no_vm_number" msgid="4164780423805688336">"भ्वाइसमेल नम्बर हराइरहेको छ"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM कार्डमा कुनै पनि भ्वाइसमेल नम्बर भण्डारण भएको छैन।"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"अर्को अनुप्रयोगमा जारी कलका कारण कल गर्न सकिँदैन।"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"आगमन कलहरू"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"छुटेका कलहरू"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"कलमाथि रोक लगाउने सुविधा"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"यो कल गर्नुले तपाईंको <xliff:g id="OTHER_APP">%1$s</xliff:g> कल अन्त्य गर्दछ।"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"कलमाथि रोक लगाउने सुविधा"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"सम्पर्क सूचीहरूमा नरहेका नम्बरहरू"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"आफ्नो सम्पर्क सूचीहरूमा सूचीबद्ध नगरिएका नम्बरहरूमाथि रोक लगाउनुहोस्"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"निजी"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"आफ्नो नम्बरको खुलासा नगरी कल गर्ने व्यक्तिहरूमाथि रोक लगाउनुहोस्"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"पे फोन"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"पे फोनहरूबाट आउने कलहरूमाथि रोक लगाउनुहोस्"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"अज्ञात"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"अज्ञात कल गर्ने व्यक्तिहरूको कलमाथि रोक लगाउनुहोस्"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"कलमाथि रोक लगाउने सुविधा"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"कलमाथि रोक लगाउने सुविधालाई असक्षम पारियो"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"आपतकालीन कल गरियो"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"आपतकालीन अवस्थामा उद्दार गर्ने मान्छेहरूलाई तपाईंलाई सम्पर्क गर्न दिन कलमाथि रोक लगाउने सुविधा असक्षम पारिएको छ।"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 324b5fb..a85d66e 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -19,15 +19,15 @@
     <string name="telecommAppLabel" product="default" msgid="382363169988504520">"Oproepbeheer"</string>
     <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefoon"</string>
     <string name="unknown" msgid="6878797917991465859">"Onbekend"</string>
-    <string name="notification_missedCallTitle" msgid="7554385905572364535">"Gemiste oproep"</string>
-    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"Gemiste zakelijke oproep"</string>
-    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Gemiste oproepen"</string>
-    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> gemiste oproepen"</string>
-    <string name="notification_missedCallTicker" msgid="504686252427747209">"Gemiste oproep van <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
+    <string name="notification_missedCallTitle" msgid="7554385905572364535">"Gemist gesprek"</string>
+    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"Gemist zakelijk gesprek"</string>
+    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Gemiste gesprekken"</string>
+    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> gemiste gesprekken"</string>
+    <string name="notification_missedCallTicker" msgid="504686252427747209">"Gemist gesprek van <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Terugbellen"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Bericht"</string>
-    <string name="accessibility_call_muted" msgid="2776111226185342220">"Oproep gedempt."</string>
-    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Telefoonluidspreker is ingeschakeld."</string>
+    <string name="accessibility_call_muted" msgid="2776111226185342220">"Gesprek gedempt."</string>
+    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Luidspreker is aan."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Kan nu niet opnemen. Alles goed?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Ik bel je zo terug."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Ik bel je later."</string>
@@ -37,24 +37,24 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Snelle reactie"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Bericht verzonden naar <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="enable_account_preference_title" msgid="2021848090086481720">"Oproepaccounts"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Gespreksaccounts"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Alleen noodoproepen zijn toegestaan."</string>
-    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Deze app kan geen uitgaande oproepen starten zonder telefoonrechten."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Deze app kan geen uitgaande gesprekken starten zonder telefoonrechten."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Als je wilt bellen, moet je een geldig nummer invoeren."</string>
-    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Oproep kan momenteel niet worden toegevoegd."</string>
+    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Gesprek kan momenteel niet worden toegevoegd."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Voicemailnummer ontbreekt"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Er is geen voicemailnummer op de simkaart opgeslagen."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Nummer toevoegen"</string>
     <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"Wil je <xliff:g id="NEW_APP">%s</xliff:g> instellen als je standaard telefoon-app?"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"Standaard instellen"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Annuleren"</string>
-    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> kan oproepen plaatsen en alle aspecten hiervan beheren. Stel alleen apps in als je standaard telefoon-app als je ze vertrouwt."</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> kan gesprekken plaatsen en alle aspecten hiervan beheren. Stel alleen apps in als je standaard telefoon-app als je ze vertrouwt."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Geblokkeerde nummers"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Je ontvangt geen oproepen of sms\'jes van geblokkeerde nummers."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Je ontvangt geen gesprekken of sms\'jes van geblokkeerde nummers."</string>
     <string name="block_number" msgid="1101252256321306179">"Een nummer toevoegen"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Blokkering van <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> opheffen?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Blokkering opheffen"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Oproepen en sms\'jes blokkeren van"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Gesprekken en sms\'jes blokkeren van"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Telefoonnummer"</string>
     <string name="block_button" msgid="8822290682524373357">"Blokkeren"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Alleen de eigenaar van het apparaat kan geblokkeerd nummers bekijken en beheren."</string>
@@ -67,20 +67,34 @@
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Kan alarmnummer niet blokkeren."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> is al geblokkeerd."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"De persoonlijke kiezer gebruiken om te bellen"</string>
-    <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_VIA">%1$s</xliff:g>-oproep van <xliff:g id="CALL_FROM">%2$s</xliff:g>"</string>
+    <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_VIA">%1$s</xliff:g>-gesprek van <xliff:g id="CALL_FROM">%2$s</xliff:g>"</string>
     <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_VIA">%1$s</xliff:g>-videogesprek van <xliff:g id="CALL_FROM">%2$s</xliff:g>"</string>
-    <string name="answering_ends_other_call" msgid="8282145910153766401">"Als je opneemt, wordt je <xliff:g id="CALL_VIA">%1$s</xliff:g>-oproep beëindigd"</string>
-    <string name="answering_ends_other_calls" msgid="1198589551399049197">"Als je opneemt, worden je <xliff:g id="CALL_VIA">%1$s</xliff:g>-oproepen beëindigd"</string>
+    <string name="answering_ends_other_call" msgid="8282145910153766401">"Als je opneemt, wordt je <xliff:g id="CALL_VIA">%1$s</xliff:g>-gesprek beëindigd"</string>
+    <string name="answering_ends_other_calls" msgid="1198589551399049197">"Als je opneemt, worden je <xliff:g id="CALL_VIA">%1$s</xliff:g>-gesprekken beëindigd"</string>
     <string name="answering_ends_other_video_call" msgid="8510410917384186360">"Als je opneemt, wordt je <xliff:g id="CALL_VIA">%1$s</xliff:g>-videogesprek beëindigd"</string>
-    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"Als je opneemt, wordt je actieve oproep beëindigd"</string>
-    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"Als je opneemt, worden je actieve oproepen beëindigd"</string>
+    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"Als je opneemt, wordt je actief gesprek beëindigd"</string>
+    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"Als je opneemt, worden je actieve gesprekken beëindigd"</string>
     <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"Als je opneemt, wordt je actieve videogesprek beëindigd"</string>
     <string name="answer_incoming_call" msgid="4140530013111794587">"Beantwoorden"</string>
     <string name="decline_incoming_call" msgid="806026168661598368">"Weigeren"</string>
-    <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"Oproep kan niet worden gestart vanwege je <xliff:g id="OTHER_CALL">%1$s</xliff:g>-oproep."</string>
-    <string name="cant_call_due_to_ongoing_calls" msgid="1380804892363503856">"Oproep kan niet worden gestart vanwege je <xliff:g id="OTHER_CALL">%1$s</xliff:g>-oproepen."</string>
-    <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Oproep kan niet worden gestart vanwege een oproep in een andere app."</string>
-    <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Inkomende oproepen"</string>
-    <string name="notification_channel_missed_call" msgid="8727062678632713146">"Gemiste oproepen"</string>
-    <string name="alert_outgoing_call" msgid="982908156825958001">"Als je deze oproep start, wordt je <xliff:g id="OTHER_APP">%1$s</xliff:g>-oproep beëindigd."</string>
+    <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"Gesprek kan niet worden gestart vanwege je <xliff:g id="OTHER_CALL">%1$s</xliff:g>-gesprek."</string>
+    <string name="cant_call_due_to_ongoing_calls" msgid="1380804892363503856">"Gesprek kan niet worden gestart vanwege je <xliff:g id="OTHER_CALL">%1$s</xliff:g>-gesprekken."</string>
+    <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Gesprek kan niet worden gestart vanwege een gesprek in een andere app."</string>
+    <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Inkomende gesprekken"</string>
+    <string name="notification_channel_missed_call" msgid="8727062678632713146">"Gemiste gesprekken"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Gesprekken blokkeren"</string>
+    <string name="alert_outgoing_call" msgid="982908156825958001">"Als je dit gesprek start, wordt je <xliff:g id="OTHER_APP">%1$s</xliff:g>-gesprek beëindigd."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Gesprekken blokkeren"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nummers die niet op je contactenlijst staan"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokkeer nummers die niet op je contactenlijst staan"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privé"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bellers met een anoniem nummer blokkeren"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Betaaltelefoon"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Gesprekken van betaaltelefoons blokkeren"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Onbekend"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Gesprekken van onbekende bellers blokkeren"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Gesprekken blokkeren"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Gesprekken blokkeren uitgeschakeld"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Noodoproep geplaatst"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Gesprekken blokkeren is uitgeschakeld zodat nooddiensten je kunnen bereiken."</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
new file mode 100644
index 0000000..c3c6cbe
--- /dev/null
+++ b/res/values-or/strings.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="telecommAppLabel" product="default" msgid="382363169988504520">"କଲ୍ ପରିଚାଳନା"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"ଫୋନ୍ କରନ୍ତୁ"</string>
+    <string name="unknown" msgid="6878797917991465859">"ଅଜଣା"</string>
+    <string name="notification_missedCallTitle" msgid="7554385905572364535">"ମିସଡ୍ କଲ୍‌"</string>
+    <string name="notification_missedWorkCallTitle" msgid="6242489980390803090">"କାର୍ଯ୍ୟସ୍ଥଳୀରୁ ଆସିଥିବା ମିସଡ୍ କଲ୍"</string>
+    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"ମିସଡ୍ କଲ୍"</string>
+    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>ଟି ମିସଡ୍ କଲ୍"</string>
+    <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>ଙ୍କ ଠାରୁ ମିସ୍-କଲ୍ ମିଳିଛି"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"କଲବ୍ୟାକ୍ କରନ୍ତୁ"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"ମେସେଜ୍‍ ଦିଅନ୍ତୁ"</string>
+    <string name="accessibility_call_muted" msgid="2776111226185342220">"କଲ୍ ମ୍ୟୁଟ୍ କରାଯାଇଛି।"</string>
+    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"ସ୍ପିକରଫୋନ୍‌କୁ ସକ୍ଷମ କରାଯାଇଛି ।"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"ବର୍ତ୍ତମାନ କଥା ହୋ‌ଇପାରିବ ନାହିଁ। କଥା କ’ଣ?"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"ମୁଁ ଟିକେ ପରେ ଆପଣଙ୍କୁ କଲ୍ କରିବି।"</string>
+    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"ମୁଁ ଆପଣଙ୍କୁ ପରେ କଲ୍ କରିବି।"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"ବର୍ତ୍ତମାନ କଥା ହୋ‌ଇପାରିବ ନାହିଁ। ମୋତେ ପରେ କଲ୍ କରିବେ?"</string>
+    <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"ଶୀଘ୍ର ଉତ୍ତର"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"ଶୀଘ୍ର ଉତ୍ତରକୁ ଏଡିଟ୍ କରନ୍ତୁ"</string>
+    <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
+    <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"ଶୀଘ୍ର ଉତ୍ତର"</string>
+    <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g>କୁ ମେସେଜ୍ ପଠାଗଲା।"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"କଲ୍ କରିବା ଆକାଉଣ୍ଟ"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"କେବଳ ଜରୁରିକାଳୀନ କଲ୍‌କୁ ଅନୁମତି ଦିଆଯାଇଛି।"</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"ଫୋନ୍‌ର ବିନାଅନୁମତିରେ ଏହି ଆପ୍ଲିକେଶନ୍ ଆଉଟ୍‌ଗୋ‌ଇଙ୍ଗ କଲ୍ କରିପାରିବ ନାହିଁ।"</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"ଗୋଟିଏ କଲ୍ କରିବା ପାଇଁ ଏକ ବୈଧ ନମ୍ବର୍ ପ୍ରବେଶ କରନ୍ତୁ।"</string>
+    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"ଏହି ସମୟରେ କଲ୍ ଯୋଡ଼ାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"ହଜିଯାଇଥିବା ଭଏସମେଲ୍ ନମ୍ବର୍"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM କାର୍ଡରେ କୌଣସି ଭଏସମେଲ୍ ନମ୍ବର୍ ଷ୍ଟୋର୍ କରାଯାଇନାହିଁ।"</string>
+    <string name="add_vm_number_str" msgid="4676479471644687453">"ନମ୍ବର୍ ଯୋଡ଼ନ୍ତୁ"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"<xliff:g id="NEW_APP">%s</xliff:g>କୁ ଆପଣଙ୍କ ଫୋନ୍‌ର ଡିଫଲ୍ଟ ଆପ୍ କରିବେ?"</string>
+    <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"ଡିଫଲ୍ଟ ସେଟ୍ କରନ୍ତୁ"</string>
+    <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> କଲ୍ କରିବା ଏବଂ କଲ୍‌ର ସମସ୍ତ ଦିଗକୁ ନିୟନ୍ତ୍ରଣ କରିବାରେ ସକ୍ଷମ ହେବ। କେବଳ ନିଜର ଭରସାଯୋଗ୍ୟ ଆପ୍‌କୁ ଡିଫଲ୍ଟ ଫୋନ୍ ଆପ୍ ଭାବେ ସେଟ୍ କରିବା ଉଚିତ୍।"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"ଅବରୋଧ କରାଯାଇଥିବା ନମ୍ବର୍"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ଅବରୋଧ କରାଯାଇଥିବା ନମ୍ବର୍‌ରୁ ଆପଣ କଲ୍ କିମ୍ବା ଟେକ୍ସଟ୍ ଗ୍ରହଣ କରିପାରିବେ ନାହିଁ।"</string>
+    <string name="block_number" msgid="1101252256321306179">"ଗୋଟିଏ ନମ୍ବର୍ ଯୋଡ଼ନ୍ତୁ"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>ରୁ ଅବରୋଧ ହଟାଇବେ?"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"ଅବରୋଧ ହଟାନ୍ତୁ"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ଏହାର କଲ୍ ଓ ଟେକ୍ସଟ୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="add_blocked_number_hint" msgid="6847675097085433553">"ଫୋନ୍ ନମ୍ଵର୍"</string>
+    <string name="block_button" msgid="8822290682524373357">"ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="non_primary_user" msgid="5180129233352533459">"କେବଳ ଡିଭାଇସ୍‌ର ମାଲିକ ଅବରୋଧ କରାଯାଇଥିବା ନମ୍ବର୍‌କୁ ଦେଖିପାରିବେ ଓ ପରିଚାଳନା କରିପାରିବେ।"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"ଅବରୋଧ ହଟାନ୍ତୁ"</string>
+    <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"ଅସ୍ଥାୟୀରୂପେ ଅବରୋଧ ଅଫ୍ ଅଛି"</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"ଆପଣ ଗୋଟିଏ ଜରୁରିକାଳୀନ ନମ୍ବର୍‌କୁ ଡାଏଲ୍ କିମ୍ବା ଟେକ୍ସଟ୍ କରିବା ପରେ, ଜରୁରିକାଳୀନ ସେବା ଆପଣଙ୍କୁ ଯୋଗାଯୋଗ କରିବାକୁ ସୁନିଶ୍ଚିତ କରିବା ପାଇଁ ଅବରୋଧକୁ ବନ୍ଦ କରିଦିଆଯାଇଥାଏ।"</string>
+    <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"ବର୍ତ୍ତମାନ ପୁନଃସକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ଅବରୋଧ କରାଯାଇଛି"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> ଅବରୋଧ ହଟାଇଦିଆଯାଇଛି"</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"ଜରୁରିକାଳୀନ ନମ୍ବର୍‌କୁ ଅବରୋଧ କରିବାରେ ଅକ୍ଷମ।"</string>
+    <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>କୁ ଅବରୋଧ କରାଯାଇସରିଛି।"</string>
+    <string name="toast_personal_call_msg" msgid="5115361633476779723">"କଲ୍ କରିବା ପାଇଁ ବ୍ୟକ୍ତିଗତ ଡାଏଲର୍‌କୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+    <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_FROM">%2$s</xliff:g> ଠାରୁ <xliff:g id="CALL_VIA">%1$s</xliff:g>କୁ କଲ୍ କରନ୍ତୁ"</string>
+    <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_FROM">%2$s</xliff:g> ଠାରୁ <xliff:g id="CALL_VIA">%1$s</xliff:g> ଭିଡିଓ କଲ୍ କରନ୍ତୁ"</string>
+    <string name="answering_ends_other_call" msgid="8282145910153766401">"ଉତ୍ତର ଦେବାଦ୍ଵାରା ଆପଣଙ୍କର <xliff:g id="CALL_VIA">%1$s</xliff:g> କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ"</string>
+    <string name="answering_ends_other_calls" msgid="1198589551399049197">"ଉତ୍ତର ଦେବାଦ୍ଵାରା ଆପଣଙ୍କର <xliff:g id="CALL_VIA">%1$s</xliff:g> କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ"</string>
+    <string name="answering_ends_other_video_call" msgid="8510410917384186360">"ଉତ୍ତର ଦେବାଦ୍ଵାରା ଆପଣଙ୍କର <xliff:g id="CALL_VIA">%1$s</xliff:g> ଭିଡିଓ କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ"</string>
+    <string name="answering_ends_other_managed_call" msgid="5186137550267947785">"ଉତ୍ତର ଦେବାଦ୍ଵାରା ଆପଣଙ୍କର ଜାରି ରହିଥିବା କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ"</string>
+    <string name="answering_ends_other_managed_calls" msgid="6429838309560397988">"ଉତ୍ତର ଦେବାଦ୍ଵାରା ଆପଣଙ୍କର ଜାରି ରହିଥିବା କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ"</string>
+    <string name="answering_ends_other_managed_video_call" msgid="1585423762458248435">"ଉତ୍ତର ଦେବାଦ୍ଵାରା ଆପଣଙ୍କର ଜାରି ରହିଥିବା ଭିଡିଓ କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ"</string>
+    <string name="answer_incoming_call" msgid="4140530013111794587">"ଉତ୍ତର ଦିଅନ୍ତୁ"</string>
+    <string name="decline_incoming_call" msgid="806026168661598368">"ଅସ୍ୱୀକାର"</string>
+    <string name="cant_call_due_to_ongoing_call" msgid="4952615196237854748">"ଆପଣଙ୍କର <xliff:g id="OTHER_CALL">%1$s</xliff:g> କଲ୍ ହେତୁ କଲ୍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="cant_call_due_to_ongoing_calls" msgid="1380804892363503856">"ଆପଣଙ୍କର <xliff:g id="OTHER_CALL">%1$s</xliff:g> କଲ୍ ହେତୁ କଲ୍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ଅନ୍ୟ ଆପ୍‌ରେ କରାଯାଇଥିବା କଲ୍ ହେତୁ କଲ୍ କରାଯାଇପାରିବ ନାହିଁ।"</string>
+    <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ଇନ୍‌କମିଙ୍ଗ କଲ୍"</string>
+    <string name="notification_channel_missed_call" msgid="8727062678632713146">"ମିସଡ୍ କଲ୍"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"କଲ୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="alert_outgoing_call" msgid="982908156825958001">"ଏହି କଲ୍‌କୁ ସ୍ଥାପନ କରିବା ଦ୍ଵାରା ଆପଣଙ୍କର <xliff:g id="OTHER_APP">%1$s</xliff:g> କଲ୍ ସମାପ୍ତ ହୋ‌ଇଯିବ।"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"କଲ୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"ଯୋଗାଯୋଗରେ ନଥିବା ନମ୍ବର୍"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ଆପଣଙ୍କ ଯୋଗାଯୋଗରେ ତାଲିକାଭୁକ୍ତ ହୋ‌ଇନଥିବା ନମ୍ବର୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ଗୋପନୀୟ"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ନିଜର ନମ୍ବର୍‌କୁ ପ୍ରକାଶ କରୁନଥିବା କଲ୍‌କର୍ତ୍ତାଙ୍କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"ପେ-ଫୋନ୍"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ପେ-ଫୋନ୍‌ରୁ କଲ୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ଅଜଣା"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ଅଚିହ୍ନା କଲକର୍ତ୍ତାଙ୍କର କଲ୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"କଲ୍‌କୁ ଅବରୋଧ କରନ୍ତୁ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"କଲ୍ ଅବରୋଧ ସୁବିଧାକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"ଜରୁରିକାଳୀନ କଲ୍ କରାଗଲା"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ଜରୁରିକାଳୀନ ସହାୟତା କର୍ମଚାରୀମାନେ ଆପଣଙ୍କୁ ଯୋଗଯୋଗ କରିବା ପାଇଁ କଲ୍ ଅବରୋଧକୁ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
+</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 5bf26f3..b9ffc07 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -49,25 +49,25 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈੱਟ ਕਰੋ"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"ਰੱਦ ਕਰੋ"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> ਕਾਲਾਂ ਕਰ ਸਕੇਗੀ ਅਤੇ ਕਾਲਾਂ ਦੇ ਸਾਰੇ ਪੱਖਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰ ਸਕੇਗੀ। ਸਿਰਫ਼ ਉਹਨਾਂ ਐਪਾਂ ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਫ਼ੋਨ ਐਪ ਵਜੋਂ ਸੈੱਟ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ ਜਿੰਨ੍ਹਾਂ \'ਤੇ ਤੁਸੀਂ ਭਰੋਸਾ ਕਰਦੇ ਹੋ।"</string>
-    <string name="blocked_numbers" msgid="2751843139572970579">"ਬਲੌਕ ਕੀਤੇ ਗਏ ਨੰਬਰ"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ਤੁਹਾਨੂੰ ਬਲੌਕ ਕੀਤੇ ਨੰਬਰਾਂ ਤੋਂ ਕਾਲਾਂ ਜਾਂ ਲਿਖਤ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੇ।"</string>
+    <string name="blocked_numbers" msgid="2751843139572970579">"ਬਲਾਕ ਕੀਤੇ ਗਏ ਨੰਬਰ"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"ਤੁਹਾਨੂੰ ਬਲਾਕ ਕੀਤੇ ਨੰਬਰਾਂ ਤੋਂ ਕਾਲਾਂ ਜਾਂ ਲਿਖਤ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਨਹੀਂ ਹੋਣਗੇ।"</string>
     <string name="block_number" msgid="1101252256321306179">"ਇੱਕ ਨੰਬਰ ਸ਼ਾਮਲ ਕਰੋ"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"ਕੀ <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ਨੂੰ ਅਣਬਲੌਕ ਕਰਨਾ ਹੈ?"</string>
-    <string name="unblock_button" msgid="3078048901972674170">"ਅਣਬਲੌਕ ਕਰੋ"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ਇਸ ਨੰਬਰ ਤੋਂ ਕਾਲਾਂ ਅਤੇ ਲਿਖਤੀ ਸੁਨੇਹਿਆਂ ਨੂੰ ਬਲੌਕ ਕਰੋ"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"ਕੀ <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
+    <string name="unblock_button" msgid="3078048901972674170">"ਅਣਬਲਾਕ ਕਰੋ"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"ਇਸ ਨੰਬਰ ਤੋਂ ਕਾਲਾਂ ਅਤੇ ਲਿਖਤੀ ਸੁਨੇਹਿਆਂ ਨੂੰ ਬਲਾਕ ਕਰੋ"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"ਫ਼ੋਨ ਨੰਬਰ"</string>
-    <string name="block_button" msgid="8822290682524373357">"ਬਲੌਕ ਕਰੋ"</string>
+    <string name="block_button" msgid="8822290682524373357">"ਬਲਾਕ ਕਰੋ"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"ਸਿਰਫ਼ ਡੀਵਾਈਸ ਮਾਲਕ ਹੀ ਬਲੌਕ ਕੀਤੇ ਗਏ ਨੰਬਰਾਂ ਨੂੰ  ਦੇਖ  ਅਤੇ ਪ੍ਰਬੰਧਿਤ ਕਰ ਸਕਦਾ ਹੈ।"</string>
-    <string name="delete_icon_description" msgid="8903995728252556724">"ਅਨਬਲੌਕ ਕਰੋ"</string>
+    <string name="delete_icon_description" msgid="8903995728252556724">"ਅਣਬਲਾਕ ਕਰੋ"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"ਬਲੌਕਿੰਗ ਆਰਜ਼ੀ ਤੌਰ \'ਤੇ ਬੰਦ ਹੈ"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਇੱਕ ਐਮਰਜੈਂਸੀ ਨੰਬਰ ਨੂੰ ਡਾਇਲ ਕੀਤੇ ਜਾਣ ਜਾਂ ਲਿਖਤ ਸੁਨੇਹਾ ਭੇਜੇ ਜਾਣ ਤੋਂ ਬਾਅਦ, ਇਹ ਯਕੀਨੀ ਬਣਾਉਣ ਲਈ ਬਲੌਕਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਕਿ ਐਮਰਜੈਂਸੀ ਸੇਵਾਵਾਂ ਤੁਹਾਨੂੰ ਸੰਪਰਕ ਕਰ ਸਕਣ।"</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"ਹੁਣੇ ਦੁਬਾਰਾ ਯੋਗ ਬਣਾਓ"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ਬਲੌਕ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> ਅਣਬਲੌਕ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> ਅਣਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"ਐਮਰਜੈਂਸੀ ਨੰਬਰ ਨੂੰ ਬਲੌਕ ਕਰਨ ਵਿੱਚ ਅਸਮਰੱਥ।"</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ਪਹਿਲਾਂ ਤੋਂ ਹੀ ਬਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"ਕਾਲ ਕਰਨ ਲਈ ਨਿੱਜੀ ਡਾਇਲਰ ਦੀ ਵਰਤੋਂ ਕਰਨੀ"</string>
-    <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_FROM">%2$s</xliff:g> ਵੱਲੋਂ <xliff:g id="CALL_VIA">%1$s</xliff:g> ਕਾਲ"</string>
+    <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_VIA">%1$s</xliff:g> ਕਾਲ ਕਰਤਾ <xliff:g id="CALL_FROM">%2$s</xliff:g>"</string>
     <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_FROM">%2$s</xliff:g> ਵੱਲੋਂ <xliff:g id="CALL_VIA">%1$s</xliff:g> ਵੀਡੀਓ ਕਾਲ"</string>
     <string name="answering_ends_other_call" msgid="8282145910153766401">"ਜਵਾਬ ਦੇਣ ਨਾਲ ਤੁਹਾਡੀ <xliff:g id="CALL_VIA">%1$s</xliff:g> ਕਾਲ ਸਮਾਪਤ ਹੋ ਜਾਵੇਗੀ"</string>
     <string name="answering_ends_other_calls" msgid="1198589551399049197">"ਜਵਾਬ ਦੇਣ ਨਾਲ ਤੁਹਾਡੀਆਂ <xliff:g id="CALL_VIA">%1$s</xliff:g> ਕਾਲਾਂ ਸਮਾਪਤ ਹੋ ਜਾਣਗੀਆਂ"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਇੱਕ ਕਾਲ ਹੋਣ ਦੇ ਕਾਰਨ ਕਾਲ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ਇਨਕਮਿੰਗ ਕਾਲਾਂ"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"ਖੁੰਝੀਆਂ ਕਾਲਾਂ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ਕਾਲ ਬਲਾਕਿੰਗ"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ਇਹ ਕਾਲ ਕਰਨ ਨਾਲ ਤੁਹਾਡੀ <xliff:g id="OTHER_APP">%1$s</xliff:g> ਕਾਲ ਸਮਾਪਤ ਹੋ ਜਾਵੇਗੀ।"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ਕਾਲ ਬਲਾਕਿੰਗ"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"ਨੰਬਰ ਜੋ ਤੁਹਾਡੇ ਸੰਪਰਕਾਂ ਵਿੱਚ ਨਹੀਂ ਹਨ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ਉਹ ਨੰਬਰ ਬਲਾਕ ਕਰੋ ਜੋ ਤੁਹਾਡੇ ਸੰਪਰਕਾਂ ਵਿੱਚ ਨਹੀਂ ਹਨ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ਨਿੱਜੀ"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ਉਹ ਕਾਲਰ ਬਲਾਕ ਕਰੋ ਜਿਨ੍ਹਾਂ ਦਾ ਨੰਬਰ ਨਹੀਂ ਦਿਖਾਈ ਦਿੰਦਾ ਹੈ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"ਜਨਤਕ ਫ਼ੋਨ"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ਜਨਤਕ ਫ਼ੋਨਾਂ ਵਾਲੀਆਂ ਕਾਲਾਂ ਬਲਾਕ ਕਰੋ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ਅਗਿਆਤ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"ਅਣਪਛਾਤੇ ਕਾਲਰਾਂ ਵਾਲੀਆਂ ਕਾਲਾਂ ਬਲਾਕ ਕਰੋ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ਕਾਲ ਬਲਾਕਿੰਗ"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ਕਾਲ ਬਲਾਕਿੰਗ ਵਿਕਲਪ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"ਸੰਕਟਕਾਲੀਨ ਕਾਲ ਕੀਤੀ ਗਈ"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ਸੰਕਟਕਾਲੀਨ ਸਥਿਤੀ ਵਿੱਚ ਮਦਦ ਕਰਨ ਵਾਲੇ ਵਿਅਕਤੀ ਨੂੰ ਤੁਹਾਨੂੰ ਸੰਪਰਕ ਕਰਨ ਦੇਣ ਲਈ ਕਾਲ ਬਲਾਕਿੰਗ ਵਿਕਲਪ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 4a5820f..1bc4863 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -54,7 +54,7 @@
     <string name="block_number" msgid="1101252256321306179">"Dodaj numer"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Odblokować <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Odblokuj"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokuj połączenia i SMS-y z:"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokuj połączenia i SMS-y z numeru:"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Numer telefonu"</string>
     <string name="block_button" msgid="8822290682524373357">"Zablokuj"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Tylko właściciel urządzenia może przeglądać zablokowane numery i nimi zarządzać."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Nie możesz zadzwonić z powodu trwającej rozmowy w innej aplikacji."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Połączenia przychodzące"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Połączenia nieodebrane"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokowanie połączeń"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Jeśli zadzwonisz, połączenie w aplikacji <xliff:g id="OTHER_APP">%1$s</xliff:g> zostanie zakończone."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokowanie połączeń"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numery spoza Kontaktów"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokuj połączenia od numerów, które nie znajdują się w Kontaktach"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Zastrzeżone"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokuj połączenia z numerów zastrzeżonych"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Budki telefoniczne"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokuj połączenia z budek telefonicznych"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Nieznane"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokuj połączenia z nieznanych numerów"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokowanie połączeń"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokowanie połączeń wyłączone"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Wykonano połączenie alarmowe"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokowanie połączeń zostało wyłączone, aby służby ratownicze mogły się z Tobą skontaktować."</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 9b7d239..8dc9405 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Não é possível efetuar a chamada devido a uma chamada noutra aplicação."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Chamadas recebidas"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Chamadas não atendidas"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bloqueio de chamadas"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ao efetuar esta chamada, irá terminar a chamada na aplicação <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bloqueio de chamadas"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Números não incluídos nos Contactos"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquear números que não estejam na sua lista de Contactos"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privadas"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquear autores de chamadas que não revelem o número"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefone público"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquear chamadas de telefones públicos"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Desconhecidas"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquear chamadas não identificadas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bloqueio de chamadas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Bloqueio de chamadas desativado"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Chamada de emergência efetuada"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"O bloqueio de chamadas foi desativado para permitir a receção de contactos de resposta a emergências."</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 597e75e..84fe5a5 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -43,28 +43,28 @@
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar uma chamada, digite um número válido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No momento, não é possível adicionar a chamada."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Número correio de voz ausente"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"Não há um número correio de voz armazenado no cartão SIM."</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"Não há um número correio de voz armazenado no chip."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Adicionar número"</string>
-    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"Usar o <xliff:g id="NEW_APP">%s</xliff:g> como seu aplicativo de smartphone padrão?"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"Usar o <xliff:g id="NEW_APP">%s</xliff:g> como seu app de telefone padrão?"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"Definir padrão"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Cancelar"</string>
-    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"O <xliff:g id="NEW_APP">%s</xliff:g> poderá ligar e controlar todos os aspectos das chamadas. Defina como aplicativo de smartphone padrão somente aqueles em que você confia."</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"O <xliff:g id="NEW_APP">%s</xliff:g> poderá ligar e controlar todos os aspectos das chamadas. Defina como aplicativo Telefone padrão somente aqueles em que você confia."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Números bloqueados"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Você não receberá chamadas nem mensagens de texto dos números bloqueados."</string>
     <string name="block_number" msgid="1101252256321306179">"Adicionar um número"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Desbloquear <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Desbloquear"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear chamadas e mensagens de texto de"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear chamadas e mensagens de:"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Nº de telefone"</string>
     <string name="block_button" msgid="8822290682524373357">"Bloquear"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Apenas o proprietário do dispositivo pode ver e gerenciar os números bloqueados."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"Desbloquear"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Bloqueio temporariamente desativado"</string>
-    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Após discar ou enviar uma mensagem de texto para um número de emergência, o bloqueio é desativado para garantir o contato do serviço de emergência com você."</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Após discar ou enviar uma mensagem de texto para um telefone de emergência, o bloqueio é desativado para garantir o contato do serviço de emergência."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Reativar agora"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"O número <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> está bloqueado."</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"O número <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> está desbloqueado."</string>
-    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Não é possível bloquear o número de emergência."</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Não é possível bloquear o telefone de emergência."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"O número <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> já está bloqueado."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Usando o discador pessoal para fazer a chamada"</string>
     <string name="notification_incoming_call" msgid="7713197997773986670">"Chamada de <xliff:g id="CALL_FROM">%2$s</xliff:g> no <xliff:g id="CALL_VIA">%1$s</xliff:g>"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Não é possível ligar com uma chamada em andamento em outro aplicativo."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Chamadas recebidas"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Chamadas perdidas"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bloqueio de chamadas"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Se você ligar agora, sua chamada será encerrada no <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bloqueio de chamadas"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Números que não estão nos contatos"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Bloquear os números que não estão nos seus contatos"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Particular"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bloquear os autores das chamadas que não divulgam o número"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Orelhão"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bloquear chamadas de orelhão"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Desconhecido"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bloquear chamadas de autores não identificados"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bloqueio de chamadas"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Bloqueio de chamadas desativado"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"A chamada de emergência foi feita"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"O bloqueio de chamadas foi desativado para permitir que a equipe de emergência entre em contato com você."</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 770fb24..ae17707 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Apelul nu poate fi inițiat din cauza unui apel din altă aplicație."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Apeluri primite"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Apeluri nepreluate"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blocarea apelurilor"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Dacă inițiați acest apel, cel din <xliff:g id="OTHER_APP">%1$s</xliff:g> va fi încheiat."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blocarea apelurilor"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numere care nu sunt în Agendă"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blocați numerele care nu sunt înregistrate în Agendă"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privat"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blocați apelanții care nu își afișează numărul"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefon public"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blocați apelurile de la telefoane publice"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Necunoscut"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blocați apelurile de la apelanți neidentificați"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blocarea apelurilor"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blocarea apelurilor este dezactivată."</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"S-a efectuat un apel de urgență."</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blocarea apelurilor a fost dezactivată pentru a permite serviciilor de urgență să vă contacteze."</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d1a7a56..cd55aaa 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -62,8 +62,8 @@
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Блокировка временно снята"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"После набора или отправки сообщения на номер экстренной службы блокировка будет выключена, чтобы специалисты могли связаться с вами."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Включить блокировку"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"Номер <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> заблокирован"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"Номер <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> разблокирован"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"Номер <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> заблокирован."</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"Номер <xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> разблокирован."</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Невозможно заблокировать номер экстренной службы."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"Номер <xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> уже заблокирован."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Набор номера с помощью персонализированной панели"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Вы не можете отправить новый вызов, пока не завершите текущий в другом приложении"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Входящие вызовы"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Пропущенные вызовы"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Блокировка вызовов"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Если вы начнете этот звонок, вызов в <xliff:g id="OTHER_APP">%1$s</xliff:g> будет завершен."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Блокировка вызовов"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Незнакомые номера"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Блокировать номера, которых нет в ваших контактах"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Скрытые номера"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Блокировать вызовы со скрытых номеров"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Телефоны-автоматы"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Блокировать вызовы с телефонов-автоматов"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Неизвестные номера"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Блокировать вызовы с неопределяемых номеров"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Блокировка вызовов"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Блокировка вызовов отключена"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Выполнен экстренный вызов"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Блокировка вызовов отключена, чтобы у экстренных служб была возможность позвонить вам."</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index b5e2110..538f96d 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"වෙනත් යෙදුමක ඇමතුමක් හේතුවෙන් ඇමතුම ගැනීමට නොහැකිය."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"එන ඇමතුම්"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"මඟ හැරුණු ඇමතුම්"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"ඇමතුම් අවහිර කිරීම"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"මෙම ඇමතුම ගැනීම ඔබේ <xliff:g id="OTHER_APP">%1$s</xliff:g> ඇමතුම අවසන් කරනු ඇත."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"ඇමතුම් අවහිර කිරීම"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"සම්බන්ධතා තුළ නොමැති අංක"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ඔබගේ සම්බන්ධතා තුළ ලැයිස්තුගත නොකරන ලද අංක අවහිර කරන්න"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"පෞද්ගලික"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ඔවුන්ගේ අංකය අනාවරණය නොකරන අමතන්නන් අවහිර කරන්න"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"ගෙවුම් දුරකථනය"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"ගෙවුම් දුරකථන වෙතින් වන ඇමතුම් අවහිර කරන්න"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"නොදනී"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"නොහඳුනන අමතන්නන් වෙතින් වන ඇමතුම් අවහිර කරන්න"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"ඇමතුම් අවහිර කිරීම"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ඇමතුම් අවහිර කිරීම අබල කර ඇත"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"හදිසි ඇමතුම් ගැනීම"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"හදිසි අමතන්නන්ට ඔබව සම්බන්ධ කර ගැනීමට ඉඩ දීමට ඇමතුම් අවහිර කිරීම අබල කර ඇත."</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 5268ba4..a9d4940 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -28,10 +28,10 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Napísať"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Zvuk hovoru bol vypnutý."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Reproduktor je povolený."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Teraz nemôžem hovoriť, o čo ide?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Teraz nemôžem hovoriť, o čo ide?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Zavolám späť."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Zavolám neskôr."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Teraz nemôžem, zavolajte inokedy."</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Nemôžem hovoriť, zavoláte neskôr?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Rýchle odpovede"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Upraviť rýchle odpovede"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -52,7 +52,7 @@
     <string name="blocked_numbers" msgid="2751843139572970579">"Blokované čísla"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Z blokovaných čísel nebudete prijímať hovory ani textové správy."</string>
     <string name="block_number" msgid="1101252256321306179">"Pridať číslo"</string>
-    <string name="unblock_dialog_body" msgid="1614238499771862793">"Odblokovať číslo <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
+    <string name="unblock_dialog_body" msgid="1614238499771862793">"Chcete odblokovať číslo <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Odblokovať"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Blokovať hovory a textové správy"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Telefónne číslo"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Hovor sa nedá uskutočniť, pretože prebieha hovor v inej aplikácii."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Prichádzajúce hovory"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Zmeškané hovory"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokovanie hovorov"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ak uskutočníte tento hovor, hovor cez <xliff:g id="OTHER_APP">%1$s</xliff:g> bude ukončený."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokovanie hovorov"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Čísla, ktoré nie sú v kontaktoch"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokovať čísla, ktoré nemáte v kontaktoch"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Skryté čísla"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokovať volajúcich, ktorí skrývajú svoje číslo"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Verejný telefón"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokovať hovory z verejných telefónov"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Neznáme číslo"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokovať hovory od nerozpoznaných volajúcich"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokovanie hovorov"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokovanie hovorov je vypnuté"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Bolo uskutočnené tiesňové volanie"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokovanie hovorov bolo vypnuté, aby vás mohli kontaktovať pracovníci tiesňových služieb."</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index de9716a..083a7c5 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Klica ni mogoče vzpostaviti zaradi klica prek druge aplikacije."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Dohodni klici"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Neodgovorjeni klici"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Blokiranje klicev"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Če opravite ta klic, bo končan klic prek aplikacije <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Blokiranje klicev"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Številke, ki niso v stikih"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blokirajte številke, ki niso na seznamu vaših stikov"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Zasebno"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blokirajte klicatelje, ki ne razkrijejo številke"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefonska govorilnica"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blokirajte klice iz telefonskih govorilnic"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Neznano"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blokirajte klice neznanih klicateljev"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Blokiranje klicev"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Blokiranje klicev je onemogočeno"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Opravljen je klic v sili"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Blokiranje klicev je onemogočeno, da lahko stik z vami vzpostavijo uslužbenci služb za klic v sili."</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 06d5f09..325f62b 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Telefonata nuk mund të kryhet për shkak të një telefonate në një aplikacion tjetër."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Telefonatat hyrëse"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Telefonatat e humbura"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Bllokimi i telefonatave"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Kryerja e kësaj telefonate do të mbyllë telefonatën tënde në <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Bllokimi i telefonatave"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Numrat që nuk janë te \"Kontaktet\""</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blloko numrat që nuk janë të listuar te \"Kontaktet\" e tua"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Private"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blloko telefonuesit që nuk e zbulojnë numrin e tyre"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefon me pagesë"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blloko telefonatat nga telefonat me pagesë"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"E panjohur"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blloko telefonatat nga telefonuesit e paidentifikuar"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Bllokimi i telefonatave"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Bllokimi i telefonatave u çaktivizua"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Telefonata e urgjencës u krye"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Bllokimi i telefonatave është çaktivizuar për të lejuar që personat që përgjigjen në rast urgjence të kontaktojnë me ty."</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index b3f1554..de2a758 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -51,7 +51,7 @@
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> ће моћи да упућује позиве и контролише све њихове аспекте. Апликацију подесите као подразумевану за телефонирање само ако је сматрате поузданом."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Блокирани бројеви"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Нећете примати позиве ни SMS-ове са блокираних бројева."</string>
-    <string name="block_number" msgid="1101252256321306179">"Додај број"</string>
+    <string name="block_number" msgid="1101252256321306179">"Додајте број"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Желите ли да деблокирате <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Деблокирај"</string>
     <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Блокирајте позиве и SMS-ове од"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Не можете да упутите позив због позива у другој апликацији."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Долазни позиви"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Пропуштени позиви"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Блокирање позива"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ако упутите овај позив, завршићете <xliff:g id="OTHER_APP">%1$s</xliff:g> позив."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Блокирање позива"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Бројеви који нису у контактима"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Блокирајте бројеве који вам нису у контактима"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Скривени бројеви"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Блокирајте позиваоце који скривају број"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Телефонска говорница"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Блокирајте позиве са телефонских говорница"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Непознати бројеви"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Блокирајте позиве неидентификованих позивалаца"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Блокирање позива"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Блокирање позива је онемогућено"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Упућен је хитни позив"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Блокирање позива је онемогућено да би хитне службе могле да вас контактирају."</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 6e4d661..216f5b0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -62,7 +62,7 @@
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Blockeringen har inaktiverats tillfälligt"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"När du ringer eller sms:ar ett nödnummer inaktiveras blockering för att säkerställa att räddningstjänsten kan kontakta dig."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Återaktivera nu"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> blockerat"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> blockerade"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> avblockerat"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"Det går inte att blockera nödnummer."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> är redan blockerat."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Det går inte att ringa på grund av ett samtal via en annan app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Inkommande samtal"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Missade samtal"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Samtalsblockering"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ringer du det här samtalet avslutas samtalet i <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Samtalsblockering"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nummer inte i Kontakter"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Blockera nummer som inte finns i Kontakter"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Privat"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Blockera uppringare som inte visar sitt nummer"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Telefonautomat"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Blockera samtal från telefonautomater"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Okänd"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Blockera samtal från oidentifierade uppringare"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Samtalsblockering"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Samtalsblockering inaktiverad"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Nödsamtal ringt"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Samtalsblockering har inaktiverats för att tillåta att räddningstjänsten kontaktar dig."</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6ba2995..86b83a5 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -45,10 +45,10 @@
     <string name="no_vm_number" msgid="4164780423805688336">"Nambari ya sauti inayokosekana"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Hakuna nambari ya ujumbe wa sauti iliyohifadhiwa katika SIM kadi."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Ongeza nambari"</string>
-    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"Unataka kufanya <xliff:g id="NEW_APP">%s</xliff:g> iwe programu chaguo-msingi ya simu?"</string>
+    <string name="change_default_dialer_dialog_title" msgid="9101655962941740507">"Unataka kufanya <xliff:g id="NEW_APP">%s</xliff:g> iwe programu chaguomsingi ya simu?"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8606546663509166276">"Fanya iwe Chaguo-Msingi"</string>
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Ghairi"</string>
-    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> itaweza kupiga simu na kudhibiti kila kipengele cha simu. Unastahili kuweka programu unazoziamini tu kama programu chaguo-msingi ya kupiga simu."</string>
+    <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> itaweza kupiga simu na kudhibiti kila kipengele cha simu. Unastahili kuweka programu unazoziamini tu kama programu chaguomsingi ya kupiga simu."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Nambari zilizozuiwa"</string>
     <string name="blocked_numbers_msg" msgid="1045015186124965643">"Hutapokea simu au SMS kutoka kwa nambari zilizozuiwa."</string>
     <string name="block_number" msgid="1101252256321306179">"Ongeza nambari"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Haiwezekani kwa sababu kuna simu inayoendelea kwenye programu nyingine."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Simu zinazoingia"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Simu ambazo hukujibu"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Kuzuia Simu"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ukipiga simu hii, simu yako kwenye <xliff:g id="OTHER_APP">%1$s</xliff:g> itakatwa."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Kuzuia Simu"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Nambari ambazo haziko kwenye Anwani"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Zuia nambari ambazo hazipo kwenye orodha ya Anwani zako"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Faragha"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Zuia wapigaji ambao wameficha nambari zao za simu"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Simu ya kulipia"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Zuia simu kutoka kwa nambari ya simu za kulipia"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Zisizojulikani"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Zuia simu kutoka kwa wapigaji wasiojulikana"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Kuzuia Simu"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Kipengele cha Kuzuia Simu kimezimwa"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Simu ya dharura imepigwa"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Kipengele cha Kuzuia Simu kimezimwa ili kuruhusu wapigaji simu za dharura kuwasiliana nawe."</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 6fbf74a..8c2d3a1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -28,10 +28,10 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"செய்தி"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"அழைப்பு முடக்கப்பட்டது."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"ஸ்பீக்கர்ஃபோன் இயக்கப்பட்டது."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"இப்போது பேசமுடியாது. என்ன விஷேசம்?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"இப்போது பேசமுடியாது. என்ன விசேஷம்?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"சிறிதுநேரம் கழித்து நான் அழைக்கிறேன்."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"பிறகு அழைக்கிறேன்."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"இப்போது பேச முடியவில்லை. பிறகு அழைக்க முடியுமா?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"இப்போது பேசமுடியாது, பிறகு அழைக்கிறீர்களா?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"விரைவு பதில்கள்"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"விரைவு பதில்களை மாற்று"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"மற்றொரு பயன்பாட்டில் அழைப்பு செயலில் உள்ளதால், புதிய அழைப்பைச் செய்ய முடியாது."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"உள்வரும் அழைப்புகள்"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"தவறிய அழைப்புகள்"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"அழைப்புத் தடுப்பு"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"புதிய அழைப்பைச் செய்தால், செயலில் உள்ள <xliff:g id="OTHER_APP">%1$s</xliff:g> அழைப்பு துண்டிக்கப்படும்."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"அழைப்புத் தடுப்பு"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"தொடர்புகளில் இல்லாத எண்கள்"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"உங்கள் தொடர்புகளில் பட்டியலிடப்படாத எண்களைத் தடுக்கும்"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"தனிப்பட்ட எண்கள்"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"எண்ணை வெளிப்படுத்தாத அழைப்பாளர்களைத் தடுக்கும்"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"கட்டண ஃபோன்"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"கட்டண ஃபோன்களில் இருந்து வரும் அழைப்புகளைத் தடுக்கும்"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"தெரியாத அழைப்புகள்"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"அடையாளம் தெரியாத அழைப்பாளர்களின் அழைப்புகளைத் தடுக்கும்"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"அழைப்புத் தடுப்பு"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"அழைப்புத் தடுப்பு முடக்கப்பட்டுள்ளது"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"அவசர அழைப்பு செய்யப்பட்டது"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"அவசரநிலையில் பதிலளிப்பவர்களை உங்களைத் தொடர்புகொள்வதற்கு அனுமதிக்க, அழைப்புத் தடுப்பு முடக்கப்பட்டுள்ளது."</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 74bc7f0..d6cd091 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"వేరొక అనువర్తనంలో కాల్ కొనసాగుతున్నందున కాల్ చేయడం సాధ్యపడదు."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"ఇన్‌కమింగ్ కాల్‌లు"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"సమాధానం ఇవ్వని కాల్‌లు"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"కాల్ బ్లాక్ చేయడం"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"ఈ కాల్ చేయడం వలన మీ <xliff:g id="OTHER_APP">%1$s</xliff:g> కాల్ ముగుస్తుంది."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"కాల్ బ్లాక్ చేయడం"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"పరిచయాలలో లేని నంబర్‌లు"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"మీ పరిచయాలలో లేని నంబర్‌లను బ్లాక్ చేయండి"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ప్రైవేట్"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"తమ నంబర్‌ను కనిపించకుండా చేసే కాలర్‌లను బ్లాక్ చేయండి"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"పే ఫోన్"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"పే ఫోన్‌ల నుండి కాల్‌లను బ్లాక్ చేయండి"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"తెలియని"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"తెలియని కాలర్‌ల నుండి కాల్‌లను బ్లాక్ చేయండి"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"కాల్ బ్లాక్ చేయడం"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"కాల్ బ్లాక్ చేయడం నిలిపివేయబడింది"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"అత్యవసర కాల్ చేయబడింది"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"మిమ్మల్ని సంప్రదించడానికి అత్యవసర ప్రతిస్పందనదారులను అనుమతించడానికి కాల్ బ్లాక్ చేయడం నిలిపివేయబడింది."</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index d130b0f..913d1da 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -50,7 +50,7 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"ยกเลิก"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> จะจัดการการโทรและควบคุมการติดต่อทุกด้าน โปรดติดตั้งเฉพาะแอปที่คุณไว้วางใจให้เป็นแอปโทรศัพท์เริ่มต้น"</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"หมายเลขที่ถูกบล็อก"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"คุณจะไม่สามารถรับสายหรือข้อความจากหมายเลขที่บล็อกได้"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"คุณจะไม่สามารถรับสายหรือข้อความจากหมายเลขที่บล็อก"</string>
     <string name="block_number" msgid="1101252256321306179">"เพิ่มหมายเลข"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"เลิกบล็อก <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ใช่ไหม"</string>
     <string name="unblock_button" msgid="3078048901972674170">"เลิกบล็อก"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"ไม่สามารถโทรออกได้เนื่องจากกำลังใช้สายอยู่ในแอปอื่น"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"สายโทรเข้า"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"สายที่ไม่ได้รับ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"การบล็อกสาย"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"การโทรออกนี้จะวางสายใน <xliff:g id="OTHER_APP">%1$s</xliff:g>"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"การบล็อกสาย"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"หมายเลขที่ไม่อยู่ในรายชื่อติดต่อ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"บล็อกหมายเลขที่ไม่อยู่ในรายชื่อติดต่อของคุณ"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"ส่วนตัว"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"บล็อกผู้โทรที่ไม่เปิดเผยหมายเลขโทรศัพท์"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"โทรศัพท์สาธารณะ"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"บล็อกสายจากโทรศัพท์สาธารณะ"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"ไม่ทราบ"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"บล็อกสายจากผู้โทรที่ไม่สามารถระบุได้"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"การบล็อกสาย"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"ปิดใช้การบล็อกสาย"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"โทรหมายเลขฉุกเฉินแล้ว"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ปิดใช้การบล็อกสายแล้วเพื่อให้ทีมฉุกเฉินติดต่อคุณ"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 8f56cb2..d4182da 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Hindi makakatawag dahil sa isang tawag sa isa pang app."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Mga papasok na tawag"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Mga hindi nasagot na tawag"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Pag-block ng Tawag"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Tatapusin ng pagtawag na ito ang iyong tawag sa <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Pag-block ng Tawag"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Mga numerong wala sa Mga Contact"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"I-block ang mga numero na hindi nakalista sa iyong Mga Contact"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Pribado"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Bina-block ang mga tumatawag na hindi hinahayag ang kanilang numero"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Pay phone"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Bina-block ang mga tawag mula sa mga pay phone"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Hindi Alam"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Bina-block ang mga tumatawag mula sa mga hindi kilalang tumatawag"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Pag-block ng Tawag"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Naka-disable ang Pag-block ng Tawag"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Ginawang emergency na tawag"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Na-disable ang Pag-block ng Tawag para payagan ang mga tumutugon sa emergency na kontakin ka."</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index ccef824..efccc27 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -50,11 +50,11 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"İptal"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> uygulaması üzerinden çağrı yapabilecek ve çağrıları her yönüyle kontrol edebileceksiniz. Yalnızca güvendiğiniz uygulamalar varsayılan Telefon uygulaması olarak ayarlanmalıdır."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Engellenen numaralar"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Engellenen numaralardan gelen çağrıları veya kısa mesajları almazsınız."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Engellenen numaralardan gelen telefon aramalarını veya kısa mesajları almayacaksınız."</string>
     <string name="block_number" msgid="1101252256321306179">"Numara ekle"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> numaralı telefonun engellemesi kaldırılsın mı?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Engellemeyi kaldır"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Şu numaradan gelen çağrıları ve kısa mesajları engelle:"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Şu numaradan gelen aramaları ve kısa mesajları engelle:"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Telefon numarası"</string>
     <string name="block_button" msgid="8822290682524373357">"Engelle"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Yalnızca cihaz sahibi engellenen numaraları görüntüleyebilir ve yönetebilir."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Başka bir uygulamada devam eden çağrınız nedeniyle telefon araması yapılamıyor."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Gelen çağrılar"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Cevapsız çağrılar"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Çağrı Engelleme"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Bu çağrıyı yaptığınızda <xliff:g id="OTHER_APP">%1$s</xliff:g> çağrınız sona erecek."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Çağrı Engelleme"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Kişiler arasında bulunmayan numaralar"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Kişileriniz arasında bulunmayan numaralar engellenir"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Gizli"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Gizli numaradan arayanlar engellenir"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Ankesörlü telefon"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Ankesörlü telefonlardan gelen çağrılar engellenir"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Bilinmeyen numaralar"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Tanımlanamayan arayanlardan gelen aramaları engelle"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Çağrı Engelleme"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Çağrı Engelleme devre dışı bırakıldı"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Acil durum çağrısı yapıldı"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Acil durum müdahale ekibinin sizinle iletişime geçmesine olanak tanımak için Çağrı Engelleme devre dışı bırakıldı."</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 5a22892..a9c1a06 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Неможливо зателефонувати через поточний виклик в іншому додатку."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Вхідні виклики"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Пропущені виклики"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Блокування викликів"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Якщо здійснити цей виклик, буде завершено виклик у додатку <xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Блокування викликів"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Номери, які не входять до списку контактів"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Блокування номерів, які не входять до вашого списку контактів"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Приватно"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Блокування абонентів, які не розкривають свої номери"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Таксофон"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Блокування викликів із таксофонів"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Невідомі"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Блокувати виклики від невизначених абонентів"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Блокування викликів"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Блокування викликів вимкнено"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Здійснено екстрений виклик"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Блокування викликів вимкнено, щоб ви могли отримувати екстрені сповіщення."</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index f94d85b..f47de6c 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -62,8 +62,8 @@
     <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"مسدود کرنا عارضی طور پر آف ہے"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"جب آپ کوئی ایمرجنسی نمبر ڈائل کرتے یا اسے متن بھیجتے ہیں تو انسداد کو آ‌ف کر دیا جاتا ہے تاکہ ہنگامی سروسز آپ سے رابطہ کر سکیں۔"</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"اب دوبارہ فعال کریں"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> مسدود ہے"</string>
-    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> غیر مسدود ہے"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> مسدود کر دیا گیا"</string>
+    <string name="blocked_numbers_number_unblocked_message" msgid="977894647366750418">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> غیر مسدود کر دیا گیا"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="917851876780698387">"ہنگامی نمبر مسدود کرنے سے قاصر۔"</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> پہلے ہی مسدود ہے۔"</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"کال کرنے کیلئے ذاتی ڈائلر استعمال ہو رہا ہے"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"کسی دوسری ایپ میں موجود کال کی کی وجہ سے کال نہیں کی جا سکتی۔"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"آنے والی کالیں"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"چھوٹی ہوئی کالیں"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"کال مسدود کرنا"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"یہ کال کرنے سے <xliff:g id="OTHER_APP">%1$s</xliff:g> کال ختم ہو جائے گی۔"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"کال مسدود کرنا"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"وہ نمبرز جو رابطوں میں نہیں ہیں"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"ان نمبرز کو مسدود کریں جو آپ کے رابطوں میں مندرج نہیں ہیں"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"نجی"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"ان کالرز کو مسدود کریں جو اپنے نمبرز کا افشاء نہیں کرتے ہیں"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"پے فون"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"پے فونز سے آنے والی کالز کو مسدود کریں"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"نامعلوم"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"غیر شناخت کردہ کالرز سے آنے والی کالز کو مسدود کریں"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"کال مسدود کرنا"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"کال مسدود کرنا غیر فعال ہو گیا ہے"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"ہنگامی کال کی گئی"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"ہنگامی حالت میں جواب دہندگان کو آپ سے رابطہ کرنے کی اجازت دینے کیلئے کال مسدود کرنا غیر فعال ہو گیا ہے۔"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 714ddc9..65aca28 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -50,7 +50,7 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"Bekor qilish"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> ilovasi qo‘ng‘iroq qilishga yodam beradi va qo‘ng‘iroqlarga tegishli boshqa barcha parametrlarni boshqaradi. Qo‘ng‘iroqlar uchun faqat ishonarli ilovani asosiy ilova qilib o‘rnatish lozim."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"Bloklangan raqamlar"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Bloklangan telefon raqamlaridan chaqiruv yoki matnli xabarlar qabul qilib bo‘lmaydi."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"Bloklangan raqamlardan keladigan chaqiruv yoki SMS xabarlar qabul qilinmaydi."</string>
     <string name="block_number" msgid="1101252256321306179">"Biror raqamni bloklash"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> raqami blokdan chiqarilsinmi?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Blokdan chiqarish"</string>
@@ -68,7 +68,7 @@
     <string name="blocked_numbers_number_already_blocked_message" msgid="4392247814500811798">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> raqami allaqachon bloklangan."</string>
     <string name="toast_personal_call_msg" msgid="5115361633476779723">"Qo‘ng‘iroq qilish uchun shaxsiy raqam tergichdan foydalanilmoqda"</string>
     <string name="notification_incoming_call" msgid="7713197997773986670">"<xliff:g id="CALL_FROM">%2$s</xliff:g> <xliff:g id="CALL_VIA">%1$s</xliff:g> orqali chaqirmoqda"</string>
-    <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_FROM">%2$s</xliff:g> <xliff:g id="CALL_VIA">%1$s</xliff:g> orqali video qo‘ng‘iroq qilmoqda"</string>
+    <string name="notification_incoming_video_call" msgid="6638486071698373893">"<xliff:g id="CALL_FROM">%2$s</xliff:g> <xliff:g id="CALL_VIA">%1$s</xliff:g> orqali video chaqiruv"</string>
     <string name="answering_ends_other_call" msgid="8282145910153766401">"Chaqiruvga javob berilsa, <xliff:g id="CALL_VIA">%1$s</xliff:g> qo‘ng‘irog‘i tugatiladi."</string>
     <string name="answering_ends_other_calls" msgid="1198589551399049197">"Chaqiruvga javob berilsa, <xliff:g id="CALL_VIA">%1$s</xliff:g> qo‘ng‘iroqlari tugatiladi."</string>
     <string name="answering_ends_other_video_call" msgid="8510410917384186360">"Chaqiruvga javob berilsa, <xliff:g id="CALL_VIA">%1$s</xliff:g> video suhbati tugatiladi."</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Boshqa ilovadagi joriy qo‘ng‘iroq tufayli boshqa raqamni chaqirib bo‘lmaydi."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Kiruvchi chaqiruvlar"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Javobsiz chaqiruvlar"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Chaqiruvlarni bloklash"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Bu qo‘ng‘iroqni amalga oshirsangiz, <xliff:g id="OTHER_APP">%1$s</xliff:g> qo‘ng‘irog‘i tugatiladi."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Chaqiruvlarni bloklash"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Notanish raqamlar"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Kontaktlaringizga kiritilmagan notanish raqamlarni bloklash"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Yashirin raqamlar"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Yashirin raqamlardan keluvchi chaqiruvlarni bloklash"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Taksofon"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Taksofondan keluvchi chaqiruvlarni bloklash"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Notanish raqamlar"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Aniqlanmagan raqamlardan keluvchi chaqiruvlarni bloklash"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Chaqiruvlarni bloklash"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Chaqiruvlarni bloklash funksiyasi yoqilmagan"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Favqulodda chaqiruv qilindi"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Sizga favqulodda chiqiruv qilish imkoni bo‘lishi uchun chaqiruvlarni bloklash funksiyasi o‘chirib qo‘yilgan."</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 3a7e4b9..b0f6560 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Không thể thực hiện cuộc gọi do có cuộc gọi trong một ứng dụng khác."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Cuộc gọi đến"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Cuộc gọi nhỡ"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Chặn cuộc gọi"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Thực hiện cuộc gọi này sẽ kết thúc cuộc gọi <xliff:g id="OTHER_APP">%1$s</xliff:g> của bạn."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Chặn cuộc gọi"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Các số không có trong Danh bạ"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Chặn các số không có trong Danh bạ của bạn"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Riêng tư"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Chặn người gọi không tiết lộ số của họ"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Điện thoại công cộng"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Chặn các cuộc gọi từ điện thoại công cộng"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Không xác định"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Chặn cuộc gọi từ người gọi không xác định"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Chặn cuộc gọi"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Đã tắt tính năng Chặn cuộc gọi"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Đã thực hiện cuộc gọi khẩn cấp"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Đã tắt tính năng Chặn cuộc gọi để cho phép người trả lời khẩn cấp liên hệ với bạn."</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2edc91a..370b718 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"由于当前正在通过其他应用通话,因此无法拨打电话。"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"来电"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"未接电话"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"来电屏蔽"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"拨打此电话将导致<xliff:g id="OTHER_APP">%1$s</xliff:g>通话结束。"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"来电屏蔽"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"不在通讯录中的号码"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"屏蔽未列在您通讯录中的号码"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"不显示号码"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"屏蔽不显示号码的来电者"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"公用电话"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"屏蔽公用电话来电"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"未知"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"屏蔽身份不明的来电者的电话"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"来电屏蔽"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"来电屏蔽功能已停用"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"已拨打紧急呼救电话"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"系统已停用来电屏蔽功能,以便急救人员与您联系。"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 5d783ad..1d2dd46 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"由於已在另一個應用程式中進行通話,因此無法撥打電話。"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"來電"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"未接來電"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"來電封鎖"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"如果撥打此電話,您的 <xliff:g id="OTHER_APP">%1$s</xliff:g> 通話將會結束。"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"來電封鎖"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"不在通訊錄中的號碼"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"封鎖不在通訊錄中的號碼"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"私人"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"封鎖不透露號碼的來電者"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"公共電話"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"封鎖公共電話來電"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"不明"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"封鎖不明來電者的來電"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"來電封鎖"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"已停用來電封鎖功能"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"已撥緊急電話"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"已停用來電封鎖功能,以便救援人員與您聯絡。"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8a9ca00..007adeb 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -33,7 +33,7 @@
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"我晚點回電。"</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"我現在不方便講話,晚點再打來好嗎?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"應答短訊"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"編輯應答短訊"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"編輯快速回應"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"快速回應"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"訊息已傳送至 <xliff:g id="PHONE_NUMBER">%s</xliff:g>。"</string>
@@ -50,7 +50,7 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"取消"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"這樣一來,<xliff:g id="NEW_APP">%s</xliff:g> 將能撥打電話及控管所有通話設定。請務必只將您信任的應用程式設為預設電話應用程式。"</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"已封鎖的號碼"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"您不會收到已封鎖號碼的來電或簡訊。"</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"你不會收到已封鎖號碼的來電或簡訊。"</string>
     <string name="block_number" msgid="1101252256321306179">"新增號碼"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"要解除封鎖 <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> 嗎?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"解除封鎖"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"你正在使用其他應用程式進行通話,因此無法撥打電話。"</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"來電"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"未接來電"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"來電封鎖"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"撥打這通電話將結束你的「<xliff:g id="OTHER_APP">%1$s</xliff:g>」通話。"</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"來電封鎖"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"不屬於聯絡人的號碼"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"封鎖不屬於聯絡人的號碼"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"不顯示號碼"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"封鎖不顯示號碼的來電者"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"公共電話"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"封鎖公共電話來電"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"不明"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"封鎖不明來電者的來電"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"來電封鎖"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"已停用來電封鎖"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"已撥打緊急電話"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"系統已停用來電封鎖功能,以便緊急應變人員與你聯絡。"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index ac7e9b5..79e5846 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -59,7 +59,7 @@
     <string name="block_button" msgid="8822290682524373357">"Vimba"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Umnikazi wedivayisi kuphela ongabuka aphinde aphathe izinombolo ezivinjelwe."</string>
     <string name="delete_icon_description" msgid="8903995728252556724">"Vulela"</string>
-    <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Ukuvimble kuvaliwe okwesikhashana"</string>
+    <string name="blocked_numbers_butter_bar_title" msgid="438170866438793182">"Ukuvimbela kuvaliwe okwesikhashana"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="2223244484319442431">"Ngemuva kokudayela noma ubhalele inombolo yesimo esiphuthumayo, ukuvimbela kuvaliwe ukuqinisekisa amasevisi esimo esiphuthumayo angakwazi ukuxhumana nawe."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2197943354922010696">"Nika amandla manje"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="7678509606805029540">"I-<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> ivinjiwe"</string>
@@ -82,5 +82,19 @@
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="149091978697302211">"Ikholi ayikwazi ukwenziwa ngenxa yekholi kolunye uhlelo lokusebenza."</string>
     <string name="notification_channel_incoming_call" msgid="3513761697082968084">"Amakholi angenayo"</string>
     <string name="notification_channel_missed_call" msgid="8727062678632713146">"Amakholi akuphuthile"</string>
+    <string name="notification_channel_call_blocking" msgid="2943358779746676070">"Ukuvimbela ikholi"</string>
     <string name="alert_outgoing_call" msgid="982908156825958001">"Ukwenza le kholi kuzoqeda enye ikholi yakho ye-<xliff:g id="OTHER_APP">%1$s</xliff:g>."</string>
+    <string name="phone_settings_call_blocking_txt" msgid="3976004073043846733">"Ukuvimbela ikholi"</string>
+    <string name="phone_settings_number_not_in_contact_txt" msgid="3126829421867168652">"Inombolo ayikho koxhumana nabo"</string>
+    <string name="phone_settings_number_not_in_contact_summary_txt" msgid="9043147855140079119">"Vimbela izinombolo ezingafakiwe kuhlu loxhumana nabo"</string>
+    <string name="phone_settings_private_num_txt" msgid="8623574188879134262">"Kuyimfihlo"</string>
+    <string name="phone_settings_private_num_summary_txt" msgid="7516314821207782191">"Vimbela abashayayo abangayidaluli inombolo yabo"</string>
+    <string name="phone_settings_payphone_txt" msgid="2493356957416981318">"Ifoni ekhokhelwayo"</string>
+    <string name="phone_settings_payphone_summary_txt" msgid="6126709946103814653">"Vimbela amakholi kusukela kumafoni akhokhelwayo"</string>
+    <string name="phone_settings_unknown_txt" msgid="5836407031508172721">"Akwaziwa"</string>
+    <string name="phone_settings_unknown_summary_txt" msgid="3457690230497753233">"Vimbela amakholi kusukela kwabashayayo abangaziwa"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="628536625775266096">"Ukuvimbela ikholi"</string>
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="6264230048947693941">"Ukuvimbela ikholi kukhutshaziwe"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="7421611725400166580">"Ikholi ephuthumayo yenziwe"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="4083285098613193052">"Ukuvimbela ikholi kukhutshaziwe ukuze kuvunyelwe abaphenduli besimo esiphuthumayo ukuthi baxhumane nawe."</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 111d86b..8c84688 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -47,7 +47,17 @@
          maximum ringing calls. -->
     <bool name="silence_incoming_when_different_service_and_maximum_ringing">false</bool>
 
-    <!-- Determines if the granting temporary location permission to the default dialer
-         during an emergency call should be allowed.  The default is false. -->
+    <!-- Determines if the granting of temporary location permission to the default dialer
+         during an emergency call should be allowed. -->
     <bool name="grant_location_permission_enabled">false</bool>
+
+    <!-- When true, a simple full intensity on/off vibration pattern will be used when calls ring.
+         When false, a fancy vibration pattern which ramps up and down will be used.
+         Devices should overlay this value based on the type of vibration hardware they employ. -->
+    <bool name="use_simple_vibration_pattern">false</bool>
+
+    <!-- When true, if Telecom is playing the ringtone, it will attempt to pause for some time
+         between repeats of the ringtone.
+         When false, the ringtone will be looping with no pause. -->
+    <bool name="should_pause_between_ringtone_repeats">true</bool>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 58e5284..bb63ad2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -252,8 +252,37 @@
     <string name="notification_channel_incoming_call">Incoming calls</string>
     <!-- Notification channel name for a channel containing missed call notifications. -->
     <string name="notification_channel_missed_call">Missed calls</string>
+    <!-- Notification channel name for a channel containing call blocking notifications. -->
+    <string name="notification_channel_call_blocking">Call Blocking</string>
 
     <!-- Alert dialog content used to inform the user that placing a new outgoing call will end the
          ongoing call in the app "other_app". -->
     <string name="alert_outgoing_call">Placing this call will end your <xliff:g id="other_app">%1$s</xliff:g> call.</string>
+
+    <!-- The name of a feature available under the Call settings. -->
+    <string name="phone_settings_call_blocking_txt">Call Blocking</string>
+    <!-- Call type to be blocked. See the explanatory text "phone_settings_number_not_in_contact_summary_txt". -->
+    <string name="phone_settings_number_not_in_contact_txt">Numbers not in Contacts</string>
+    <!-- Explanatory text under the call type "phone_settings_number_not_in_contact_txt". -->
+    <string name="phone_settings_number_not_in_contact_summary_txt">Block numbers that are not listed in your Contacts</string>
+    <!-- Call type to be blocked. See the explanatory text "phone_settings_private_num_summary_txt". This is also called Anonymous or Withheld depending on region. -->
+    <string name="phone_settings_private_num_txt">Private</string>
+    <!-- Explanatory text under the call type "phone_settings_payphone_summary_txt". -->
+    <string name="phone_settings_private_num_summary_txt">Block callers that do not disclose their number</string>
+    <!-- Call type to be blocked. See the explanatory text "phone_settings_private_num_summary_txt".  -->
+    <string name="phone_settings_payphone_txt">Pay phone</string>
+    <!-- Explanatory text under the call type "phone_settings_payphone_txt". -->
+    <string name="phone_settings_payphone_summary_txt">Block calls from pay phones</string>
+    <!-- Call type to be blocked. See the explanatory text "phone_settings_unknown_summary_txt".  -->
+    <string name="phone_settings_unknown_txt">Unknown</string>
+    <!-- Explanatory text under the call type "phone_settings_unknown_txt". This occurs when no caller ID information unavailable (e.g., international calls, IP phones that operators cannot identify the caller) -->
+    <string name="phone_settings_unknown_summary_txt">Block calls from unidentified callers</string>
+    <!-- Notification. Make this translation consistent with "phone_settings_call_blocking_txt". -->
+    <string name="phone_strings_call_blocking_turned_off_notification_title_txt">Call Blocking</string>
+    <!-- Notification that appears when the feature Call Blocking has been disabled. -->
+    <string name="phone_strings_call_blocking_turned_off_notification_text_txt">Call Blocking disabled</string>
+    <!-- Title of the dialog "phone_strings_emergency_call_made_dialog_text_txt". -->
+    <string name="phone_strings_emergency_call_made_dialog_title_txt">Emergency call made</string>
+    <!-- Notification details that appear when the user taps the notification "phone_strings_call_blocking_turned_off_notification_text_txt". -->
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt">Call Blocking has been disabled to allow emergency responders to contact you.</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 29c5566..3216719 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -39,6 +39,7 @@
         <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:colorAccent">@color/theme_color</item>
+        <item name="android:listDivider">@null</item>
     </style>
 
     <style name="TelecomDialerSettingsActionBarStyle" parent="android:Widget.Material.ActionBar">
diff --git a/res/xml/activity_blocked_numbers.xml b/res/xml/activity_blocked_numbers.xml
index 4eb4f85..15e1859 100644
--- a/res/xml/activity_blocked_numbers.xml
+++ b/res/xml/activity_blocked_numbers.xml
@@ -33,9 +33,7 @@
 
         <FrameLayout
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:paddingLeft="@dimen/blocked_numbers_large_padding"
-                android:paddingRight="@dimen/blocked_numbers_large_padding">
+                android:layout_height="match_parent">
 
             <TextView
                     android:id="@+id/non_primary_user"
@@ -43,6 +41,8 @@
                     android:layout_height="wrap_content"
                     android:text="@string/non_primary_user"
                     android:paddingTop="@dimen/blocked_numbers_large_padding"
+                    android:paddingLeft="@dimen/blocked_numbers_large_padding"
+                    android:paddingRight="@dimen/blocked_numbers_large_padding"
                     style="@style/BlockedNumbersTitleText"
                     android:visibility="gone" />
 
@@ -50,40 +50,48 @@
                     android:id="@+id/manage_blocked_ui"
                     android:orientation="vertical"
                     android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingTop="@dimen/blocked_numbers_large_padding">
+                    android:layout_height="wrap_content">
 
-                <TextView
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:text="@string/blocked_numbers_msg"
-                        android:paddingBottom="@dimen/blocked_numbers_extra_large_padding"
-                        style="@style/BlockedNumbersTitleText" />
-
-                <TextView
-                        android:id="@+id/add_blocked"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:text="@string/block_number"
-                        android:layout_marginBottom="@dimen/blocked_numbers_button_bottom_margin"
-                        style="@style/BlockedNumbersButton"
-                        android:background="?android:attr/selectableItemBackgroundBorderless" />
-
-                <ProgressBar
-                        android:id="@+id/progress_bar"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="center"
-                        android:indeterminate="true"
-                        android:layout_marginTop="@dimen/blocked_numbers_progress_bar_padding"
-                        style="@android:style/Widget.ProgressBar.Large" />
-
-                <ListView
-                        android:id="@android:id/list"
+                <FrameLayout
+                        android:id="@+id/enhanced_call_blocking_container"
                         android:layout_width="match_parent"
-                        android:layout_height="match_parent"
-                        android:nestedScrollingEnabled="true"
-                        android:paddingBottom="@dimen/blocked_numbers_extra_large_padding" />
+                        android:layout_height="wrap_content" />
+
+                <LinearLayout
+                        android:orientation="vertical"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:paddingTop="@dimen/blocked_numbers_large_padding"
+                        android:paddingLeft="@dimen/blocked_numbers_large_padding"
+                        android:paddingRight="@dimen/blocked_numbers_large_padding">
+
+                    <TextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/blocked_numbers_msg"
+                            android:paddingBottom="@dimen/blocked_numbers_extra_large_padding"
+                            style="@style/BlockedNumbersTitleText" />
+
+                    <TextView
+                            android:id="@+id/add_blocked"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/block_number"
+                            android:layout_marginBottom="@dimen/blocked_numbers_button_bottom_margin"
+                            style="@style/BlockedNumbersButton"
+                            android:background="?android:attr/selectableItemBackgroundBorderless" />
+
+                    <ProgressBar
+                            android:id="@+id/progress_bar"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"
+                            android:indeterminate="true"
+                            android:layout_marginTop="@dimen/blocked_numbers_progress_bar_padding"
+                            style="@android:style/Widget.ProgressBar.Large" />
+
+                    <include layout="@xml/layout_customized_listview" />
+                </LinearLayout>
             </LinearLayout>
         </FrameLayout>
     </LinearLayout>
diff --git a/res/xml/blocking_suppressed_butterbar.xml b/res/xml/blocking_suppressed_butterbar.xml
index cd4579e..d6a8472 100644
--- a/res/xml/blocking_suppressed_butterbar.xml
+++ b/res/xml/blocking_suppressed_butterbar.xml
@@ -64,11 +64,10 @@
             style="@style/BlockedNumbersButton"
             android:background="?android:attr/selectableItemBackgroundBorderless" />
 
-    <View
-          xmlns:tools="http://schemas.android.com/tools"
-          android:layout_width="match_parent"
-          android:layout_height="@dimen/blocked_numbers_divider_stroke"
-          android:layout_below="@id/reenable_button"
-          android:layout_marginTop="@dimen/blocked_numbers_large_padding"
-          android:background="@color/blocked_numbers_divider_color" />
+    <include
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/reenable_button"
+            android:layout_marginTop="@dimen/blocked_numbers_large_padding"
+            layout="@xml/layout_divider" />
 </RelativeLayout>
\ No newline at end of file
diff --git a/res/xml/enhanced_call_blocking_settings.xml b/res/xml/enhanced_call_blocking_settings.xml
new file mode 100644
index 0000000..32c4c5d
--- /dev/null
+++ b/res/xml/enhanced_call_blocking_settings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/phone_settings_call_blocking_txt">
+    <SwitchPreference
+        android:key="block_numbers_not_in_contacts_setting"
+        android:title="@string/phone_settings_number_not_in_contact_txt"
+        android:summary="@string/phone_settings_number_not_in_contact_summary_txt"
+        android:persistent="false"
+        android:defaultValue="false"/>
+    <SwitchPreference
+        android:key="block_private_number_calls_setting"
+        android:title="@string/phone_settings_private_num_txt"
+        android:summary="@string/phone_settings_private_num_summary_txt"
+        android:persistent="false"
+        android:defaultValue="false"/>
+    <SwitchPreference
+        android:key="block_payphone_calls_setting"
+        android:title="@string/phone_settings_payphone_txt"
+        android:summary="@string/phone_settings_payphone_summary_txt"
+        android:persistent="false"
+        android:defaultValue="false"/>
+    <SwitchPreference
+        android:key="block_unknown_calls_setting"
+        android:title="@string/phone_settings_unknown_txt"
+        android:summary="@string/phone_settings_unknown_summary_txt"
+        android:persistent="false"
+        android:defaultValue="false"/>
+    <!--Add divider to separate this enhanced call blocking settings from other settings-->
+    <Preference
+        android:key="enhanced_call_blocking_divider"
+        android:persistent="false"
+        android:layout="@xml/layout_divider"/>
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/layout_customized_listview.xml b/res/xml/layout_customized_listview.xml
new file mode 100644
index 0000000..f5ced8e
--- /dev/null
+++ b/res/xml/layout_customized_listview.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<com.android.server.telecom.components.NonScrollListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:nestedScrollingEnabled="true" />
diff --git a/res/xml/layout_divider.xml b/res/xml/layout_divider.xml
new file mode 100644
index 0000000..2eae636
--- /dev/null
+++ b/res/xml/layout_divider.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/blocked_numbers_divider_stroke"
+        android:background="@color/blocked_numbers_divider_color" />
+</LinearLayout>
diff --git a/scripts/telecom_testing.sh b/scripts/telecom_testing.sh
index 723b041..779f449 100644
--- a/scripts/telecom_testing.sh
+++ b/scripts/telecom_testing.sh
@@ -58,7 +58,7 @@
       build_dir="packages/services/Telecomm/tests"
       apk_loc="data/app/TelecomUnitTests/TelecomUnitTests.apk"
       package_prefix="com.android.server.telecom.tests"
-      instrumentation="android.test.InstrumentationTestRunner";;
+      instrumentation="android.support.test.runner.AndroidJUnitRunner";;
     "telephony")
       build_dir="frameworks/opt/telephony/tests/"
       apk_loc="data/app/FrameworksTelephonyTests/FrameworksTelephonyTests.apk"
diff --git a/src/com/android/server/telecom/Analytics.java b/src/com/android/server/telecom/Analytics.java
index dbe1a29..d2cdeca 100644
--- a/src/com/android/server/telecom/Analytics.java
+++ b/src/com/android/server/telecom/Analytics.java
@@ -16,6 +16,8 @@
 
 package com.android.server.telecom;
 
+import android.os.SystemProperties;
+
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.Logging.EventManager;
@@ -141,6 +143,9 @@
                         ParcelableCallAnalytics.EventTiming.FILTERING_COMPLETED_TIMING);
                 put(LogUtils.Events.Timings.FILTERING_TIMED_OUT_TIMING,
                         ParcelableCallAnalytics.EventTiming.FILTERING_TIMED_OUT_TIMING);
+                put(LogUtils.Events.Timings.START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING,
+                        ParcelableCallAnalytics.EventTiming.
+                                START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING);
             }};
 
     public static final Map<Integer, String> sSessionIdToLogSession = new HashMap<>();
@@ -189,6 +194,9 @@
 
         public void addCallProperties(int properties) {
         }
+
+        public void setCallSource(int callSource) {
+        }
     }
 
     /**
@@ -222,6 +230,7 @@
         public List<TelecomLogClass.VideoEvent> videoEvents;
         public List<TelecomLogClass.InCallServiceInfo> inCallServiceInfos;
         public int callProperties = 0;
+        public int callSource = CALL_SOURCE_UNSPECIFIED;
 
         private long mTimeOfLastVideoEvent = -1;
 
@@ -251,6 +260,7 @@
             this.isVideo = other.isVideo;
             this.videoEvents = other.videoEvents;
             this.callProperties = other.callProperties;
+            this.callSource = other.callSource;
 
             if (other.callTerminationReason != null) {
                 this.callTerminationReason = new DisconnectCause(
@@ -354,6 +364,11 @@
         }
 
         @Override
+        public void setCallSource(int callSource) {
+            this.callSource = callSource;
+        }
+
+        @Override
         public String toString() {
             return "{\n"
                     + "    startTime: " + startTime + '\n'
@@ -368,6 +383,7 @@
                     + "    inCallServices: " + getInCallServicesString() + '\n'
                     + "    callProperties: " + Connection.propertiesToStringShort(callProperties)
                     + '\n'
+                    + "    callSource: " + getCallSourceString() + '\n'
                     + "}\n";
         }
 
@@ -410,6 +426,8 @@
                             videoEventProto.getVideoState())
                     ).collect(Collectors.toList()));
 
+            result.setCallSource(analyticsProto.getCallSource());
+
             return result;
         }
 
@@ -436,7 +454,8 @@
                     .setIsCreatedFromExistingConnection(createdFromExistingConnection)
                     .setIsEmergencyCall(isEmergency)
                     .setIsVideoCall(isVideo)
-                    .setConnectionProperties(callProperties);
+                    .setConnectionProperties(callProperties)
+                    .setCallSource(callSource);
 
             result.connectionService = new String[] {connectionService};
             if (callEvents != null) {
@@ -500,6 +519,19 @@
             s.append("]");
             return s.toString();
         }
+
+        private String getCallSourceString() {
+            switch (callSource) {
+                case CALL_SOURCE_UNSPECIFIED:
+                    return "UNSPECIFIED";
+                case CALL_SOURCE_EMERGENCY_DIALPAD:
+                    return "EMERGENCY_DIALPAD";
+                case CALL_SOURCE_EMERGENCY_SHORTCUT:
+                    return "EMERGENCY_SHORTCUT";
+                default:
+                    return "UNSPECIFIED";
+            }
+        }
     }
     public static final String TAG = "TelecomAnalytics";
 
@@ -515,6 +547,14 @@
     public static final int SIP_PHONE = ParcelableCallAnalytics.SIP_PHONE;
     public static final int THIRD_PARTY_PHONE = ParcelableCallAnalytics.THIRD_PARTY_PHONE;
 
+    // Constants for call source
+    public static final int CALL_SOURCE_UNSPECIFIED =
+            ParcelableCallAnalytics.CALL_SOURCE_UNSPECIFIED;
+    public static final int CALL_SOURCE_EMERGENCY_DIALPAD =
+            ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD;
+    public static final int CALL_SOURCE_EMERGENCY_SHORTCUT =
+            ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT;
+
     // Constants for video events
     public static final int SEND_LOCAL_SESSION_MODIFY_REQUEST =
             ParcelableCallAnalytics.VideoEvent.SEND_LOCAL_SESSION_MODIFY_REQUEST;
@@ -583,6 +623,7 @@
                             .setSessionEntryPoint(timing.getKey())
                             .setTimeMillis(timing.getTime()))
                     .toArray(TelecomLogClass.LogSessionTiming[]::new);
+            result.setHardwareRevision(SystemProperties.get("ro.boot.revision", ""));
             if (args.length > 1 && CLEAR_ANALYTICS_ARG.equals(args[1])) {
                 sCallIdToInfo.clear();
                 sSessionTimings.clear();
@@ -628,6 +669,7 @@
                     .filter(e -> sSessionIdToLogSession.containsKey(e.getKey()))
                     .forEach(e -> writer.printf("%s: %.2f\n",
                             sSessionIdToLogSession.get(e.getKey()), e.getValue()));
+            writer.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", ""));
         }
     }
 
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 7ed1c85..0940429 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -47,6 +47,26 @@
     /** The current ringtone. Only used by the ringtone thread. */
     private Ringtone mRingtone;
 
+    /**
+     * Determines if the {@link AsyncRingtonePlayer} should pause between repeats of the ringtone.
+     * When {@code true}, the system will check if the ringtone has stopped every
+     * {@link #RESTART_RINGER_MILLIS} and restart the ringtone if it has stopped.  This does not
+     * guarantee that there is {@link #RESTART_RINGER_MILLIS} between each repeat of the ringtone,
+     * rather it ensures that for short ringtones, or ringtones which are not a multiple of
+     * {@link #RESTART_RINGER_MILLIS} in duration that there will be some pause between repetitions.
+     * When {@code false}, the ringtone will be looped continually with no attempt to pause between
+     * repeats.
+     */
+    private boolean mShouldPauseBetweenRepeat = true;
+
+    public AsyncRingtonePlayer() {
+        // Empty
+    }
+
+    public AsyncRingtonePlayer(boolean shouldPauseBetweenRepeat) {
+        mShouldPauseBetweenRepeat = shouldPauseBetweenRepeat;
+    }
+
     /** Plays the ringtone. */
     public void play(RingtoneFactory factory, Call incomingCall) {
         Log.d(this, "Posting play.");
@@ -92,7 +112,7 @@
         HandlerThread thread = new HandlerThread("ringtone-player");
         thread.start();
 
-        return new Handler(thread.getLooper()) {
+        return new Handler(thread.getLooper(), null /*callback*/, true /*async*/) {
             @Override
             public void handleMessage(Message msg) {
                 switch(msg.what) {
@@ -144,7 +164,15 @@
             }
         }
 
-        handleRepeat();
+        if (mShouldPauseBetweenRepeat) {
+            // We're trying to pause between repeats, so the ringtone will not intentionally loop.
+            // Instead, we'll use a handler message to perform repeats.
+            handleRepeat();
+        } else {
+            mRingtone.setLooping(true);
+            mRingtone.play();
+            Log.i(this, "Play ringtone, looping.");
+        }
     }
 
     private void handleRepeat() {
diff --git a/src/com/android/server/telecom/BluetoothHeadsetProxy.java b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
index 07cc626..0f492df 100644
--- a/src/com/android/server/telecom/BluetoothHeadsetProxy.java
+++ b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
@@ -56,15 +56,31 @@
         return mBluetoothHeadset.getConnectionState(device);
     }
 
-    public boolean isAudioConnected(BluetoothDevice device) {
-        return mBluetoothHeadset.isAudioConnected(device);
+    public int getAudioState(BluetoothDevice device) {
+        return mBluetoothHeadset.getAudioState(device);
     }
 
     public boolean connectAudio() {
         return mBluetoothHeadset.connectAudio();
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        return mBluetoothHeadset.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        return mBluetoothHeadset.getActiveDevice();
+    }
+
+    public boolean isAudioOn() {
+        return mBluetoothHeadset.isAudioOn();
+    }
+
     public boolean disconnectAudio() {
         return mBluetoothHeadset.disconnectAudio();
     }
+
+    public boolean isInbandRingingEnabled() {
+        return mBluetoothHeadset.isInbandRingingEnabled();
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/server/telecom/BluetoothManager.java b/src/com/android/server/telecom/BluetoothManager.java
deleted file mode 100644
index d043cc5..0000000
--- a/src/com/android/server/telecom/BluetoothManager.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.telecom;
-
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.telecom.Log;
-import android.telecom.Logging.Runnable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.util.List;
-
-/**
- * Listens to and caches bluetooth headset state.  Used By the CallAudioManager for maintaining
- * overall audio state. Also provides method for connecting the bluetooth headset to the phone call.
- */
-public class BluetoothManager {
-    public static final int BLUETOOTH_UNINITIALIZED = 0;
-    public static final int BLUETOOTH_DISCONNECTED = 1;
-    public static final int BLUETOOTH_DEVICE_CONNECTED = 2;
-    public static final int BLUETOOTH_AUDIO_PENDING = 3;
-    public static final int BLUETOOTH_AUDIO_CONNECTED = 4;
-
-    public interface BluetoothStateListener {
-        void onBluetoothStateChange(int oldState, int newState);
-    }
-
-    private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
-            new BluetoothProfile.ServiceListener() {
-                @Override
-                public void onServiceConnected(int profile, BluetoothProfile proxy) {
-                    Log.startSession("BMSL.oSC");
-                    try {
-                        if (profile == BluetoothProfile.HEADSET) {
-                            mBluetoothHeadset = new BluetoothHeadsetProxy((BluetoothHeadset) proxy);
-                            Log.v(this, "- Got BluetoothHeadset: " + mBluetoothHeadset);
-                        } else {
-                            Log.w(this, "Connected to non-headset bluetooth service. Not changing" +
-                                    " bluetooth headset.");
-                        }
-                        updateListenerOfBluetoothState(true);
-                    } finally {
-                        Log.endSession();
-                    }
-                }
-
-                @Override
-                public void onServiceDisconnected(int profile) {
-                    Log.startSession("BMSL.oSD");
-                    try {
-                        mBluetoothHeadset = null;
-                        Log.v(this, "Lost BluetoothHeadset: " + mBluetoothHeadset);
-                        updateListenerOfBluetoothState(false);
-                    } finally {
-                        Log.endSession();
-                    }
-                }
-           };
-
-    /**
-     * Receiver for misc intent broadcasts the BluetoothManager cares about.
-     */
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.startSession("BM.oR");
-            try {
-                String action = intent.getAction();
-
-                if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
-                    int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                            BluetoothHeadset.STATE_DISCONNECTED);
-                    Log.i(this, "mReceiver: HEADSET_STATE_CHANGED_ACTION");
-                    Log.i(this, "==> new state: %s ", bluetoothHeadsetState);
-                    updateListenerOfBluetoothState(
-                            bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTING);
-                } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
-                    int bluetoothHeadsetAudioState =
-                            intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                    BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
-                    Log.i(this, "mReceiver: HEADSET_AUDIO_STATE_CHANGED_ACTION");
-                    Log.i(this, "==> new state: %s", bluetoothHeadsetAudioState);
-                    updateListenerOfBluetoothState(
-                            bluetoothHeadsetAudioState ==
-                                    BluetoothHeadset.STATE_AUDIO_CONNECTING
-                            || bluetoothHeadsetAudioState ==
-                                    BluetoothHeadset.STATE_AUDIO_CONNECTED);
-                }
-            } finally {
-                Log.endSession();
-            }
-        }
-    };
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    private final BluetoothAdapterProxy mBluetoothAdapter;
-    private BluetoothStateListener mBluetoothStateListener;
-
-    private BluetoothHeadsetProxy mBluetoothHeadset;
-    private long mBluetoothConnectionRequestTime;
-    private final Runnable mBluetoothConnectionTimeout = new Runnable("BM.cBA", null /*lock*/) {
-        @Override
-        public void loggedRun() {
-            if (!isBluetoothAudioConnected()) {
-                Log.v(this, "Bluetooth audio inexplicably disconnected within 5 seconds of " +
-                        "connection. Updating UI.");
-            }
-            updateListenerOfBluetoothState(false);
-        }
-    };
-
-    private final Runnable mRetryConnectAudio = new Runnable("BM.rCA", null /*lock*/) {
-        @Override
-        public void loggedRun() {
-            Log.i(this, "Retrying connecting to bluetooth audio.");
-            if (!mBluetoothHeadset.connectAudio()) {
-                Log.w(this, "Retry of bluetooth audio connection failed. Giving up.");
-            } else {
-                setBluetoothStatePending();
-            }
-        }
-    };
-
-    private final Context mContext;
-    private int mBluetoothState = BLUETOOTH_UNINITIALIZED;
-
-    public BluetoothManager(Context context, BluetoothAdapterProxy bluetoothAdapterProxy) {
-        mBluetoothAdapter = bluetoothAdapterProxy;
-        mContext = context;
-
-        if (mBluetoothAdapter != null) {
-            mBluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
-                                    BluetoothProfile.HEADSET);
-        }
-
-        // Register for misc other intent broadcasts.
-        IntentFilter intentFilter =
-                new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
-        context.registerReceiver(mReceiver, intentFilter);
-    }
-
-    public void setBluetoothStateListener(BluetoothStateListener bluetoothStateListener) {
-        mBluetoothStateListener = bluetoothStateListener;
-    }
-
-    //
-    // Bluetooth helper methods.
-    //
-    // - BluetoothAdapter is the Bluetooth system service.  If
-    //   getDefaultAdapter() returns null
-    //   then the device is not BT capable.  Use BluetoothDevice.isEnabled()
-    //   to see if BT is enabled on the device.
-    //
-    // - BluetoothHeadset is the API for the control connection to a
-    //   Bluetooth Headset.  This lets you completely connect/disconnect a
-    //   headset (which we don't do from the Phone UI!) but also lets you
-    //   get the address of the currently active headset and see whether
-    //   it's currently connected.
-
-    /**
-     * @return true if the Bluetooth on/off switch in the UI should be
-     *         available to the user (i.e. if the device is BT-capable
-     *         and a headset is connected.)
-     */
-    @VisibleForTesting
-    public boolean isBluetoothAvailable() {
-        Log.v(this, "isBluetoothAvailable()...");
-
-        // There's no need to ask the Bluetooth system service if BT is enabled:
-        //
-        //    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        //    if ((adapter == null) || !adapter.isEnabled()) {
-        //        Log.d(this, "  ==> FALSE (BT not enabled)");
-        //        return false;
-        //    }
-        //    Log.d(this, "  - BT enabled!  device name " + adapter.getName()
-        //                 + ", address " + adapter.getAddress());
-        //
-        // ...since we already have a BluetoothHeadset instance.  We can just
-        // call isConnected() on that, and assume it'll be false if BT isn't
-        // enabled at all.
-
-        // Check if there's a connected headset, using the BluetoothHeadset API.
-        boolean isConnected = false;
-        if (mBluetoothHeadset != null) {
-            List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
-
-            if (deviceList.size() > 0) {
-                isConnected = true;
-                for (int i = 0; i < deviceList.size(); i++) {
-                    BluetoothDevice device = deviceList.get(i);
-                    Log.v(this, "state = " + mBluetoothHeadset.getConnectionState(device)
-                            + "for headset: " + device);
-                }
-            }
-        }
-
-        Log.v(this, "  ==> " + isConnected);
-        return isConnected;
-    }
-
-    /**
-     * @return true if a BT Headset is available, and its audio is currently connected.
-     */
-    @VisibleForTesting
-    public boolean isBluetoothAudioConnected() {
-        if (mBluetoothHeadset == null) {
-            Log.v(this, "isBluetoothAudioConnected: ==> FALSE (null mBluetoothHeadset)");
-            return false;
-        }
-        List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
-
-        if (deviceList.isEmpty()) {
-            return false;
-        }
-        for (int i = 0; i < deviceList.size(); i++) {
-            BluetoothDevice device = deviceList.get(i);
-            boolean isAudioOn = mBluetoothHeadset.isAudioConnected(device);
-            Log.v(this, "isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn
-                    + "for headset: " + device);
-            if (isAudioOn) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Helper method used to control the onscreen "Bluetooth" indication;
-     *
-     * @return true if a BT device is available and its audio is currently connected,
-     *              <b>or</b> if we issued a BluetoothHeadset.connectAudio()
-     *              call within the last 5 seconds (which presumably means
-     *              that the BT audio connection is currently being set
-     *              up, and will be connected soon.)
-     */
-    @VisibleForTesting
-    public boolean isBluetoothAudioConnectedOrPending() {
-        if (isBluetoothAudioConnected()) {
-            Log.v(this, "isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)");
-            return true;
-        }
-
-        // If we issued a connectAudio() call "recently enough", even
-        // if BT isn't actually connected yet, let's still pretend BT is
-        // on.  This makes the onscreen indication more responsive.
-        if (isBluetoothAudioPending()) {
-            long timeSinceRequest =
-                    SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime;
-            Log.v(this, "isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
-                    + timeSinceRequest + " msec ago)");
-            return true;
-        }
-
-        Log.v(this, "isBluetoothAudioConnectedOrPending: ==> FALSE");
-        return false;
-    }
-
-    private boolean isBluetoothAudioPending() {
-        return mBluetoothState == BLUETOOTH_AUDIO_PENDING;
-    }
-
-    /**
-     * Notified audio manager of a change to the bluetooth state.
-     */
-    private void updateListenerOfBluetoothState(boolean canBePending) {
-        int newState;
-        if (isBluetoothAudioConnected()) {
-            newState = BLUETOOTH_AUDIO_CONNECTED;
-        } else if (canBePending && isBluetoothAudioPending()) {
-            newState = BLUETOOTH_AUDIO_PENDING;
-        } else if (isBluetoothAvailable()) {
-            newState = BLUETOOTH_DEVICE_CONNECTED;
-        } else {
-            newState = BLUETOOTH_DISCONNECTED;
-        }
-        if (mBluetoothState != newState) {
-            mBluetoothStateListener.onBluetoothStateChange(mBluetoothState, newState);
-            mBluetoothState = newState;
-        }
-    }
-
-    @VisibleForTesting
-    public void connectBluetoothAudio() {
-        Log.v(this, "connectBluetoothAudio()...");
-        if (mBluetoothHeadset != null) {
-            if (!mBluetoothHeadset.connectAudio()) {
-                mHandler.postDelayed(mRetryConnectAudio.prepare(),
-                        Timeouts.getRetryBluetoothConnectAudioBackoffMillis(
-                                mContext.getContentResolver()));
-            }
-        }
-        // The call to connectAudio is asynchronous and may take some time to complete. However,
-        // if connectAudio() returns false, we know that it has failed and therefore will
-        // schedule a retry to happen some time later. We set bluetooth state to pending now and
-        // show bluetooth as connected in the UI, but confirmation that we are connected will
-        // arrive through mReceiver.
-        setBluetoothStatePending();
-    }
-
-    private void setBluetoothStatePending() {
-        mBluetoothState = BLUETOOTH_AUDIO_PENDING;
-        mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
-        mHandler.removeCallbacks(mBluetoothConnectionTimeout.getRunnableToCancel());
-        mBluetoothConnectionTimeout.cancel();
-        // If the mBluetoothConnectionTimeout runnable has run, the session had been cleared...
-        // Create a new Session before putting it back in the queue to possibly run again.
-        mHandler.postDelayed(mBluetoothConnectionTimeout.prepare(),
-                Timeouts.getBluetoothPendingTimeoutMillis(mContext.getContentResolver()));
-    }
-
-    @VisibleForTesting
-    public void disconnectBluetoothAudio() {
-        Log.v(this, "disconnectBluetoothAudio()...");
-        if (mBluetoothHeadset != null) {
-            mBluetoothState = BLUETOOTH_DEVICE_CONNECTED;
-            mBluetoothHeadset.disconnectAudio();
-        } else {
-            mBluetoothState = BLUETOOTH_DISCONNECTED;
-        }
-        mHandler.removeCallbacks(mBluetoothConnectionTimeout.getRunnableToCancel());
-        mBluetoothConnectionTimeout.cancel();
-    }
-
-    /**
-     * Dumps the state of the {@link BluetoothManager}.
-     *
-     * @param pw The {@code IndentingPrintWriter} to write the state to.
-     */
-    public void dump(IndentingPrintWriter pw) {
-        pw.println("isBluetoothAvailable: " + isBluetoothAvailable());
-        pw.println("isBluetoothAudioConnected: " + isBluetoothAudioConnected());
-        pw.println("isBluetoothAudioConnectedOrPending: " + isBluetoothAudioConnectedOrPending());
-
-        if (mBluetoothAdapter != null) {
-            if (mBluetoothHeadset != null) {
-                List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
-
-                if (deviceList.size() > 0) {
-                    BluetoothDevice device = deviceList.get(0);
-                    pw.println("BluetoothHeadset.getCurrentDevice: " + device);
-                    pw.println("BluetoothHeadset.State: "
-                            + mBluetoothHeadset.getConnectionState(device));
-                    pw.println("BluetoothHeadset audio connected: " +
-                            mBluetoothHeadset.isAudioConnected(device));
-                }
-            } else {
-                pw.println("mBluetoothHeadset is null");
-            }
-        } else {
-            pw.println("mBluetoothAdapter is null; device is not BT capable");
-        }
-    }
-
-    /**
-     * Set the bluetooth headset proxy for testing purposes.
-     * @param bluetoothHeadsetProxy
-     */
-    @VisibleForTesting
-    public void setBluetoothHeadsetForTesting(BluetoothHeadsetProxy bluetoothHeadsetProxy) {
-        mBluetoothHeadset = bluetoothHeadsetProxy;
-    }
-
-    /**
-     * Set mBluetoothState for testing.
-     * @param state
-     */
-    @VisibleForTesting
-    public void setInternalBluetoothState(int state) {
-        mBluetoothState = state;
-    }
-}
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index 93d4f71..804909c 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -66,6 +66,7 @@
     private static final int CALL_STATE_INCOMING = 4;
     private static final int CALL_STATE_WAITING = 5;
     private static final int CALL_STATE_IDLE = 6;
+    private static final int CALL_STATE_DISCONNECTED = 7;
 
     // match up with bthf_call_state_t of bt_hf.h
     // Terminate all held or set UDUB("busy") to a waiting call
@@ -84,6 +85,7 @@
     private String mRingingAddress = null;
     private int mRingingAddressType = 0;
     private Call mOldHeldCall = null;
+    private boolean mIsDisconnectedTonePlaying = false;
 
     /**
      * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
@@ -387,6 +389,12 @@
             }
             updateHeadsetWithCallState(false /* force */);
         }
+
+        @Override
+        public void onDisconnectedTonePlaying(boolean isTonePlaying) {
+            mIsDisconnectedTonePlaying = isTonePlaying;
+            updateHeadsetWithCallState(false /* force */);
+        }
     };
 
     /**
@@ -400,6 +408,7 @@
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     synchronized (mLock) {
                         setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy));
+                        updateHeadsetWithCallState(true /* force */);
                     }
                 }
 
@@ -816,6 +825,7 @@
         CallsManager callsManager = mCallsManager;
         Call ringingCall = mCallsManager.getRingingCall();
         Call dialingCall = mCallsManager.getOutgoingCall();
+        boolean hasOnlyDisconnectedCalls = mCallsManager.hasOnlyDisconnectedCalls();
 
         //
         // !! WARNING !!
@@ -831,6 +841,9 @@
             bluetoothCallState = CALL_STATE_INCOMING;
         } else if (dialingCall != null) {
             bluetoothCallState = CALL_STATE_ALERTING;
+        } else if (hasOnlyDisconnectedCalls || mIsDisconnectedTonePlaying) {
+            // Keep the DISCONNECTED state until the disconnect tone's playback is done
+            bluetoothCallState = CALL_STATE_DISCONNECTED;
         }
         return bluetoothCallState;
     }
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 90c1fe8..bc20d1c 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -21,6 +21,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -47,6 +48,7 @@
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
+import android.util.StatsLog;
 import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -74,7 +76,8 @@
  *  connected etc).
  */
 @VisibleForTesting
-public class Call implements CreateConnectionResponse, EventManager.Loggable {
+public class Call implements CreateConnectionResponse, EventManager.Loggable,
+        ConnectionServiceFocusManager.CallFocus {
     public final static String CALL_ID_UNKNOWN = "-1";
     public final static long DATA_USAGE_NOT_SET = -1;
 
@@ -92,6 +95,9 @@
     private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
 
     private static final int INVALID_RTT_REQUEST_ID = -1;
+
+    private static final char NO_DTMF_TONE = '\0';
+
     /**
      * Listener for events on the call.
      */
@@ -131,7 +137,9 @@
         void onRttInitiationFailure(Call call, int reason);
         void onRemoteRttRequest(Call call, int requestId);
         void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
-                                 Bundle extras);
+                                 Bundle extras, boolean isLegacy);
+        void onHandoverFailed(Call call, int error);
+        void onHandoverComplete(Call call);
     }
 
     public abstract static class ListenerBase implements Listener {
@@ -205,7 +213,11 @@
         public void onRemoteRttRequest(Call call, int requestId) {}
         @Override
         public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
-                                        Bundle extras) {}
+                                        Bundle extras, boolean isLegacy) {}
+        @Override
+        public void onHandoverFailed(Call call, int error) {}
+        @Override
+        public void onHandoverComplete(Call call) {}
     }
 
     private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
@@ -403,8 +415,10 @@
     private final String mId;
     private String mConnectionId;
     private Analytics.CallInfo mAnalytics;
+    private char mPlayingDtmfTone;
 
     private boolean mWasConferencePreviouslyMerged = false;
+    private boolean mWasHighDefAudio = false;
 
     // For conferences which support merge/swap at their level, we retain a notion of an active
     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
@@ -421,6 +435,12 @@
 
     private boolean mIsWorkCall;
 
+    /**
+     * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has
+     * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set.
+     */
+    private boolean mUseCallRecordingTone;
+
     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
 
@@ -465,10 +485,20 @@
      */
     private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
     private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
+
+    /**
+     * True if we're supposed to start this call with RTT, either due to the master switch or due
+     * to an extra.
+     */
+    private boolean mDidRequestToStartWithRtt = false;
     /**
      * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
      */
     private int mRttMode;
+    /**
+     * True if the call was ever an RTT call.
+     */
+    private boolean mWasEverRtt = false;
 
     /**
      * Integer indicating the remote RTT request ID that is pending a response from the user.
@@ -477,13 +507,13 @@
 
     /**
      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
-     * int, Bundle)}, contains the call which this call is being handed over to.
+     * int, Bundle, boolean)}, contains the call which this call is being handed over to.
      */
     private Call mHandoverDestinationCall = null;
 
     /**
      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
-     * int, Bundle)}, contains the call which this call is being handed over from.
+     * int, Bundle, boolean)}, contains the call which this call is being handed over from.
      */
     private Call mHandoverSourceCall = null;
 
@@ -636,9 +666,35 @@
             mCallerInfo.cachedPhotoIcon = null;
             mCallerInfo.cachedPhoto = null;
         }
+
         Log.addEvent(this, LogUtils.Events.DESTROYED);
     }
 
+    private void closeRttStreams() {
+        if (mConnectionServiceToInCallStreams != null) {
+            for (ParcelFileDescriptor fd : mConnectionServiceToInCallStreams) {
+                if (fd != null) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+        if (mInCallToConnectionServiceStreams != null) {
+            for (ParcelFileDescriptor fd : mInCallToConnectionServiceStreams) {
+                if (fd != null) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     public String toString() {
@@ -736,11 +792,28 @@
         return sb.toString();
     }
 
+    @Override
+    public ConnectionServiceFocusManager.ConnectionServiceFocus getConnectionServiceWrapper() {
+        return mConnectionService;
+    }
+
     @VisibleForTesting
     public int getState() {
         return mState;
     }
 
+    /**
+     * Determines if this {@link Call} can receive call focus via the
+     * {@link ConnectionServiceFocusManager}.
+     * Only top-level calls and non-external calls are eligible.
+     * @return {@code true} if this call is focusable, {@code false} otherwise.
+     */
+    @Override
+    public boolean isFocusable() {
+        boolean isChild = getParentCall() != null;
+        return !isChild && !isExternalCall();
+    }
+
     private boolean shouldContinueProcessingAfterDisconnect() {
         // Stop processing once the call is active.
         if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
@@ -875,6 +948,10 @@
                 }
                 Log.addEvent(this, event, stringData);
             }
+            int statsdDisconnectCause = (newState == CallState.DISCONNECTED) ?
+                    getDisconnectCause().getCode() : DisconnectCause.UNKNOWN;
+            StatsLog.write(StatsLog.CALL_STATE_CHANGED, newState, statsdDisconnectCause,
+                    isSelfManaged(), isExternalCall());
         }
     }
 
@@ -913,7 +990,7 @@
         }
     }
 
-    int getHandlePresentation() {
+    public int getHandlePresentation() {
         return mHandlePresentation;
     }
 
@@ -1040,7 +1117,7 @@
                 l.onConnectionManagerPhoneAccountChanged(this);
             }
         }
-
+        checkIfRttCapable();
     }
 
     @VisibleForTesting
@@ -1055,9 +1132,10 @@
             for (Listener l : mListeners) {
                 l.onTargetPhoneAccountChanged(this);
             }
-            configureIsWorkCall();
+            configureCallAttributes();
         }
         checkIfVideoCapable();
+        checkIfRttCapable();
     }
 
     public CharSequence getTargetPhoneAccountLabel() {
@@ -1094,6 +1172,17 @@
             return false;
         }
 
+        if (getHandle() == null) {
+            // No point in logging a null-handle call. Some self-managed calls will have this.
+            return false;
+        }
+
+        if (!PhoneAccount.SCHEME_SIP.equals(getHandle().getScheme()) &&
+                !PhoneAccount.SCHEME_TEL.equals(getHandle().getScheme())) {
+            // Can't log schemes other than SIP or TEL for now.
+            return false;
+        }
+
         return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean(
                 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false);
     }
@@ -1112,6 +1201,10 @@
         return mIsWorkCall;
     }
 
+    public boolean isUsingCallRecordingTone() {
+        return mUseCallRecordingTone;
+    }
+
     public boolean isVideoCallingSupported() {
         return mIsVideoCallingSupported;
     }
@@ -1179,9 +1272,10 @@
         return mHandoverState;
     }
 
-    private void configureIsWorkCall() {
+    private void configureCallAttributes() {
         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
         boolean isWorkCall = false;
+        boolean isCallRecordingToneSupported = false;
         PhoneAccount phoneAccount =
                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
         if (phoneAccount != null) {
@@ -1194,8 +1288,14 @@
             if (userHandle != null) {
                 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
             }
+
+            isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
+                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
+                    && phoneAccount.getExtras().getBoolean(
+                    PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
         }
         mIsWorkCall = isWorkCall;
+        mUseCallRecordingTone = isCallRecordingToneSupported;
     }
 
     /**
@@ -1225,6 +1325,33 @@
         }
     }
 
+    private void checkIfRttCapable() {
+        PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
+        if (mTargetPhoneAccountHandle == null) {
+            return;
+        }
+
+        // Check both the target phone account and the connection manager phone account -- if
+        // either support RTT, just set the streams and have them set/unset the RTT property as
+        // needed.
+        PhoneAccount phoneAccount =
+                phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
+        PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
+                        mConnectionManagerPhoneAccountHandle);
+        boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
+                PhoneAccount.CAPABILITY_RTT);
+        boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
+                && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
+
+        if ((isConnectionManagerRttSupported || isRttSupported)
+                && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
+            // If the phone account got set to an RTT capable one and we haven't set the streams
+            // yet, do so now.
+            createRttStreams();
+            Log.i(this, "Setting RTT streams after target phone account selected");
+        }
+    }
+
     boolean shouldAttachToExistingConnection() {
         return mShouldAttachToExistingConnection;
     }
@@ -1326,10 +1453,23 @@
         if (changedProperties != 0) {
             int previousProperties = mConnectionProperties;
             mConnectionProperties = connectionProperties;
-            setRttStreams((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
-                    Connection.PROPERTY_IS_RTT);
             boolean didRttChange =
                     (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
+            if (didRttChange && (mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
+                    Connection.PROPERTY_IS_RTT) {
+                createRttStreams();
+                // Call startRtt to pass the RTT pipes down to the connection service.
+                // They already turned on the RTT property so no request should be sent.
+                mConnectionService.startRtt(this,
+                        getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
+                mWasEverRtt = true;
+                if (isEmergencyCall()) {
+                    mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
+                    mCallsManager.mute(false);
+                }
+            }
+            mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
+                    Connection.PROPERTY_HIGH_DEF_AUDIO;
             for (Listener l : mListeners) {
                 l.onConnectionPropertiesChanged(this, didRttChange);
             }
@@ -1505,6 +1645,7 @@
 
         setConnectionCapabilities(connection.getConnectionCapabilities());
         setConnectionProperties(connection.getConnectionProperties());
+        setIsVoipAudioMode(connection.getIsVoipAudioMode());
         setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
         setVideoProvider(connection.getVideoProvider());
         setVideoState(connection.getVideoState());
@@ -1568,7 +1709,8 @@
     /**
      * Plays the specified DTMF tone.
      */
-    void playDtmfTone(char digit) {
+    @VisibleForTesting
+    public void playDtmfTone(char digit) {
         if (mConnectionService == null) {
             Log.w(this, "playDtmfTone() request on a call without a connection service.");
         } else {
@@ -1576,12 +1718,14 @@
             mConnectionService.playDtmfTone(this, digit);
             Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
         }
+        mPlayingDtmfTone = digit;
     }
 
     /**
      * Stops playing any currently playing DTMF tone.
      */
-    void stopDtmfTone() {
+    @VisibleForTesting
+    public void stopDtmfTone() {
         if (mConnectionService == null) {
             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
         } else {
@@ -1589,6 +1733,15 @@
             Log.addEvent(this, LogUtils.Events.STOP_DTMF);
             mConnectionService.stopDtmfTone(this);
         }
+        mPlayingDtmfTone = NO_DTMF_TONE;
+    }
+
+    /**
+     * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
+     * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
+     */
+    boolean isDtmfTonePlaying() {
+        return mPlayingDtmfTone != NO_DTMF_TONE;
     }
 
     /**
@@ -1609,12 +1762,28 @@
         disconnect(0);
     }
 
+    @VisibleForTesting
+    public void disconnect(String reason) {
+        disconnect(0, reason);
+    }
+
     /**
      * Attempts to disconnect the call through the connection service.
      */
     @VisibleForTesting
     public void disconnect(long disconnectionTimeout) {
-        Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT);
+        disconnect(disconnectionTimeout, "internal" /** reason */);
+    }
+
+    /**
+     * Attempts to disconnect the call through the connection service.
+     * @param reason the reason for the disconnect; used for logging purposes only.  In some cases
+     *               this can be a package name if the disconnect was initiated through an API such
+     *               as TelecomManager.
+     */
+    @VisibleForTesting
+    public void disconnect(long disconnectionTimeout, String reason) {
+        Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason);
 
         // Track that the call is now locally disconnecting.
         setLocallyDisconnecting(true);
@@ -1703,6 +1872,31 @@
     }
 
     /**
+     * Deflects the call if it is ringing.
+     *
+     * @param address address to be deflected to.
+     */
+    @VisibleForTesting
+    public void deflect(Uri address) {
+        // Check to verify that the call is still in the ringing state. A call can change states
+        // between the time the user hits 'deflect' and Telecomm receives the command.
+        if (isRinging("deflect")) {
+            // At this point, we are asking the connection service to deflect but we don't assume
+            // that it will work. Instead, we wait until confirmation from the connection service
+            // that the call is in a non-STATE_RINGING state before changing the UI. See
+            // {@link ConnectionServiceAdapter#setActive} and other set* methods.
+            mVideoStateHistory |= mVideoState;
+            if (mConnectionService != null) {
+                mConnectionService.deflect(this, address);
+            } else {
+                Log.e(this, new NullPointerException(),
+                        "deflect call failed due to null CS callId=%s", getId());
+            }
+            Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address));
+        }
+    }
+
+    /**
      * Rejects the call if it is ringing.
      *
      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
@@ -1710,6 +1904,19 @@
      */
     @VisibleForTesting
     public void reject(boolean rejectWithMessage, String textMessage) {
+        reject(rejectWithMessage, textMessage, "internal" /** reason */);
+    }
+
+    /**
+     * Rejects the call if it is ringing.
+     *
+     * @param rejectWithMessage Whether to send a text message as part of the call rejection.
+     * @param textMessage An optional text message to send as part of the rejection.
+     * @param reason The reason for the reject; used for logging purposes.  May be a package name
+     *               if the reject is initiated from an API such as TelecomManager.
+     */
+    @VisibleForTesting
+    public void reject(boolean rejectWithMessage, String textMessage, String reason) {
         // Check to verify that the call is still in the ringing state. A call can change states
         // between the time the user hits 'reject' and Telecomm receives the command.
         if (isRinging("reject")) {
@@ -1722,15 +1929,19 @@
                 Log.e(this, new NullPointerException(),
                         "reject call failed due to null CS callId=%s", getId());
             }
-            Log.addEvent(this, LogUtils.Events.REQUEST_REJECT);
-
+            Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
         }
     }
 
     /**
      * Puts the call on hold if it is currently active.
      */
-    void hold() {
+    @VisibleForTesting
+    public void hold() {
+        hold(null /* reason */);
+    }
+
+    public void hold(String reason) {
         if (mState == CallState.ACTIVE) {
             if (mConnectionService != null) {
                 mConnectionService.hold(this);
@@ -1738,14 +1949,19 @@
                 Log.e(this, new NullPointerException(),
                         "hold call failed due to null CS callId=%s", getId());
             }
-            Log.addEvent(this, LogUtils.Events.REQUEST_HOLD);
+            Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason);
         }
     }
 
     /**
      * Releases the call from hold if it is currently active.
      */
-    void unhold() {
+    @VisibleForTesting
+    public void unhold() {
+        unhold(null /* reason */);
+    }
+
+    public void unhold(String reason) {
         if (mState == CallState.ON_HOLD) {
             if (mConnectionService != null) {
                 mConnectionService.unhold(this);
@@ -1753,7 +1969,7 @@
                 Log.e(this, new NullPointerException(),
                         "unhold call failed due to null CS callId=%s", getId());
             }
-            Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD);
+            Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason);
         }
     }
 
@@ -1993,16 +2209,34 @@
     }
 
     /**
-     * Sends a call event to the {@link ConnectionService} for this call.
-     *
-     * See {@link Call#sendCallEvent(String, Bundle)}.
+     * Sends a call event to the {@link ConnectionService} for this call. This function is
+     * called for event other than {@link Call#EVENT_REQUEST_HANDOVER}
      *
      * @param event The call event.
      * @param extras Associated extras.
      */
     public void sendCallEvent(String event, Bundle extras) {
+        sendCallEvent(event, 0/*For Event != EVENT_REQUEST_HANDOVER*/, extras);
+    }
+
+    /**
+     * Sends a call event to the {@link ConnectionService} for this call.
+     *
+     * See {@link Call#sendCallEvent(String, Bundle)}.
+     *
+     * @param event The call event.
+     * @param targetSdkVer SDK version of the app calling this api
+     * @param extras Associated extras.
+     */
+    public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
         if (mConnectionService != null) {
             if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
+                if (targetSdkVer > Build.VERSION_CODES.O_MR1) {
+                    Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
+                            " for API > 27(O-MR1)");
+                    // TODO: Add "return" after DUO team adds new API support for handover
+                }
+
                 // Handover requests are targeted at Telecom, not the ConnectionService.
                 if (extras == null) {
                     Log.w(this, "sendCallEvent: %s event received with null extras.",
@@ -2029,7 +2263,7 @@
                 if (handoverExtras instanceof Bundle) {
                     handoverExtrasBundle = (Bundle) handoverExtras;
                 }
-                requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle);
+                requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true);
             } else {
                 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
                 mConnectionService.sendCallEvent(this, event, extras);
@@ -2041,6 +2275,17 @@
     }
 
     /**
+     * Initiates a handover of this Call to the {@link ConnectionService} identified
+     * by destAcct.
+     * @param destAcct ConnectionService to which the call should be handed over.
+     * @param videoState The video state desired after the handover.
+     * @param extras Extra information to be passed to ConnectionService
+     */
+    public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) {
+        requestHandover(destAcct, videoState, extras, false);
+    }
+
+    /**
      * Sets this {@link Call} to has the specified {@code parentCall}.  Also sets the parent to
      * have this call as a child.
      * @param parentCall
@@ -2260,7 +2505,7 @@
             return;
         }
 
-        if (!handle.equals(mHandle)) {
+        if ((handle != null) && !handle.equals(mHandle)) {
             Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
             return;
         }
@@ -2330,6 +2575,10 @@
         return mSpeakerphoneOn;
     }
 
+    public void setRequestedToStartWithRtt() {
+        mDidRequestToStartWithRtt = true;
+    }
+
     public void stopRtt() {
         if (mConnectionService != null) {
             mConnectionService.stopRtt(this);
@@ -2341,29 +2590,29 @@
     }
 
     public void sendRttRequest() {
-        setRttStreams(true);
+        createRttStreams();
         mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
     }
 
-    public void setRttStreams(boolean shouldBeRtt) {
-        boolean areStreamsInitialized = mInCallToConnectionServiceStreams != null
+    private boolean areRttStreamsInitialized() {
+        return mInCallToConnectionServiceStreams != null
                 && mConnectionServiceToInCallStreams != null;
-        if (shouldBeRtt && !areStreamsInitialized) {
+    }
+
+    public void createRttStreams() {
+        if (!areRttStreamsInitialized()) {
+            Log.i(this, "Initializing RTT streams");
             try {
                 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
                 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
             } catch (IOException e) {
                 Log.e(this, e, "Failed to create pipes for RTT call.");
             }
-        } else if (!shouldBeRtt && areStreamsInitialized) {
-            closeRttPipes();
-            mInCallToConnectionServiceStreams = null;
-            mConnectionServiceToInCallStreams = null;
         }
     }
 
     public void onRttConnectionFailure(int reason) {
-        setRttStreams(false);
+        Log.i(this, "Got RTT initiation failure with reason %d", reason);
         for (Listener l : mListeners) {
             l.onRttInitiationFailure(this, reason);
         }
@@ -2390,8 +2639,8 @@
             Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
             return;
         }
-        setRttStreams(accept);
         if (accept) {
+            createRttStreams();
             Log.i(this, "RTT request %d accepted.", id);
             mConnectionService.respondToRttRequest(
                     this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
@@ -2401,14 +2650,14 @@
         }
     }
 
-    public void closeRttPipes() {
-        // TODO: may defer this until call is removed?
-    }
-
     public boolean isRttCall() {
         return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
     }
 
+    public boolean wasEverRttCall() {
+        return mWasEverRtt;
+    }
+
     public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
         return mConnectionServiceToInCallStreams == null ? null
                 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
@@ -2439,6 +2688,11 @@
     public void setVideoProvider(IVideoProvider videoProvider) {
         Log.v(this, "setVideoProvider");
 
+        if (mVideoProviderProxy != null) {
+            mVideoProviderProxy.clearVideoCallback();
+            mVideoProviderProxy = null;
+        }
+
         if (videoProvider != null ) {
             try {
                 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
@@ -2446,8 +2700,6 @@
             } catch (RemoteException ignored) {
                 // Ignore RemoteException.
             }
-        } else {
-            mVideoProviderProxy = null;
         }
 
         mVideoProvider = videoProvider;
@@ -2709,6 +2961,31 @@
         }
     }
 
+    /**
+     * Notifies interested parties that the handover has completed.
+     * Notifies:
+     * 1. {@link InCallController} which communicates this to the
+     * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}.
+     * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of
+     * the successful handover.
+     */
+    public void onHandoverComplete() {
+        Log.i(this, "onHandoverComplete; callId=%s", getId());
+        if (mConnectionService != null) {
+            mConnectionService.handoverComplete(this);
+        }
+        for (Listener l : mListeners) {
+            l.onHandoverComplete(this);
+        }
+    }
+
+    public void onHandoverFailed(int handoverError) {
+        Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError);
+        for (Listener l : mListeners) {
+            l.onHandoverFailed(this, handoverError);
+        }
+    }
+
     public void setOriginalConnectionId(String originalConnectionId) {
         mOriginalConnectionId = originalConnectionId;
     }
@@ -2727,6 +3004,10 @@
         return mOriginalConnectionId;
     }
 
+    public ConnectionServiceFocusManager getConnectionServiceFocusManager() {
+        return mCallsManager.getConnectionServiceFocusManager();
+    }
+
     /**
      * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
      * remotely or locally.
@@ -2758,9 +3039,9 @@
      *      {@link android.telecom.InCallService}.
      */
     private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState,
-                                 Bundle extras) {
+                                 Bundle extras, boolean isLegacy) {
         for (Listener l : mListeners) {
-            l.onHandoverRequested(this, handoverToHandle, videoState, extras);
+            l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy);
         }
     }
 
@@ -2780,4 +3061,12 @@
         mVideoStateHistory |= mVideoState;
     }
 
+    /**
+     * Returns whether or not high definition audio was used.
+     *
+     * @return true if high definition audio was used during this call.
+     */
+    boolean wasHighDefAudio() {
+        return mWasHighDefAudio;
+    }
 }
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 705d5a8..56f8db9 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -48,6 +49,7 @@
 
     private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
     private final CallAudioModeStateMachine mCallAudioModeStateMachine;
+    private final BluetoothStateReceiver mBluetoothStateReceiver;
     private final CallsManager mCallsManager;
     private final InCallTonePlayer.Factory mPlayerFactory;
     private final Ringer mRinger;
@@ -56,6 +58,7 @@
 
     private Call mForegroundCall;
     private boolean mIsTonePlaying = false;
+    private boolean mIsDisconnectedTonePlaying = false;
     private InCallTonePlayer mHoldTonePlayer;
 
     public CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine,
@@ -64,6 +67,7 @@
             InCallTonePlayer.Factory playerFactory,
             Ringer ringer,
             RingbackPlayer ringbackPlayer,
+            BluetoothStateReceiver bluetoothStateReceiver,
             DtmfLocalTonePlayer dtmfLocalTonePlayer) {
         mActiveDialingOrConnectingCalls = new LinkedHashSet<>();
         mRingingCalls = new LinkedHashSet<>();
@@ -84,10 +88,12 @@
         mPlayerFactory = playerFactory;
         mRinger = ringer;
         mRingbackPlayer = ringbackPlayer;
+        mBluetoothStateReceiver = bluetoothStateReceiver;
         mDtmfLocalTonePlayer = dtmfLocalTonePlayer;
 
         mPlayerFactory.setCallAudioManager(this);
         mCallAudioModeStateMachine.setCallAudioManager(this);
+        mCallAudioRouteStateMachine.setCallAudioManager(this);
     }
 
     @Override
@@ -147,6 +153,9 @@
         }
         updateForegroundCall();
         mCalls.add(call);
+        if (mCalls.size() == 1) {
+            mBluetoothStateReceiver.setIsInCall(true);
+        }
 
         onCallEnteringState(call, call.getState());
     }
@@ -165,6 +174,9 @@
 
         updateForegroundCall();
         mCalls.remove(call);
+        if (mCalls.size() == 0) {
+            mBluetoothStateReceiver.setIsInCall(false);
+        }
 
         onCallLeavingState(call, call.getState());
     }
@@ -363,11 +375,23 @@
         return null;
     }
 
-    void toggleMute() {
+    @VisibleForTesting
+    public void toggleMute() {
+        // Don't mute if there are any emergency calls.
+        if (mCallsManager.hasEmergencyCall()) {
+            Log.v(this, "ignoring toggleMute for emergency call");
+            return;
+        }
         mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.TOGGLE_MUTE);
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void onRingerModeChange() {
+        mCallAudioModeStateMachine.sendMessageWithArgs(
+                CallAudioModeStateMachine.RINGER_MODE_CHANGE, makeArgsForModeStateMachine());
+    }
+
     @VisibleForTesting
     public void mute(boolean shouldMute) {
         Log.v(this, "mute, shouldMute: %b", shouldMute);
@@ -386,13 +410,15 @@
      * Changed the audio route, for example from earpiece to speaker phone.
      *
      * @param route The new audio route to use. See {@link CallAudioState}.
+     * @param bluetoothAddress the address of the desired bluetooth device, if route is
+     * {@link CallAudioState#ROUTE_BLUETOOTH}.
      */
-    void setAudioRoute(int route) {
+    void setAudioRoute(int route, String bluetoothAddress) {
         Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
         switch (route) {
             case CallAudioState.ROUTE_BLUETOOTH:
                 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
-                        CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH);
+                        CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH, 0, bluetoothAddress);
                 return;
             case CallAudioState.ROUTE_SPEAKER:
                 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
@@ -416,6 +442,17 @@
         }
     }
 
+    /**
+     * Switch call audio routing to the baseline route, including bluetooth headsets if there are
+     * any connected.
+     */
+    void switchBaseline() {
+        Log.i(this, "switchBaseline");
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
+                CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE);
+    }
+
     void silenceRingers() {
         for (Call call : mRingingCalls) {
             call.silence();
@@ -506,6 +543,11 @@
                 isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING
                         : CallAudioModeStateMachine.TONE_STOPPED_PLAYING,
                 makeArgsForModeStateMachine());
+
+        if (!isTonePlaying && mIsDisconnectedTonePlaying) {
+            mCallsManager.onDisconnectedTonePlaying(false);
+            mIsDisconnectedTonePlaying = false;
+        }
     }
 
     private void onCallLeavingState(Call call, int state) {
@@ -686,6 +728,8 @@
 
             if (toneToPlay != InCallTonePlayer.TONE_INVALID) {
                 mPlayerFactory.createPlayer(toneToPlay).startTone();
+                mCallsManager.onDisconnectedTonePlaying(true);
+                mIsDisconnectedTonePlaying = true;
             }
         }
     }
@@ -779,4 +823,4 @@
     public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() {
         return mCallStateToCalls;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index b5c7e7a..716f23a 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -87,6 +87,8 @@
 
     public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001;
 
+    public static final int RINGER_MODE_CHANGE = 5001;
+
     public static final int RUN_RUNNABLE = 9001;
 
     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
@@ -105,6 +107,7 @@
         put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING");
         put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING");
         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
+        put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE");
 
         put(RUN_RUNNABLE, "RUN_RUNNABLE");
     }};
@@ -202,18 +205,22 @@
     }
 
     private class RingingFocusState extends BaseState {
-        @Override
-        public void enter() {
-            Log.i(LOG_TAG, "Audio focus entering RINGING state");
+        private void tryStartRinging() {
             if (mCallAudioManager.startRinging()) {
                 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                 mAudioManager.setMode(AudioManager.MODE_RINGTONE);
-                mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.RINGING_FOCUS);
+                mCallAudioManager.setCallAudioRouteFocusState(
+                        CallAudioRouteStateMachine.RINGING_FOCUS);
             } else {
-                Log.i(LOG_TAG, "Entering RINGING but not acquiring focus -- silent ringtone");
+                Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
             }
+        }
 
+        @Override
+        public void enter() {
+            Log.i(LOG_TAG, "Audio focus entering RINGING state");
+            tryStartRinging();
             mCallAudioManager.stopCallWaiting();
         }
 
@@ -275,6 +282,11 @@
                     transitionTo(args.foregroundCallIsVoip
                             ? mVoipCallFocusState : mSimCallFocusState);
                     return HANDLED;
+                case RINGER_MODE_CHANGE: {
+                    Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE");
+                    tryStartRinging();
+                    return HANDLED;
+                }
                 default:
                     // The forced focus switch commands are handled by BaseState.
                     return NOT_HANDLED;
diff --git a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
index 96646b2..a871dfc 100644
--- a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
+++ b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
@@ -46,45 +46,35 @@
     }
 
     @Override
-    public void onBluetoothStateChange(int oldState, int newState) {
-        switch (oldState) {
-            case BluetoothRouteManager.BLUETOOTH_DISCONNECTED:
-            case BluetoothRouteManager.BLUETOOTH_UNINITIALIZED:
-                switch (newState) {
-                    case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED:
-                    case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED:
-                        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
-                                CallAudioRouteStateMachine.CONNECT_BLUETOOTH);
-                        break;
-                }
-                break;
-            case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED:
-                switch (newState) {
-                    case BluetoothRouteManager.BLUETOOTH_DISCONNECTED:
-                        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
-                                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH);
-                        break;
-                    case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED:
-                        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
-                                CallAudioRouteStateMachine.SWITCH_BLUETOOTH);
-                        break;
-                }
-                break;
-            case BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED:
-            case BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING:
-                switch (newState) {
-                    case BluetoothRouteManager.BLUETOOTH_DISCONNECTED:
-                        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
-                                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH);
-                        break;
-                    case BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED:
-                        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
-                                CallAudioRouteStateMachine.BT_AUDIO_DISCONNECT);
-                        break;
-                }
-                break;
-        }
+    public void onBluetoothDeviceListChanged() {
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED);
     }
+
+    @Override
+    public void onBluetoothActiveDevicePresent() {
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT);
+    }
+
+    @Override
+    public void onBluetoothActiveDeviceGone() {
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE);
+    }
+
+    @Override
+    public void onBluetoothAudioConnected() {
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+    }
+
+    @Override
+    public void onBluetoothAudioDisconnected() {
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_AUDIO_DISCONNECTED);
+    }
+
     /**
       * Updates the audio route when the headset plugged in state changes. For example, if audio is
       * being routed over speakerphone and a headset is plugged in then switch to wired headset.
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index d96ed1c..4274017 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -18,14 +18,18 @@
 
 
 import android.app.ActivityManager;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.UserInfo;
+import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.os.Binder;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.telecom.CallAudioState;
 import android.telecom.Log;
@@ -33,13 +37,16 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.IState;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Objects;
 
 /**
  * This class describes the available routes of a call as a state machine.
@@ -63,8 +70,11 @@
  * mIsMuted: a boolean indicating whether the audio is muted
  */
 public class CallAudioRouteStateMachine extends StateMachine {
-    private static final String TELECOM_PACKAGE =
-            CallAudioRouteStateMachine.class.getPackage().getName();
+
+    /** Values for CallAudioRouteStateMachine constructor's earPieceRouting arg. */
+    public static final int EARPIECE_FORCE_DISABLED = 0;
+    public static final int EARPIECE_FORCE_ENABLED  = 1;
+    public static final int EARPIECE_AUTO_DETECT    = 2;
 
     /** Direct the audio stream through the device's earpiece. */
     public static final int ROUTE_EARPIECE      = CallAudioState.ROUTE_EARPIECE;
@@ -81,10 +91,11 @@
     /** Valid values for msg.what */
     public static final int CONNECT_WIRED_HEADSET = 1;
     public static final int DISCONNECT_WIRED_HEADSET = 2;
-    public static final int CONNECT_BLUETOOTH = 3;
-    public static final int DISCONNECT_BLUETOOTH = 4;
     public static final int CONNECT_DOCK = 5;
     public static final int DISCONNECT_DOCK = 6;
+    public static final int BLUETOOTH_DEVICE_LIST_CHANGED = 7;
+    public static final int BT_ACTIVE_DEVICE_PRESENT = 8;
+    public static final int BT_ACTIVE_DEVICE_GONE = 9;
 
     public static final int SWITCH_EARPIECE = 1001;
     public static final int SWITCH_BLUETOOTH = 1002;
@@ -92,7 +103,6 @@
     public static final int SWITCH_SPEAKER = 1004;
     // Wired headset, earpiece, or speakerphone, in that order of precedence.
     public static final int SWITCH_BASELINE_ROUTE = 1005;
-    public static final int BT_AUDIO_DISCONNECT = 1006;
 
     public static final int USER_SWITCH_EARPIECE = 1101;
     public static final int USER_SWITCH_BLUETOOTH = 1102;
@@ -102,9 +112,19 @@
 
     public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201;
 
+    // These three messages indicate state changes that come from BluetoothRouteManager.
+    // They may be triggered by the BT stack doing something on its own or they may be sent after
+    // we request that the BT stack do something. Any logic for these messages should take into
+    // account the possibility that the event indicated has already been processed (i.e. handling
+    // should be idempotent).
+    public static final int BT_AUDIO_DISCONNECTED = 1301;
+    public static final int BT_AUDIO_CONNECTED = 1302;
+    public static final int BT_AUDIO_PENDING = 1303;
+
     public static final int MUTE_ON = 3001;
     public static final int MUTE_OFF = 3002;
     public static final int TOGGLE_MUTE = 3003;
+    public static final int MUTE_EXTERNALLY_CHANGED = 3004;
 
     public static final int SWITCH_FOCUS = 4001;
 
@@ -131,17 +151,17 @@
     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
         put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET");
         put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET");
-        put(CONNECT_BLUETOOTH, "CONNECT_BLUETOOTH");
-        put(DISCONNECT_BLUETOOTH, "DISCONNECT_BLUETOOTH");
         put(CONNECT_DOCK, "CONNECT_DOCK");
         put(DISCONNECT_DOCK, "DISCONNECT_DOCK");
+        put(BLUETOOTH_DEVICE_LIST_CHANGED, "BLUETOOTH_DEVICE_LIST_CHANGED");
+        put(BT_ACTIVE_DEVICE_PRESENT, "BT_ACTIVE_DEVICE_PRESENT");
+        put(BT_ACTIVE_DEVICE_GONE, "BT_ACTIVE_DEVICE_GONE");
 
         put(SWITCH_EARPIECE, "SWITCH_EARPIECE");
         put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH");
         put(SWITCH_HEADSET, "SWITCH_HEADSET");
         put(SWITCH_SPEAKER, "SWITCH_SPEAKER");
         put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE");
-        put(BT_AUDIO_DISCONNECT, "BT_AUDIO_DISCONNECT");
 
         put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE");
         put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH");
@@ -151,9 +171,14 @@
 
         put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE");
 
+        put(BT_AUDIO_DISCONNECTED, "BT_AUDIO_DISCONNECTED");
+        put(BT_AUDIO_CONNECTED, "BT_AUDIO_CONNECTED");
+        put(BT_AUDIO_PENDING, "BT_AUDIO_PENDING");
+
         put(MUTE_ON, "MUTE_ON");
         put(MUTE_OFF, "MUTE_OFF");
         put(TOGGLE_MUTE, "TOGGLE_MUTE");
+        put(MUTE_EXTERNALLY_CHANGED, "MUTE_EXTERNALLY_CHANGED");
 
         put(SWITCH_FOCUS, "SWITCH_FOCUS");
 
@@ -174,9 +199,10 @@
 
     @Override
     protected void onPreHandleMessage(Message msg) {
-        if (msg.obj != null && msg.obj instanceof Session) {
+        if (msg.obj != null && msg.obj instanceof SomeArgs) {
+            Session session = (Session) ((SomeArgs) msg.obj).arg1;
             String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown");
-            Log.continueSession((Session) msg.obj, "CARSM.pM_" + messageCodeName);
+            Log.continueSession(session, "CARSM.pM_" + messageCodeName);
             Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1);
         }
     }
@@ -184,6 +210,9 @@
     @Override
     protected void onPostHandleMessage(Message msg) {
         Log.endSession();
+        if (msg.obj != null && msg.obj instanceof SomeArgs) {
+            ((SomeArgs) msg.obj).recycle();
+        }
     }
 
     abstract class AudioState extends State {
@@ -209,7 +238,10 @@
         public boolean processMessage(Message msg) {
             int addedRoutes = 0;
             int removedRoutes = 0;
+            boolean isHandled = NOT_HANDLED;
 
+            Log.i(this, "Processing message %s",
+                    MESSAGE_CODE_TO_NAME.get(msg.what, Integer.toString(msg.what)));
             switch (msg.what) {
                 case CONNECT_WIRED_HEADSET:
                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
@@ -217,11 +249,6 @@
                     removedRoutes |= ROUTE_EARPIECE;
                     addedRoutes |= ROUTE_WIRED_HEADSET;
                     break;
-                case CONNECT_BLUETOOTH:
-                    Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
-                            "Bluetooth connected");
-                    addedRoutes |= ROUTE_BLUETOOTH;
-                    break;
                 case DISCONNECT_WIRED_HEADSET:
                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
                             "Wired headset disconnected");
@@ -230,10 +257,25 @@
                         addedRoutes |= ROUTE_EARPIECE;
                     }
                     break;
-                case DISCONNECT_BLUETOOTH:
+                case BT_ACTIVE_DEVICE_PRESENT:
                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
-                            "Bluetooth disconnected");
-                    removedRoutes |= ROUTE_BLUETOOTH;
+                            "Bluetooth active device present");
+                    break;
+                case BT_ACTIVE_DEVICE_GONE:
+                    Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
+                            "Bluetooth active device gone");
+                    break;
+                case BLUETOOTH_DEVICE_LIST_CHANGED:
+                    Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
+                            "Bluetooth device list changed");
+                    Collection<BluetoothDevice> connectedDevices =
+                            mBluetoothRouteManager.getConnectedDevices();
+                    if (connectedDevices.size() > 0) {
+                        addedRoutes |= ROUTE_BLUETOOTH;
+                    } else {
+                        removedRoutes |= ROUTE_BLUETOOTH;
+                    }
+                    isHandled = HANDLED;
                     break;
                 case SWITCH_BASELINE_ROUTE:
                     sendInternalMessage(calculateBaselineRouteMessage(false,
@@ -243,6 +285,10 @@
                     sendInternalMessage(calculateBaselineRouteMessage(true,
                             msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE));
                     return HANDLED;
+                case USER_SWITCH_BLUETOOTH:
+                    // If the user tries to switch to BT, reset the explicitly-switched-away flag.
+                    mHasUserExplicitlyLeftBluetooth = false;
+                    return NOT_HANDLED;
                 case SWITCH_FOCUS:
                     mAudioFocusType = msg.arg1;
                     return NOT_HANDLED;
@@ -250,13 +296,15 @@
                     return NOT_HANDLED;
             }
 
-            if (addedRoutes != 0 || removedRoutes != 0) {
+            if (addedRoutes != 0 || removedRoutes != 0
+                    || msg.what == BLUETOOTH_DEVICE_LIST_CHANGED) {
                 mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true);
                 mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes,
                         addedRoutes, false);
+                updateSystemAudioState();
             }
 
-            return NOT_HANDLED;
+            return isHandled;
         }
 
         // Behavior will depend on whether the state is an active one or a quiescent one.
@@ -280,9 +328,10 @@
         public void enter() {
             super.enter();
             setSpeakerphoneOn(false);
-            setBluetoothOn(false);
+            setBluetoothOff();
             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
-                    mAvailableRoutes);
+                    mAvailableRoutes, null,
+                    mBluetoothRouteManager.getConnectedDevices());
             setSystemAudioState(newState, true);
             updateInternalCallAudioState();
         }
@@ -303,11 +352,21 @@
                 case USER_SWITCH_EARPIECE:
                     // Nothing to do here
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
-                        transitionTo(mAudioFocusType == ACTIVE_FOCUS ?
-                                mActiveBluetoothRoute : mRingingBluetoothRoute);
+                        if (mAudioFocusType == ACTIVE_FOCUS
+                                || mBluetoothRouteManager.isInbandRingingEnabled()) {
+                            String address = (msg.obj instanceof SomeArgs) ?
+                                    (String) ((SomeArgs) msg.obj).arg2 : null;
+                            // Omit transition to ActiveBluetoothRoute
+                            setBluetoothOn(address);
+                        } else {
+                            transitionTo(mRingingBluetoothRoute);
+                        }
                     } else {
                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
                     }
@@ -368,6 +427,10 @@
                 case USER_SWITCH_EARPIECE:
                     // Nothing to do here
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    Log.w(this, "BT Audio came on in quiescent earpiece route.");
+                    transitionTo(mActiveBluetoothRoute);
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
@@ -414,26 +477,23 @@
                 case CONNECT_WIRED_HEADSET:
                     sendInternalMessage(SWITCH_HEADSET);
                     return HANDLED;
-                case CONNECT_BLUETOOTH:
+                case BT_ACTIVE_DEVICE_PRESENT:
                     if (!mHasUserExplicitlyLeftBluetooth) {
                         sendInternalMessage(SWITCH_BLUETOOTH);
                     } else {
                         Log.i(this, "Not switching to BT route from earpiece because user has " +
                                 "explicitly disconnected.");
-                        updateSystemAudioState();
                     }
                     return HANDLED;
-                case DISCONNECT_BLUETOOTH:
-                    updateSystemAudioState();
+                case BT_ACTIVE_DEVICE_GONE:
                     // No change in audio route required
                     return HANDLED;
                 case DISCONNECT_WIRED_HEADSET:
                     Log.e(this, new IllegalStateException(),
                             "Wired headset should not go from connected to not when on " +
                             "earpiece");
-                    updateSystemAudioState();
                     return HANDLED;
-                case BT_AUDIO_DISCONNECT:
+                case BT_AUDIO_DISCONNECTED:
                     // This may be sent as a confirmation by the BT stack after switch off BT.
                     return HANDLED;
                 case CONNECT_DOCK:
@@ -463,9 +523,9 @@
         public void enter() {
             super.enter();
             setSpeakerphoneOn(false);
-            setBluetoothOn(false);
+            setBluetoothOff();
             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET,
-                    mAvailableRoutes);
+                    mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
             setSystemAudioState(newState, true);
             updateInternalCallAudioState();
         }
@@ -490,11 +550,21 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
-                        transitionTo(mAudioFocusType == ACTIVE_FOCUS ?
-                                mActiveBluetoothRoute : mRingingBluetoothRoute);
+                        if (mAudioFocusType == ACTIVE_FOCUS
+                                || mBluetoothRouteManager.isInbandRingingEnabled()) {
+                            String address = (msg.obj instanceof SomeArgs) ?
+                                    (String) ((SomeArgs) msg.obj).arg2 : null;
+                            // Omit transition to ActiveBluetoothRoute until actual connection.
+                            setBluetoothOn(address);
+                        } else {
+                            transitionTo(mRingingBluetoothRoute);
+                        }
                     } else {
                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
                     }
@@ -555,6 +625,10 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    Log.w(this, "BT Audio came on in quiescent headset route.");
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
@@ -597,20 +671,16 @@
                 case CONNECT_WIRED_HEADSET:
                     Log.e(this, new IllegalStateException(),
                             "Wired headset should already be connected.");
-                    mAvailableRoutes |= ROUTE_WIRED_HEADSET;
-                    updateSystemAudioState();
                     return HANDLED;
-                case CONNECT_BLUETOOTH:
+                case BT_ACTIVE_DEVICE_PRESENT:
                     if (!mHasUserExplicitlyLeftBluetooth) {
                         sendInternalMessage(SWITCH_BLUETOOTH);
                     } else {
                         Log.i(this, "Not switching to BT route from headset because user has " +
                                 "explicitly disconnected.");
-                        updateSystemAudioState();
                     }
                     return HANDLED;
-                case DISCONNECT_BLUETOOTH:
-                    updateSystemAudioState();
+                case BT_ACTIVE_DEVICE_GONE:
                     // No change in audio route required
                     return HANDLED;
                 case DISCONNECT_WIRED_HEADSET:
@@ -620,7 +690,7 @@
                         sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
                     }
                     return HANDLED;
-                case BT_AUDIO_DISCONNECT:
+                case BT_AUDIO_DISCONNECTED:
                     // This may be sent as a confirmation by the BT stack after switch off BT.
                     return HANDLED;
                 case CONNECT_DOCK:
@@ -635,6 +705,10 @@
         }
     }
 
+    // Note: transitions to/from this class work a bit differently -- we delegate to
+    // BluetoothRouteManager to manage all Bluetooth state, so instead of transitioning to one of
+    // the bluetooth states immediately when there's an request to do so, we wait for
+    // BluetoothRouteManager to report its state before we go into this state.
     class ActiveBluetoothRoute extends BluetoothRoute {
         @Override
         public String getName() {
@@ -650,11 +724,15 @@
         public void enter() {
             super.enter();
             setSpeakerphoneOn(false);
-            setBluetoothOn(true);
             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
-                    mAvailableRoutes);
+                    mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
+                    mBluetoothRouteManager.getConnectedDevices());
             setSystemAudioState(newState, true);
             updateInternalCallAudioState();
+            // Do not send RINGER_MODE_CHANGE if no Bluetooth SCO audio device is available
+            if (mBluetoothRouteManager.getBluetoothAudioConnectedDevice() != null) {
+                mCallAudioManager.onRingerModeChange();
+            }
         }
 
         @Override
@@ -679,9 +757,18 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    // Send ringer mode change because we transit to ActiveBluetoothState even
+                    // when HFP is connecting
+                    mCallAudioManager.onRingerModeChange();
+                    // Update the in-call app on the new active BT device in case that changed.
+                    updateSystemAudioState();
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
-                    // Nothing to do
+                    String address = (msg.obj instanceof SomeArgs) ?
+                            (String) ((SomeArgs) msg.obj).arg2 : null;
+                    setBluetoothOn(address);
                     return HANDLED;
                 case USER_SWITCH_HEADSET:
                     mHasUserExplicitlyLeftBluetooth = true;
@@ -701,12 +788,15 @@
                     return HANDLED;
                 case SWITCH_FOCUS:
                     if (msg.arg1 == NO_FOCUS) {
+                        setBluetoothOff();
                         reinitialize();
-                    } else if (msg.arg1 == RINGING_FOCUS) {
+                    } else if (msg.arg1 == RINGING_FOCUS
+                            && !mBluetoothRouteManager.isInbandRingingEnabled()) {
+                        setBluetoothOff();
                         transitionTo(mRingingBluetoothRoute);
                     }
                     return HANDLED;
-                case BT_AUDIO_DISCONNECT:
+                case BT_AUDIO_DISCONNECTED:
                     sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
                     return HANDLED;
                 default:
@@ -715,6 +805,8 @@
         }
     }
 
+    // This state is only used when the device doesn't support in-band ring. If it does,
+    // ActiveBluetoothRoute is used instead.
     class RingingBluetoothRoute extends BluetoothRoute {
         @Override
         public String getName() {
@@ -732,7 +824,8 @@
             setSpeakerphoneOn(false);
             // Do not enable SCO audio here, since RING is being sent to the headset.
             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
-                    mAvailableRoutes);
+                    mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
+                    mBluetoothRouteManager.getConnectedDevices());
             setSystemAudioState(newState);
             updateInternalCallAudioState();
         }
@@ -759,6 +852,9 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     // Nothing to do
@@ -783,12 +879,12 @@
                     if (msg.arg1 == NO_FOCUS) {
                         reinitialize();
                     } else if (msg.arg1 == ACTIVE_FOCUS) {
-                        transitionTo(mActiveBluetoothRoute);
+                        setBluetoothOn(null);
                     }
                     return HANDLED;
-                case BT_AUDIO_DISCONNECT:
-                    // BT SCO might be connected when in-band ringing is enabled
-                    sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
+                case BT_AUDIO_DISCONNECTED:
+                    // Ignore this -- audio disconnecting while ringing w/o in-band should not
+                    // cause a route switch, since the device is still connected.
                     return HANDLED;
                 default:
                     return NOT_HANDLED;
@@ -812,7 +908,6 @@
             super.enter();
             mHasUserExplicitlyLeftBluetooth = false;
             updateInternalCallAudioState();
-            setBluetoothOn(false);
         }
 
         @Override
@@ -834,6 +929,9 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     // Nothing to do
@@ -852,12 +950,16 @@
                     return HANDLED;
                 case SWITCH_FOCUS:
                     if (msg.arg1 == ACTIVE_FOCUS) {
-                        transitionTo(mActiveBluetoothRoute);
+                        setBluetoothOn(null);
                     } else if (msg.arg1 == RINGING_FOCUS) {
-                        transitionTo(mRingingBluetoothRoute);
+                        if (mBluetoothRouteManager.isInbandRingingEnabled()) {
+                            setBluetoothOn(null);
+                        } else {
+                            transitionTo(mRingingBluetoothRoute);
+                        }
                     }
                     return HANDLED;
-                case BT_AUDIO_DISCONNECT:
+                case BT_AUDIO_DISCONNECTED:
                     // Ignore this -- audio disconnecting while quiescent should not cause a
                     // route switch, since the device is still connected.
                     return HANDLED;
@@ -882,17 +984,15 @@
                 case CONNECT_WIRED_HEADSET:
                     sendInternalMessage(SWITCH_HEADSET);
                     return HANDLED;
-                case CONNECT_BLUETOOTH:
-                    // We can't tell when a change in bluetooth state corresponds to an
-                    // actual connection or disconnection, so we'll just ignore it if we're already
-                    // in the bluetooth route.
+                case BT_ACTIVE_DEVICE_PRESENT:
+                    Log.w(this, "Bluetooth active device should not"
+                            + " have been null while we were in BT route.");
                     return HANDLED;
-                case DISCONNECT_BLUETOOTH:
+                case BT_ACTIVE_DEVICE_GONE:
                     sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
                     mWasOnSpeaker = false;
                     return HANDLED;
                 case DISCONNECT_WIRED_HEADSET:
-                    updateSystemAudioState();
                     // No change in audio route required
                     return HANDLED;
                 case CONNECT_DOCK:
@@ -923,10 +1023,10 @@
             super.enter();
             mWasOnSpeaker = true;
             setSpeakerphoneOn(true);
-            setBluetoothOn(false);
+            setBluetoothOff();
             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
-                    mAvailableRoutes);
-            setSystemAudioState(newState);
+                    mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
+            setSystemAudioState(newState, true);
             updateInternalCallAudioState();
         }
 
@@ -952,13 +1052,23 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    return HANDLED;
                 case USER_SWITCH_BLUETOOTH:
                     mWasOnSpeaker = false;
                     // fall through
                 case SWITCH_BLUETOOTH:
+                    String address = (msg.obj instanceof SomeArgs) ?
+                            (String) ((SomeArgs) msg.obj).arg2 : null;
                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
-                        transitionTo(mAudioFocusType == ACTIVE_FOCUS ?
-                                mActiveBluetoothRoute : mRingingBluetoothRoute);
+                        if (mAudioFocusType == ACTIVE_FOCUS
+                                || mBluetoothRouteManager.isInbandRingingEnabled()) {
+                            // Omit transition to ActiveBluetoothRoute
+                            setBluetoothOn(address);
+                        } else {
+                            transitionTo(mRingingBluetoothRoute);
+                        }
                     } else {
                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
                     }
@@ -1027,6 +1137,10 @@
                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
                     }
                     return HANDLED;
+                case BT_AUDIO_CONNECTED:
+                    transitionTo(mActiveBluetoothRoute);
+                    Log.w(this, "BT audio reported as connected while in quiescent speaker");
+                    return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
@@ -1073,24 +1187,21 @@
                 case CONNECT_WIRED_HEADSET:
                     sendInternalMessage(SWITCH_HEADSET);
                     return HANDLED;
-                case CONNECT_BLUETOOTH:
+                case BT_ACTIVE_DEVICE_PRESENT:
                     if (!mHasUserExplicitlyLeftBluetooth) {
                         sendInternalMessage(SWITCH_BLUETOOTH);
                     } else {
                         Log.i(this, "Not switching to BT route from speaker because user has " +
                                 "explicitly disconnected.");
-                        updateSystemAudioState();
                     }
                     return HANDLED;
-                case DISCONNECT_BLUETOOTH:
-                    updateSystemAudioState();
+                case BT_ACTIVE_DEVICE_GONE:
                     // No change in audio route required
                     return HANDLED;
                 case DISCONNECT_WIRED_HEADSET:
-                    updateSystemAudioState();
                     // No change in audio route required
                     return HANDLED;
-                case BT_AUDIO_DISCONNECT:
+                case BT_AUDIO_DISCONNECTED:
                     // This may be sent as a confirmation by the BT stack after switch off BT.
                     return HANDLED;
                 case CONNECT_DOCK:
@@ -1105,6 +1216,24 @@
         }
     }
 
+    private final BroadcastReceiver mMuteChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.startSession("CARSM.mCR");
+            if (AudioManager.ACTION_MICROPHONE_MUTE_CHANGED.equals(intent.getAction())) {
+                if (mCallsManager.hasEmergencyCall()) {
+                    Log.i(this, "Mute was externally changed when there's an emergency call. " +
+                            "Forcing mute back off.");
+                    sendInternalMessage(MUTE_OFF);
+                } else {
+                    sendInternalMessage(MUTE_EXTERNALLY_CHANGED);
+                }
+            } else {
+                Log.w(this, "Received non-mute-change intent");
+            }
+        }
+    };
+
     private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute();
     private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute();
     private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute();
@@ -1144,6 +1273,8 @@
     private CallAudioState mCurrentCallAudioState;
     private CallAudioState mLastKnownCallAudioState;
 
+    private CallAudioManager mCallAudioManager;
+
     public CallAudioRouteStateMachine(
             Context context,
             CallsManager callsManager,
@@ -1151,7 +1282,7 @@
             WiredHeadsetManager wiredHeadsetManager,
             StatusBarNotifier statusBarNotifier,
             CallAudioManager.AudioServiceFactory audioServiceFactory,
-            boolean doesDeviceSupportEarpieceRoute) {
+            int earpieceControl) {
         super(NAME);
         addState(mActiveEarpieceRoute);
         addState(mActiveHeadsetRoute);
@@ -1170,7 +1301,16 @@
         mWiredHeadsetManager = wiredHeadsetManager;
         mStatusBarNotifier = statusBarNotifier;
         mAudioServiceFactory = audioServiceFactory;
-        mDoesDeviceSupportEarpieceRoute = doesDeviceSupportEarpieceRoute;
+        switch (earpieceControl) {
+            case EARPIECE_FORCE_DISABLED:
+                mDoesDeviceSupportEarpieceRoute = false;
+                break;
+            case EARPIECE_FORCE_ENABLED:
+                mDoesDeviceSupportEarpieceRoute = true;
+                break;
+            default:
+                mDoesDeviceSupportEarpieceRoute = checkForEarpieceSupport();
+        }
         mLock = callsManager.getLock();
 
         mStateNameToRouteCode = new HashMap<>(8);
@@ -1191,6 +1331,10 @@
         mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
     }
 
+    public void setCallAudioManager(CallAudioManager callAudioManager) {
+        mCallAudioManager = callAudioManager;
+    }
+
     /**
      * Initializes the state machine with info on initial audio route, supported audio routes,
      * and mute status.
@@ -1212,6 +1356,8 @@
         mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
         mIsMuted = initState.isMuted();
         mWasOnSpeaker = false;
+        mContext.registerReceiver(mMuteChangeReceiver,
+                new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED));
 
         mStatusBarNotifier.notifyMute(initState.isMuted());
         mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
@@ -1228,11 +1374,18 @@
     }
 
     public void sendMessageWithSessionInfo(int message, int arg) {
-        sendMessage(message, arg, 0, Log.createSubsession());
+        sendMessageWithSessionInfo(message, arg, null);
     }
 
     public void sendMessageWithSessionInfo(int message) {
-        sendMessage(message, 0, 0, Log.createSubsession());
+        sendMessageWithSessionInfo(message, 0, null);
+    }
+
+    public void sendMessageWithSessionInfo(int message, int arg, String data) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = Log.createSubsession();
+        args.arg2 = data;
+        sendMessage(message, arg, 0, args);
     }
 
     /**
@@ -1241,23 +1394,20 @@
      */
     @Override
     protected void unhandledMessage(Message msg) {
-        CallAudioState newCallAudioState;
         switch (msg.what) {
             case MUTE_ON:
                 setMuteOn(true);
-                newCallAudioState = new CallAudioState(mIsMuted,
-                        mCurrentCallAudioState.getRoute(),
-                        mAvailableRoutes);
-                setSystemAudioState(newCallAudioState);
-                updateInternalCallAudioState();
+                updateSystemMuteState();
                 return;
             case MUTE_OFF:
                 setMuteOn(false);
-                newCallAudioState = new CallAudioState(mIsMuted,
-                        mCurrentCallAudioState.getRoute(),
-                        mAvailableRoutes);
-                setSystemAudioState(newCallAudioState);
-                updateInternalCallAudioState();
+                updateSystemMuteState();
+                return;
+            case MUTE_EXTERNALLY_CHANGED:
+                mIsMuted = mAudioManager.isMicrophoneMute();
+                if (isInActiveState()) {
+                    updateSystemMuteState();
+                }
                 return;
             case TOGGLE_MUTE:
                 if (mIsMuted) {
@@ -1275,8 +1425,7 @@
                 r.run();
                 return;
             default:
-                Log.e(this, new IllegalStateException(),
-                        "Unexpected message code %d", msg.what);
+                Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what);
         }
     }
 
@@ -1298,15 +1447,29 @@
         mStatusBarNotifier.notifySpeakerphone(on);
     }
 
-    private void setBluetoothOn(boolean on) {
+    private void setBluetoothOn(String address) {
         if (mBluetoothRouteManager.isBluetoothAvailable()) {
-            if (on != mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) {
-                Log.i(this, "connecting bluetooth %s", on);
-                if (on) {
-                    mBluetoothRouteManager.connectBluetoothAudio(null /*TODO: add real address*/);
-                } else {
-                    mBluetoothRouteManager.disconnectBluetoothAudio();
-                }
+            BluetoothDevice connectedDevice =
+                    mBluetoothRouteManager.getBluetoothAudioConnectedDevice();
+            if (address == null && connectedDevice != null) {
+                // null means connect to any device, so don't bother reconnecting. Also, send a
+                // message to ourselves telling us that BT audio is already connected.
+                Log.i(this, "HFP audio already on. Skipping connecting.");
+                sendInternalMessage(BT_AUDIO_CONNECTED);
+                return;
+            }
+            if (connectedDevice == null || !Objects.equals(address, connectedDevice.getAddress())) {
+                Log.i(this, "connecting bluetooth audio: %s", address);
+                mBluetoothRouteManager.connectBluetoothAudio(address);
+            }
+        }
+    }
+
+    private void setBluetoothOff() {
+        if (mBluetoothRouteManager.isBluetoothAvailable()) {
+            if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) {
+                Log.i(this, "disconnecting bluetooth audio");
+                mBluetoothRouteManager.disconnectBluetoothAudio();
             }
         }
     }
@@ -1328,8 +1491,6 @@
                     // user and not the current foreground, which we want to avoid.
                     audio.setMicrophoneMute(
                             mute, mContext.getOpPackageName(), getCurrentUserId());
-                    mStatusBarNotifier.notifyMute(mute);
-
                 } catch (RemoteException e) {
                     Log.e(this, e, "Remote exception while toggling mute.");
                 }
@@ -1339,6 +1500,16 @@
         }
     }
 
+    private void updateSystemMuteState() {
+        CallAudioState newCallAudioState = new CallAudioState(mIsMuted,
+                mCurrentCallAudioState.getRoute(),
+                mAvailableRoutes,
+                mCurrentCallAudioState.getActiveBluetoothDevice(),
+                mBluetoothRouteManager.getConnectedDevices());
+        setSystemAudioState(newCallAudioState);
+        updateInternalCallAudioState();
+    }
+
     /**
      * Updates the CallAudioState object from current internal state. The result is used for
      * external communication only.
@@ -1349,11 +1520,15 @@
             Log.e(this, new IllegalStateException(), "Current state should never be null" +
                     " when updateInternalCallAudioState is called.");
             mCurrentCallAudioState = new CallAudioState(
-                    mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes);
+                    mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes,
+                    mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
+                    mBluetoothRouteManager.getConnectedDevices());
             return;
         }
         int currentRoute = mStateNameToRouteCode.get(currentState.getName());
-        mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes);
+        mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes,
+                mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
+                mBluetoothRouteManager.getConnectedDevices());
     }
 
     private void setSystemAudioState(CallAudioState newCallAudioState) {
@@ -1369,7 +1544,7 @@
             Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState,
                     newCallAudioState);
             if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) {
-
+                mStatusBarNotifier.notifyMute(newCallAudioState.isMuted());
                 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState);
                 updateAudioForForegroundCall(newCallAudioState);
                 mLastKnownCallAudioState = newCallAudioState;
@@ -1419,7 +1594,9 @@
         // 7. State machine handler processes SWITCH_HEADSET.
         Session subsession = Log.createSubsession();
         if(subsession != null) {
-            sendMessageAtFrontOfQueue(messageCode, arg1, 0, subsession);
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = subsession;
+            sendMessageAtFrontOfQueue(messageCode, arg1, 0, args);
         } else {
             sendMessageAtFrontOfQueue(messageCode, arg1);
         }
@@ -1439,7 +1616,8 @@
             route = ROUTE_SPEAKER;
         }
 
-        return new CallAudioState(false, route, supportedRouteMask);
+        return new CallAudioState(false, route, supportedRouteMask, null,
+                mBluetoothRouteManager.getConnectedDevices());
     }
 
     private int getCurrentUserId() {
@@ -1455,7 +1633,7 @@
         return UserHandle.USER_OWNER;
     }
 
-    private boolean isInActiveState() {
+    public boolean isInActiveState() {
         AudioState currentState = (AudioState) getCurrentState();
         if (currentState == null) {
             Log.w(this, "Current state is null, assuming inactive state");
@@ -1464,14 +1642,15 @@
         return currentState.isActive();
     }
 
-    public static boolean doesDeviceSupportEarpieceRoute() {
-        String[] characteristics = SystemProperties.get("ro.build.characteristics").split(",");
-        for (String characteristic : characteristics) {
-            if ("watch".equals(characteristic)) {
-                return false;
+    private boolean checkForEarpieceSupport() {
+        AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        for (AudioDeviceInfo device: deviceList) {
+            if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
+                return true;
             }
         }
-        return true;
+        // No earpiece found
+        return false;
     }
 
     private int calculateBaselineRouteMessage(boolean isExplicitUserRequest,
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index fdd4ebc..ff3b7b2 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -101,9 +101,9 @@
         String scheme = handle.getScheme();
         String uriString = handle.getSchemeSpecificPart();
 
-        if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
-            handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
-                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
+        // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
+        if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
+            handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
         }
 
         PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
@@ -127,10 +127,17 @@
                 VideoProfile.STATE_AUDIO_ONLY);
         clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
 
-        boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
-        // Show the toast to warn user that it is a personal call though initiated in work profile.
-        if (fixedInitiatingUser) {
-            Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
+        if (!callsManager.isSelfManaged(phoneAccountHandle,
+                (UserHandle) intent.getParcelableExtra(KEY_INITIATING_USER))) {
+            boolean fixedInitiatingUser = fixInitiatingUserIfNecessary(context, intent);
+            // Show the toast to warn user that it is a personal call though initiated in work
+            // profile.
+            if (fixedInitiatingUser) {
+                Toast.makeText(context, R.string.toast_personal_call_msg, Toast.LENGTH_LONG).show();
+            }
+        } else {
+            Log.i(CallIntentProcessor.class,
+                    "processOutgoingCallIntent: skip initiating user check");
         }
 
         UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
@@ -182,6 +189,9 @@
                         userManager.getProfileParent(
                                 initiatingUser.getIdentifier()).getUserHandle();
                 intent.putExtra(KEY_INITIATING_USER, parentUserHandle);
+
+                Log.i(CallIntentProcessor.class, "fixInitiatingUserIfNecessary: no dialer installed"
+                        + " for current user; setting initiator to parent %s" + parentUserHandle);
                 return true;
             }
         }
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 01b0de8..6e71b73 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -40,7 +40,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CallerInfo;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Helper class that provides functionality to write information about calls and their associated
@@ -69,12 +76,13 @@
          * @param creationDate Time when the call was created (milliseconds since epoch).
          * @param durationInMillis Duration of the call (milliseconds).
          * @param dataUsage Data usage in bytes, or null if not applicable.
+         * @param isRead Indicates if the entry has been read or not.
          * @param logCallCompletedListener optional callback called after the call is logged.
          */
         public AddCallArgs(Context context, CallerInfo callerInfo, String number,
                 String postDialDigits, String viaNumber, int presentation, int callType,
                 int features, PhoneAccountHandle accountHandle, long creationDate,
-                long durationInMillis, Long dataUsage, UserHandle initiatingUser,
+                long durationInMillis, Long dataUsage, UserHandle initiatingUser, boolean isRead,
                 @Nullable LogCallCompletedListener logCallCompletedListener) {
             this.context = context;
             this.callerInfo = callerInfo;
@@ -89,6 +97,7 @@
             this.durationInSec = (int)(durationInMillis / 1000);
             this.dataUsage = dataUsage;
             this.initiatingUser = initiatingUser;
+            this.isRead = isRead;
             this.logCallCompletedListener = logCallCompletedListener;
         }
         // Since the members are accessed directly, we don't use the
@@ -106,6 +115,7 @@
         public final int durationInSec;
         public final Long dataUsage;
         public final UserHandle initiatingUser;
+        public final boolean isRead;
 
         @Nullable
         public final LogCallCompletedListener logCallCompletedListener;
@@ -228,12 +238,14 @@
 
         int callFeatures = getCallFeatures(call.getVideoStateHistory(),
                 call.getDisconnectCause().getCode() == DisconnectCause.CALL_PULLED,
+                shouldSaveHdInfo(call, accountHandle),
                 (call.getConnectionProperties() & Connection.PROPERTY_ASSISTED_DIALING_USED) ==
-                        Connection.PROPERTY_ASSISTED_DIALING_USED);
+                        Connection.PROPERTY_ASSISTED_DIALING_USED,
+                call.wasEverRttCall());
         logCall(call.getCallerInfo(), logNumber, call.getPostDialDigits(), formattedViaNumber,
                 call.getHandlePresentation(), callLogType, callFeatures, accountHandle,
                 creationTime, age, callDataUsage, call.isEmergencyCall(), call.getInitiatingUser(),
-                logCallCompletedListener);
+                call.isSelfManaged(), logCallCompletedListener);
     }
 
     /**
@@ -251,6 +263,8 @@
      * @param dataUsage The data usage for the call, null if not applicable.
      * @param isEmergency {@code true} if this is an emergency call, {@code false} otherwise.
      * @param logCallCompletedListener optional callback called after the call is logged.
+     * @param initiatingUser The user the call was initiated under.
+     * @param isSelfManaged {@code true} if this is a self-managed call, {@code false} otherwise.
      */
     private void logCall(
             CallerInfo callerInfo,
@@ -266,6 +280,7 @@
             Long dataUsage,
             boolean isEmergency,
             UserHandle initiatingUser,
+            boolean isSelfManaged,
             @Nullable LogCallCompletedListener logCallCompletedListener) {
 
         // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
@@ -282,32 +297,56 @@
         }
 
         // Don't log emergency numbers if the device doesn't allow it.
-        final boolean isOkToLogThisCall = !isEmergency || okToLogEmergencyNumber;
+        final boolean isOkToLogThisCall = (!isEmergency || okToLogEmergencyNumber)
+                && !isUnloggableNumber(number, configBundle);
 
         sendAddCallBroadcast(callType, duration);
 
         if (isOkToLogThisCall) {
-            Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
+            Log.d(TAG, "Logging Call log entry: " + callerInfo + ", "
                     + Log.pii(number) + "," + presentation + ", " + callType
                     + ", " + start + ", " + duration);
+            boolean isRead = false;
+            if (isSelfManaged) {
+                // Mark self-managed calls are read since they're being handled by their own app.
+                // Their inclusion in the call log is informational only.
+                isRead = true;
+            }
             AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, postDialDigits,
                     viaNumber, presentation, callType, features, accountHandle, start, duration,
-                    dataUsage, initiatingUser, logCallCompletedListener);
+                    dataUsage, initiatingUser, isRead, logCallCompletedListener);
             logCallAsync(args);
         } else {
           Log.d(TAG, "Not adding emergency call to call log.");
         }
     }
 
+    private boolean isUnloggableNumber(String callNumber, PersistableBundle carrierConfig) {
+        String normalizedNumber = PhoneNumberUtils.normalizeNumber(callNumber);
+        String[] unloggableNumbersFromCarrierConfig = carrierConfig == null ? null
+                : carrierConfig.getStringArray(
+                        CarrierConfigManager.KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY);
+        String[] unloggableNumbersFromMccConfig = mContext.getResources()
+                .getStringArray(com.android.internal.R.array.unloggable_phone_numbers);
+        return Stream.concat(
+                unloggableNumbersFromCarrierConfig == null ?
+                        Stream.empty() : Arrays.stream(unloggableNumbersFromCarrierConfig),
+                unloggableNumbersFromMccConfig == null ?
+                        Stream.empty() : Arrays.stream(unloggableNumbersFromMccConfig)
+        ).anyMatch(unloggableNumber -> Objects.equals(unloggableNumber, normalizedNumber));
+    }
+
     /**
      * Based on the video state of the call, determines the call features applicable for the call.
      *
      * @param videoState The video state.
      * @param isPulledCall {@code true} if this call was pulled to another device.
+     * @param isStoreHd {@code true} if this call was used HD.
+     * @param isUsingAssistedDialing {@code true} if this call used assisted dialing.
      * @return The call features.
      */
-    private static int getCallFeatures(int videoState, boolean isPulledCall,
-                                       boolean isUsingAssistedDialing) {
+    private static int getCallFeatures(int videoState, boolean isPulledCall, boolean isStoreHd,
+            boolean isUsingAssistedDialing, boolean isRtt) {
         int features = 0;
         if (VideoProfile.isVideo(videoState)) {
             features |= Calls.FEATURES_VIDEO;
@@ -315,12 +354,34 @@
         if (isPulledCall) {
             features |= Calls.FEATURES_PULLED_EXTERNALLY;
         }
+        if (isStoreHd) {
+            features |= Calls.FEATURES_HD_CALL;
+        }
         if (isUsingAssistedDialing) {
             features |= Calls.FEATURES_ASSISTED_DIALING_USED;
         }
+        if (isRtt) {
+            features |= Calls.FEATURES_RTT;
+        }
         return features;
     }
 
+    private boolean shouldSaveHdInfo(Call call, PhoneAccountHandle accountHandle) {
+        CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle configBundle = null;
+        if (configManager != null) {
+            configBundle = configManager.getConfigForSubId(
+                    mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle));
+        }
+        if (configBundle != null && configBundle.getBoolean(
+                CarrierConfigManager.KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL)
+                && call.wasHighDefAudio()) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Retrieve the phone number from the call, and then process it before returning the
      * actual number that is to be logged.
@@ -414,7 +475,7 @@
             return Calls.addCall(c.callerInfo, c.context, c.number, c.postDialDigits, c.viaNumber,
                     c.presentation, c.callType, c.features, c.accountHandle, c.timestamp,
                     c.durationInSec, c.dataUsage, userToBeInserted == null,
-                    userToBeInserted);
+                    userToBeInserted, c.isRead);
         }
 
 
diff --git a/src/com/android/server/telecom/CallRecordingTonePlayer.java b/src/com/android/server/telecom/CallRecordingTonePlayer.java
new file mode 100644
index 0000000..9b1c4a5
--- /dev/null
+++ b/src/com/android/server/telecom/CallRecordingTonePlayer.java
@@ -0,0 +1,306 @@
+/*
+ * 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.server.telecom;
+
+import android.content.Context;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioRecordingConfiguration;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.Looper;
+import android.telecom.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Plays a periodic, repeating tone to the remote party when an app on the device is recording
+ * a call.  A call recording tone is played on the called party's audio if an app begins recording.
+ * This ensures that the remote party is aware of the fact call recording is in progress.
+ */
+public class CallRecordingTonePlayer extends CallsManagerListenerBase {
+    /**
+     * Callback registered with {@link AudioManager} to track apps which are recording audio.
+     * Registered when a SIM call is added and unregistered when it ends.
+     */
+    private AudioManager.AudioRecordingCallback mAudioRecordingCallback =
+            new AudioManager.AudioRecordingCallback() {
+                @Override
+                public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
+                    synchronized (mLock) {
+                        try {
+                            Log.startSession("CRTP.oRCC");
+                            handleRecordingConfigurationChange(configs);
+                            maybeStartCallAudioTone();
+                            maybeStopCallAudioTone();
+                        } finally {
+                            Log.endSession();
+                        }
+                    }
+                }
+    };
+
+    private final AudioManager mAudioManager;
+    private final Context mContext;
+    private final TelecomSystem.SyncRoot mLock;
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
+    private boolean mIsRecording = false;
+    private MediaPlayer mRecordingTonePlayer = null;
+    private List<Call> mCalls = new ArrayList<>();
+
+    public CallRecordingTonePlayer(Context context, AudioManager audioManager,
+            TelecomSystem.SyncRoot lock) {
+        mContext = context;
+        mAudioManager = audioManager;
+        mLock = lock;
+    }
+
+    @Override
+    public void onCallAdded(Call call) {
+        if (!shouldUseRecordingTone(call)) {
+            return; // Ignore calls which don't use the recording tone.
+        }
+
+        addCall(call);
+    }
+
+    @Override
+    public void onCallRemoved(Call call) {
+        if (!shouldUseRecordingTone(call)) {
+            return; // Ignore calls which don't use the recording tone.
+        }
+
+        removeCall(call);
+    }
+
+    @Override
+    public void onCallStateChanged(Call call, int oldState, int newState) {
+        if (!shouldUseRecordingTone(call)) {
+            return; // Ignore calls which don't use the recording tone.
+        }
+
+        if (mIsRecording) {
+            // Handle start and stop now; could be stopping if we held a call.
+            maybeStartCallAudioTone();
+            maybeStopCallAudioTone();
+        }
+    }
+
+    /**
+     * Handles addition of a new call by:
+     * 1. Registering an audio manager listener to track changes to recording state.
+     * 2. Checking if there is recording in progress.
+     * 3. Potentially starting the call recording tone.
+     *
+     * @param toAdd The call to start tracking.
+     */
+    private void addCall(Call toAdd) {
+        boolean isFirstCall = mCalls.isEmpty();
+
+        mCalls.add(toAdd);
+        if (isFirstCall) {
+            // First call, so register the recording callback.  Also check for recordings which
+            // started before we registered the callback (we don't receive a callback for those).
+            handleRecordingConfigurationChange(mAudioManager.getActiveRecordingConfigurations());
+            mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback,
+                    mMainThreadHandler);
+        }
+
+        maybeStartCallAudioTone();
+    }
+
+    /**
+     * Handles removal of tracked call by unregistering the audio recording callback and stopping
+     * the recording tone if this is the last call.
+     * @param toRemove The call to stop tracking.
+     */
+    private void removeCall(Call toRemove) {
+        mCalls.remove(toRemove);
+        boolean isLastCall = mCalls.isEmpty();
+
+        if (isLastCall) {
+            mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+            maybeStopCallAudioTone();
+        }
+    }
+
+    /**
+     * Determines whether a call is applicable for call recording tone generation.
+     * Only top level sim calls are considered which have
+     * {@link android.telecom.PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set on their target
+     * {@link android.telecom.PhoneAccount}.
+     * @param call The call to check.
+     * @return {@code true} if the call is should use the recording tone, {@code false} otherwise.
+     */
+    private boolean shouldUseRecordingTone(Call call) {
+        return call.getParentCall() == null && !call.isExternalCall() &&
+                !call.isEmergencyCall() && call.isUsingCallRecordingTone();
+    }
+
+    /**
+     * Starts the call recording tone if recording has started and there are calls.
+     */
+    private void maybeStartCallAudioTone() {
+        if (mIsRecording && hasActiveCall()) {
+            startCallRecordingTone(mContext);
+        }
+    }
+
+    /**
+     * Stops the call recording tone if recording has stopped or there are no longer any calls.
+     */
+    private void maybeStopCallAudioTone() {
+        if (!mIsRecording || !hasActiveCall()) {
+            stopCallRecordingTone();
+        }
+    }
+
+    /**
+     * Determines if any of the calls tracked are active.
+     * @return {@code true} if there is an active call, {@code false} otherwise.
+     */
+    private boolean hasActiveCall() {
+        return !mCalls.isEmpty() && mCalls.stream()
+                .filter(call -> call.isActive())
+                .count() > 0;
+    }
+
+    /**
+     * Handles changes to recording configuration changes.
+     * @param configs the recording configurations.
+     */
+    private void handleRecordingConfigurationChange(List<AudioRecordingConfiguration> configs) {
+        if (configs == null) {
+            configs = Collections.emptyList();
+        }
+        boolean wasRecording = mIsRecording;
+        boolean isRecording = isRecordingInProgress(configs);
+        if (wasRecording != isRecording) {
+            mIsRecording = isRecording;
+            if (isRecording) {
+                Log.i(this, "handleRecordingConfigurationChange: recording started");
+            } else {
+                Log.i(this, "handleRecordingConfigurationChange: recording stopped");
+            }
+        }
+    }
+
+    /**
+     * Determines if call recording is potentially in progress.
+     * Excludes from consideration any recordings from packages which have active calls themselves.
+     * Presumably a call with an active recording session is doing so in order to capture the audio
+     * for the purpose of making a call.  In practice Telephony calls don't show up in the
+     * recording configurations, but it is reasonable to consider Connection Managers which are
+     * using an over the top voip solution for calling.
+     * @param configs the ongoing recording configurations.
+     * @return {@code true} if there are active audio recordings for which we want to generate a
+     * call recording tone, {@code false} otherwise.
+     */
+    private boolean isRecordingInProgress(List<AudioRecordingConfiguration> configs) {
+        String recordingPackages = configs.stream()
+                .map(config -> config.getClientPackageName())
+                .collect(Collectors.joining(", "));
+        Log.i(this, "isRecordingInProgress: recordingPackages=%s", recordingPackages);
+        return configs.stream()
+                .filter(config -> !hasCallForPackage(config.getClientPackageName()))
+                .count() > 0;
+    }
+
+    /**
+     * Begins playing the call recording tone to the remote end of the call.
+     * The call recording tone is played via the telephony audio output device; this means that it
+     * will only be audible to the remote end of the call, not the local side.
+     *
+     * @param context required for obtaining media player.
+     */
+    private void startCallRecordingTone(Context context) {
+        if (mRecordingTonePlayer != null) {
+            return;
+        }
+        AudioDeviceInfo telephonyDevice = getTelephonyDevice(mAudioManager);
+        if (telephonyDevice != null) {
+            Log.i(this ,"startCallRecordingTone: playing call recording tone to remote end.");
+            mRecordingTonePlayer = MediaPlayer.create(context, R.raw.record);
+            mRecordingTonePlayer.setLooping(true);
+            mRecordingTonePlayer.setPreferredDevice(telephonyDevice);
+            mRecordingTonePlayer.setVolume(0.1f);
+            mRecordingTonePlayer.start();
+        } else {
+            Log.w(this ,"startCallRecordingTone: can't find telephony audio device.");
+        }
+    }
+
+    /**
+     * Attempts to stop the call recording tone if it is playing.
+     */
+    private void stopCallRecordingTone() {
+        if (mRecordingTonePlayer != null) {
+            Log.i(this ,"stopCallRecordingTone: stopping call recording tone.");
+            mRecordingTonePlayer.stop();
+            mRecordingTonePlayer = null;
+        }
+    }
+
+    /**
+     * Finds the the output device of type {@link AudioDeviceInfo#TYPE_TELEPHONY}.  This device is
+     * the one on which outgoing audio for SIM calls is played.
+     * @param audioManager the audio manage.
+     * @return the {@link AudioDeviceInfo} corresponding to the telephony device, or {@code null}
+     * if none can be found.
+     */
+    private AudioDeviceInfo getTelephonyDevice(AudioManager audioManager) {
+        AudioDeviceInfo[] deviceList = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+        for (AudioDeviceInfo device: deviceList) {
+            if (device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
+                return device;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Determines if any of the known calls belongs to a {@link android.telecom.PhoneAccount} with
+     * the specified package name.
+     * @param packageName The package name.
+     * @return {@code true} if a call exists for this package, {@code false} otherwise.
+     */
+    private boolean hasCallForPackage(String packageName) {
+        return mCalls.stream()
+                .filter(call -> (call.getTargetPhoneAccount() != null &&
+                        call.getTargetPhoneAccount()
+                                .getComponentName().getPackageName().equals(packageName)) ||
+                        (call.getConnectionManagerPhoneAccount() != null &&
+                                call.getConnectionManagerPhoneAccount()
+                                        .getComponentName().getPackageName().equals(packageName)))
+                .count() >= 1;
+    }
+
+    @VisibleForTesting
+    public boolean hasCalls() {
+        return mCalls.size() > 0;
+    }
+
+    @VisibleForTesting
+    public boolean isRecording() {
+        return mIsRecording;
+    }
+}
diff --git a/src/com/android/server/telecom/CallState.java b/src/com/android/server/telecom/CallState.java
index 0aa928f..05ac38e 100644
--- a/src/com/android/server/telecom/CallState.java
+++ b/src/com/android/server/telecom/CallState.java
@@ -16,6 +16,8 @@
 
 package com.android.server.telecom;
 
+import android.telecom.TelecomProtoEnums;
+
 /**
  * Defines call-state constants of the different states in which a call can exist. Although states
  * have the notion of normal transitions, due to the volatile nature of telephony systems, code
@@ -32,20 +34,20 @@
      * not expected to ever interact with NEW calls, but {@link android.telecom.InCallService}s will
      * see calls in this state.
      */
-    public static final int NEW = 0;
+    public static final int NEW = TelecomProtoEnums.NEW; // = 0
 
     /**
      * The initial state of an outgoing {@code Call}.
      * Common transitions are to {@link #DIALING} state for a successful call or
      * {@link #DISCONNECTED} if it failed.
      */
-    public static final int CONNECTING = 1;
+    public static final int CONNECTING = TelecomProtoEnums.CONNECTING; // = 1
 
     /**
      * The state of an outgoing {@code Call} when waiting on user to select a
      * {@link android.telecom.PhoneAccount} through which to place the call.
      */
-    public static final int SELECT_PHONE_ACCOUNT = 2;
+    public static final int SELECT_PHONE_ACCOUNT = TelecomProtoEnums.SELECT_PHONE_ACCOUNT; // = 2
 
     /**
      * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
@@ -53,7 +55,7 @@
      * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
      * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
      */
-    public static final int DIALING = 3;
+    public static final int DIALING = TelecomProtoEnums.DIALING; // = 3
 
     /**
      * Indicates that a call is incoming and the user still has the option of answering, rejecting,
@@ -61,14 +63,14 @@
      * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
      * otherwise.
      */
-    public static final int RINGING = 4;
+    public static final int RINGING = TelecomProtoEnums.RINGING; // = 4
 
     /**
      * Indicates that a call is currently connected to another party and a communication channel is
      * open between them. The normal transition to this state is by the user answering a
      * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
      */
-    public static final int ACTIVE = 5;
+    public static final int ACTIVE = TelecomProtoEnums.ACTIVE; // = 5
 
     /**
      * Indicates that the call is currently on hold. In this state, the call is not terminated
@@ -76,7 +78,7 @@
      * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
      * an action, such as clicking the hold button.
      */
-    public static final int ON_HOLD = 6;
+    public static final int ON_HOLD = TelecomProtoEnums.ON_HOLD; // = 6
 
     /**
      * Indicates that a call is currently disconnected. All states can transition to this state
@@ -85,13 +87,13 @@
      * the disconnection or communication was lost to the call service currently responsible for
      * this call (e.g., call service crashes).
      */
-    public static final int DISCONNECTED = 7;
+    public static final int DISCONNECTED = TelecomProtoEnums.DISCONNECTED; // = 7
 
     /**
      * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
      * time of writing) but cancelled before it was successfully connected.
      */
-    public static final int ABORTED = 8;
+    public static final int ABORTED = TelecomProtoEnums.ABORTED; // = 8
 
     /**
      * Indicates that the call is in the process of being disconnected and will transition next
@@ -101,7 +103,7 @@
      * to the InCall UI for calls where disconnection has been initiated by the user but the
      * ConnectionService has confirmed the call as disconnected.
      */
-    public static final int DISCONNECTING = 9;
+    public static final int DISCONNECTING = TelecomProtoEnums.DISCONNECTING; // = 9
 
     /**
      * Indicates that the call is in the process of being pulled to the local device.
@@ -110,7 +112,7 @@
      * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} and
      * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}.
      */
-    public static final int PULLING = 10;
+    public static final int PULLING = TelecomProtoEnums.PULLING; // = 10
 
     public static String toString(int callState) {
         switch (callState) {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 3e8e3d0..ad7c03d 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -17,10 +17,14 @@
 package com.android.server.telecom;
 
 import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -32,6 +36,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.BlockedNumberContract.SystemContract;
 import android.provider.CallLog.Calls;
 import android.provider.Settings;
 import android.telecom.CallAudioState;
@@ -47,6 +52,7 @@
 import android.telecom.Logging.Runnable;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -57,6 +63,7 @@
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
@@ -65,10 +72,12 @@
 import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
 import com.android.server.telecom.callfiltering.IncomingCallFilter;
 import com.android.server.telecom.components.ErrorDialogActivity;
+import com.android.server.telecom.settings.BlockedNumbersUtil;
 import com.android.server.telecom.ui.ConfirmCallDialogActivity;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -118,6 +127,12 @@
         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
         void onHoldToneRequested(Call call);
         void onExternalCallChanged(Call call, boolean isExternalCall);
+        void onDisconnectedTonePlaying(boolean isTonePlaying);
+    }
+
+    /** Interface used to define the action which is executed delay under some condition. */
+    interface PendingAction {
+        void performAction();
     }
 
     private static final String TAG = "CallsManager";
@@ -231,6 +246,7 @@
     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
     private final InCallController mInCallController;
     private final CallAudioManager mCallAudioManager;
+    private final CallRecordingTonePlayer mCallRecordingTonePlayer;
     private RespondViaSmsManager mRespondViaSmsManager;
     private final Ringer mRinger;
     private final InCallWakeLockController mInCallWakeLockController;
@@ -260,10 +276,28 @@
     private final ClockProxy mClockProxy;
     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
+    private final ConnectionServiceFocusManager mConnectionSvrFocusMgr;
     /* Handler tied to thread in which CallManager was initialized. */
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final EmergencyCallHelper mEmergencyCallHelper;
 
+    private final ConnectionServiceFocusManager.CallsManagerRequester mRequester =
+            new ConnectionServiceFocusManager.CallsManagerRequester() {
+                @Override
+                public void releaseConnectionService(
+                        ConnectionServiceFocusManager.ConnectionServiceFocus connectionService) {
+                    mCalls.stream()
+                            .filter(c -> c.getConnectionServiceWrapper().equals(connectionService))
+                            .forEach(c -> c.disconnect("release " +
+                                    connectionService.getComponentName().getPackageName()));
+                }
+
+                @Override
+                public void setCallsManagerListener(CallsManagerListener listener) {
+                    mListeners.add(listener);
+                }
+            };
+
     private boolean mCanAddCall = true;
 
     private TelephonyManager.MultiSimVariants mRadioSimVariants = null;
@@ -286,9 +320,28 @@
     };
 
     /**
+     * Receiver for enhanced call blocking feature to update the emergency call notification
+     * in below cases:
+     *  1) Carrier config changed.
+     *  2) Blocking suppression state changed.
+     */
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
+                    || SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED.equals(action)) {
+                BlockedNumbersUtil.updateEmergencyCallNotification(context,
+                        SystemContract.shouldShowEmergencyCallNotification(context));
+             }
+        }
+    };
+
+    /**
      * Initializes the required Telecom components.
      */
-    CallsManager(
+    @VisibleForTesting
+    public CallsManager(
             Context context,
             TelecomSystem.SyncRoot lock,
             ContactsAsyncHelper contactsAsyncHelper,
@@ -298,6 +351,8 @@
             HeadsetMediaButtonFactory headsetMediaButtonFactory,
             ProximitySensorManagerFactory proximitySensorManagerFactory,
             InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
+            ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory
+                    connectionServiceFocusManagerFactory,
             CallAudioManager.AudioServiceFactory audioServiceFactory,
             BluetoothRouteManager bluetoothManager,
             WiredHeadsetManager wiredHeadsetManager,
@@ -308,7 +363,9 @@
             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
             EmergencyCallHelper emergencyCallHelper,
             InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory,
-            ClockProxy clockProxy) {
+            ClockProxy clockProxy,
+            BluetoothStateReceiver bluetoothStateReceiver,
+            InCallControllerFactory inCallControllerFactory) {
         mContext = context;
         mLock = lock;
         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -336,7 +393,7 @@
                 wiredHeadsetManager,
                 statusBarNotifier,
                 audioServiceFactory,
-                CallAudioRouteStateMachine.doesDeviceSupportEarpieceRoute()
+                CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT
         );
         callAudioRouteStateMachine.initialize();
 
@@ -353,17 +410,21 @@
         SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
         RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
         SystemVibrator systemVibrator = new SystemVibrator(context);
-        mInCallController = new InCallController(
-                context, mLock, this, systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
+        mInCallController = inCallControllerFactory.create(context, mLock, this,
+                systemStateProvider, defaultDialerCache, mTimeoutsAdapter,
                 emergencyCallHelper);
         mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
                 ringtoneFactory, systemVibrator, mInCallController);
-
+        mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext,
+                (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE), mLock);
         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                 this,new CallAudioModeStateMachine((AudioManager)
                         mContext.getSystemService(Context.AUDIO_SERVICE)),
-                playerFactory, mRinger, new RingbackPlayer(playerFactory), mDtmfLocalTonePlayer);
+                playerFactory, mRinger, new RingbackPlayer(playerFactory),
+                bluetoothStateReceiver, mDtmfLocalTonePlayer);
 
+        mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
+                mRequester, Looper.getMainLooper());
         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
@@ -380,6 +441,7 @@
         mListeners.add(mPhoneStateBroadcaster);
         mListeners.add(mInCallController);
         mListeners.add(mCallAudioManager);
+        mListeners.add(mCallRecordingTonePlayer);
         mListeners.add(missedCallNotifier);
         mListeners.add(mHeadsetMediaButton);
         mListeners.add(mProximitySensorManager);
@@ -390,6 +452,11 @@
         if (userManager.isPrimaryUser()) {
             onUserSwitch(Process.myUserHandle());
         }
+        // Register BroadcastReceiver to handle enhanced call blocking feature related event.
+        IntentFilter intentFilter = new IntentFilter(
+                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
+        context.registerReceiver(mReceiver, intentFilter);
     }
 
     public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -453,7 +520,8 @@
 
         List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();
         filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));
-        filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter()));
+        filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter(),
+                mCallerInfoLookupHelper));
         filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar,
                 mDefaultDialerCache, new ParcelableCallUtils.Converter(), mLock));
         new IncomingCallFilter(mContext, this, incomingCall, mLock,
@@ -726,8 +794,12 @@
      */
     @Override
     public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
-                                    Bundle extras) {
-        requestHandover(call, handoverTo, videoState, extras);
+                                    Bundle extras, boolean isLegacy) {
+        if (isLegacy) {
+            requestHandoverViaEvents(call, handoverTo, videoState, extras);
+        } else {
+            requestHandover(call, handoverTo, videoState, extras);
+        }
     }
 
     @VisibleForTesting
@@ -766,7 +838,20 @@
         return false;
     }
 
-    boolean hasOnlyDisconnectedCalls() {
+    public boolean hasEmergencyRttCall() {
+        for (Call call : mCalls) {
+            if (call.isEmergencyCall() && call.isRttCall()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    public boolean hasOnlyDisconnectedCalls() {
+        if (mCalls.size() == 0) {
+            return false;
+        }
         for (Call call : mCalls) {
             if (!call.isDisconnected()) {
                 return false;
@@ -851,20 +936,19 @@
                 // Self managed calls will always be voip audio mode.
                 call.setIsVoipAudioMode(true);
             } else {
-                // Incoming call is not self-managed, so we need to set extras on it to indicate
-                // whether answering will cause a background self-managed call to drop.
-                if (hasSelfManagedCalls()) {
+                // Incoming call is managed, the active call is self-managed and can't be held.
+                // We need to set extras on it to indicate whether answering will cause a 
+                // active self-managed call to drop.
+                Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+                if (activeCall != null && !canHold(activeCall) && activeCall.isSelfManaged()) {
                     Bundle dropCallExtras = new Bundle();
                     dropCallExtras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
 
                     // Include the name of the app which will drop the call.
-                    Call foregroundCall = getForegroundCall();
-                    if (foregroundCall != null) {
-                        CharSequence droppedApp = foregroundCall.getTargetPhoneAccountLabel();
-                        dropCallExtras.putCharSequence(
-                                Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME, droppedApp);
-                        Log.i(this, "Incoming managed call will drop %s call.", droppedApp);
-                    }
+                    CharSequence droppedApp = activeCall.getTargetPhoneAccountLabel();
+                    dropCallExtras.putCharSequence(
+                            Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME, droppedApp);
+                    Log.i(this, "Incoming managed call will drop %s call.", droppedApp);
                     call.putExtras(Call.SOURCE_CONNECTION_SERVICE, dropCallExtras);
                 }
             }
@@ -875,11 +959,13 @@
                 call.setIsVoipAudioMode(true);
             }
         }
-        if (extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
-            if (phoneAccount != null &&
-                    phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
-                call.setRttStreams(true);
-            }
+        if (isRttSettingOn() ||
+                extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+            Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn());
+            call.createRttStreams();
+            // Even if the phone account doesn't support RTT yet, the connection manager might
+            // change that. Set this to check it later.
+            call.setRequestedToStartWithRtt();
         }
         // If the extras specifies a video state, set it on the call if the PhoneAccount supports
         // video.
@@ -907,7 +993,9 @@
                 final String handleScheme = handle.getSchemeSpecificPart();
                 Call fromCall = mCalls.stream()
                         .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber(
-                                c.getHandle().getSchemeSpecificPart(), handleScheme))
+                                (c.getHandle() == null
+                                        ? null : c.getHandle().getSchemeSpecificPart()),
+                                handleScheme))
                         .findFirst()
                         .orElse(null);
                 if (fromCall != null) {
@@ -1026,13 +1114,15 @@
      * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
      * @param originalIntent
      */
-    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
+    @VisibleForTesting
+    public Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
             UserHandle initiatingUser, Intent originalIntent) {
         boolean isReusedCall = true;
         Call call = reuseOutgoingCall(handle);
 
         PhoneAccount account =
                 mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
+        boolean isSelfManaged = account != null && account.isSelfManaged();
 
         // Create a call with original handle. The handle may be changed when the call is attached
         // to a connection service, but in most cases will remain the same.
@@ -1058,22 +1148,20 @@
             // will be overridden when the actual connection is returned in startCreateConnection,
             // however doing this now ensures the logs and any other logic will treat this call as
             // self-managed from the moment it is created.
-            if (account != null) {
-                call.setIsSelfManaged(account.isSelfManaged());
-                if (call.isSelfManaged()) {
-                    // Self-managed calls will ALWAYS use voip audio mode.
-                    call.setIsVoipAudioMode(true);
-                }
+            call.setIsSelfManaged(isSelfManaged);
+            if (isSelfManaged) {
+                // Self-managed calls will ALWAYS use voip audio mode.
+                call.setIsVoipAudioMode(true);
             }
-
             call.setInitiatingUser(initiatingUser);
             isReusedCall = false;
         }
 
+        int videoState = VideoProfile.STATE_AUDIO_ONLY;
         if (extras != null) {
             // Set the video state on the call early so that when it is added to the InCall UI the
             // UI knows to configure itself as a video call immediately.
-            int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+            videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                     VideoProfile.STATE_AUDIO_ONLY);
 
             // If this is an emergency video call, we need to check if the phone account supports
@@ -1101,44 +1189,13 @@
             call.setVideoState(videoState);
         }
 
-        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
-                phoneAccountHandle, initiatingUser);
-        boolean isSelfManaged = targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();
-
-        List<PhoneAccountHandle> accounts;
-        if (!isSelfManaged) {
-            accounts = constructPossiblePhoneAccounts(handle, initiatingUser);
-            Log.v(this, "startOutgoingCall found accounts = " + accounts);
-
-            // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this
-            // call as if a phoneAccount was not specified (does the default behavior instead).
-            // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
-            if (phoneAccountHandle != null) {
-                if (!accounts.contains(phoneAccountHandle)) {
-                    phoneAccountHandle = null;
-                }
-            }
-
-            if (phoneAccountHandle == null && accounts.size() > 0) {
-                // No preset account, check if default exists that supports the URI scheme for the
-                // handle and verify it can be used.
-                if (accounts.size() > 1) {
-                    PhoneAccountHandle defaultPhoneAccountHandle =
-                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
-                                    handle.getScheme(), initiatingUser);
-                    if (defaultPhoneAccountHandle != null &&
-                            accounts.contains(defaultPhoneAccountHandle)) {
-                        phoneAccountHandle = defaultPhoneAccountHandle;
-                    }
-                } else {
-                    // Use the only PhoneAccount that is available
-                    phoneAccountHandle = accounts.get(0);
-                }
-            }
+        List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount(
+                phoneAccountHandle, handle, VideoProfile.isVideo(videoState), initiatingUser);
+        if (potentialPhoneAccounts.size() == 1) {
+            phoneAccountHandle = potentialPhoneAccounts.get(0);
         } else {
-            accounts = Collections.EMPTY_LIST;
+            phoneAccountHandle = null;
         }
-
         call.setTargetPhoneAccount(phoneAccountHandle);
 
         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged;
@@ -1147,27 +1204,35 @@
         // a call, or cancel this call altogether. If a call is being reused, then it has already
         // passed the makeRoomForOutgoingCall check once and will fail the second time due to the
         // call transitioning into the CONNECTING state.
-        if (!isSelfManaged && !isPotentialInCallMMICode && (!isReusedCall &&
-                !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
-            // just cancel at this point.
-            Log.i(this, "No remaining room for outgoing call: %s", call);
-            if (mCalls.contains(call)) {
-                // This call can already exist if it is a reused call,
-                // See {@link #reuseOutgoingCall}.
-                call.disconnect();
+        if (!isPotentialInCallMMICode && (!isReusedCall
+                && !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
+            Call foregroundCall = getForegroundCall();
+            Log.d(this, "No more room for outgoing call %s ", call);
+            if (foregroundCall.isSelfManaged()) {
+                // If the ongoing call is a self-managed call, then prompt the user to ask if they'd
+                // like to disconnect their ongoing call and place the outgoing call.
+                call.setOriginalCallIntent(originalIntent);
+                startCallConfirmation(call);
+            } else {
+                // If the ongoing call is a managed call, we will prevent the outgoing call from
+                // dialing.
+                notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
             }
             return null;
         }
 
-        boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
-                !call.isEmergencyCall() && !isSelfManaged;
+        // The outgoing call can be placed, go forward.
 
+        boolean needsAccountSelection =
+                phoneAccountHandle == null && potentialPhoneAccounts.size() > 1
+                        && !call.isEmergencyCall() && !isSelfManaged;
         if (needsAccountSelection) {
             // This is the state where the user is expected to select an account
             call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
             // Create our own instance to modify (since extras may be Bundle.EMPTY)
             extras = new Bundle(extras);
-            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
+            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS,
+                    potentialPhoneAccounts);
         } else {
             PhoneAccount accountToUse =
                     mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
@@ -1183,25 +1248,30 @@
             call.setState(
                     CallState.CONNECTING,
                     phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
-            if (extras != null
-                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+
+            boolean isVoicemail = (call.getHandle() != null)
+                    && (PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())
+                    || (accountToUse != null && mPhoneAccountRegistrar.isVoiceMailNumber(
+                    accountToUse.getAccountHandle(), call.getHandle().getSchemeSpecificPart())));
+
+            if (!isVoicemail && (isRttSettingOn() || (extras != null
+                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)))) {
+                Log.d(this, "Outgoing call requesting RTT, rtt setting is %b", isRttSettingOn());
                 if (accountToUse != null
                         && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
-                    call.setRttStreams(true);
+                    call.createRttStreams();
                 }
+                // Even if the phone account doesn't support RTT yet, the connection manager might
+                // change that. Set this to check it later.
+                call.setRequestedToStartWithRtt();
             }
         }
         setIntentExtrasAndStartTime(call, extras);
+        setCallSourceToAnalytics(call, originalIntent);
 
-        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode)
-                && !needsAccountSelection) {
+        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
             // Do not add the call if it is a potential MMI code.
             call.addListener(this);
-        } else if (!isSelfManaged && hasSelfManagedCalls() && !call.isEmergencyCall()) {
-            // Adding a managed call and there are ongoing self-managed call(s).
-            call.setOriginalCallIntent(originalIntent);
-            startCallConfirmation(call);
-            return null;
         } else if (!mCalls.contains(call)) {
             // We check if mCalls already contains the call because we could potentially be reusing
             // a call which was previously added (See {@link #reuseOutgoingCall}).
@@ -1212,6 +1282,91 @@
     }
 
     /**
+     * Finds the {@link PhoneAccountHandle}(s) which could potentially be used to place an outgoing
+     * call.  Takes into account the following:
+     * 1. Any pre-chosen {@link PhoneAccountHandle} which was specified on the
+     * {@link Intent#ACTION_CALL} intent.  If one was chosen it will be used if possible.
+     * 2. Whether the call is a video call.  If the call being placed is a video call, an attempt is
+     * first made to consider video capable phone accounts.  If no video capable phone accounts are
+     * found, the usual non-video capable phone accounts will be considered.
+     * 3. Whether there is a user-chosen default phone account; that one will be used if possible.
+     *
+     * @param targetPhoneAccountHandle The pre-chosen {@link PhoneAccountHandle} passed in when the
+     *                                 call was placed.  Will be {@code null} if the
+     *                                 {@link Intent#ACTION_CALL} intent did not specify a target
+     *                                 phone account.
+     * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching
+     *               phone accounts.
+     * @param isVideo {@code true} if the call is a video call, {@code false} otherwise.
+     * @param initiatingUser The {@link UserHandle} the call is placed on.
+     * @return
+     */
+    @VisibleForTesting
+    public List<PhoneAccountHandle> findOutgoingCallPhoneAccount(
+            PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo,
+            UserHandle initiatingUser) {
+        boolean isSelfManaged = isSelfManaged(targetPhoneAccountHandle, initiatingUser);
+
+        List<PhoneAccountHandle> accounts;
+        if (!isSelfManaged) {
+            // Try to find a potential phone account, taking into account whether this is a video
+            // call.
+            accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo);
+            if (isVideo && accounts.size() == 0) {
+                // Placing a video call but no video capable accounts were found, so consider any
+                // call capable accounts (we can fallback to audio).
+                accounts = constructPossiblePhoneAccounts(handle, initiatingUser,
+                        false /* isVideo */);
+            }
+            Log.v(this, "findOutgoingCallPhoneAccount: accounts = " + accounts);
+
+            // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this
+            // call as if a phoneAccount was not specified (does the default behavior instead).
+            // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
+            if (targetPhoneAccountHandle != null) {
+                if (!accounts.contains(targetPhoneAccountHandle)) {
+                    targetPhoneAccountHandle = null;
+                } else {
+                    // The target phone account is valid and was found.
+                    return Arrays.asList(targetPhoneAccountHandle);
+                }
+            }
+
+            if (targetPhoneAccountHandle == null && accounts.size() > 0) {
+                // No preset account, check if default exists that supports the URI scheme for the
+                // handle and verify it can be used.
+                if (accounts.size() > 1) {
+                    PhoneAccountHandle defaultPhoneAccountHandle =
+                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
+                                    handle.getScheme(), initiatingUser);
+                    if (defaultPhoneAccountHandle != null &&
+                            accounts.contains(defaultPhoneAccountHandle)) {
+                        accounts.clear();
+                        accounts.add(defaultPhoneAccountHandle);
+                    }
+                }
+            }
+        } else {
+            // Self-managed ConnectionServices can only have a single potential account.
+            accounts = Arrays.asList(targetPhoneAccountHandle);
+        }
+        return accounts;
+    }
+
+    /**
+     * Determines if a {@link PhoneAccountHandle} is for a self-managed ConnectionService.
+     * @param targetPhoneAccountHandle The phone account to check.
+     * @param initiatingUser The user associated with the account.
+     * @return {@code true} if the phone account is self-managed, {@code false} otherwise.
+     */
+    public boolean isSelfManaged(PhoneAccountHandle targetPhoneAccountHandle,
+            UserHandle initiatingUser) {
+        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
+                targetPhoneAccountHandle, initiatingUser);
+        return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged();
+    }
+
+    /**
      * Attempts to issue/connect the specified call.
      *
      * @param handle Handle to connect the call with.
@@ -1268,23 +1423,23 @@
                 com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
         final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
                 call.getTargetPhoneAccount());
+        final String callHandleScheme =
+                call.getHandle() == null ? null : call.getHandle().getScheme();
         if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
             // If the account has been set, proceed to place the outgoing call.
             // Otherwise the connection will be initiated when the account is set by the user.
             if (call.isSelfManaged() && !isOutgoingCallPermitted) {
                 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
-            } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) {
-                markCallDisconnectedDueToSelfManagedCall(call);
             } else {
                 if (call.isEmergencyCall()) {
-                    // Disconnect all self-managed calls to make priority for emergency call.
-                    disconnectSelfManagedCalls();
+                    // Drop any ongoing self-managed calls to make way for an emergency call.
+                    disconnectSelfManagedCalls("place emerg call" /* reason */);
                 }
 
                 call.startCreateConnection(mPhoneAccountRegistrar);
             }
         } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
-                requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false,
+                requireCallCapableAccountByHandle ? callHandleScheme : null, false,
                 call.getInitiatingUser()).isEmpty()) {
             // If there are no call capable accounts, disconnect the call.
             markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
@@ -1317,60 +1472,27 @@
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to answer a non-existent call %s", call);
         } else {
-            Call foregroundCall = getForegroundCall();
-            // If the foreground call is not the ringing call and it is currently isActive() or
-            // STATE_DIALING, put it on hold before answering the call.
-            if (foregroundCall != null && foregroundCall != call &&
-                    (foregroundCall.isActive() ||
-                     foregroundCall.getState() == CallState.DIALING ||
-                     foregroundCall.getState() == CallState.PULLING)) {
-                if (!foregroundCall.getTargetPhoneAccount().equals(
-                                call.getTargetPhoneAccount()) &&
-                        ((call.isSelfManaged() != foregroundCall.isSelfManaged()) ||
-                         call.isSelfManaged())) {
-                    // The foreground call is from another connection service, and either:
-                    // 1. FG call's managed state doesn't match that of the incoming call.
-                    //    E.g. Incoming is self-managed and FG is managed, or incoming is managed
-                    //    and foreground is self-managed.
-                    // 2. The incoming call is self-managed.
-                    //    E.g. The incoming call is
-                    Log.i(this, "Answering call from %s CS; disconnecting calls from %s CS.",
-                            foregroundCall.isSelfManaged() ? "selfMg" : "mg",
-                            call.isSelfManaged() ? "selfMg" : "mg");
-                    disconnectOtherCalls(call.getTargetPhoneAccount());
-                } else if (0 == (foregroundCall.getConnectionCapabilities()
-                        & Connection.CAPABILITY_HOLD)) {
-                    // This call does not support hold.  If it is from a different connection
-                    // service, then disconnect it, otherwise allow the connection service to
-                    // figure out the right states.
-                    if (foregroundCall.getConnectionService() != call.getConnectionService()) {
-                        foregroundCall.disconnect();
-                    }
-                } else {
-                    Call heldCall = getHeldCall();
-                    if (heldCall != null) {
-                        Log.i(this, "Disconnecting held call %s before holding active call.",
-                                heldCall);
-                        heldCall.disconnect();
-                    }
+            // Hold or disconnect the active call and request call focus for the incoming call.
+            Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+            Log.d(this, "Incoming call = %s Ongoing call %s", call, activeCall);
+            holdActiveCallForNewCall(call);
+            mConnectionSvrFocusMgr.requestFocus(
+                    call,
+                    new RequestCallback(new ActionAnswerCall(call, videoState)));
+        }
+    }
 
-                    foregroundCall.hold();
-                }
-                // TODO: Wait until we get confirmation of the active call being
-                // on-hold before answering the new call.
-                // TODO: Import logic from CallManager.acceptCall()
-            }
-
-            for (CallsManagerListener listener : mListeners) {
-                listener.onIncomingCallAnswered(call);
-            }
-
-            // We do not update the UI until we get confirmation of the answer() through
-            // {@link #markCallAsActive}.
-            call.answer(videoState);
-            if (isSpeakerphoneAutoEnabledForVideoCalls(videoState)) {
-                call.setStartWithSpeakerphoneOn(true);
-            }
+    /**
+     * Instructs Telecom to deflect the specified call. Intended to be invoked by the in-call
+     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
+     * the user opting to deflect said call.
+     */
+    @VisibleForTesting
+    public void deflectCall(Call call, Uri address) {
+        if (!mCalls.contains(call)) {
+            Log.i(this, "Request to deflect a non-existent call %s", call);
+        } else {
+            call.deflect(address);
         }
     }
 
@@ -1440,8 +1562,12 @@
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
         } else {
-            call.playDtmfTone(digit);
-            mDtmfLocalTonePlayer.playTone(call, digit);
+            if (call.getState() != CallState.ON_HOLD) {
+                call.playDtmfTone(digit);
+                mDtmfLocalTonePlayer.playTone(call, digit);
+            } else {
+                Log.i(this, "Request to play DTMF tone for held call %s", call.getId());
+            }
         }
     }
 
@@ -1536,24 +1662,40 @@
         if (!mCalls.contains(call)) {
             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
         } else {
-            boolean otherCallHeld = false;
-            Log.d(this, "unholding call: (%s)", call);
-            for (Call c : mCalls) {
-                // Only attempt to hold parent calls and not the individual children.
-                if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
-                    otherCallHeld = true;
-                    Log.addEvent(c, LogUtils.Events.SWAP);
-                    c.hold();
+            Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+            String activeCallId = null;
+            if (activeCall != null) {
+                activeCallId = activeCall.getId();
+                if (canHold(activeCall)) {
+                    activeCall.hold("Swap to " + call.getId());
+                    Log.addEvent(activeCall, LogUtils.Events.SWAP, "To " + call.getId());
+                    Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId());
+                } else {
+                    // This call does not support hold. If it is from a different connection
+                    // service, then disconnect it, otherwise invoke call.hold() and allow the
+                    // connection service to handle the situation.
+                    if (activeCall.getConnectionService() != call.getConnectionService()) {
+                        activeCall.disconnect("Swap to " + call.getId());
+                    } else {
+                        activeCall.hold("Swap to " + call.getId());
+                    }
                 }
             }
-            if (otherCallHeld) {
-                Log.addEvent(call, LogUtils.Events.SWAP);
-            }
-            call.unhold();
+            mConnectionSvrFocusMgr.requestFocus(
+                    call,
+                    new RequestCallback(new ActionUnHoldCall(call, activeCallId)));
         }
     }
 
     @Override
+    public void onExtrasRemoved(Call c, int source, List<String> keys) {
+        if (source != Call.SOURCE_CONNECTION_SERVICE) {
+            return;
+        }
+        updateCanAddCall();
+    }
+
+    @Override
     public void onExtrasChanged(Call c, int source, Bundle extras) {
         if (source != Call.SOURCE_CONNECTION_SERVICE) {
             return;
@@ -1566,12 +1708,17 @@
     // Construct the list of possible PhoneAccounts that the outgoing call can use based on the
     // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,
     // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
-    private List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user) {
+    @VisibleForTesting
+    public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
+            boolean isVideo) {
         if (handle == null) {
             return Collections.emptyList();
         }
+        // If we're specifically looking for video capable accounts, then include that capability,
+        // otherwise specify no additional capability constraints.
         List<PhoneAccountHandle> allAccounts =
-                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user);
+                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user,
+                        isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0 /* any */);
         // First check the Radio SIM Technology
         if(mRadioSimVariants == null) {
             TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
@@ -1640,6 +1787,10 @@
 
     /** Called by the in-call UI to change the mute state. */
     void mute(boolean shouldMute) {
+        if (hasEmergencyCall() && shouldMute) {
+            Log.i(this, "Refusing to turn on mute because we're in an emergency call");
+            shouldMute = false;
+        }
         mCallAudioManager.mute(shouldMute);
     }
 
@@ -1647,8 +1798,13 @@
       * Called by the in-call UI to change the audio route, for example to change from earpiece to
       * speaker phone.
       */
-    void setAudioRoute(int route) {
-        mCallAudioManager.setAudioRoute(route);
+    void setAudioRoute(int route, String bluetoothAddress) {
+        if (hasEmergencyRttCall() && route != CallAudioState.ROUTE_SPEAKER) {
+            Log.i(this, "In an emergency RTT call. Forcing route to speaker.");
+            route = CallAudioState.ROUTE_SPEAKER;
+            bluetoothAddress = null;
+        }
+        mCallAudioManager.setAudioRoute(route, bluetoothAddress);
     }
 
     /** Called by the in-call UI to turn the proximity sensor on. */
@@ -1665,6 +1821,11 @@
         mProximitySensorManager.turnOff(screenOnImmediately);
     }
 
+    private boolean isRttSettingOn() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+    }
+
     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Attempted to add account to unknown call %s", call);
@@ -1678,12 +1839,24 @@
                 Log.d("phoneAccountSelected: default to voip mode for call %s", call.getId());
                 call.setIsVoipAudioMode(true);
             }
-            if (call.getIntentExtras()
-                    .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
+
+            boolean isVoicemail = (call.getHandle() != null)
+                    && (PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())
+                    || (realPhoneAccount != null && mPhoneAccountRegistrar.isVoiceMailNumber(
+                    realPhoneAccount.getAccountHandle(),
+                    call.getHandle().getSchemeSpecificPart())));
+
+            if (!isVoicemail && (isRttSettingOn() || call.getIntentExtras()
+                    .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false))) {
+                Log.d(this, "Outgoing call after account selection requesting RTT," +
+                        " rtt setting is %b", isRttSettingOn());
                 if (realPhoneAccount != null
                         && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
-                    call.setRttStreams(true);
+                    call.createRttStreams();
                 }
+                // Even if the phone account doesn't support RTT yet, the connection manager might
+                // change that. Set this to check it later.
+                call.setRequestedToStartWithRtt();
             }
 
             if (!call.isNewOutgoingCallIntentBroadcastDone()) {
@@ -1695,7 +1868,7 @@
             if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
                 call.startCreateConnection(mPhoneAccountRegistrar);
             } else {
-                call.disconnect();
+                call.disconnect("no room");
             }
 
             if (setDefault) {
@@ -1715,6 +1888,21 @@
         }
     }
 
+    /**
+     * Called when disconnect tone is started or stopped, including any InCallTone
+     * after disconnected call.
+     *
+     * @param isTonePlaying true if the disconnected tone is started, otherwise the disconnected
+     * tone is stopped.
+     */
+    @VisibleForTesting
+    public void onDisconnectedTonePlaying(boolean isTonePlaying) {
+        Log.v(this, "onDisconnectedTonePlaying, %s", isTonePlaying ? "started" : "stopped");
+        for (CallsManagerListener listener : mListeners) {
+            listener.onDisconnectedTonePlaying(isTonePlaying);
+        }
+    }
+
     void markCallAsRinging(Call call) {
         setCallState(call, CallState.RINGING, "ringing set explicitly");
     }
@@ -1722,6 +1910,8 @@
     void markCallAsDialing(Call call) {
         setCallState(call, CallState.DIALING, "dialing set explicitly");
         maybeMoveToSpeakerPhone(call);
+        maybeTurnOffMute(call);
+        ensureCallAudible();
     }
 
     void markCallAsPulling(Call call) {
@@ -1729,12 +1919,76 @@
         maybeMoveToSpeakerPhone(call);
     }
 
-    void markCallAsActive(Call call) {
-        setCallState(call, CallState.ACTIVE, "active set explicitly");
-        maybeMoveToSpeakerPhone(call);
+    /**
+     * Returns true if the active call is held.
+     */
+    boolean holdActiveCallForNewCall(Call call) {
+        Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+        if (activeCall != null && activeCall != call) {
+            if (canHold(activeCall)) {
+                activeCall.hold();
+                return true;
+            } else if (supportsHold(activeCall)
+                    && activeCall.getConnectionService() == call.getConnectionService()) {
+
+                // Handle the case where the active call and the new call are from the same CS, and
+                // the currently active call supports hold but cannot currently be held.
+                // In this case we'll look for the other held call for this connectionService and
+                // disconnect it prior to holding the active call.
+                // E.g.
+                // Call A - Held   (Supports hold, can't hold)
+                // Call B - Active (Supports hold, can't hold)
+                // Call C - Incoming
+                // Here we need to disconnect A prior to holding B so that C can be answered.
+                // This case is driven by telephony requirements ultimately.
+                Call heldCall = getHeldCallByConnectionService(call.getConnectionService());
+                if (heldCall != null) {
+                    heldCall.disconnect();
+                    Log.i(this, "holdActiveCallForNewCall: Disconnect held call %s before "
+                                    + "holding active call %s.",
+                            heldCall.getId(), activeCall.getId());
+                }
+                Log.i(this, "holdActiveCallForNewCall: Holding active %s before making %s active.",
+                        activeCall.getId(), call.getId());
+                activeCall.hold();
+                return true;
+            } else {
+                // This call does not support hold. If it is from a different connection
+                // service, then disconnect it, otherwise allow the connection service to
+                // figure out the right states.
+                if (activeCall.getConnectionService() != call.getConnectionService()) {
+                    Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be "
+                            + "made active.", activeCall.getId(), call.getId());
+                    activeCall.disconnect();
+                }
+            }
+        }
+        return false;
     }
 
-    void markCallAsOnHold(Call call) {
+    @VisibleForTesting
+    public void markCallAsActive(Call call) {
+        if (call.isSelfManaged()) {
+            // backward compatibility, the self-managed connection service will set the call state
+            // to active directly. We should hold or disconnect the current active call based on the
+            // holdability, and request the call focus for the self-managed call before the state
+            // change.
+            holdActiveCallForNewCall(call);
+            mConnectionSvrFocusMgr.requestFocus(
+                    call,
+                    new RequestCallback(new ActionSetCallState(
+                            call,
+                            CallState.ACTIVE,
+                            "active set explicitly for self-managed")));
+        } else {
+            setCallState(call, CallState.ACTIVE, "active set explicitly");
+            maybeMoveToSpeakerPhone(call);
+            ensureCallAudible();
+        }
+    }
+
+    @VisibleForTesting
+    public void markCallAsOnHold(Call call) {
         setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
     }
 
@@ -1864,7 +2118,7 @@
                     Log.addEvent(callToHangup, LogUtils.Events.INFO,
                             "media btn short press - end call.");
                     if (callToHangup != null) {
-                        callToHangup.disconnect();
+                        disconnectCall(callToHangup);
                         return true;
                     }
                 } else {
@@ -1952,6 +2206,14 @@
         return getFirstCallWithState(CallState.ON_HOLD);
     }
 
+    public Call getHeldCallByConnectionService(ConnectionServiceWrapper connSvr) {
+        Optional<Call> heldCall = mCalls.stream()
+                .filter(call -> call.getConnectionService() == connSvr
+                        && call.getState() == CallState.ON_HOLD)
+                .findFirst();
+        return heldCall.isPresent() ? heldCall.get() : null;
+    }
+
     @VisibleForTesting
     public int getNumHeldCalls() {
         int count = 0;
@@ -2131,7 +2393,8 @@
      *
      * @param call The call to add.
      */
-    private void addCall(Call call) {
+    @VisibleForTesting
+    public void addCall(Call call) {
         Trace.beginSection("addCall");
         Log.v(this, "addCall(%s)", call);
         call.addListener(this);
@@ -2204,6 +2467,12 @@
         Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
                 CallState.toString(newState), call);
         if (newState != oldState) {
+            // If the call switches to held state while a DTMF tone is playing, stop the tone to
+            // ensure that the tone generator stops playing the tone.
+            if (newState == CallState.ON_HOLD && call.isDtmfTonePlaying()) {
+                stopDtmfTone(call);
+            }
+
             // Unfortunately, in the telephony world the radio is king. So if the call notifies
             // us that the call is in a particular state, we allow it even if it doesn't make
             // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
@@ -2294,8 +2563,11 @@
         // completed; this allows the InCallService to be notified that a handover it
         // initiated completed.
         call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null);
+        call.onHandoverComplete();
+
         // Inform the "to" ConnectionService that handover to it has completed.
         handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null);
+        handoverTo.onHandoverComplete();
         answerCall(handoverTo, handoverTo.getVideoState());
         call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE);
 
@@ -2309,15 +2581,17 @@
     private void rejectHandoverTo(Call handoverTo) {
         Call handoverFrom = handoverTo.getHandoverSourceCall();
         Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId());
-        Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s",
+        Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected",
                 handoverTo.getId(), handoverFrom.getId());
-        Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s",
+        Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected",
                 handoverTo.getId(), handoverFrom.getId());
 
         // Inform the "from" Call (ie the source call) that the handover from it has
         // failed; this allows the InCallService to be notified that a handover it
         // initiated failed.
         handoverFrom.onConnectionEvent(Connection.EVENT_HANDOVER_FAILED, null);
+        handoverFrom.onHandoverFailed(android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED);
+
         // Inform the "to" ConnectionService that handover to it has failed.  This
         // allows the ConnectionService the call was being handed over
         if (handoverTo.getConnectionService() != null) {
@@ -2325,6 +2599,8 @@
             // early on in the handover process, the CS will be unbound and we won't be
             // able to send the call event.
             handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null);
+            handoverTo.getConnectionService().handoverFailed(handoverTo,
+                    android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED);
         }
         handoverTo.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_FAILED);
     }
@@ -2333,7 +2609,9 @@
         Call handoverFrom = handoverTo.getHandoverSourceCall();
         Log.i(this, "acceptHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId());
         handoverTo.setHandoverState(HandoverState.HANDOVER_ACCEPTED);
+        handoverTo.onHandoverComplete();
         handoverFrom.setHandoverState(HandoverState.HANDOVER_ACCEPTED);
+        handoverFrom.onHandoverComplete();
 
         Log.addEvent(handoverTo, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s",
                 handoverFrom.getId(), handoverTo.getId());
@@ -2445,6 +2723,11 @@
         return (int) callsStream.count();
     }
 
+    private boolean hasMaximumLiveCalls(Call exceptCall) {
+        return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
+                exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES);
+    }
+
     private boolean hasMaximumManagedLiveCalls(Call exceptCall) {
         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(false /* isSelfManaged */,
                 exceptCall, null /* phoneAccountHandle */, LIVE_CALL_STATES);
@@ -2472,6 +2755,11 @@
                 phoneAccountHandle, CallState.RINGING);
     }
 
+    private boolean hasMaximumOutgoingCalls(Call exceptCall) {
+        return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL,
+                exceptCall, null /* phoneAccountHandle */, OUTGOING_CALL_STATES);
+    }
+
     private boolean hasMaximumManagedOutgoingCalls(Call exceptCall) {
         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall,
                 null /* phoneAccountHandle */, OUTGOING_CALL_STATES);
@@ -2559,7 +2847,7 @@
     }
 
     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
-        if (hasMaximumManagedLiveCalls(call)) {
+        if (hasMaximumLiveCalls(call)) {
             // NOTE: If the amount of live calls changes beyond 1, this logic will probably
             // have to change.
             Call liveCall = getFirstCallWithState(LIVE_CALL_STATES);
@@ -2573,7 +2861,7 @@
                 return true;
             }
 
-            if (hasMaximumManagedOutgoingCalls(call)) {
+            if (hasMaximumOutgoingCalls(call)) {
                 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
                 if (isEmergency && !outgoingCall.isEmergencyCall()) {
                     // Disconnect the current outgoing call if it's not an emergency call. If the
@@ -2595,21 +2883,16 @@
                 return false;
             }
 
-            if (hasMaximumManagedHoldingCalls(call)) {
-                // There is no more room for any more calls, unless it's an emergency.
-                if (isEmergency) {
-                    // Kill the current active call, this is easier then trying to disconnect a
-                    // holding call and hold an active call.
-                    call.getAnalytics().setCallIsAdditional(true);
-                    liveCall.getAnalytics().setCallIsInterrupted(true);
-                    liveCall.disconnect();
-                    return true;
-                }
-                return false;  // No more room!
+            // If we have the max number of held managed calls and we're placing an emergency call,
+            // we'll disconnect the ongoing call if it cannot be held.
+            if (hasMaximumManagedHoldingCalls(call) && isEmergency && !canHold(liveCall)) {
+                call.getAnalytics().setCallIsAdditional(true);
+                liveCall.getAnalytics().setCallIsInterrupted(true);
+                liveCall.disconnect("disconnecting to make room for emergency call "
+                        + call.getId());
+                return true;
             }
 
-            // We have room for at least one more holding call at this point.
-
             // TODO: Remove once b/23035408 has been corrected.
             // If the live call is a conference, it will not have a target phone account set.  This
             // means the check to see if the live call has the same target phone account as the new
@@ -2645,11 +2928,11 @@
             }
 
             // Try to hold the live call before attempting the new outgoing call.
-            if (liveCall.can(Connection.CAPABILITY_HOLD)) {
+            if (canHold(liveCall)) {
                 Log.i(this, "makeRoomForOutgoingCall: holding live call.");
                 call.getAnalytics().setCallIsAdditional(true);
                 liveCall.getAnalytics().setCallIsInterrupted(true);
-                liveCall.hold();
+                liveCall.hold("calling " + call.getId());
                 return true;
             }
 
@@ -2685,12 +2968,34 @@
             return;
         }
         if (call.getStartWithSpeakerphoneOn()) {
-            setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+            setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
             call.setStartWithSpeakerphoneOn(false);
         }
     }
 
     /**
+     * Checks to see if the call is an emergency call and if so, turn off mute.
+     */
+    private void maybeTurnOffMute(Call call) {
+        if (call.isEmergencyCall()) {
+            mute(false);
+        }
+    }
+
+    private void ensureCallAudible() {
+        AudioManager am = mContext.getSystemService(AudioManager.class);
+        if (am == null) {
+            Log.w(this, "ensureCallAudible: audio manager is null");
+            return;
+        }
+        if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) {
+            Log.i(this, "ensureCallAudible: voice call stream has volume 0. Adjusting to default.");
+            am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
+                    AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0);
+        }
+    }
+
+    /**
      * Creates a new call for an existing connection.
      *
      * @param callId The id of the new call.
@@ -2890,16 +3195,33 @@
                     !hasMaximumManagedLiveCalls(excludeCall) &&
                     !hasMaximumManagedHoldingCalls(excludeCall);
         } else {
-            // Only permit outgoing calls if there is no ongoing emergency calls and all other calls
-            // are associated with the current PhoneAccountHandle.
-            return !hasEmergencyCall() && (
-                    (excludeCall != null && excludeCall.getHandoverSourceCall() != null) || (
-                            !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle)
-                                    && !hasCallsForOtherPhoneAccount(phoneAccountHandle)
-                                    && !hasManagedCalls()));
+            // Only permit self-managed outgoing calls if
+            // 1. there is no emergency ongoing call
+            // 2. The outgoing call is an handover call or it not hit the self-managed call limit
+            // and the current active call can be held.
+            Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+            return !hasEmergencyCall() &&
+                    ((excludeCall != null && excludeCall.getHandoverSourceCall() != null) ||
+                            (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) &&
+                                    (activeCall == null || canHold(activeCall))));
         }
     }
 
+    public boolean isReplyWithSmsAllowed(int uid) {
+        UserHandle callingUser = UserHandle.of(UserHandle.getUserId(uid));
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
+
+        boolean isUserRestricted = userManager != null
+                && userManager.hasUserRestriction(UserManager.DISALLOW_SMS, callingUser);
+        boolean isLockscreenRestricted = keyguardManager != null
+                && keyguardManager.isDeviceLocked();
+        Log.d(this, "isReplyWithSmsAllowed: isUserRestricted: %s, isLockscreenRestricted: %s",
+                isUserRestricted, isLockscreenRestricted);
+
+        // TODO(hallliu): actually check the lockscreen once b/77731473 is fixed
+        return !isUserRestricted;
+    }
     /**
      * Blocks execution until all Telecom handlers have completed their current work.
      */
@@ -2937,7 +3259,7 @@
 
             // We are going to place the new outgoing call, so disconnect any ongoing self-managed
             // calls which are ongoing at this time.
-            disconnectSelfManagedCalls();
+            disconnectSelfManagedCalls("outgoing call " + callId);
 
             // Kick of the new outgoing call intent from where it left off prior to confirming the
             // call.
@@ -2982,35 +3304,37 @@
         mPendingCall = call;
 
         // Figure out the name of the app in charge of the self-managed call(s).
-        Call selfManagedCall = mCalls.stream()
-                .filter(c -> c.isSelfManaged())
-                .findFirst()
-                .orElse(null);
-        CharSequence ongoingAppName = "";
-        if (selfManagedCall != null) {
-            ongoingAppName = selfManagedCall.getTargetPhoneAccountLabel();
-        }
-        Log.i(this, "startCallConfirmation: callId=%s, ongoingApp=%s", call.getId(),
-                ongoingAppName);
+        Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+        if (activeCall != null) {
+            CharSequence ongoingAppName = activeCall.getTargetPhoneAccountLabel();
+            Log.i(this, "startCallConfirmation: callId=%s, ongoingApp=%s", call.getId(),
+                    ongoingAppName);
 
-        Intent confirmIntent = new Intent(mContext, ConfirmCallDialogActivity.class);
-        confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID, call.getId());
-        confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_ONGOING_APP_NAME, ongoingAppName);
-        confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
+            Intent confirmIntent = new Intent(mContext, ConfirmCallDialogActivity.class);
+            confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID, call.getId());
+            confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_ONGOING_APP_NAME, ongoingAppName);
+            confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT);
+        }
     }
 
     /**
      * Disconnects all self-managed calls.
      */
-    private void disconnectSelfManagedCalls() {
+    private void disconnectSelfManagedCalls(String reason) {
         // Disconnect all self-managed calls to make priority for emergency call.
         // Use Call.disconnect() to command the ConnectionService to disconnect the calls.
         // CallsManager.markCallAsDisconnected doesn't actually tell the ConnectionService to
         // disconnect.
         mCalls.stream()
                 .filter(c -> c.isSelfManaged())
-                .forEach(c -> c.disconnect());
+                .forEach(c -> c.disconnect(reason));
+
+        // When disconnecting all self-managed calls, switch audio routing back to the baseline
+        // route.  This ensures if, for example, the self-managed ConnectionService was routed to
+        // speakerphone that we'll switch back to earpiece for the managed call which necessitated
+        // disconnecting the self-managed calls.
+        mCallAudioManager.switchBaseline();
     }
 
     /**
@@ -3077,7 +3401,7 @@
     */
     private void maybeShowErrorDialogOnDisconnect(Call call) {
         if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle())
-                || isPotentialInCallMMICode(call.getHandle()))) {
+                || isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(call)) {
             DisconnectCause disconnectCause = call.getDisconnectCause();
             if (!TextUtils.isEmpty(disconnectCause.getDescription()) && (disconnectCause.getCode()
                     == DisconnectCause.ERROR)) {
@@ -3091,15 +3415,31 @@
     }
 
     private void setIntentExtrasAndStartTime(Call call, Bundle extras) {
-      // Create our own instance to modify (since extras may be Bundle.EMPTY)
-      extras = new Bundle(extras);
+        if (extras != null) {
+            // Create our own instance to modify (since extras may be Bundle.EMPTY)
+            extras = new Bundle(extras);
+        } else {
+            extras = new Bundle();
+        }
 
-      // Specifies the time telecom began routing the call. This is used by the dialer for
-      // analytics.
-      extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS,
+        // Specifies the time telecom began routing the call. This is used by the dialer for
+        // analytics.
+        extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS,
               SystemClock.elapsedRealtime());
 
-      call.setIntentExtras(extras);
+        call.setIntentExtras(extras);
+    }
+
+    private void setCallSourceToAnalytics(Call call, Intent originalIntent) {
+        if (originalIntent == null) {
+            return;
+        }
+
+        int callSource = originalIntent.getIntExtra(TelecomManager.EXTRA_CALL_SOURCE,
+                Analytics.CALL_SOURCE_UNSPECIFIED);
+
+        // Call source is only used by metrics, so we simply set it to Analytics directly.
+        call.getAnalytics().setCallSource(callSource);
     }
 
     /**
@@ -3125,6 +3465,20 @@
     }
 
     /**
+     * Notifies the {@link android.telecom.ConnectionService} associated with a
+     * {@link PhoneAccountHandle} that the attempt to handover a call has failed.
+     *
+     * @param call The handover call
+     * @param reason The error reason code for handover failure
+     */
+    private void notifyHandoverFailed(Call call, int reason) {
+        ConnectionServiceWrapper service = call.getConnectionService();
+        service.handoverFailed(call, reason);
+        call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
+        call.disconnect("handover failed");
+    }
+
+    /**
      * Called in response to a {@link Call} receiving a {@link Call#sendCallEvent(String, Bundle)}
      * of type {@link android.telecom.Call#EVENT_REQUEST_HANDOVER} indicating the
      * {@link android.telecom.InCallService} has requested a handover to another
@@ -3138,8 +3492,9 @@
      * @param initiatingExtras Extras associated with the handover, to be passed to the handover
      *               {@link android.telecom.ConnectionService}.
      */
-    private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle,
-                                 int videoState, Bundle initiatingExtras) {
+    private void requestHandoverViaEvents(Call handoverFromCall,
+                                          PhoneAccountHandle handoverToHandle,
+                                          int videoState, Bundle initiatingExtras) {
 
         boolean isHandoverFromSupported = isHandoverFromPhoneAccountSupported(
                 handoverFromCall.getTargetPhoneAccount());
@@ -3177,6 +3532,138 @@
     }
 
     /**
+     * Called in response to a {@link Call} receiving a {@link Call#handoverTo(PhoneAccountHandle,
+     * int, Bundle)} indicating the {@link android.telecom.InCallService} has requested a
+     * handover to another {@link android.telecom.ConnectionService}.
+     *
+     * We will explicitly disallow a handover when there is an emergency call present.
+     *
+     * @param handoverFromCall The {@link Call} to be handed over.
+     * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to.
+     * @param videoState The desired video state of {@link Call} after handover.
+     * @param extras Extras associated with the handover, to be passed to the handover
+     *               {@link android.telecom.ConnectionService}.
+     */
+    private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle,
+                                 int videoState, Bundle extras) {
+
+        // Send an error back if there are any ongoing emergency calls.
+        if (hasEmergencyCall()) {
+            handoverFromCall.onHandoverFailed(
+                    android.telecom.Call.Callback.HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL);
+            return;
+        }
+
+        // If source and destination phone accounts don't support handover, send an error back.
+        boolean isHandoverFromSupported = isHandoverFromPhoneAccountSupported(
+                handoverFromCall.getTargetPhoneAccount());
+        boolean isHandoverToSupported = isHandoverToPhoneAccountSupported(handoverToHandle);
+        if (!isHandoverFromSupported || !isHandoverToSupported) {
+            handoverFromCall.onHandoverFailed(
+                    android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED);
+            return;
+        }
+
+        Log.addEvent(handoverFromCall, LogUtils.Events.HANDOVER_REQUEST, handoverToHandle);
+
+        // Create a new instance of Call
+        PhoneAccount account =
+                mPhoneAccountRegistrar.getPhoneAccount(handoverToHandle, getCurrentUserHandle());
+        boolean isSelfManaged = account != null && account.isSelfManaged();
+
+        Call call = new Call(getNextCallId(), mContext,
+                this, mLock, mConnectionServiceRepository,
+                mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, mPhoneNumberUtilsAdapter,
+                handoverFromCall.getHandle(), null,
+                null, null,
+                Call.CALL_DIRECTION_OUTGOING, false,
+                false, mClockProxy);
+        call.initAnalytics();
+
+        // Set self-managed and voipAudioMode if destination is self-managed CS
+        call.setIsSelfManaged(isSelfManaged);
+        if (isSelfManaged) {
+            call.setIsVoipAudioMode(true);
+        }
+        call.setInitiatingUser(getCurrentUserHandle());
+
+        // Ensure we don't try to place an outgoing call with video if video is not
+        // supported.
+        if (VideoProfile.isVideo(videoState) && account != null &&
+                !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
+            call.setVideoState(VideoProfile.STATE_AUDIO_ONLY);
+        } else {
+            call.setVideoState(videoState);
+        }
+
+        // Set target phone account to destAcct.
+        call.setTargetPhoneAccount(handoverToHandle);
+
+        if (account != null && account.getExtras() != null && account.getExtras()
+                    .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) {
+            Log.d(this, "requestHandover: defaulting to voip mode for call %s",
+                        call.getId());
+            call.setIsVoipAudioMode(true);
+        }
+
+        // Set call state to connecting
+        call.setState(
+                CallState.CONNECTING,
+                handoverToHandle == null ? "no-handle" : handoverToHandle.toString());
+
+        // Mark as handover so that the ConnectionService knows this is a handover request.
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true);
+        extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
+                handoverFromCall.getTargetPhoneAccount());
+        setIntentExtrasAndStartTime(call, extras);
+
+        // Add call to call tracker
+        if (!mCalls.contains(call)) {
+            addCall(call);
+        }
+
+        Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER,
+                "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), call.getId());
+
+        handoverFromCall.setHandoverDestinationCall(call);
+        handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
+        call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
+        call.setHandoverSourceCall(handoverFromCall);
+        call.setNewOutgoingCallIntentBroadcastIsDone();
+
+        // Auto-enable speakerphone if the originating intent specified to do so, if the call
+        // is a video call, of if using speaker when docked
+        final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
+                R.bool.use_speaker_when_docked);
+        final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
+        final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);
+        call.setStartWithSpeakerphoneOn(false || useSpeakerForVideoCall
+                || (useSpeakerWhenDocked && useSpeakerForDock));
+        call.setVideoState(videoState);
+
+        final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call,
+                call.getTargetPhoneAccount());
+
+        // If the account has been set, proceed to place the outgoing call.
+        if (call.isSelfManaged() && !isOutgoingCallPermitted) {
+            notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call);
+        } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) {
+            markCallDisconnectedDueToSelfManagedCall(call);
+        } else {
+            if (call.isEmergencyCall()) {
+                // Disconnect all self-managed calls to make priority for emergency call.
+                disconnectSelfManagedCalls("emergency call");
+            }
+
+            call.startCreateConnection(mPhoneAccountRegistrar);
+        }
+
+    }
+
+    /**
      * Determines if handover from the specified {@link PhoneAccountHandle} is supported.
      *
      * @param from The {@link PhoneAccountHandle} the handover originates from.
@@ -3270,4 +3757,174 @@
         }
         return ;
     }
+
+    public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) {
+        final String handleScheme = srcAddr.getSchemeSpecificPart();
+        Call fromCall = mCalls.stream()
+                .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber(
+                        (c.getHandle() == null ? null : c.getHandle().getSchemeSpecificPart()),
+                        handleScheme))
+                .findFirst()
+                .orElse(null);
+
+        Call call = new Call(
+                getNextCallId(),
+                mContext,
+                this,
+                mLock,
+                mConnectionServiceRepository,
+                mContactsAsyncHelper,
+                mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
+                srcAddr,
+                null /* gatewayInfo */,
+                null /* connectionManagerPhoneAccount */,
+                destAcct,
+                Call.CALL_DIRECTION_INCOMING /* callDirection */,
+                false /* forceAttachToExistingConnection */,
+                false, /* isConference */
+                mClockProxy);
+
+        if (fromCall == null || isHandoverInProgress() ||
+                !isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount()) ||
+                !isHandoverToPhoneAccountSupported(destAcct) ||
+                hasEmergencyCall()) {
+            Log.w(this, "acceptHandover: Handover not supported");
+            notifyHandoverFailed(call,
+                    android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED);
+            return;
+        }
+
+        PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(destAcct);
+        if (phoneAccount == null) {
+            Log.w(this, "acceptHandover: Handover not supported. phoneAccount = null");
+            notifyHandoverFailed(call,
+                    android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED);
+            return;
+        }
+        call.setIsSelfManaged(phoneAccount.isSelfManaged());
+        if (call.isSelfManaged() || (phoneAccount.getExtras() != null &&
+                phoneAccount.getExtras().getBoolean(
+                        PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE))) {
+            call.setIsVoipAudioMode(true);
+        }
+        if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
+            call.setVideoState(VideoProfile.STATE_AUDIO_ONLY);
+        } else {
+            call.setVideoState(videoState);
+        }
+
+        call.initAnalytics();
+        call.addListener(this);
+
+        fromCall.setHandoverDestinationCall(call);
+        call.setHandoverSourceCall(fromCall);
+        call.setHandoverState(HandoverState.HANDOVER_TO_STARTED);
+        fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED);
+
+        if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) {
+            // Ensure when the call goes active that it will go to speakerphone if the
+            // handover to call is a video call.
+            call.setStartWithSpeakerphoneOn(true);
+        }
+
+        Bundle extras = call.getIntentExtras();
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true);
+        extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
+                fromCall.getTargetPhoneAccount());
+
+        call.startCreateConnection(mPhoneAccountRegistrar);
+    }
+
+    ConnectionServiceFocusManager getConnectionServiceFocusManager() {
+        return mConnectionSvrFocusMgr;
+    }
+
+    private boolean canHold(Call call) {
+        return call.can(Connection.CAPABILITY_HOLD);
+    }
+
+    private boolean supportsHold(Call call) {
+        return call.can(Connection.CAPABILITY_SUPPORT_HOLD);
+    }
+
+    private final class ActionSetCallState implements PendingAction {
+
+        private final Call mCall;
+        private final int mState;
+        private final String mTag;
+
+        ActionSetCallState(Call call, int state, String tag) {
+            mCall = call;
+            mState = state;
+            mTag = tag;
+        }
+
+        @Override
+        public void performAction() {
+            Log.d(this, "perform set call state for %s, state = %s", mCall, mState);
+            setCallState(mCall, mState, mTag);
+        }
+    }
+
+    private final class ActionUnHoldCall implements PendingAction {
+        private final Call mCall;
+        private final String mPreviouslyHeldCallId;
+
+        ActionUnHoldCall(Call call, String previouslyHeldCallId) {
+            mCall = call;
+            mPreviouslyHeldCallId = previouslyHeldCallId;
+        }
+
+        @Override
+        public void performAction() {
+            Log.d(this, "perform unhold call for %s", mCall);
+            mCall.unhold("held " + mPreviouslyHeldCallId);
+        }
+    }
+
+    private final class ActionAnswerCall implements PendingAction {
+        private final Call mCall;
+        private final int mVideoState;
+
+        ActionAnswerCall(Call call, int videoState) {
+            mCall = call;
+            mVideoState = videoState;
+        }
+
+        @Override
+        public void performAction() {
+            Log.d(this, "perform answer call for %s, videoState = %d", mCall, mVideoState);
+            for (CallsManagerListener listener : mListeners) {
+                listener.onIncomingCallAnswered(mCall);
+            }
+
+            // We do not update the UI until we get confirmation of the answer() through
+            // {@link #markCallAsActive}.
+            mCall.answer(mVideoState);
+            if (isSpeakerphoneAutoEnabledForVideoCalls(mVideoState)) {
+                mCall.setStartWithSpeakerphoneOn(true);
+            }
+        }
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public static final class RequestCallback implements
+            ConnectionServiceFocusManager.RequestFocusCallback {
+        private PendingAction mPendingAction;
+
+        RequestCallback(PendingAction pendingAction) {
+            mPendingAction = pendingAction;
+        }
+
+        @Override
+        public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) {
+            if (mPendingAction != null) {
+                mPendingAction.performAction();
+            }
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index a4c76c1..c0a71eb 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -88,4 +88,8 @@
     @Override
     public void onExternalCallChanged(Call call, boolean isExternalCall) {
     }
+
+    @Override
+    public void onDisconnectedTonePlaying(boolean isTonePlaying) {
+    }
 }
diff --git a/src/com/android/server/telecom/ConnectionServiceFocusManager.java b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
new file mode 100644
index 0000000..92570a0
--- /dev/null
+++ b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telecom.Log;
+import android.telecom.Logging.Session;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class ConnectionServiceFocusManager {
+    private static final String TAG = "ConnectionSvrFocusMgr";
+
+    /** Factory interface used to create the {@link ConnectionServiceFocusManager} instance. */
+    public interface ConnectionServiceFocusManagerFactory {
+        ConnectionServiceFocusManager create(CallsManagerRequester requester, Looper looper);
+    }
+
+    /**
+     * Interface used by ConnectionServiceFocusManager to communicate with
+     * {@link ConnectionServiceWrapper}.
+     */
+    public interface ConnectionServiceFocus {
+        /**
+         * Notifies the {@link android.telecom.ConnectionService} that it has lose the connection
+         * service focus. It should release all call resource i.e camera, audio once it lost the
+         * focus.
+         */
+        void connectionServiceFocusLost();
+
+        /**
+         * Notifies the {@link android.telecom.ConnectionService} that it has gain the connection
+         * service focus. It can request the call resource i.e camera, audio as they expected to be
+         * free at the moment.
+         */
+        void connectionServiceFocusGained();
+
+        /**
+         * Sets the ConnectionServiceFocusListener.
+         *
+         * @see {@link ConnectionServiceFocusListener}.
+         */
+        void setConnectionServiceFocusListener(ConnectionServiceFocusListener listener);
+
+        /**
+         * Get the {@link ComponentName} of the ConnectionService for logging purposes.
+         * @return the {@link ComponentName}.
+         */
+        ComponentName getComponentName();
+    }
+
+    /**
+     * Interface used to receive the changed of {@link android.telecom.ConnectionService} that
+     * ConnectionServiceFocusManager cares about.
+     */
+    public interface ConnectionServiceFocusListener {
+        /**
+         * Calls when {@link android.telecom.ConnectionService} has released the call resource. This
+         * usually happen after the {@link android.telecom.ConnectionService} lost the focus.
+         *
+         * @param connectionServiceFocus the {@link android.telecom.ConnectionService} that released
+         * the call resources.
+         */
+        void onConnectionServiceReleased(ConnectionServiceFocus connectionServiceFocus);
+
+        /**
+         * Calls when {@link android.telecom.ConnectionService} is disconnected.
+         *
+         * @param connectionServiceFocus the {@link android.telecom.ConnectionService} which is
+         * disconnected.
+         */
+        void onConnectionServiceDeath(ConnectionServiceFocus connectionServiceFocus);
+    }
+
+    /**
+     * Interface define to expose few information of {@link Call} that ConnectionServiceFocusManager
+     * cares about.
+     */
+    public interface CallFocus {
+        /**
+         * Returns the ConnectionService associated with the call.
+         */
+        ConnectionServiceFocus getConnectionServiceWrapper();
+
+        /**
+         * Returns the state of the call.
+         *
+         * @see {@link CallState}
+         */
+        int getState();
+
+        /**
+         * @return {@code True} if this call can receive focus, {@code false} otherwise.
+         */
+        boolean isFocusable();
+    }
+
+    /** Interface define a call back for focus request event. */
+    public interface RequestFocusCallback {
+        /**
+         * Invokes after the focus request is done.
+         *
+         * @param call the call associated with the focus request.
+         */
+        void onRequestFocusDone(CallFocus call);
+    }
+
+    /**
+     * Interface define to allow the ConnectionServiceFocusManager to communicate with
+     * {@link CallsManager}.
+     */
+    public interface CallsManagerRequester {
+        /**
+         * Requests {@link CallsManager} to disconnect a {@link ConnectionServiceFocus}. This
+         * usually happen when the connection service doesn't respond to focus lost event.
+         */
+        void releaseConnectionService(ConnectionServiceFocus connectionService);
+
+        /**
+         * Sets the {@link com.android.server.telecom.CallsManager.CallsManagerListener} to listen
+         * the call event that ConnectionServiceFocusManager cares about.
+         */
+        void setCallsManagerListener(CallsManager.CallsManagerListener listener);
+    }
+
+    private static final int[] PRIORITY_FOCUS_CALL_STATE = new int[] {
+            CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING
+    };
+
+    private static final int MSG_REQUEST_FOCUS = 1;
+    private static final int MSG_RELEASE_CONNECTION_FOCUS = 2;
+    private static final int MSG_RELEASE_FOCUS_TIMEOUT = 3;
+    private static final int MSG_CONNECTION_SERVICE_DEATH = 4;
+    private static final int MSG_ADD_CALL = 5;
+    private static final int MSG_REMOVE_CALL = 6;
+    private static final int MSG_CALL_STATE_CHANGED = 7;
+
+    @VisibleForTesting
+    public static final int RELEASE_FOCUS_TIMEOUT_MS = 5000;
+
+    private final List<CallFocus> mCalls;
+
+    private final CallsManagerListenerBase mCallsManagerListener =
+            new CallsManagerListenerBase() {
+                @Override
+                public void onCallAdded(Call call) {
+                    if (callShouldBeIgnored(call)) {
+                        return;
+                    }
+
+                    mEventHandler
+                            .obtainMessage(MSG_ADD_CALL,
+                                    new MessageArgs(
+                                            Log.createSubsession(),
+                                            "CSFM.oCA",
+                                            call))
+                            .sendToTarget();
+                }
+
+                @Override
+                public void onCallRemoved(Call call) {
+                    if (callShouldBeIgnored(call)) {
+                        return;
+                    }
+
+                    mEventHandler
+                            .obtainMessage(MSG_REMOVE_CALL,
+                                    new MessageArgs(
+                                            Log.createSubsession(),
+                                            "CSFM.oCR",
+                                            call))
+                            .sendToTarget();
+                }
+
+                @Override
+                public void onCallStateChanged(Call call, int oldState, int newState) {
+                    if (callShouldBeIgnored(call)) {
+                        return;
+                    }
+
+                    mEventHandler
+                            .obtainMessage(MSG_CALL_STATE_CHANGED, oldState, newState,
+                                    new MessageArgs(
+                                            Log.createSubsession(),
+                                            "CSFM.oCSS",
+                                            call))
+                            .sendToTarget();
+                }
+
+                @Override
+                public void onExternalCallChanged(Call call, boolean isExternalCall) {
+                    if (isExternalCall) {
+                        mEventHandler
+                                .obtainMessage(MSG_REMOVE_CALL,
+                                        new MessageArgs(
+                                                Log.createSubsession(),
+                                                "CSFM.oECC",
+                                                call))
+                                .sendToTarget();
+                    } else {
+                        mEventHandler
+                                .obtainMessage(MSG_ADD_CALL,
+                                        new MessageArgs(
+                                                Log.createSubsession(),
+                                                "CSFM.oECC",
+                                                call))
+                                .sendToTarget();
+                    }
+                }
+
+                boolean callShouldBeIgnored(Call call) {
+                    return call.isExternalCall();
+                }
+            };
+
+    private final ConnectionServiceFocusListener mConnectionServiceFocusListener =
+            new ConnectionServiceFocusListener() {
+                @Override
+                public void onConnectionServiceReleased(
+                        ConnectionServiceFocus connectionServiceFocus) {
+                    mEventHandler
+                            .obtainMessage(MSG_RELEASE_CONNECTION_FOCUS,
+                                    new MessageArgs(
+                                            Log.createSubsession(),
+                                            "CSFM.oCSR",
+                                            connectionServiceFocus))
+                            .sendToTarget();
+                }
+
+                @Override
+                public void onConnectionServiceDeath(
+                        ConnectionServiceFocus connectionServiceFocus) {
+                    mEventHandler
+                            .obtainMessage(MSG_CONNECTION_SERVICE_DEATH,
+                                    new MessageArgs(
+                                            Log.createSubsession(),
+                                            "CSFM.oCSD",
+                                            connectionServiceFocus))
+                            .sendToTarget();
+                }
+            };
+
+    private ConnectionServiceFocus mCurrentFocus;
+    private CallFocus mCurrentFocusCall;
+    private CallsManagerRequester mCallsManagerRequester;
+    private FocusRequest mCurrentFocusRequest;
+    private FocusManagerHandler mEventHandler;
+
+    public ConnectionServiceFocusManager(
+            CallsManagerRequester callsManagerRequester, Looper looper) {
+        mCallsManagerRequester = callsManagerRequester;
+        mCallsManagerRequester.setCallsManagerListener(mCallsManagerListener);
+        mEventHandler = new FocusManagerHandler(looper);
+        mCalls = new ArrayList<>();
+    }
+
+    /**
+     * Requests the call focus for the given call. The {@code callback} will be invoked once
+     * the request is done.
+     * @param focus the call need to be focus.
+     * @param callback the callback associated with this request.
+     */
+    public void requestFocus(CallFocus focus, RequestFocusCallback callback) {
+        mEventHandler.obtainMessage(MSG_REQUEST_FOCUS,
+                new MessageArgs(
+                        Log.createSubsession(),
+                        "CSFM.rF",
+                        new FocusRequest(focus, callback)))
+                .sendToTarget();
+    }
+
+    /**
+     * Returns the current focus call. The {@link android.telecom.ConnectionService} of the focus
+     * call is the current connection service focus. Also the state of the focus call must be one
+     * of {@link #PRIORITY_FOCUS_CALL_STATE}.
+     */
+    public CallFocus getCurrentFocusCall() {
+        return mCurrentFocusCall;
+    }
+
+    /** Returns the current connection service focus. */
+    public ConnectionServiceFocus getCurrentFocusConnectionService() {
+        return mCurrentFocus;
+    }
+
+    @VisibleForTesting
+    public Handler getHandler() {
+        return mEventHandler;
+    }
+
+    @VisibleForTesting
+    public List<CallFocus> getAllCall() { return mCalls; }
+
+    private void updateConnectionServiceFocus(ConnectionServiceFocus connSvrFocus) {
+        if (!Objects.equals(mCurrentFocus, connSvrFocus)) {
+            if (connSvrFocus != null) {
+                connSvrFocus.setConnectionServiceFocusListener(mConnectionServiceFocusListener);
+                connSvrFocus.connectionServiceFocusGained();
+            }
+            mCurrentFocus = connSvrFocus;
+            Log.d(this, "updateConnectionServiceFocus connSvr = %s", connSvrFocus);
+        }
+    }
+
+    private void updateCurrentFocusCall() {
+        mCurrentFocusCall = null;
+
+        if (mCurrentFocus == null) {
+            return;
+        }
+
+        List<CallFocus> calls = mCalls
+                .stream()
+                .filter(call -> mCurrentFocus.equals(call.getConnectionServiceWrapper())
+                        && call.isFocusable())
+                .collect(Collectors.toList());
+
+        for (int i = 0; i < PRIORITY_FOCUS_CALL_STATE.length; i++) {
+            for (CallFocus call : calls) {
+                if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i]) {
+                    mCurrentFocusCall = call;
+                    Log.d(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
+                    return;
+                }
+            }
+        }
+
+        Log.d(this, "updateCurrentFocusCall = null");
+    }
+
+    private void onRequestFocusDone(FocusRequest focusRequest) {
+        if (focusRequest.callback != null) {
+            focusRequest.callback.onRequestFocusDone(focusRequest.call);
+        }
+    }
+
+    private void handleRequestFocus(FocusRequest focusRequest) {
+        Log.d(this, "handleRequestFocus req = %s", focusRequest);
+        if (mCurrentFocus == null
+                || mCurrentFocus.equals(focusRequest.call.getConnectionServiceWrapper())) {
+            updateConnectionServiceFocus(focusRequest.call.getConnectionServiceWrapper());
+            updateCurrentFocusCall();
+            onRequestFocusDone(focusRequest);
+        } else {
+            mCurrentFocus.connectionServiceFocusLost();
+            mCurrentFocusRequest = focusRequest;
+            Message msg = mEventHandler.obtainMessage(
+                    MSG_RELEASE_FOCUS_TIMEOUT,
+                    new MessageArgs(
+                            Log.createSubsession(),
+                            "CSFM.hRF",
+                            focusRequest));
+            mEventHandler.sendMessageDelayed(msg, RELEASE_FOCUS_TIMEOUT_MS);
+        }
+    }
+
+    private void handleReleasedFocus(ConnectionServiceFocus connectionServiceFocus) {
+        Log.d(this, "handleReleasedFocus connSvr = %s", connectionServiceFocus);
+        // The ConnectionService can call onConnectionServiceFocusReleased even if it's not the
+        // current focus connection service, nothing will be changed in this case.
+        if (Objects.equals(mCurrentFocus, connectionServiceFocus)) {
+            mEventHandler.removeMessages(MSG_RELEASE_FOCUS_TIMEOUT);
+            ConnectionServiceFocus newCSF = null;
+            if (mCurrentFocusRequest != null) {
+                newCSF = mCurrentFocusRequest.call.getConnectionServiceWrapper();
+            }
+            updateConnectionServiceFocus(newCSF);
+            updateCurrentFocusCall();
+            if (mCurrentFocusRequest != null) {
+                onRequestFocusDone(mCurrentFocusRequest);
+                mCurrentFocusRequest = null;
+            }
+        }
+    }
+
+    private void handleReleasedFocusTimeout(FocusRequest focusRequest) {
+        Log.d(this, "handleReleasedFocusTimeout req = %s", focusRequest);
+        mCallsManagerRequester.releaseConnectionService(mCurrentFocus);
+        updateConnectionServiceFocus(focusRequest.call.getConnectionServiceWrapper());
+        updateCurrentFocusCall();
+        onRequestFocusDone(focusRequest);
+        mCurrentFocusRequest = null;
+    }
+
+    private void handleConnectionServiceDeath(ConnectionServiceFocus connectionServiceFocus) {
+        Log.d(this, "handleConnectionServiceDeath %s", connectionServiceFocus);
+        if (Objects.equals(connectionServiceFocus, mCurrentFocus)) {
+            updateConnectionServiceFocus(null);
+            updateCurrentFocusCall();
+        }
+    }
+
+    private void handleAddedCall(CallFocus call) {
+        Log.d(this, "handleAddedCall %s", call);
+        if (!mCalls.contains(call)) {
+            mCalls.add(call);
+        }
+        if (Objects.equals(mCurrentFocus, call.getConnectionServiceWrapper())) {
+            updateCurrentFocusCall();
+        }
+    }
+
+    private void handleRemovedCall(CallFocus call) {
+        Log.d(this, "handleRemovedCall %s", call);
+        mCalls.remove(call);
+        if (call.equals(mCurrentFocusCall)) {
+            updateCurrentFocusCall();
+        }
+    }
+
+    private void handleCallStateChanged(CallFocus call, int oldState, int newState) {
+        Log.d(this,
+                "handleCallStateChanged %s, oldState = %d, newState = %d",
+                call,
+                oldState,
+                newState);
+        if (mCalls.contains(call)
+                && Objects.equals(mCurrentFocus, call.getConnectionServiceWrapper())) {
+            updateCurrentFocusCall();
+        }
+    }
+
+    private final class FocusManagerHandler extends Handler {
+        FocusManagerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            Session session = ((MessageArgs) msg.obj).logSession;
+            String shortName = ((MessageArgs) msg.obj).shortName;
+            if (TextUtils.isEmpty(shortName)) {
+                shortName = "hM";
+            }
+            Log.continueSession(session, shortName);
+            Object msgObj = ((MessageArgs) msg.obj).obj;
+
+            try {
+                switch (msg.what) {
+                    case MSG_REQUEST_FOCUS:
+                        handleRequestFocus((FocusRequest) msgObj);
+                        break;
+                    case MSG_RELEASE_CONNECTION_FOCUS:
+                        handleReleasedFocus((ConnectionServiceFocus) msgObj);
+                        break;
+                    case MSG_RELEASE_FOCUS_TIMEOUT:
+                        handleReleasedFocusTimeout((FocusRequest) msgObj);
+                        break;
+                    case MSG_CONNECTION_SERVICE_DEATH:
+                        handleConnectionServiceDeath((ConnectionServiceFocus) msgObj);
+                        break;
+                    case MSG_ADD_CALL:
+                        handleAddedCall((CallFocus) msgObj);
+                        break;
+                    case MSG_REMOVE_CALL:
+                        handleRemovedCall((CallFocus) msgObj);
+                        break;
+                    case MSG_CALL_STATE_CHANGED:
+                        handleCallStateChanged((CallFocus) msgObj, msg.arg1, msg.arg2);
+                        break;
+                }
+            } finally {
+                Log.endSession();
+            }
+        }
+    }
+
+    private static final class FocusRequest {
+        CallFocus call;
+        @Nullable RequestFocusCallback callback;
+
+        FocusRequest(CallFocus call, RequestFocusCallback callback) {
+            this.call = call;
+            this.callback = callback;
+        }
+    }
+
+    private static final class MessageArgs {
+        Session logSession;
+        String shortName;
+        Object obj;
+
+        MessageArgs(Session logSession, String shortName, Object obj) {
+            this.logSession = logSession;
+            this.shortName = shortName;
+            this.obj = obj;
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/ConnectionServiceRepository.java b/src/com/android/server/telecom/ConnectionServiceRepository.java
index 4685704..d34ea3c 100644
--- a/src/com/android/server/telecom/ConnectionServiceRepository.java
+++ b/src/com/android/server/telecom/ConnectionServiceRepository.java
@@ -43,7 +43,8 @@
                 @Override
                 public void onUnbind(ConnectionServiceWrapper service) {
                     synchronized (mLock) {
-                        mServiceCache.remove(service.getComponentName());
+                        mServiceCache.remove(Pair.create(service.getComponentName(),
+                                service.getUserHandle()));
                     }
                 }
             };
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 845cd37..6dd9a3a 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -40,6 +40,7 @@
 import android.telecom.StatusHints;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.IConnectionService;
@@ -63,7 +64,8 @@
  * {@link IConnectionService}.
  */
 @VisibleForTesting
-public class ConnectionServiceWrapper extends ServiceBinder {
+public class ConnectionServiceWrapper extends ServiceBinder implements
+        ConnectionServiceFocusManager.ConnectionServiceFocus {
 
     private final class Adapter extends IConnectionServiceAdapter.Stub {
 
@@ -87,6 +89,9 @@
                         }
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -107,6 +112,9 @@
                         // Log.w(this, "setActive, unknown call id: %s", msg.obj);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -127,6 +135,9 @@
                         // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -146,6 +157,9 @@
                         call.setVideoProvider(videoProvider);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -166,6 +180,9 @@
                         // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -173,7 +190,7 @@
         }
 
         @Override
-	    public void setPulling(String callId, Session.Info sessionInfo) {
+        public void setPulling(String callId, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, LogUtils.Sessions.CSW_SET_PULLING);
             long token = Binder.clearCallingIdentity();
             try {
@@ -184,6 +201,9 @@
                         mCallsManager.markCallAsPulling(call);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -206,6 +226,9 @@
                         // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -226,6 +249,9 @@
                         // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -247,6 +273,9 @@
                         // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -270,6 +299,9 @@
                         }
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -292,6 +324,9 @@
                         // "setConnectionCapabilities, unknown call id: %s", msg.obj);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -311,6 +346,9 @@
                         call.setConnectionProperties(connectionProperties);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -338,6 +376,9 @@
                         // Log.w(this, "setIsConferenced, unknown call id: %s", args.arg1);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -361,6 +402,9 @@
                         Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -449,6 +493,9 @@
                         }
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -470,6 +517,9 @@
                         // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -491,6 +541,9 @@
                         // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -509,6 +562,9 @@
                     ConnectionServiceWrapper.this
                             .queryRemoteConnectionServices(callingUserHandle, callback);
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -527,6 +583,9 @@
                         call.setVideoState(videoState);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -545,6 +604,9 @@
                         call.setIsVoipAudioMode(isVoip);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -552,15 +614,19 @@
         }
 
         @Override
-        public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+        public void setAudioRoute(String callId, int audioRoute,
+                String bluetoothAddress, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, "CSW.sAR");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setAudioRoute %s %s", callId,
                             CallAudioState.audioRouteToString(audioRoute));
-                    mCallsManager.setAudioRoute(audioRoute);
+                    mCallsManager.setAudioRoute(audioRoute, bluetoothAddress);
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -580,6 +646,9 @@
                         call.setStatusHints(statusHints);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -598,6 +667,9 @@
                         call.putExtras(Call.SOURCE_CONNECTION_SERVICE, extras);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -616,6 +688,9 @@
                         call.removeExtras(Call.SOURCE_CONNECTION_SERVICE, keys);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -635,6 +710,9 @@
                         call.setHandle(address, presentation);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -655,6 +733,9 @@
                         call.setCallerDisplayName(callerDisplayName, presentation);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -684,6 +765,9 @@
                         call.setConferenceableCalls(conferenceableCalls);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -691,8 +775,8 @@
         }
 
         @Override
-	public void addExistingConnection(String callId, ParcelableConnection connection,
-	        Session.Info sessionInfo) {
+        public void addExistingConnection(String callId, ParcelableConnection connection,
+                Session.Info sessionInfo) {
             Log.startSession(sessionInfo, "CSW.aEC");
             UserHandle userHandle = Binder.getCallingUserHandle();
             // Check that the Calling Package matches PhoneAccountHandle's Component Package
@@ -757,6 +841,9 @@
                                 "addExistingConnection.");
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -776,6 +863,9 @@
                         call.onConnectionEvent(event, extras);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -800,6 +890,9 @@
                         call.onRttConnectionFailure(reason);
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -824,6 +917,54 @@
                         call.onRemoteRttRequest();
                     }
                 }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void onPhoneAccountChanged(String callId, PhoneAccountHandle pHandle,
+                Session.Info sessionInfo) throws RemoteException {
+            // Check that the Calling Package matches PhoneAccountHandle's Component Package
+            if (pHandle != null) {
+                mAppOpsManager.checkPackage(Binder.getCallingUid(),
+                        pHandle.getComponentName().getPackageName());
+            }
+            Log.startSession(sessionInfo, "CSW.oPAC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setTargetPhoneAccount(pHandle);
+                    }
+                }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void onConnectionServiceFocusReleased(Session.Info sessionInfo)
+                throws RemoteException {
+            Log.startSession(sessionInfo, "CSW.oCSFR");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mConnSvrFocusListener.onConnectionServiceReleased(
+                            ConnectionServiceWrapper.this);
+                }
+            } catch (Throwable t) {
+                Log.e(ConnectionServiceWrapper.this, t, "");
+                throw t;
             } finally {
                 Binder.restoreCallingIdentity(token);
                 Log.endSession();
@@ -842,6 +983,8 @@
     private final CallsManager mCallsManager;
     private final AppOpsManager mAppOpsManager;
 
+    private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
+
     /**
      * Creates a connection service.
      *
@@ -1026,6 +1169,70 @@
         mBinder.bind(callback, call);
     }
 
+    void handoverFailed(final Call call, final int reason) {
+        Log.d(this, "handoverFailed(%s) via %s.", call, getComponentName());
+        BindCallback callback = new BindCallback() {
+            @Override
+            public void onSuccess() {
+                final String callId = mCallIdMapper.getCallId(call);
+                // If still bound, tell the connection service create connection has failed.
+                if (callId != null && isServiceValid("handoverFailed")) {
+                    Log.addEvent(call, LogUtils.Events.HANDOVER_FAILED,
+                            Log.piiHandle(call.getHandle()));
+                    try {
+                        mServiceInterface.handoverFailed(
+                                callId,
+                                new ConnectionRequest(
+                                        call.getTargetPhoneAccount(),
+                                        call.getHandle(),
+                                        call.getIntentExtras(),
+                                        call.getVideoState(),
+                                        callId,
+                                        false), reason, Log.getExternalSession());
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+            @Override
+            public void onFailure() {
+                // Binding failed.
+                Log.w(this, "onFailure - could not bind to CS for call %s",
+                        call.getId());
+            }
+        };
+
+        mBinder.bind(callback, call);
+    }
+
+    void handoverComplete(final Call call) {
+        Log.d(this, "handoverComplete(%s) via %s.", call, getComponentName());
+        BindCallback callback = new BindCallback() {
+            @Override
+            public void onSuccess() {
+                final String callId = mCallIdMapper.getCallId(call);
+                // If still bound, tell the connection service create connection has failed.
+                if (callId != null && isServiceValid("handoverComplete")) {
+                    try {
+                        mServiceInterface.handoverComplete(
+                                callId,
+                                Log.getExternalSession());
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+            @Override
+            public void onFailure() {
+                // Binding failed.
+                Log.w(this, "onFailure - could not bind to CS for call %s",
+                        call.getId());
+            }
+        };
+
+        mBinder.bind(callback, call);
+    }
+
     /** @see IConnectionService#abort(String, Session.Info)  */
     void abort(Call call) {
         // Clear out any pending outgoing call data
@@ -1121,6 +1328,18 @@
         }
     }
 
+    /** @see IConnectionService#deflect(String, Uri , Session.Info) */
+    void deflect(Call call, Uri address) {
+        final String callId = mCallIdMapper.getCallId(call);
+        if (callId != null && isServiceValid("deflect")) {
+            try {
+                logOutgoing("deflect %s", callId);
+                mServiceInterface.deflect(callId, address, Log.getExternalSession());
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     /** @see IConnectionService#reject(String, Session.Info) */
     void reject(Call call, boolean rejectWithMessage, String message) {
         final String callId = mCallIdMapper.getCallId(call);
@@ -1345,6 +1564,53 @@
         mServiceInterface = null;
     }
 
+    @Override
+    public void connectionServiceFocusLost() {
+        // Immediately response to the Telecom that it has released the call resources.
+        // TODO(mpq): Change back to the default implementation once b/69651192 done.
+        if (mConnSvrFocusListener != null) {
+            mConnSvrFocusListener.onConnectionServiceReleased(ConnectionServiceWrapper.this);
+        }
+        BindCallback callback = new BindCallback() {
+            @Override
+            public void onSuccess() {
+                try {
+                    mServiceInterface.connectionServiceFocusLost(Log.getExternalSession());
+                } catch (RemoteException ignored) {
+                    Log.d(this, "failed to inform the focus lost event");
+                }
+            }
+
+            @Override
+            public void onFailure() {}
+        };
+        mBinder.bind(callback, null /* null call */);
+    }
+
+    @Override
+    public void connectionServiceFocusGained() {
+        BindCallback callback = new BindCallback() {
+            @Override
+            public void onSuccess() {
+                try {
+                    mServiceInterface.connectionServiceFocusGained(Log.getExternalSession());
+                } catch (RemoteException ignored) {
+                    Log.d(this, "failed to inform the focus gained event");
+                }
+            }
+
+            @Override
+            public void onFailure() {}
+        };
+        mBinder.bind(callback, null /* null call */);
+    }
+
+    @Override
+    public void setConnectionServiceFocusListener(
+            ConnectionServiceFocusManager.ConnectionServiceFocusListener listener) {
+        mConnSvrFocusListener = listener;
+    }
+
     private void handleCreateConnectionComplete(
             String callId,
             ConnectionRequest request,
@@ -1379,6 +1645,10 @@
             }
         }
         mCallIdMapper.clear();
+
+        if (mConnSvrFocusListener != null) {
+            mConnSvrFocusListener.onConnectionServiceDeath(this);
+        }
     }
 
     private void logIncoming(String msg, Object... params) {
@@ -1431,8 +1701,16 @@
                 @Override
                 public void onSuccess() {
                     Log.d(this, "Adding simService %s", currentSimService.getComponentName());
-                    simServiceComponentNames.add(currentSimService.getComponentName());
-                    simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
+                    if (currentSimService.mServiceInterface == null) {
+                        // The remote ConnectionService died, so do not add it.
+                        // We will still perform maybeComplete() and notify the caller with an empty
+                        // list of sim services via maybeComplete().
+                        Log.w(this, "queryRemoteConnectionServices: simService %s died - Skipping.",
+                                currentSimService.getComponentName());
+                    } else {
+                        simServiceComponentNames.add(currentSimService.getComponentName());
+                        simServiceBinders.add(currentSimService.mServiceInterface.asBinder());
+                    }
                     maybeComplete();
                 }
 
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 629e949..7eb3801 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -213,8 +213,24 @@
                 mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
                 mCall.setConnectionService(mService);
                 setTimeoutIfNeeded(mService, attempt);
+                if (mCall.isIncoming()) {
+                    mService.createConnection(mCall, CreateConnectionProcessor.this);
+                } else {
+                    // Start to create the connection for outgoing call after the ConnectionService
+                    // of the call has gained the focus.
+                    mCall.getConnectionServiceFocusManager().requestFocus(
+                            mCall,
+                            new CallsManager.RequestCallback(new CallsManager.PendingAction() {
+                                @Override
+                                public void performAction() {
+                                    Log.d(this, "perform create connection");
+                                    mService.createConnection(
+                                            mCall,
+                                            CreateConnectionProcessor.this);
+                                }
+                            }));
 
-                mService.createConnection(mCall, this);
+                }
             }
         } else {
             Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
@@ -329,15 +345,20 @@
 
             // Next, add all SIM phone accounts which can place emergency calls.
             TelephonyUtil.sortSimPhoneAccounts(mContext, allAccounts);
-            for (PhoneAccount phoneAccount : allAccounts) {
-                if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
-                        phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-                    PhoneAccountHandle phoneAccountHandle = phoneAccount.getAccountHandle();
-                    // Don't add the preferred account since it has already been added previously.
-                    if (!phoneAccountHandle.equals(preferredPAH)) {
+
+            // If preferredPA already has an emergency PhoneAccount, do not add others since the
+            // emergency call be redialed in Telephony.
+            if (mAttemptRecords.isEmpty()) {
+                for (PhoneAccount phoneAccount : allAccounts) {
+                    if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+                            && phoneAccount.hasCapabilities(
+                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+                        PhoneAccountHandle phoneAccountHandle = phoneAccount.getAccountHandle();
                         Log.i(this, "Will try PSTN account %s for emergency", phoneAccountHandle);
                         mAttemptRecords.add(new CallAttemptRecord(phoneAccountHandle,
                                 phoneAccountHandle));
+                        // Add only one emergency SIM PhoneAccount to the attempt list.
+                        break;
                     }
                 }
             }
@@ -353,7 +374,8 @@
                         PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
                     CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
                             mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
-                                    mCall.getHandle().getScheme()));
+                                    mCall.getHandle() == null
+                                            ? null : mCall.getHandle().getScheme()));
                     if (!mAttemptRecords.contains(callAttemptRecord)) {
                         Log.i(this, "Will try Connection Manager account %s for emergency",
                                 callManager);
diff --git a/src/com/android/server/telecom/HeadsetMediaButton.java b/src/com/android/server/telecom/HeadsetMediaButton.java
index 64de874..ec77289 100644
--- a/src/com/android/server/telecom/HeadsetMediaButton.java
+++ b/src/com/android/server/telecom/HeadsetMediaButton.java
@@ -45,18 +45,23 @@
     private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
         @Override
         public boolean onMediaButtonEvent(Intent intent) {
-            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
-            Log.v(this, "SessionCallback.onMediaButton()...  event = %s.", event);
-            if ((event != null) && ((event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK) ||
-                                    (event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))) {
-                synchronized (mLock) {
-                    Log.v(this, "SessionCallback: HEADSETHOOK/MEDIA_PLAY_PAUSE");
-                    boolean consumed = handleCallMediaButton(event);
-                    Log.v(this, "==> handleCallMediaButton(): consumed = %b.", consumed);
-                    return consumed;
+            try {
+                Log.startSession("HMB.oMBE");
+                KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+                Log.v(this, "SessionCallback.onMediaButton()...  event = %s.", event);
+                if ((event != null) && ((event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK) ||
+                        (event.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE))) {
+                    synchronized (mLock) {
+                        Log.v(this, "SessionCallback: HEADSETHOOK/MEDIA_PLAY_PAUSE");
+                        boolean consumed = handleCallMediaButton(event);
+                        Log.v(this, "==> handleCallMediaButton(): consumed = %b.", consumed);
+                        return consumed;
+                    }
                 }
+                return true;
+            } finally {
+                Log.endSession();
             }
-            return true;
         }
     };
 
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index 0eeec44..8de27be 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom;
 
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.telecom.Log;
@@ -69,12 +70,45 @@
     }
 
     @Override
-    public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
+    public void deflectCall(String callId, Uri address) {
         try {
-            Log.startSession(LogUtils.Sessions.ICA_REJECT_CALL, mOwnerComponentName);
+            Log.startSession(LogUtils.Sessions.ICA_DEFLECT_CALL, mOwnerComponentName);
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
+                    Log.i(this, "deflectCall - %s, %s ", callId, Log.pii(address));
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.deflectCall(call, address);
+                    } else {
+                        Log.w(this, "deflectCall, unknown call id: %s", callId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
+
+    @Override
+    public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
+        try {
+            Log.startSession(LogUtils.Sessions.ICA_REJECT_CALL, mOwnerComponentName);
+
+            int callingUid = Binder.getCallingUid();
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    // Check to make sure the in-call app's user isn't restricted from sending SMS.
+                    // If so, silently drop the outgoing message. Also drop message if the screen is
+                    // locked.
+                    if (!mCallsManager.isReplyWithSmsAllowed(callingUid)) {
+                        rejectWithMessage = false;
+                        textMessage = null;
+                    }
+
                     Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
@@ -268,13 +302,13 @@
     }
 
     @Override
-    public void setAudioRoute(int route) {
+    public void setAudioRoute(int route, String bluetoothAddress) {
         try {
             Log.startSession(LogUtils.Sessions.ICA_SET_AUDIO_ROUTE, mOwnerComponentName);
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    mCallsManager.setAudioRoute(route);
+                    mCallsManager.setAudioRoute(route, bluetoothAddress);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -396,7 +430,7 @@
     }
 
     @Override
-    public void sendCallEvent(String callId, String event, Bundle extras) {
+    public void sendCallEvent(String callId, String event, int targetSdkVer, Bundle extras) {
         try {
             Log.startSession("ICA.sCE", mOwnerComponentName);
             long token = Binder.clearCallingIdentity();
@@ -404,7 +438,7 @@
                 synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
-                        call.sendCallEvent(event, extras);
+                        call.sendCallEvent(event, targetSdkVer, extras);
                     } else {
                         Log.w(this, "sendCallEvent, unknown call id: %s", callId);
                     }
@@ -577,4 +611,27 @@
             Log.endSession();
         }
     }
+
+    @Override
+    public void handoverTo(String callId, PhoneAccountHandle destAcct, int videoState,
+                           Bundle extras) {
+        try {
+            Log.startSession("ICA.hT", mOwnerComponentName);
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.handoverTo(destAcct, videoState, extras);
+                    } else {
+                        Log.w(this, "handoverTo, unknown call id: %s", callId);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 58f489c..9d20d4a 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -677,6 +677,16 @@
         }
 
         @Override
+        public void onHandoverFailed(Call call, int error) {
+            notifyHandoverFailed(call, error);
+        }
+
+        @Override
+        public void onHandoverComplete(Call call) {
+            notifyHandoverComplete(call);
+        }
+
+        @Override
         public void onRttInitiationFailure(Call call, int reason) {
             notifyRttInitiationFailure(call, reason);
             updateCall(call, false, true);
@@ -1020,6 +1030,29 @@
                     });
         }
     }
+
+    private void notifyHandoverFailed(Call call, int error) {
+        if (!mInCallServices.isEmpty()) {
+            for (IInCallService inCallService : mInCallServices.values()) {
+                try {
+                    inCallService.onHandoverFailed(mCallIdMapper.getCallId(call), error);
+                } catch (RemoteException ignored) {
+                }
+            }
+        }
+    }
+
+    private void notifyHandoverComplete(Call call) {
+        if (!mInCallServices.isEmpty()) {
+            for (IInCallService inCallService : mInCallServices.values()) {
+                try {
+                    inCallService.onHandoverComplete(mCallIdMapper.getCallId(call));
+                } catch (RemoteException ignored) {
+                }
+            }
+        }
+    }
+
     /**
      * Unbinds an existing bound connection to the in-call app.
      */
diff --git a/src/com/android/server/telecom/InCallControllerFactory.java b/src/com/android/server/telecom/InCallControllerFactory.java
new file mode 100644
index 0000000..e384af7
--- /dev/null
+++ b/src/com/android/server/telecom/InCallControllerFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+
+/**
+ * Abstracts out creation of InCallController for unit test purposes.
+ */
+public interface InCallControllerFactory {
+    InCallController create(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
+            SystemStateProvider systemStateProvider, DefaultDialerCache defaultDialerCache,
+            Timeouts.Adapter timeoutsAdapter, EmergencyCallHelper emergencyCallHelper);
+}
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 0411355..02a3b77 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -33,6 +33,7 @@
 
     public static final class Sessions {
         public static final String ICA_ANSWER_CALL = "ICA.aC";
+        public static final String ICA_DEFLECT_CALL = "ICA.defC";
         public static final String ICA_REJECT_CALL = "ICA.rC";
         public static final String ICA_DISCONNECT_CALL = "ICA.dC";
         public static final String ICA_HOLD_CALL = "ICA.hC";
@@ -71,6 +72,7 @@
         public static final String REQUEST_UNHOLD = "REQUEST_UNHOLD";
         public static final String REQUEST_DISCONNECT = "REQUEST_DISCONNECT";
         public static final String REQUEST_ACCEPT = "REQUEST_ACCEPT";
+        public static final String REQUEST_DEFLECT = "REQUEST_DEFLECT";
         public static final String REQUEST_REJECT = "REQUEST_REJECT";
         public static final String START_DTMF = "START_DTMF";
         public static final String STOP_DTMF = "STOP_DTMF";
@@ -132,6 +134,8 @@
         public static final String ACCEPT_HANDOVER = "ACCEPT_HANDOVER";
         public static final String HANDOVER_COMPLETE = "HANDOVER_COMPLETE";
         public static final String HANDOVER_FAILED = "HANDOVER_FAILED";
+        public static final String START_RINBACK = "START_RINGBACK";
+        public static final String STOP_RINGBACK = "STOP_RINGBACK";
 
         public static class Timings {
             public static final String ACCEPT_TIMING = "accept";
@@ -146,6 +150,8 @@
             public static final String BLOCK_CHECK_FINISHED_TIMING = "block_check_finished";
             public static final String FILTERING_COMPLETED_TIMING = "filtering_completed";
             public static final String FILTERING_TIMED_OUT_TIMING = "filtering_timed_out";
+            public static final String START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING =
+                    "start_connection_to_request_disconnect";
 
             private static final TimedEventPair[] sTimedEvents = {
                     new TimedEventPair(REQUEST_ACCEPT, SET_ACTIVE, ACCEPT_TIMING),
@@ -166,6 +172,8 @@
                             FILTERING_COMPLETED_TIMING),
                     new TimedEventPair(FILTERING_INITIATED, FILTERING_TIMED_OUT,
                             FILTERING_TIMED_OUT_TIMING, 6000L),
+                    new TimedEventPair(START_CONNECTION, REQUEST_DISCONNECT,
+                            START_CONNECTION_TO_REQUEST_DISCONNECT_TIMING),
             };
         }
     }
@@ -190,7 +198,6 @@
     public static void initLogging(Context context) {
         android.telecom.Log.setTag(TAG);
         android.telecom.Log.setSessionContext(context);
-        android.telecom.Log.initMd5Sum();
         for (EventManager.TimedEventPair p : Events.Timings.sTimedEvents) {
             android.telecom.Log.addRequestResponsePair(p);
         }
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index c592fcd..3797c68 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -144,6 +144,9 @@
                         return;
                     }
 
+                    // TODO: Remove the assumption that phone numbers are either SIP or TEL.
+                    // This does not impact self-managed ConnectionServices as they do not use the
+                    // NewOutgoingCallIntentBroadcaster.
                     Uri resultHandleUri = Uri.fromParts(
                             mPhoneNumberUtilsAdapter.isUriNumber(resultNumber) ?
                                     PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
@@ -162,8 +165,7 @@
                     }
 
                     GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
-                    mCall.setNewOutgoingCallIntentBroadcastIsDone();
-                    mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
+                    placeOutgoingCallImmediately(mCall, resultHandleUri, gatewayInfo,
                             mIntent.getBooleanExtra(
                                     TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false),
                             mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
@@ -207,16 +209,10 @@
             if (Intent.ACTION_CALL.equals(action)
                     || Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
                 // Voicemail calls will be handled directly by the telephony connection manager
-                Log.i(this, "Placing call immediately instead of waiting for "
-                        + " OutgoingCallBroadcastReceiver: %s", intent);
-
-                // Since we are not going to go through "Outgoing call broadcast", make sure
-                // we mark it as ready.
-                mCall.setNewOutgoingCallIntentBroadcastIsDone();
 
                 boolean speakerphoneOn = mIntent.getBooleanExtra(
                         TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
-                mCallsManager.placeOutgoingCall(mCall, handle, null, speakerphoneOn,
+                placeOutgoingCallImmediately(mCall, handle, null, speakerphoneOn,
                         VideoProfile.STATE_AUDIO_ONLY);
 
                 return DisconnectCause.NOT_DISCONNECTED;
@@ -226,75 +222,88 @@
             }
         }
 
-        String number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);
-        if (TextUtils.isEmpty(number)) {
-            Log.w(this, "Empty number obtained from the call intent.");
-            return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;
-        }
-
-        boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
-        if (!isUriNumber) {
-            number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
-            number = mPhoneNumberUtilsAdapter.stripSeparators(number);
-        }
-
-        final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
-        Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);
-
-        rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
-        action = intent.getAction();
-        // True for certain types of numbers that are not intended to be intercepted or modified
-        // by third parties (e.g. emergency numbers).
-        boolean callImmediately = false;
-
-        if (Intent.ACTION_CALL.equals(action)) {
-            if (isPotentialEmergencyNumber) {
-                if (!mIsDefaultOrSystemPhoneApp) {
-                    Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
-                            + "unless caller is system or default dialer.", number, intent);
-                    launchSystemDialer(intent.getData());
-                    return DisconnectCause.OUTGOING_CANCELED;
-                } else {
-                    callImmediately = true;
-                }
-            }
-        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
-            if (!isPotentialEmergencyNumber) {
-                Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
-                        + "Intent %s.", number, intent);
-                return DisconnectCause.OUTGOING_CANCELED;
-            }
-            callImmediately = true;
-        } else {
-            Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
-            return DisconnectCause.INVALID_NUMBER;
-        }
-
-        // True for all managed calls, false for self-managed calls.
-        boolean sendNewOutgoingCallBroadcast = true;
         PhoneAccountHandle targetPhoneAccount = mIntent.getParcelableExtra(
                 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+        boolean isSelfManaged = false;
         if (targetPhoneAccount != null) {
             PhoneAccount phoneAccount =
                     mCallsManager.getPhoneAccountRegistrar().getPhoneAccountUnchecked(
                             targetPhoneAccount);
-            if (phoneAccount != null && phoneAccount.isSelfManaged()) {
-                callImmediately = true;
-                sendNewOutgoingCallBroadcast = false;
-                Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
+            if (phoneAccount != null) {
+                isSelfManaged = phoneAccount.isSelfManaged();
             }
         }
 
-        if (callImmediately) {
-            Log.i(this, "Placing call immediately instead of waiting for "
-                    + " OutgoingCallBroadcastReceiver: %s", intent);
+        String number = "";
+        // True for certain types of numbers that are not intended to be intercepted or modified
+        // by third parties (e.g. emergency numbers).
+        boolean callImmediately = false;
+        // True for all managed calls, false for self-managed calls.
+        boolean sendNewOutgoingCallBroadcast = true;
+        Uri callingAddress = handle;
+
+        if (!isSelfManaged) {
+            // Placing a managed call
+            number = mPhoneNumberUtilsAdapter.getNumberFromIntent(intent, mContext);
+            if (TextUtils.isEmpty(number)) {
+                Log.w(this, "Empty number obtained from the call intent.");
+                return DisconnectCause.NO_PHONE_NUMBER_SUPPLIED;
+            }
+
+            // TODO: Cleanup this dialing code; it makes the assumption that we're dialing with a
+            // SIP or TEL URI.
+            boolean isUriNumber = mPhoneNumberUtilsAdapter.isUriNumber(number);
+            if (!isUriNumber) {
+                number = mPhoneNumberUtilsAdapter.convertKeypadLettersToDigits(number);
+                number = mPhoneNumberUtilsAdapter.stripSeparators(number);
+            }
+
+            final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
+            Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);
+
+            rewriteCallIntentAction(intent, isPotentialEmergencyNumber);
+            action = intent.getAction();
+
+            if (Intent.ACTION_CALL.equals(action)) {
+                if (isPotentialEmergencyNumber) {
+                    if (!mIsDefaultOrSystemPhoneApp) {
+                        Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
+                                + "unless caller is system or default dialer.", number, intent);
+                        launchSystemDialer(intent.getData());
+                        return DisconnectCause.OUTGOING_CANCELED;
+                    } else {
+                        callImmediately = true;
+                    }
+                }
+            } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
+                if (!isPotentialEmergencyNumber) {
+                    Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
+                            + "Intent %s.", number, intent);
+                    return DisconnectCause.OUTGOING_CANCELED;
+                }
+                callImmediately = true;
+            } else {
+                Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
+                return DisconnectCause.INVALID_NUMBER;
+            }
+
+            // TODO: Support dialing using URIs instead of just assuming SIP or TEL.
             String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
+            callingAddress = Uri.fromParts(scheme, number, null);
+        } else {
+            // Self-managed call.
+            callImmediately = true;
+            sendNewOutgoingCallBroadcast = false;
+            Log.i(this, "Skipping NewOutgoingCallBroadcast for self-managed call.");
+        }
+
+        if (callImmediately) {
             boolean speakerphoneOn = mIntent.getBooleanExtra(
                     TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
             int videoState = mIntent.getIntExtra(
                     TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                     VideoProfile.STATE_AUDIO_ONLY);
-            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
+            placeOutgoingCallImmediately(mCall, callingAddress, null,
                     speakerphoneOn, videoState);
 
             // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
@@ -414,6 +423,16 @@
         return null;
     }
 
+    private void placeOutgoingCallImmediately(Call call, Uri handle, GatewayInfo gatewayInfo,
+            boolean speakerphoneOn, int videoState) {
+        Log.i(this,
+                "Placing call immediately instead of waiting for OutgoingCallBroadcastReceiver");
+        // Since we are not going to go through "Outgoing call broadcast", make sure
+        // we mark it as ready.
+        mCall.setNewOutgoingCallIntentBroadcastIsDone();
+        mCallsManager.placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState);
+    }
+
     private void launchSystemDialer(Uri handle) {
         Intent systemDialerIntent = new Intent();
         final Resources resources = mContext.getResources();
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 527e8e6..77598c8 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -301,7 +301,10 @@
         android.telecom.Call.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
 
         Connection.CAPABILITY_CAN_PULL_CALL,
-        android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL
+        android.telecom.Call.Details.CAPABILITY_CAN_PULL_CALL,
+
+        Connection.CAPABILITY_SUPPORT_DEFLECT,
+        android.telecom.Call.Details.CAPABILITY_SUPPORT_DEFLECT
     };
 
     private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
@@ -339,7 +342,10 @@
         android.telecom.Call.Details.PROPERTY_SELF_MANAGED,
 
         Connection.PROPERTY_ASSISTED_DIALING_USED,
-        android.telecom.Call.Details.PROPERTY_ASSISTED_DIALING_USED
+        android.telecom.Call.Details.PROPERTY_ASSISTED_DIALING_USED,
+
+        Connection.PROPERTY_IS_RTT,
+        android.telecom.Call.Details.PROPERTY_RTT
     };
 
     private static int convertConnectionToCallProperties(int connectionProperties) {
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 074f325..d84fca5 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -571,12 +571,34 @@
      * specified URI scheme.
      *
      * @param uriScheme The URI scheme.
+     * @param includeDisabledAccounts {@code} if disabled {@link PhoneAccount}s should be included
+     *      in the results.
+     * @param userHandle The {@link UserHandle} to retrieve the {@link PhoneAccount}s for.
      * @return The phone account handles.
      */
     public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
             String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle) {
+        return getCallCapablePhoneAccounts(uriScheme, includeDisabledAccounts, userHandle,
+                0 /* capabilities */);
+    }
+
+    /**
+     * Retrieves a list of all phone account call provider phone accounts supporting the
+     * specified URI scheme.
+     *
+     * @param uriScheme The URI scheme.
+     * @param includeDisabledAccounts {@code} if disabled {@link PhoneAccount}s should be included
+     *      in the results.
+     * @param userHandle The {@link UserHandle} to retrieve the {@link PhoneAccount}s for.
+     * @param capabilities Extra {@link PhoneAccount} capabilities which matching
+     *      {@link PhoneAccount}s must have.
+     * @return The phone account handles.
+     */
+    public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
+            String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle,
+            int capabilities) {
         return getPhoneAccountHandles(
-                PhoneAccount.CAPABILITY_CALL_PROVIDER,
+                PhoneAccount.CAPABILITY_CALL_PROVIDER | capabilities,
                 PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY /*excludedCapabilities*/,
                 uriScheme, null, includeDisabledAccounts, userHandle);
     }
diff --git a/src/com/android/server/telecom/PhoneStateBroadcaster.java b/src/com/android/server/telecom/PhoneStateBroadcaster.java
index a44fc03..d574589 100644
--- a/src/com/android/server/telecom/PhoneStateBroadcaster.java
+++ b/src/com/android/server/telecom/PhoneStateBroadcaster.java
@@ -106,7 +106,9 @@
         mCurrentState = phoneState;
 
         String callHandle = null;
-        if (call.getHandle() != null) {
+        // Only report phone numbers in phone state broadcast for regular mobile calls; do not
+        // include numbers from 3rd party apps.
+        if (!call.isSelfManaged() && call.getHandle() != null) {
             callHandle = call.getHandle().getSchemeSpecificPart();
         }
 
diff --git a/src/com/android/server/telecom/RingbackPlayer.java b/src/com/android/server/telecom/RingbackPlayer.java
index 47b6dfe..a8af3ac 100644
--- a/src/com/android/server/telecom/RingbackPlayer.java
+++ b/src/com/android/server/telecom/RingbackPlayer.java
@@ -16,6 +16,9 @@
 
 package com.android.server.telecom;
 
+import static com.android.server.telecom.LogUtils.Events.START_RINBACK;
+import static com.android.server.telecom.LogUtils.Events.STOP_RINGBACK;
+
 import com.android.internal.util.Preconditions;
 import android.telecom.Log;
 
@@ -64,7 +67,8 @@
 
         mCall = call;
         if (mTonePlayer == null) {
-            Log.d(this, "Playing the ringback tone for %s.", call);
+            Log.i(this, "Playing the ringback tone for %s.", call);
+            Log.addEvent(call, START_RINBACK);
             mTonePlayer = mPlayerFactory.createPlayer(InCallTonePlayer.TONE_RING_BACK);
             mTonePlayer.startTone();
         }
@@ -85,6 +89,7 @@
                 Log.w(this, "No player found to stop.");
             } else {
                 Log.i(this, "Stopping the ringback tone for %s.", call);
+                Log.addEvent(call, STOP_RINGBACK);
                 mTonePlayer.stopTone();
                 mTonePlayer = null;
             }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 36bb4ed..1d27f45 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -18,23 +18,29 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.Person;
 import android.content.Context;
 import android.os.VibrationEffect;
 import android.telecom.Log;
+import android.telecom.TelecomManager;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
+import android.media.Ringtone;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Vibrator;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
+
 /**
  * Controls the ringtone player.
  */
 @VisibleForTesting
 public class Ringer {
-    VibrationEffect mVibrationEffect;
+    @VisibleForTesting
+    public VibrationEffect mDefaultVibrationEffect;
 
     private static final long[] PULSE_PATTERN = {0,12,250,12,500, // priming  + interval
             50,50,50,50,50,50,50,50,50,50,50,50,50,50, // ease-in
@@ -46,6 +52,18 @@
             255, // Peak
             0}; // pause before repetition
 
+    private static final long[] SIMPLE_VIBRATION_PATTERN = {
+            0, // No delay before starting
+            1000, // How long to vibrate
+            1000, // How long to wait before vibrating again
+    };
+
+    private static final int[] SIMPLE_VIBRATION_AMPLITUDE = {
+            0, // No delay before starting
+            255, // Vibrate full amplitude
+            0, // No amplitude while waiting
+    };
+
     /**
      * Indicates that vibration should be repeated at element 5 in the {@link #PULSE_AMPLITUDE} and
      * {@link #PULSE_PATTERN} arrays.  This means repetition will happen for the main ease-in/peak
@@ -53,6 +71,8 @@
      */
     private static final int REPEAT_VIBRATION_AT = 5;
 
+    private static final int REPEAT_SIMPLE_VIBRATION_AT = 1;
+
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
@@ -107,8 +127,13 @@
         mRingtoneFactory = ringtoneFactory;
         mInCallController = inCallController;
 
-        mVibrationEffect = VibrationEffect.createWaveform(PULSE_PATTERN, PULSE_AMPLITUDE,
-                REPEAT_VIBRATION_AT);
+        if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
+            mDefaultVibrationEffect = VibrationEffect.createWaveform(SIMPLE_VIBRATION_PATTERN,
+                    SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
+        } else {
+            mDefaultVibrationEffect = VibrationEffect.createWaveform(PULSE_PATTERN,
+                    PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
+        }
     }
 
     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
@@ -125,6 +150,7 @@
         boolean isSelfManaged = foregroundCall.isSelfManaged();
 
         boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent;
+        boolean hasExternalRinger = hasExternalRinger(foregroundCall);
         // Acquire audio focus under any of the following conditions:
         // 1. Should ring for contact and there's an HFP device attached
         // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
@@ -136,19 +162,22 @@
         // Don't do call waiting operations or vibration unless these are false.
         boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
-        boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged;
+        boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
+                hasExternalRinger;
 
         if (endEarly) {
             if (letDialerHandleRinging) {
                 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING);
             }
             Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
-                    "isSelfManaged=%s", isTheaterModeOn, letDialerHandleRinging, isSelfManaged);
+                    "isSelfManaged=%s, hasExternalRinger=%s", isTheaterModeOn,
+                    letDialerHandleRinging, isSelfManaged, hasExternalRinger);
             return shouldAcquireAudioFocus;
         }
 
         stopCallWaiting();
 
+        VibrationEffect effect;
         if (isRingerAudible) {
             mRingingCall = foregroundCall;
             Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
@@ -157,15 +186,16 @@
             // ringtones should be available by the time this code executes. We can safely
             // request the custom ringtone from the call and expect it to be current.
             mRingtonePlayer.play(mRingtoneFactory, foregroundCall);
+            effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
         } else {
             Log.i(this, "startRinging: skipping because ringer would not be audible. " +
                     "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
                     isVolumeOverZero, shouldRingForContact, isRingtonePresent);
+            effect = mDefaultVibrationEffect;
         }
 
         if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
-            mVibratingCall = foregroundCall;
-            mVibrator.vibrate(mVibrationEffect, VIBRATION_ATTRIBUTES);
+            mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
             mIsVibrating = true;
         } else if (mIsVibrating) {
             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
@@ -174,6 +204,20 @@
         return shouldAcquireAudioFocus;
     }
 
+    private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) {
+        VibrationEffect effect = null;
+        Ringtone ringtone = factory.getRingtone(call);
+        Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null;
+        if (ringtoneUri != null) {
+            effect = VibrationEffect.get(ringtoneUri, mContext);
+        }
+
+        if (effect == null) {
+            effect = mDefaultVibrationEffect;
+        }
+        return effect;
+    }
+
     public void startCallWaiting(Call call) {
         if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
             return;
@@ -234,11 +278,22 @@
     private boolean shouldRingForContact(Uri contactUri) {
         final NotificationManager manager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        final Bundle extras = new Bundle();
+        final Bundle peopleExtras = new Bundle();
         if (contactUri != null) {
-            extras.putStringArray(Notification.EXTRA_PEOPLE, new String[] {contactUri.toString()});
+            ArrayList<Person> personList = new ArrayList<>();
+            personList.add(new Person.Builder().setUri(contactUri.toString()).build());
+            peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList);
         }
-        return manager.matchesCallFilter(extras);
+        return manager.matchesCallFilter(peopleExtras);
+    }
+
+    private boolean hasExternalRinger(Call foregroundCall) {
+        Bundle intentExtras = foregroundCall.getIntentExtras();
+        if (intentExtras != null) {
+            return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, false);
+        } else {
+            return false;
+        }
     }
 
     private boolean shouldVibrate(Context context, Call call) {
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index 47ba295..f15570b 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -261,6 +261,10 @@
         mUserHandle = userHandle;
     }
 
+    final UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
     final void incrementAssociatedCallCount() {
         mAssociatedCallCount++;
         Log.v(this, "Call count increment %d, %s", mAssociatedCallCount,
@@ -299,13 +303,14 @@
             mIsBindingAborted = true;
         } else {
             logServiceDisconnected("unbind");
+            unlinkDeathRecipient();
             mContext.unbindService(mServiceConnection);
             mServiceConnection = null;
             setBinder(null);
         }
     }
 
-    final ComponentName getComponentName() {
+    public final ComponentName getComponentName() {
         return mComponentName;
     }
 
@@ -367,9 +372,25 @@
      * Handles a service disconnection.
      */
     private void handleServiceDisconnected() {
+        unlinkDeathRecipient();
         setBinder(null);
     }
 
+    /**
+     * Handles un-linking the death recipient from the service's binder.
+     */
+    private void unlinkDeathRecipient() {
+        if (mServiceDeathRecipient != null && mBinder != null) {
+            boolean unlinked = mBinder.unlinkToDeath(mServiceDeathRecipient, 0);
+            if (!unlinked) {
+                Log.i(this, "unlinkDeathRecipient: failed to unlink %s", mComponentName);
+            }
+            mServiceDeathRecipient = null;
+        } else {
+            Log.w(this, "unlinkDeathRecipient: death recipient is null.");
+        }
+    }
+
     private void clearAbort() {
         mIsBindingAborted = false;
     }
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index d5fafdf..0db1764 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom;
 
 import static android.Manifest.permission.CALL_PHONE;
+import static android.Manifest.permission.CALL_PRIVILEGED;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
 import static android.Manifest.permission.READ_PHONE_STATE;
@@ -783,15 +784,17 @@
          * @see android.telecom.TelecomManager#endCall
          */
         @Override
-        public boolean endCall() {
+        public boolean endCall(String callingPackage) {
             try {
                 Log.startSession("TSI.eC");
                 synchronized (mLock) {
-                    enforceModifyPermission();
+                    if (!enforceAnswerCallPermission(callingPackage, Binder.getCallingUid())) {
+                        throw new SecurityException("requires ANSWER_PHONE_CALLS permission");
+                    }
 
                     long token = Binder.clearCallingIdentity();
                     try {
-                        return endCallInternal();
+                        return endCallInternal(callingPackage);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
@@ -997,15 +1000,9 @@
         public boolean isTtySupported(String callingPackage) {
             try {
                 Log.startSession("TSI.iTS");
-                if (!isPrivilegedDialerCalling(callingPackage)) {
-                    try {
-                        enforceModifyPermission(
-                                "isTtySupported requires MODIFY_PHONE_STATE permission.");
-                    } catch (SecurityException e) {
-                        EventLog.writeEvent(0x534e4554, "62347125", "isTtySupported: " +
-                                callingPackage);
-                        throw e;
-                    }
+                if (!canReadPhoneState(callingPackage, "isTtySupported")) {
+                    throw new SecurityException("Only default dialer or an app with" +
+                            "READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE can call this api");
                 }
 
                 synchronized (mLock) {
@@ -1099,6 +1096,55 @@
         }
 
         /**
+         * @see android.telecom.TelecomManager#acceptHandover
+         */
+        @Override
+        public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) {
+            try {
+                Log.startSession("TSI.aHO");
+                synchronized (mLock) {
+                    Log.i(this, "acceptHandover; srcAddr=%s, videoState=%s, dest=%s",
+                            Log.pii(srcAddr), VideoProfile.videoStateToString(videoState),
+                            destAcct);
+
+                    if (destAcct != null && destAcct.getComponentName() != null) {
+                        mAppOpsManager.checkPackage(
+                                Binder.getCallingUid(),
+                                destAcct.getComponentName().getPackageName());
+                        enforceUserHandleMatchesCaller(destAcct);
+                        enforcePhoneAccountIsRegisteredEnabled(destAcct,
+                                Binder.getCallingUserHandle());
+                        if (isSelfManagedConnectionService(destAcct)) {
+                            // Self-managed phone account, ensure it has MANAGE_OWN_CALLS.
+                            mContext.enforceCallingOrSelfPermission(
+                                    android.Manifest.permission.MANAGE_OWN_CALLS,
+                                    "Self-managed phone accounts must have MANAGE_OWN_CALLS " +
+                                            "permission.");
+                        }
+                        if (!enforceAcceptHandoverPermission(
+                                destAcct.getComponentName().getPackageName(),
+                                Binder.getCallingUid())) {
+                            throw new SecurityException("App must be granted runtime "
+                                    + "ACCEPT_HANDOVER permission.");
+                        }
+
+                        long token = Binder.clearCallingIdentity();
+                        try {
+                            mCallsManager.acceptHandover(srcAddr, videoState, destAcct);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    } else {
+                        Log.w(this, "Null phoneAccountHandle. Ignoring request " +
+                                "to handover the call");
+                    }
+                }
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        /**
          * @see android.telecom.TelecomManager#addNewUnknownCall
          */
         @Override
@@ -1201,12 +1247,20 @@
 
                 final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
                         PackageManager.PERMISSION_GRANTED;
+                // The Emergency Dialer has call privileged permission and uses this to place
+                // emergency calls.  We ensure permission checks in
+                // NewOutgoingCallIntentBroadcaster#process pass by sending this to
+                // Telecom as an ACTION_CALL_PRIVILEGED intent (which makes sense since the
+                // com.android.phone process has that permission).
+                final boolean hasCallPrivilegedPermission = mContext.checkCallingPermission(
+                        CALL_PRIVILEGED) == PackageManager.PERMISSION_GRANTED;
 
                 synchronized (mLock) {
                     final UserHandle userHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
                     try {
-                        final Intent intent = new Intent(Intent.ACTION_CALL, handle);
+                        final Intent intent = new Intent(hasCallPrivilegedPermission ?
+                                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                         if (extras != null) {
                             extras.setDefusable(true);
                             intent.putExtras(extras);
@@ -1214,7 +1268,8 @@
                         mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                 .processIntent(
                                         intent, callingPackage, isSelfManaged ||
-                                                (hasCallAppOp && hasCallPermission));
+                                                (hasCallAppOp && hasCallPermission),
+                                        true /* isLocalInvocation */);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
@@ -1430,6 +1485,24 @@
         return true;
     }
 
+    /**
+     * @return {@code true} if the app has the handover permission and has received runtime
+     * permission to perform that operation, {@code false}.
+     * @throws SecurityException same as {@link Context#enforceCallingOrSelfPermission}
+     */
+    private boolean enforceAcceptHandoverPermission(String packageName, int uid) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCEPT_HANDOVER,
+                "App requires ACCEPT_HANDOVER permission to accept handovers.");
+
+        final int opCode = AppOpsManager.permissionToOpCode(Manifest.permission.ACCEPT_HANDOVER);
+        if (opCode != AppOpsManager.OP_ACCEPT_HANDOVER || (
+                mAppOpsManager.checkOp(opCode, uid, packageName)
+                        != AppOpsManager.MODE_ALLOWED)) {
+            return false;
+        }
+        return true;
+    }
+
     private Context mContext;
     private AppOpsManager mAppOpsManager;
     private PackageManager mPackageManager;
@@ -1510,7 +1583,7 @@
         }
     }
 
-    private boolean endCallInternal() {
+    private boolean endCallInternal(String callingPackage) {
         // Always operate on the foreground call if one exists, otherwise get the first call in
         // priority order by call-state.
         Call call = mCallsManager.getForegroundCall();
@@ -1524,10 +1597,15 @@
         }
 
         if (call != null) {
+            if (call.isEmergencyCall()) {
+                android.util.EventLog.writeEvent(0x534e4554, "132438333", -1, "");
+                return false;
+            }
+
             if (call.getState() == CallState.RINGING) {
-                call.reject(false /* rejectWithMessage */, null);
+                call.reject(false /* rejectWithMessage */, null, callingPackage);
             } else {
-                call.disconnect();
+                call.disconnect(0 /* disconnectionTimeout */, callingPackage);
             }
             return true;
         }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 31afbba..3fd0e21 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -19,6 +19,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.ui.IncomingCallNotifier;
@@ -185,6 +186,8 @@
             AudioServiceFactory audioServiceFactory,
             BluetoothPhoneServiceImplFactory
                     bluetoothPhoneServiceImplFactory,
+            ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory
+                    connectionServiceFocusManagerFactory,
             Timeouts.Adapter timeoutsAdapter,
             AsyncRingtonePlayer asyncRingtonePlayer,
             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
@@ -227,6 +230,10 @@
                 new BluetoothAdapterProxy(), mLock);
         BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
                 bluetoothDeviceManager, new Timeouts.Adapter());
+        BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
+                bluetoothDeviceManager, bluetoothRouteManager);
+        mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
+
         WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
         SystemStateProvider systemStateProvider = new SystemStateProvider(mContext);
 
@@ -236,6 +243,17 @@
         EmergencyCallHelper emergencyCallHelper = new EmergencyCallHelper(mContext,
                 mContext.getResources().getString(R.string.ui_default_package), timeoutsAdapter);
 
+        InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
+            @Override
+            public InCallController create(Context context, SyncRoot lock,
+                    CallsManager callsManager, SystemStateProvider systemStateProvider,
+                    DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
+                    EmergencyCallHelper emergencyCallHelper) {
+                return new InCallController(context, lock, callsManager, systemStateProvider,
+                        defaultDialerCache, timeoutsAdapter, emergencyCallHelper);
+            }
+        };
+
         mCallsManager = new CallsManager(
                 mContext,
                 mLock,
@@ -246,6 +264,7 @@
                 headsetMediaButtonFactory,
                 proximitySensorManagerFactory,
                 inCallWakeLockControllerFactory,
+                connectionServiceFocusManagerFactory,
                 audioServiceFactory,
                 bluetoothRouteManager,
                 wiredHeadsetManager,
@@ -256,7 +275,9 @@
                 phoneNumberUtilsAdapter,
                 emergencyCallHelper,
                 toneGeneratorFactory,
-                clockProxy);
+                clockProxy,
+                bluetoothStateReceiver,
+                inCallControllerFactory);
 
         mIncomingCallNotifier = incomingCallNotifier;
         incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/src/com/android/server/telecom/VideoProviderProxy.java b/src/com/android/server/telecom/VideoProviderProxy.java
index 3354622..7d90a6d 100644
--- a/src/com/android/server/telecom/VideoProviderProxy.java
+++ b/src/com/android/server/telecom/VideoProviderProxy.java
@@ -129,6 +129,13 @@
         mCurrentUserProxy = currentUserProxy;
     }
 
+    public void clearVideoCallback() {
+        try {
+            mConectionServiceVideoProvider.removeVideoCallback(mVideoCallListenerBinder);
+        } catch (RemoteException e) {
+        }
+    }
+
     /**
      * IVideoCallback stub implementation.  An instance of this class receives callbacks from the
      * {@code ConnectionService}'s video provider.
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 665b956..26fe94e 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -71,7 +72,7 @@
                                     mConnectedDevicesByAddress.values());
                             mConnectedDevicesByAddress.clear();
                             for (BluetoothDevice device : devicesToRemove) {
-                                mBluetoothRouteManager.onDeviceLost(device);
+                                mBluetoothRouteManager.onDeviceLost(device.getAddress());
                             }
                         }
                     } finally {
@@ -80,49 +81,6 @@
                 }
            };
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.startSession("BM.oR");
-            try {
-                String action = intent.getAction();
-
-                if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
-                    int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                            BluetoothHeadset.STATE_DISCONNECTED);
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
-                    if (device == null) {
-                        Log.w(BluetoothDeviceManager.this, "Got null device from broadcast. " +
-                                "Ignoring.");
-                        return;
-                    }
-
-                    Log.i(BluetoothDeviceManager.this, "Device %s changed state to %d",
-                            device.getAddress(), bluetoothHeadsetState);
-
-                    synchronized (mLock) {
-                        if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) {
-                            if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) {
-                                mConnectedDevicesByAddress.put(device.getAddress(), device);
-                                mBluetoothRouteManager.onDeviceAdded(device);
-                            }
-                        } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED
-                                || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) {
-                            if (mConnectedDevicesByAddress.containsKey(device.getAddress())) {
-                                mConnectedDevicesByAddress.remove(device.getAddress());
-                                mBluetoothRouteManager.onDeviceLost(device);
-                            }
-                        }
-                    }
-                }
-            } finally {
-                Log.endSession();
-            }
-        }
-    };
-
     private final LinkedHashMap<String, BluetoothDevice> mConnectedDevicesByAddress =
             new LinkedHashMap<>();
     private final TelecomSystem.SyncRoot mLock;
@@ -138,9 +96,6 @@
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                     BluetoothProfile.HEADSET);
         }
-        IntentFilter intentFilter =
-                new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        context.registerReceiver(mReceiver, intentFilter);
     }
 
     public void setBluetoothRouteManager(BluetoothRouteManager brm) {
@@ -151,6 +106,10 @@
         return mConnectedDevicesByAddress.size();
     }
 
+    public Collection<BluetoothDevice> getConnectedDevices() {
+        return mConnectedDevicesByAddress.values();
+    }
+
     public String getMostRecentlyConnectedDevice(String excludeAddress) {
         String result = null;
         synchronized (mLock) {
@@ -170,4 +129,31 @@
     public void setHeadsetServiceForTesting(BluetoothHeadsetProxy bluetoothHeadset) {
         mBluetoothHeadsetService = bluetoothHeadset;
     }
+
+    public BluetoothDevice getDeviceFromAddress(String address) {
+        return mConnectedDevicesByAddress.get(address);
+    }
+
+    void onDeviceConnected(BluetoothDevice device) {
+        synchronized (mLock) {
+            if (mBluetoothHeadsetService == null) {
+                Log.w(this, "Headset service null when receiving device added broadcast");
+                return;
+            }
+
+            if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) {
+                mConnectedDevicesByAddress.put(device.getAddress(), device);
+                mBluetoothRouteManager.onDeviceAdded(device.getAddress());
+            }
+        }
+    }
+
+    void onDeviceDisconnected(BluetoothDevice device) {
+        synchronized (mLock) {
+            if (mConnectedDevicesByAddress.containsKey(device.getAddress())) {
+                mConnectedDevicesByAddress.remove(device.getAddress());
+                mBluetoothRouteManager.onDeviceLost(device.getAddress());
+            }
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 0e07922..f72c342 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -18,10 +18,7 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Message;
 import android.telecom.Log;
 import android.telecom.Logging.Session;
@@ -36,6 +33,9 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -44,7 +44,8 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class BluetoothRouteManager extends StateMachine {
@@ -59,66 +60,25 @@
          put(HFP_IS_ON, "HFP_IS_ON");
          put(HFP_LOST, "HFP_LOST");
          put(CONNECTION_TIMEOUT, "CONNECTION_TIMEOUT");
+         put(GET_CURRENT_STATE, "GET_CURRENT_STATE");
          put(RUN_RUNNABLE, "RUN_RUNNABLE");
     }};
 
-    // Constants for compatiblity with current CARSM/CARPA
-    // TODO: delete and replace with new direct interface to CARPA.
-    public static final int BLUETOOTH_UNINITIALIZED = 0;
-    public static final int BLUETOOTH_DISCONNECTED = 1;
-    public static final int BLUETOOTH_DEVICE_CONNECTED = 2;
-    public static final int BLUETOOTH_AUDIO_PENDING = 3;
-    public static final int BLUETOOTH_AUDIO_CONNECTED = 4;
-
     public static final String AUDIO_OFF_STATE_NAME = "AudioOff";
     public static final String AUDIO_CONNECTING_STATE_NAME_PREFIX = "Connecting";
     public static final String AUDIO_CONNECTED_STATE_NAME_PREFIX = "Connected";
 
+    // Timeout for querying the current state from the state machine handler.
+    private static final int GET_STATE_TIMEOUT = 1000;
+
     public interface BluetoothStateListener {
-        void onBluetoothStateChange(int oldState, int newState);
+        void onBluetoothDeviceListChanged();
+        void onBluetoothActiveDevicePresent();
+        void onBluetoothActiveDeviceGone();
+        void onBluetoothAudioConnected();
+        void onBluetoothAudioDisconnected();
     }
 
-    // Broadcast receiver to receive audio state change broadcasts from the BT stack
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.startSession("BRM.oR");
-            try {
-                String action = intent.getAction();
-
-                if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
-                    int bluetoothHeadsetAudioState =
-                            intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                    BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                    if (device == null) {
-                        Log.w(BluetoothRouteManager.this, "Got null device from broadcast. " +
-                                "Ignoring.");
-                        return;
-                    }
-
-                    Log.i(BluetoothRouteManager.this, "Device %s transitioned to audio state %d",
-                            device.getAddress(), bluetoothHeadsetAudioState);
-                    Session session = Log.createSubsession();
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = session;
-                    args.arg2 = device.getAddress();
-                    switch (bluetoothHeadsetAudioState) {
-                        case BluetoothHeadset.STATE_AUDIO_CONNECTED:
-                            sendMessage(HFP_IS_ON, args);
-                            break;
-                        case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
-                            sendMessage(HFP_LOST, args);
-                            break;
-                    }
-                }
-            } finally {
-                Log.endSession();
-            }
-        }
-    };
-
     /**
      * Constants representing messages sent to the state machine.
      * Messages are expected to be sent with {@link SomeArgs} as the obj.
@@ -144,6 +104,10 @@
     // No args; only used internally
     public static final int CONNECTION_TIMEOUT = 300;
 
+    // Get the current state and send it through the BlockingQueue<IState> provided as the object
+    // arg.
+    public static final int GET_CURRENT_STATE = 400;
+
     // arg2: Runnable
     public static final int RUN_RUNNABLE = 9001;
 
@@ -165,6 +129,9 @@
                 disconnectAudio();
             }
             cleanupStatesForDisconnectedDevices();
+            if (mListener != null) {
+                mListener.onBluetoothAudioDisconnected();
+            }
         }
 
         @Override
@@ -178,31 +145,15 @@
             try {
                 switch (msg.what) {
                     case NEW_DEVICE_CONNECTED:
-                        // If the device isn't new, don't bother passing it up.
-                        if (addDevice((String) args.arg2)) {
-                            // TODO: replace with new interface
-                            if (mDeviceManager.getNumConnectedDevices() == 1) {
-                                mListener.onBluetoothStateChange(
-                                        BLUETOOTH_DISCONNECTED, BLUETOOTH_DEVICE_CONNECTED);
-                            }
-                        }
+                        addDevice((String) args.arg2);
                         break;
                     case LOST_DEVICE:
-                        // If the device has already been removed, don't bother passing it up.
-                        if (removeDevice((String) args.arg2)) {
-                            // TODO: replace with new interface
-                            if (mDeviceManager.getNumConnectedDevices() == 0) {
-                                mListener.onBluetoothStateChange(
-                                        BLUETOOTH_DEVICE_CONNECTED, BLUETOOTH_DISCONNECTED);
-                            }
-                        }
+                        removeDevice((String) args.arg2);
                         break;
                     case CONNECT_HFP:
                         String actualAddress = connectHfpAudio((String) args.arg2);
 
                         if (actualAddress != null) {
-                            mListener.onBluetoothStateChange(BLUETOOTH_DEVICE_CONNECTED,
-                                    BLUETOOTH_AUDIO_PENDING);
                             transitionTo(getConnectingStateForAddress(actualAddress,
                                     "AudioOff/CONNECT_HFP"));
                         } else {
@@ -218,8 +169,6 @@
                         String retryAddress = connectHfpAudio((String) args.arg2, args.argi1);
 
                         if (retryAddress != null) {
-                            mListener.onBluetoothStateChange(BLUETOOTH_DEVICE_CONNECTED,
-                                    BLUETOOTH_AUDIO_PENDING);
                             transitionTo(getConnectingStateForAddress(retryAddress,
                                     "AudioOff/RETRY_HFP_CONNECTION"));
                         } else {
@@ -232,14 +181,16 @@
                     case HFP_IS_ON:
                         String address = (String) args.arg2;
                         Log.w(LOG_TAG, "HFP audio unexpectedly turned on from device %s", address);
-                        mListener.onBluetoothStateChange(BLUETOOTH_DEVICE_CONNECTED,
-                                BLUETOOTH_AUDIO_CONNECTED);
                         transitionTo(getConnectedStateForAddress(address, "AudioOff/HFP_IS_ON"));
                         break;
                     case HFP_LOST:
                         Log.i(LOG_TAG, "Received HFP off for device %s while HFP off.",
                                 (String) args.arg2);
                         break;
+                    case GET_CURRENT_STATE:
+                        BlockingQueue<IState> sink = (BlockingQueue<IState>) args.arg3;
+                        sink.offer(this);
+                        break;
                 }
             } finally {
                 args.recycle();
@@ -267,6 +218,8 @@
             sendMessageDelayed(CONNECTION_TIMEOUT, args,
                     mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
                             mContext.getContentResolver()));
+            // Pretend like audio is connected when communicating w/ CARSM.
+            mListener.onBluetoothAudioConnected();
         }
 
         @Override
@@ -287,31 +240,12 @@
                 switch (msg.what) {
                     case NEW_DEVICE_CONNECTED:
                         // If the device isn't new, don't bother passing it up.
-                        if (addDevice(address)) {
-                            // TODO: replace with new interface
-                            if (mDeviceManager.getNumConnectedDevices() == 1) {
-                                Log.w(LOG_TAG, "Newly connected device is only device" +
-                                        " while audio pending.");
-                            }
-                        }
+                        addDevice(address);
                         break;
                     case LOST_DEVICE:
                         removeDevice((String) args.arg2);
-
                         if (Objects.equals(address, mDeviceAddress)) {
-                            String newAddress = connectHfpAudio(null);
-                            if (newAddress != null) {
-                                mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_PENDING,
-                                        BLUETOOTH_AUDIO_PENDING);
-                                transitionTo(getConnectingStateForAddress(newAddress,
-                                        "AudioConnecting/LOST_DEVICE"));
-                            } else {
-                                int numConnectedDevices = mDeviceManager.getNumConnectedDevices();
-                                mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_PENDING,
-                                        numConnectedDevices == 0 ? BLUETOOTH_DISCONNECTED :
-                                                BLUETOOTH_DEVICE_CONNECTED);
-                                transitionTo(mAudioOffState);
-                            }
+                            transitionToActualState();
                         }
                         break;
                     case CONNECT_HFP:
@@ -322,8 +256,6 @@
                         String actualAddress = connectHfpAudio(address);
 
                         if (actualAddress != null) {
-                            mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_PENDING,
-                                    BLUETOOTH_AUDIO_PENDING);
                             transitionTo(getConnectingStateForAddress(actualAddress,
                                     "AudioConnecting/CONNECT_HFP"));
                         } else {
@@ -333,8 +265,6 @@
                         break;
                     case DISCONNECT_HFP:
                         disconnectAudio();
-                        mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_PENDING,
-                                BLUETOOTH_DEVICE_CONNECTED);
                         transitionTo(mAudioOffState);
                         break;
                     case RETRY_HFP_CONNECTION:
@@ -353,7 +283,7 @@
                     case CONNECTION_TIMEOUT:
                         Log.i(LOG_TAG, "Connection with device %s timed out.",
                                 mDeviceAddress);
-                        transitionToActualState(BLUETOOTH_AUDIO_PENDING);
+                        transitionToActualState();
                         break;
                     case HFP_IS_ON:
                         if (Objects.equals(mDeviceAddress, address)) {
@@ -365,19 +295,21 @@
                             transitionTo(getConnectedStateForAddress(address,
                                     "AudioConnecting/HFP_IS_ON"));
                         }
-                        mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_PENDING,
-                                BLUETOOTH_AUDIO_CONNECTED);
                         break;
                     case HFP_LOST:
                         if (Objects.equals(mDeviceAddress, address)) {
                             Log.i(LOG_TAG, "Connection with device %s failed.",
                                     mDeviceAddress);
-                            transitionToActualState(BLUETOOTH_AUDIO_PENDING);
+                            transitionToActualState();
                         } else {
                             Log.w(LOG_TAG, "Got HFP lost message for device %s while" +
                                     " connecting to %s.", address, mDeviceAddress);
                         }
                         break;
+                    case GET_CURRENT_STATE:
+                        BlockingQueue<IState> sink = (BlockingQueue<IState>) args.arg3;
+                        sink.offer(this);
+                        break;
                 }
             } finally {
                 args.recycle();
@@ -406,6 +338,7 @@
             // Remove and add to ensure that the device is at the top.
             mMostRecentlyUsedDevices.remove(mDeviceAddress);
             mMostRecentlyUsedDevices.add(mDeviceAddress);
+            mListener.onBluetoothAudioConnected();
         }
 
         @Override
@@ -420,32 +353,12 @@
             try {
                 switch (msg.what) {
                     case NEW_DEVICE_CONNECTED:
-                        // If the device isn't new, don't bother passing it up.
-                        if (addDevice(address)) {
-                            // TODO: Replace with new interface
-                            if (mDeviceManager.getNumConnectedDevices() == 1) {
-                                Log.w(LOG_TAG, "Newly connected device is only" +
-                                        " device while audio connected.");
-                            }
-                        }
+                        addDevice(address);
                         break;
                     case LOST_DEVICE:
                         removeDevice((String) args.arg2);
-
                         if (Objects.equals(address, mDeviceAddress)) {
-                            String newAddress = connectHfpAudio(null);
-                            if (newAddress != null) {
-                                mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_CONNECTED,
-                                        BLUETOOTH_AUDIO_PENDING);
-                                transitionTo(getConnectingStateForAddress(newAddress,
-                                        "AudioConnected/LOST_DEVICE"));
-                            } else {
-                                int numConnectedDevices = mDeviceManager.getNumConnectedDevices();
-                                mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_CONNECTED,
-                                        numConnectedDevices == 0 ? BLUETOOTH_DISCONNECTED :
-                                                BLUETOOTH_DEVICE_CONNECTED);
-                                transitionTo(mAudioOffState);
-                            }
+                            transitionToActualState();
                         }
                         break;
                     case CONNECT_HFP:
@@ -456,8 +369,6 @@
                         String actualAddress = connectHfpAudio(address);
 
                         if (actualAddress != null) {
-                            mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_CONNECTED,
-                                    BLUETOOTH_AUDIO_PENDING);
                             transitionTo(getConnectingStateForAddress(address,
                                     "AudioConnected/CONNECT_HFP"));
                         } else {
@@ -467,8 +378,6 @@
                         break;
                     case DISCONNECT_HFP:
                         disconnectAudio();
-                        mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_CONNECTED,
-                                BLUETOOTH_DEVICE_CONNECTED);
                         transitionTo(mAudioOffState);
                         break;
                     case RETRY_HFP_CONNECTION:
@@ -477,8 +386,6 @@
                         } else {
                             String retryAddress = connectHfpAudio(address, args.argi1);
                             if (retryAddress != null) {
-                                mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_CONNECTED,
-                                        BLUETOOTH_AUDIO_PENDING);
                                 transitionTo(getConnectingStateForAddress(retryAddress,
                                         "AudioConnected/RETRY_HFP_CONNECTION"));
                             } else {
@@ -502,21 +409,16 @@
                     case HFP_LOST:
                         if (Objects.equals(mDeviceAddress, address)) {
                             Log.i(LOG_TAG, "HFP connection with device %s lost.", mDeviceAddress);
-                            String nextAddress = connectHfpAudio(null, mDeviceAddress);
-                            if (nextAddress == null) {
-                                Log.i(LOG_TAG, "No suitable fallback device. Going to AUDIO_OFF.");
-                                transitionToActualState(BLUETOOTH_AUDIO_CONNECTED);
-                            } else {
-                                mListener.onBluetoothStateChange(BLUETOOTH_AUDIO_CONNECTED,
-                                        BLUETOOTH_AUDIO_PENDING);
-                                transitionTo(getConnectingStateForAddress(nextAddress,
-                                        "AudioConnected/HFP_LOST"));
-                            }
+                            transitionToActualState();
                         } else {
                             Log.w(LOG_TAG, "Got HFP lost message for device %s while" +
                                     " connected to %s.", address, mDeviceAddress);
                         }
                         break;
+                    case GET_CURRENT_STATE:
+                        BlockingQueue<IState> sink = (BlockingQueue<IState>) args.arg3;
+                        sink.offer(this);
+                        break;
                 }
             } finally {
                 args.recycle();
@@ -537,6 +439,8 @@
 
     private BluetoothStateListener mListener;
     private BluetoothDeviceManager mDeviceManager;
+    // Tracks the active device in the BT stack.
+    private BluetoothDevice mActiveDeviceCache = null;
 
     public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock,
             BluetoothDeviceManager deviceManager, Timeouts.Adapter timeoutsAdapter) {
@@ -547,9 +451,6 @@
         mDeviceManager.setBluetoothRouteManager(this);
         mTimeoutsAdapter = timeoutsAdapter;
 
-        IntentFilter intentFilter = new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
-        context.registerReceiver(mReceiver, intentFilter);
-
         mAudioOffState = new AudioOffState();
         addState(mAudioOffState);
         setInitialState(mAudioOffState);
@@ -593,20 +494,25 @@
      * @return
      */
     public boolean isBluetoothAudioConnectedOrPending() {
-        IState[] state = new IState[] {null};
-        CountDownLatch latch = new CountDownLatch(1);
-        Runnable r = () -> {
-            state[0] = getCurrentState();
-            latch.countDown();
-        };
-        sendMessage(RUN_RUNNABLE, r);
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = Log.createSubsession();
+        BlockingQueue<IState> stateQueue = new LinkedBlockingQueue<>();
+        // Use arg3 because arg2 is reserved for the device address
+        args.arg3 = stateQueue;
+        sendMessage(GET_CURRENT_STATE, args);
+
         try {
-            latch.await(1000, TimeUnit.MILLISECONDS);
+            IState currentState = stateQueue.poll(GET_STATE_TIMEOUT, TimeUnit.MILLISECONDS);
+            if (currentState == null) {
+                Log.w(LOG_TAG, "Failed to get a state from the state machine in time -- Handler " +
+                        "stuck?");
+                return false;
+            }
+            return currentState != mAudioOffState;
         } catch (InterruptedException e) {
             Log.w(LOG_TAG, "isBluetoothAudioConnectedOrPending -- interrupted getting state");
             return false;
         }
-        return (state[0] != null) && (state[0] != mAudioOffState);
     }
 
     /**
@@ -635,30 +541,43 @@
         mListener = listener;
     }
 
-    public void onDeviceAdded(BluetoothDevice newDevice) {
+    public void onDeviceAdded(String newDeviceAddress) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = Log.createSubsession();
-        args.arg2 = newDevice.getAddress();
+        args.arg2 = newDeviceAddress;
         sendMessage(NEW_DEVICE_CONNECTED, args);
+
+        mListener.onBluetoothDeviceListChanged();
     }
 
-    public void onDeviceLost(BluetoothDevice lostDevice) {
+    public void onDeviceLost(String lostDeviceAddress) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = Log.createSubsession();
-        args.arg2 = lostDevice.getAddress();
+        args.arg2 = lostDeviceAddress;
         sendMessage(LOST_DEVICE, args);
+
+        mListener.onBluetoothDeviceListChanged();
+    }
+
+    public void onActiveDeviceChanged(BluetoothDevice device) {
+        BluetoothDevice oldActiveDevice = mActiveDeviceCache;
+        mActiveDeviceCache = device;
+        if ((oldActiveDevice == null) ^ (device == null)) {
+            if (device == null) {
+                mListener.onBluetoothActiveDeviceGone();
+            } else {
+                mListener.onBluetoothActiveDevicePresent();
+            }
+        }
+    }
+
+    public Collection<BluetoothDevice> getConnectedDevices() {
+        return Collections.unmodifiableCollection(
+                new ArrayList<>(mDeviceManager.getConnectedDevices()));
     }
 
     private String connectHfpAudio(String address) {
-        return connectHfpAudio(address, 0, null);
-    }
-
-    private String connectHfpAudio(String address, int retryCount) {
-        return connectHfpAudio(address, retryCount, null);
-    }
-
-    private String connectHfpAudio(String address, String excludeAddress) {
-        return connectHfpAudio(address, 0, excludeAddress);
+        return connectHfpAudio(address, 0);
     }
 
     /**
@@ -667,28 +586,32 @@
      * Telecom from within it.
      * @param address The address that should be tried first. May be null.
      * @param retryCount The number of times this connection attempt has been retried.
-     * @param excludeAddress Don't connect to this address.
      * @return The address of the device that's actually being connected to, or null if no
      * connection was successful.
      */
-    private String connectHfpAudio(String address, int retryCount, String excludeAddress) {
-        BluetoothHeadsetProxy bluetoothHeadset = mDeviceManager.getHeadsetService();
-        if (bluetoothHeadset == null) {
-            Log.i(this, "connectHfpAudio: no headset service available.");
-            return null;
-        }
-        List<BluetoothDevice> deviceList = bluetoothHeadset.getConnectedDevices();
+    private String connectHfpAudio(String address, int retryCount) {
+        Collection<BluetoothDevice> deviceList = getConnectedDevices();
         Optional<BluetoothDevice> matchingDevice = deviceList.stream()
                 .filter(d -> Objects.equals(d.getAddress(), address))
                 .findAny();
 
-        String actualAddress = matchingDevice.isPresent() ?
-                address : getPreferredDevice(excludeAddress);
+        String actualAddress = matchingDevice.isPresent()
+                ? address : getActiveDeviceAddress();
+        if (actualAddress == null) {
+            Log.i(this, "No device specified and BT stack has no active device."
+                    + " Using arbitrary device");
+            if (deviceList.size() > 0) {
+                actualAddress = deviceList.iterator().next().getAddress();
+            } else {
+                Log.i(this, "No devices available at all. Not connecting.");
+                return null;
+            }
+        }
         if (!matchingDevice.isPresent()) {
             Log.i(this, "No device with address %s available. Using %s instead.",
                     address, actualAddress);
         }
-        if (actualAddress != null && !connectAudio(actualAddress)) {
+        if (!connectAudio(actualAddress)) {
             boolean shouldRetry = retryCount < MAX_CONNECTION_RETRIES;
             Log.w(LOG_TAG, "Could not connect to %s. Will %s", actualAddress,
                     shouldRetry ? "retry" : "not retry");
@@ -707,33 +630,19 @@
         return actualAddress;
     }
 
-    private String getPreferredDevice(String excludeAddress) {
-        String preferredDevice = null;
-        for (String address : mMostRecentlyUsedDevices) {
-            if (!Objects.equals(excludeAddress, address)) {
-                preferredDevice = address;
-            }
-        }
-        if (preferredDevice == null) {
-            return mDeviceManager.getMostRecentlyConnectedDevice(excludeAddress);
-        }
-        return preferredDevice;
+    private String getActiveDeviceAddress() {
+        return mActiveDeviceCache == null ? null : mActiveDeviceCache.getAddress();
     }
 
-    private void transitionToActualState(int currentBtState) {
+    private void transitionToActualState() {
         BluetoothDevice possiblyAlreadyConnectedDevice = getBluetoothAudioConnectedDevice();
         if (possiblyAlreadyConnectedDevice != null) {
             Log.i(LOG_TAG, "Device %s is already connected; going to AudioConnected.",
                     possiblyAlreadyConnectedDevice);
             transitionTo(getConnectedStateForAddress(
                     possiblyAlreadyConnectedDevice.getAddress(), "transitionToActualState"));
-            // TODO: replace with new interface
-            mListener.onBluetoothStateChange(currentBtState, BLUETOOTH_AUDIO_CONNECTED);
         } else {
             transitionTo(mAudioOffState);
-            mListener.onBluetoothStateChange(currentBtState,
-                    mDeviceManager.getNumConnectedDevices() > 0 ?
-                            BLUETOOTH_DEVICE_CONNECTED : BLUETOOTH_DISCONNECTED);
         }
     }
 
@@ -751,7 +660,8 @@
 
         for (int i = 0; i < deviceList.size(); i++) {
             BluetoothDevice device = deviceList.get(i);
-            boolean isAudioOn = bluetoothHeadset.isAudioConnected(device);
+            boolean isAudioOn = bluetoothHeadset.getAudioState(device)
+                    != BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
             Log.v(this, "isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn
                     + "for headset: " + device);
             if (isAudioOn) {
@@ -761,14 +671,42 @@
         return null;
     }
 
+    /**
+     * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an
+     * active connection.
+     *
+     * @return true if in-band ringing is enabled, false if in-band ringing is disabled
+     */
+    @VisibleForTesting
+    public boolean isInbandRingingEnabled() {
+        BluetoothHeadsetProxy bluetoothHeadset = mDeviceManager.getHeadsetService();
+        if (bluetoothHeadset == null) {
+            Log.i(this, "isInbandRingingEnabled: no headset service available.");
+            return false;
+        }
+        return bluetoothHeadset.isInbandRingingEnabled();
+    }
+
     private boolean connectAudio(String address) {
         BluetoothHeadsetProxy bluetoothHeadset = mDeviceManager.getHeadsetService();
         if (bluetoothHeadset == null) {
             Log.w(this, "Trying to connect audio but no headset service exists.");
             return false;
         }
-        // TODO: update once connectAudio supports passing in a device.
-        return bluetoothHeadset.connectAudio();
+        BluetoothDevice device = mDeviceManager.getDeviceFromAddress(address);
+        if (device == null) {
+            Log.w(this, "Attempting to turn on audio for a disconnected device");
+            return false;
+        }
+        boolean success = bluetoothHeadset.setActiveDevice(device);
+        if (!success) {
+            Log.w(LOG_TAG, "Couldn't set active device to %s", address);
+            return false;
+        }
+        if (!bluetoothHeadset.isAudioOn()) {
+            return bluetoothHeadset.connectAudio();
+        }
+        return true;
     }
 
     private void disconnectAudio() {
@@ -852,4 +790,9 @@
                 break;
         }
     }
+
+    @VisibleForTesting
+    public void setActiveDeviceCacheForTesting(BluetoothDevice device) {
+        mActiveDeviceCache = device;
+    }
 }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
new file mode 100644
index 0000000..f9c6437
--- /dev/null
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -0,0 +1,141 @@
+/*
+ * 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.server.telecom.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.telecom.Log;
+import android.telecom.Logging.Session;
+
+import com.android.internal.os.SomeArgs;
+
+import static com.android.server.telecom.bluetooth.BluetoothRouteManager.HFP_IS_ON;
+import static com.android.server.telecom.bluetooth.BluetoothRouteManager.HFP_LOST;
+
+
+public class BluetoothStateReceiver extends BroadcastReceiver {
+    private static final String LOG_TAG = BluetoothStateReceiver.class.getSimpleName();
+    public static final IntentFilter INTENT_FILTER;
+    static {
+        INTENT_FILTER = new IntentFilter();
+        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+    }
+
+    // If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since
+    // other apps could be turning it on and off. We don't want to interfere.
+    private boolean mIsInCall = false;
+    private final BluetoothRouteManager mBluetoothRouteManager;
+    private final BluetoothDeviceManager mBluetoothDeviceManager;
+
+    public void onReceive(Context context, Intent intent) {
+        Log.startSession("BSR.oR");
+        try {
+            String action = intent.getAction();
+            switch (action) {
+                case BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED:
+                    handleAudioStateChanged(intent);
+                    break;
+                case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
+                    handleConnectionStateChanged(intent);
+                    break;
+                case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
+                    handleActiveDeviceChanged(intent);
+                    break;
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
+
+    private void handleAudioStateChanged(Intent intent) {
+        if (!mIsInCall) {
+            Log.i(LOG_TAG, "Ignoring BT audio state change since we're not in a call");
+            return;
+        }
+        int bluetoothHeadsetAudioState =
+                intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+        BluetoothDevice device =
+                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+        if (device == null) {
+            Log.w(LOG_TAG, "Got null device from broadcast. " +
+                    "Ignoring.");
+            return;
+        }
+
+        Log.i(LOG_TAG, "Device %s transitioned to audio state %d",
+                device.getAddress(), bluetoothHeadsetAudioState);
+        Session session = Log.createSubsession();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = session;
+        args.arg2 = device.getAddress();
+        switch (bluetoothHeadsetAudioState) {
+            case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+                mBluetoothRouteManager.sendMessage(HFP_IS_ON, args);
+                break;
+            case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+                mBluetoothRouteManager.sendMessage(HFP_LOST, args);
+                break;
+        }
+    }
+
+    private void handleConnectionStateChanged(Intent intent) {
+        int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                BluetoothHeadset.STATE_DISCONNECTED);
+        BluetoothDevice device =
+                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+        if (device == null) {
+            Log.w(LOG_TAG, "Got null device from broadcast. " +
+                    "Ignoring.");
+            return;
+        }
+
+        Log.i(LOG_TAG, "Device %s changed state to %d",
+                device.getAddress(), bluetoothHeadsetState);
+
+        if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) {
+            mBluetoothDeviceManager.onDeviceConnected(device);
+        } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED
+                || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) {
+            mBluetoothDeviceManager.onDeviceDisconnected(device);
+        }
+    }
+
+    private void handleActiveDeviceChanged(Intent intent) {
+        BluetoothDevice device =
+                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+        Log.i(LOG_TAG, "Device %s is now the preferred HFP device", device);
+        mBluetoothRouteManager.onActiveDeviceChanged(device);
+    }
+
+    public BluetoothStateReceiver(BluetoothDeviceManager deviceManager,
+            BluetoothRouteManager routeManager) {
+        mBluetoothDeviceManager = deviceManager;
+        mBluetoothRouteManager = routeManager;
+    }
+
+    public void setIsInCall(boolean isInCall) {
+        mIsInCall = isInCall;
+    }
+}
diff --git a/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java b/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java
index 2f78d23..748d15e 100644
--- a/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java
+++ b/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java
@@ -17,12 +17,19 @@
 package com.android.server.telecom.callfiltering;
 
 import android.content.Context;
+import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.BlockedNumberContract;
 import android.telecom.Log;
 import android.telecom.Logging.Session;
+import android.telecom.TelecomManager;
 
+import com.android.internal.telephony.CallerInfo;
 import com.android.server.telecom.Call;
+import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.LogUtils;
+import com.android.server.telecom.settings.BlockedNumbersUtil;
 
 /**
  * An {@link AsyncTask} that checks if a call needs to be blocked.
@@ -37,10 +44,13 @@
     private Session mBackgroundTaskSubsession;
     private Session mPostExecuteSubsession;
     private CallFilterResultCallback mCallback;
+    private CallerInfoLookupHelper mCallerInfoLookupHelper;
 
-    public AsyncBlockCheckFilter(Context context, BlockCheckerAdapter blockCheckerAdapter) {
+    public AsyncBlockCheckFilter(Context context, BlockCheckerAdapter blockCheckerAdapter,
+            CallerInfoLookupHelper callerInfoLookupHelper) {
         mContext = context;
         mBlockCheckerAdapter = blockCheckerAdapter;
+        mCallerInfoLookupHelper = callerInfoLookupHelper;
     }
 
     @Override
@@ -49,7 +59,29 @@
         mIncomingCall = call;
         String number = call.getHandle() == null ?
                 null : call.getHandle().getSchemeSpecificPart();
-        this.execute(number);
+        if (BlockedNumbersUtil.isEnhancedCallBlockingEnabledByPlatform(mContext)) {
+            int presentation = mIncomingCall.getHandlePresentation();
+            if (presentation == TelecomManager.PRESENTATION_ALLOWED) {
+                mCallerInfoLookupHelper.startLookup(call.getHandle(),
+                        new CallerInfoLookupHelper.OnQueryCompleteListener() {
+                            @Override
+                            public void onCallerInfoQueryComplete(Uri handle, CallerInfo info) {
+                                boolean contactExists = info == null ? false : info.contactExists;
+                                execute(number, String.valueOf(presentation),
+                                        String.valueOf(contactExists));
+                            }
+
+                            @Override
+                            public void onContactPhotoQueryComplete(Uri handle, CallerInfo info) {
+                                // ignore
+                            }
+                        });
+            } else {
+                this.execute(number, String.valueOf(presentation));
+            }
+        } else {
+            this.execute(number);
+        }
     }
 
     @Override
@@ -63,7 +95,16 @@
         try {
             Log.continueSession(mBackgroundTaskSubsession, "ABCF.dIB");
             Log.addEvent(mIncomingCall, LogUtils.Events.BLOCK_CHECK_INITIATED);
-            return mBlockCheckerAdapter.isBlocked(mContext, params[0]);
+            Bundle extras = new Bundle();
+            if (params.length > 1) {
+                extras.putInt(BlockedNumberContract.EXTRA_CALL_PRESENTATION,
+                        Integer.valueOf(params[1]));
+            }
+            if (params.length > 2) {
+                extras.putBoolean(BlockedNumberContract.EXTRA_CONTACT_EXIST,
+                        Boolean.valueOf(params[2]));
+            }
+            return mBlockCheckerAdapter.isBlocked(mContext, params[0], extras);
         } finally {
             Log.endSession();
         }
diff --git a/src/com/android/server/telecom/callfiltering/BlockCheckerAdapter.java b/src/com/android/server/telecom/callfiltering/BlockCheckerAdapter.java
index f15a507..8c74fa9 100644
--- a/src/com/android/server/telecom/callfiltering/BlockCheckerAdapter.java
+++ b/src/com/android/server/telecom/callfiltering/BlockCheckerAdapter.java
@@ -17,13 +17,22 @@
 package com.android.server.telecom.callfiltering;
 
 import android.content.Context;
+import android.os.Bundle;
 
 import com.android.internal.telephony.BlockChecker;
 
 public class BlockCheckerAdapter {
     public BlockCheckerAdapter() { }
 
-    public boolean isBlocked(Context context, String number) {
-        return BlockChecker.isBlocked(context, number);
+    /**
+     * Check whether the number is blocked.
+     *
+     * @param context the context of the caller.
+     * @param number the number to check.
+     * @param extras the extra attribute of the number.
+     * @return {@code true} if the number is blocked. {@code false} otherwise.
+     */
+    public boolean isBlocked(Context context, String number, Bundle extras) {
+        return BlockChecker.isBlocked(context, number, extras);
     }
 }
diff --git a/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java b/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java
index 41e4351..2ac82dc 100644
--- a/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java
+++ b/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java
@@ -43,7 +43,7 @@
                     @Override
                     public void onCallerInfoQueryComplete(Uri handle, CallerInfo info) {
                         CallFilteringResult result;
-                        if (Objects.equals(callHandle, handle)) {
+                        if ((handle != null) && Objects.equals(callHandle, handle)) {
                             if (info != null && info.shouldSendToVoicemail) {
                                 result = new CallFilteringResult(
                                         false, // shouldAllowCall
@@ -62,8 +62,16 @@
                             Log.addEvent(call, LogUtils.Events.DIRECT_TO_VM_FINISHED, result);
                             callback.onCallFilteringComplete(call, result);
                         } else {
+                            result = new CallFilteringResult(
+                                true, // shouldAllowCall
+                                false, // shouldReject
+                                true, // shouldAddToCallLog
+                                true // shouldShowNotification
+                            );
+                            Log.addEvent(call, LogUtils.Events.DIRECT_TO_VM_FINISHED, result);
                             Log.w(this, "CallerInfo lookup returned with a different handle than " +
                                     "what was passed in. Was %s, should be %s", handle, callHandle);
+                            callback.onCallFilteringComplete(call, result);
                         }
                     }
 
diff --git a/src/com/android/server/telecom/components/NonScrollListView.java b/src/com/android/server/telecom/components/NonScrollListView.java
new file mode 100644
index 0000000..ce6e638
--- /dev/null
+++ b/src/com/android/server/telecom/components/NonScrollListView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.components;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+public class NonScrollListView extends ListView {
+
+    public NonScrollListView(Context context) {
+        super(context);
+    }
+
+    public NonScrollListView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int customizedHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+                Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
+        super.onMeasure(widthMeasureSpec, customizedHeightMeasureSpec);
+        ViewGroup.LayoutParams params = getLayoutParams();
+        params.height = getMeasuredHeight();
+    }
+}
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 19dd404..9a09636 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -23,6 +23,7 @@
 import android.media.IAudioService;
 import android.media.ToneGenerator;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -35,6 +36,7 @@
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
@@ -46,6 +48,7 @@
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.ProximitySensorManager;
+import com.android.server.telecom.R;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TelecomWakeLock;
 import com.android.server.telecom.Timeouts;
@@ -82,6 +85,9 @@
             NotificationChannelManager notificationChannelManager =
                     new NotificationChannelManager();
             notificationChannelManager.createChannels(context);
+
+            boolean shouldPauseBetweenRingtoneRepeat = context.getResources().getBoolean(
+                    R.bool.should_pause_between_ringtone_repeats);
             TelecomSystem.setInstance(
                     new TelecomSystem(
                             context,
@@ -97,7 +103,9 @@
                             },
                             new CallerInfoAsyncQueryFactory() {
                                 @Override
-                                public CallerInfoAsyncQuery startQuery(int token, Context context,
+                                public CallerInfoAsyncQuery startQuery(
+                                        int token,
+                                        Context context,
                                         String number,
                                         CallerInfoAsyncQuery.OnQueryCompleteListener listener,
                                         Object cookie) {
@@ -133,7 +141,7 @@
                             new InCallWakeLockControllerFactory() {
                                 @Override
                                 public InCallWakeLockController create(Context context,
-                                        CallsManager callsManager) {
+                                                                       CallsManager callsManager) {
                                     return new InCallWakeLockController(
                                             new TelecomWakeLock(context,
                                                     PowerManager.FULL_WAKE_LOCK,
@@ -159,8 +167,17 @@
                                             phoneAccountRegistrar);
                                 }
                             },
+                            new ConnectionServiceFocusManager
+                                    .ConnectionServiceFocusManagerFactory() {
+                                @Override
+                                public ConnectionServiceFocusManager create(
+                                        ConnectionServiceFocusManager.CallsManagerRequester requester,
+                                        Looper looper) {
+                                    return new ConnectionServiceFocusManager(requester, looper);
+                                }
+                            },
                             new Timeouts.Adapter(),
-                            new AsyncRingtonePlayer(),
+                            new AsyncRingtonePlayer(shouldPauseBetweenRingtoneRepeat),
                             new PhoneNumberUtilsAdapterImpl(),
                             new IncomingCallNotifier(context),
                             ToneGenerator::new,
diff --git a/src/com/android/server/telecom/components/UserCallActivity.java b/src/com/android/server/telecom/components/UserCallActivity.java
index dbee450..ca8fef7 100644
--- a/src/com/android/server/telecom/components/UserCallActivity.java
+++ b/src/com/android/server/telecom/components/UserCallActivity.java
@@ -74,7 +74,7 @@
             // ActivityThread.ActivityClientRecord#intent directly.
             // Modifying directly may be a potential risk when relaunching this activity.
             new UserCallIntentProcessor(this, userHandle).processIntent(new Intent(intent),
-                    getCallingPackage(), true /* hasCallAppOp*/);
+                    getCallingPackage(), true /* hasCallAppOp*/, false /* isLocalInvocation */);
         } finally {
             Log.endSession();
             wakelock.release();
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index a95768e..0c8525f 100644
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -16,17 +16,9 @@
 
 package com.android.server.telecom.components;
 
-import com.android.server.telecom.CallIntentProcessor;
-import com.android.server.telecom.R;
-import com.android.server.telecom.TelephonyUtil;
-import com.android.server.telecom.UserUtil;
-import com.android.settingslib.RestrictedLockUtils;
-import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
-import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -37,7 +29,12 @@
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
-import android.widget.Toast;
+
+import com.android.server.telecom.CallIntentProcessor;
+import com.android.server.telecom.R;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.TelephonyUtil;
+import com.android.server.telecom.UserUtil;
 
 // TODO: Needed for move to system service: import com.android.internal.R;
 
@@ -72,9 +69,18 @@
      * Processes intents sent to the activity.
      *
      * @param intent The intent.
+     * @param callingPackageName The package name of the calling app.
+     * @param canCallNonEmergency {@code true} if the caller is permitted to call non-emergency
+     *                            numbers.
+     * @param isLocalInvocation {@code true} if the caller is within the system service (i.e. the
+     *                            caller is {@link com.android.server.telecom.TelecomServiceImpl})
+     *                            and we can skip the re-broadcast of the intent to Telecom.
+     *                            When {@code false}, we need to re-broadcast the intent to Telcom
+     *                            to trampoline it to the system service where the Telecom
+     *                            service resides.
      */
     public void processIntent(Intent intent, String callingPackageName,
-            boolean canCallNonEmergency) {
+            boolean canCallNonEmergency, boolean isLocalInvocation) {
         // Ensure call intents are not processed on devices that are not capable of calling.
         if (!isVoiceCapable()) {
             return;
@@ -85,19 +91,20 @@
         if (Intent.ACTION_CALL.equals(action) ||
                 Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                 Intent.ACTION_CALL_EMERGENCY.equals(action)) {
-            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency);
+            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
+                    isLocalInvocation);
         }
     }
 
     private void processOutgoingCallIntent(Intent intent, String callingPackageName,
-            boolean canCallNonEmergency) {
+            boolean canCallNonEmergency, boolean isLocalInvocation) {
         Uri handle = intent.getData();
         String scheme = handle.getScheme();
         String uriString = handle.getSchemeSpecificPart();
 
-        if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
-            handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(uriString) ?
-                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, uriString, null);
+        // Ensure sip URIs dialed using TEL scheme get converted to SIP scheme.
+        if (PhoneAccount.SCHEME_TEL.equals(scheme) && PhoneNumberUtils.isUriNumber(uriString)) {
+            handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
         }
 
         // Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this check in a managed
@@ -118,8 +125,16 @@
                     return;
                 } else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
                         mUserHandle)) {
-                    RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext,
-                            EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN);
+                    final DevicePolicyManager dpm =
+                            mContext.getSystemService(DevicePolicyManager.class);
+                    if (dpm == null) {
+                        return;
+                    }
+                    final Intent adminSupportIntent = dpm.createAdminSupportIntent(
+                            UserManager.DISALLOW_OUTGOING_CALLS);
+                    if (adminSupportIntent != null) {
+                        mContext.startActivity(adminSupportIntent);
+                    }
                     return;
                 }
             }
@@ -144,7 +159,7 @@
         // Save the user handle of current user before forwarding the intent to primary user.
         intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
 
-        sendBroadcastToReceiver(intent);
+        sendIntentToDestination(intent, isLocalInvocation);
     }
 
     private boolean isDefaultOrSystemDialer(String callingPackageName) {
@@ -174,14 +189,28 @@
     }
 
     /**
-     * Trampolines the intent to the broadcast receiver that runs only as the primary user.
+     * Potentially trampolines the intent to the broadcast receiver that runs only as the primary
+     * user.  If the caller is local to the Telecom service, we send the intent to Telecom without
+     * rebroadcasting it.
      */
-    private boolean sendBroadcastToReceiver(Intent intent) {
+    private boolean sendIntentToDestination(Intent intent, boolean isLocalInvocation) {
         intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, false);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setClass(mContext, PrimaryCallReceiver.class);
-        Log.d(this, "Sending broadcast as user to CallReceiver");
-        mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        if (isLocalInvocation) {
+            // We are invoking this from TelecomServiceImpl, so TelecomSystem is available.  Don't
+            // bother trampolining the intent, just sent it directly to the call intent processor.
+            // TODO: We should not be using an intent here; this whole flows needs cleanup.
+            Log.i(this, "sendIntentToDestination: send intent to Telecom directly.");
+            synchronized (TelecomSystem.getInstance().getLock()) {
+                TelecomSystem.getInstance().getCallIntentProcessor().processIntent(intent);
+            }
+        } else {
+            // We're calling from the UserCallActivity, so the TelecomSystem is not in the same
+            // process; we need to trampoline to TelecomSystem in the system server process.
+            Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
+            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+        }
         return true;
     }
 
diff --git a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
index 7090adb..ae7e661 100644
--- a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
+++ b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
@@ -19,7 +19,9 @@
 import android.annotation.Nullable;
 import android.app.ActionBar;
 import android.app.AlertDialog;
+import android.app.Fragment;
 import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.app.ListActivity;
 import android.app.LoaderManager;
 import android.content.BroadcastReceiver;
@@ -137,6 +139,9 @@
 
         updateButterBar();
 
+        updateEnhancedCallBlockingFragment(
+                BlockedNumbersUtil.isEnhancedCallBlockingEnabledByPlatform(this));
+
         mBlockingStatusReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -176,6 +181,30 @@
         }
     }
 
+    /**
+     * Update the visibility of {@link EnhancedCallBlockingFragment}.
+     */
+    private void updateEnhancedCallBlockingFragment(boolean show) {
+        FragmentManager fragmentManager = getFragmentManager();
+        Fragment fragment = fragmentManager.findFragmentById(R.id.enhanced_call_blocking_container);
+        if (!show && fragment == null) {
+            // Nothing to show, so bail early.
+            return;
+        }
+        final FragmentTransaction transaction = fragmentManager.beginTransaction();
+        if (show) {
+            if (fragment == null) {
+                fragment = new EnhancedCallBlockingFragment();
+                transaction.add(R.id.enhanced_call_blocking_container, fragment);
+            } else {
+                transaction.show(fragment);
+            }
+        } else {
+            transaction.hide(fragment);
+        }
+        transaction.commit();
+    }
+
     @Override
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
         return new CursorLoader(this, BlockedNumberContract.BlockedNumbers.CONTENT_URI,
diff --git a/src/com/android/server/telecom/settings/BlockedNumbersUtil.java b/src/com/android/server/telecom/settings/BlockedNumbersUtil.java
index b75c4b3..4f45720 100644
--- a/src/com/android/server/telecom/settings/BlockedNumbersUtil.java
+++ b/src/com/android/server/telecom/settings/BlockedNumbersUtil.java
@@ -16,18 +16,32 @@
 
 package com.android.server.telecom.settings;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.provider.BlockedNumberContract.SystemContract;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.text.BidiFormatter;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextDirectionHeuristics;
 import android.widget.Toast;
+
+import com.android.server.telecom.R;
+import com.android.server.telecom.ui.NotificationChannelManager;
+
 import java.util.Locale;
 
 public final class BlockedNumbersUtil {
     private BlockedNumbersUtil() {}
 
+    private static final int EMERGENCY_CALL_NOTIFICATION = 150;
+
     /**
      * @return locale and default to US if no locale was returned.
      */
@@ -67,4 +81,82 @@
                 messageSpannable,
                 Toast.LENGTH_SHORT).show();
     }
+
+    /**
+     * Updates an emergency call notification
+     *
+     * @param context context to start CallBlockDisabledActivity.
+     * @param showNotification if {@code true} show notification, {@code false} cancel notification.
+     */
+    public static void updateEmergencyCallNotification(Context context, boolean showNotification) {
+        NotificationManager notificationManager =
+                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        if (showNotification) {
+            Intent intent = new Intent(context, CallBlockDisabledActivity.class);
+            PendingIntent pendingIntent = PendingIntent.getActivity(
+                    context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+            String title = context.getString(
+                    R.string.phone_strings_call_blocking_turned_off_notification_title_txt);
+            String message = context.getString(
+                    R.string.phone_strings_call_blocking_turned_off_notification_text_txt);
+            Notification.Builder builder = new Notification.Builder(context);
+            Notification notification = builder.setSmallIcon(android.R.drawable.stat_sys_warning)
+                    .setTicker(message)
+                    .setContentTitle(title)
+                    .setContentText(message)
+                    .setContentIntent(pendingIntent)
+                    .setShowWhen(true)
+                    .setChannel(NotificationChannelManager.CHANNEL_ID_CALL_BLOCKING)
+                    .build();
+
+            notification.flags |= Notification.FLAG_NO_CLEAR;
+            notificationManager.notifyAsUser(null /* tag */ , EMERGENCY_CALL_NOTIFICATION,
+                    notification, new UserHandle(UserHandle.USER_OWNER));
+        } else {
+            notificationManager.cancelAsUser(null /* tag */ , EMERGENCY_CALL_NOTIFICATION,
+                    new UserHandle(UserHandle.USER_OWNER));
+        }
+    }
+
+    /**
+     * Returns the platform configuration for whether to enable enhanced call blocking feature.
+     *
+     * @param context the application context
+     * @return If {@code true} means enhanced call blocking enabled by platform,
+     *            {@code false} otherwise.
+     */
+    public static boolean isEnhancedCallBlockingEnabledByPlatform(Context context) {
+        CarrierConfigManager configManager = (CarrierConfigManager) context.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle carrierConfig = configManager.getConfig();
+        if (carrierConfig == null) {
+            carrierConfig = configManager.getDefaultConfig();
+        }
+        return carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL);
+    }
+
+    /**
+     * Get the blocking setting status from {@link BlockedNumberProvider} SharedPreferences.
+     *
+     * @param context the application context
+     * @param key preference key of SharedPreferences.
+     * @return If {@code true} means the key enabled in the SharedPreferences,
+     *            {@code false} otherwise.
+     */
+    public static boolean getEnhancedBlockSetting(Context context, String key) {
+        return SystemContract.getEnhancedBlockSetting(context, key);
+    }
+
+    /**
+     * Set the blocking setting status to {@link BlockedNumberProvider} SharedPreferences.
+     *
+     * @param context the application context
+     * @param key preference key of SharedPreferences.
+     * @param value the register value to the SharedPreferences.
+     */
+    public static void setEnhancedBlockSetting(Context context, String key, boolean value) {
+        SystemContract.setEnhancedBlockSetting(context, key, value);
+    }
 }
diff --git a/src/com/android/server/telecom/settings/CallBlockDisabledActivity.java b/src/com/android/server/telecom/settings/CallBlockDisabledActivity.java
new file mode 100644
index 0000000..5f42b37
--- /dev/null
+++ b/src/com/android/server/telecom/settings/CallBlockDisabledActivity.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.settings;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.provider.BlockedNumberContract;
+
+import com.android.server.telecom.R;
+
+/**
+ * Shows a dialog when user taps an notification in notification tray.
+ */
+public class CallBlockDisabledActivity extends Activity {
+    private AlertDialog mDialog;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        showCallBlockingOffDialog();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        dismissCallBlockingOffDialog();
+    }
+
+    /**
+     * Shows the dialog that notifies user that [Call Blocking] has been turned off.
+     */
+    private void showCallBlockingOffDialog() {
+        if (isShowingCallBlockingOffDialog()) {
+            return;
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        mDialog = builder
+                .setTitle(R.string.phone_strings_emergency_call_made_dialog_title_txt)
+                .setMessage(R.string
+                        .phone_strings_emergency_call_made_dialog_call_blocking_text_txt)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        BlockedNumbersUtil.setEnhancedBlockSetting(
+                                CallBlockDisabledActivity.this,
+                                BlockedNumberContract.SystemContract
+                                        .ENHANCED_SETTING_KEY_SHOW_EMERGENCY_CALL_NOTIFICATION,
+                                false);
+                        BlockedNumbersUtil.updateEmergencyCallNotification(
+                                CallBlockDisabledActivity.this, false);
+                        finish();
+                    }
+                })
+                .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                    @Override
+                    public void onCancel(DialogInterface dialog) {
+                        finish();
+                    }
+                })
+                .create();
+        mDialog.setCanceledOnTouchOutside(false);
+        mDialog.show();
+    }
+
+    /**
+     * Dismisses the dialog that notifies user that [Call Blocking] has been turned off.
+     */
+    private void dismissCallBlockingOffDialog() {
+        if (isShowingCallBlockingOffDialog()) {
+            mDialog.dismiss();
+        }
+        mDialog = null;
+    }
+
+    /**
+     * Checks whether the dialog is currently showing.
+     *
+     * @return true if the dialog is currently showing, false otherwise.
+     */
+    private boolean isShowingCallBlockingOffDialog() {
+        return (mDialog != null && mDialog.isShowing());
+    }
+}
diff --git a/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java b/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java
new file mode 100644
index 0000000..219c3e9
--- /dev/null
+++ b/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.settings;
+
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.provider.BlockedNumberContract.SystemContract;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.server.telecom.R;
+
+public class EnhancedCallBlockingFragment extends PreferenceFragment
+        implements Preference.OnPreferenceChangeListener {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.enhanced_call_blocking_settings);
+
+        setOnPreferenceChangeListener(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED);
+        setOnPreferenceChangeListener(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE);
+        setOnPreferenceChangeListener(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE);
+        setOnPreferenceChangeListener(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN);
+    }
+
+    /**
+     * Set OnPreferenceChangeListener for the preference.
+     */
+    private void setOnPreferenceChangeListener(String key) {
+        SwitchPreference pref = (SwitchPreference) findPreference(key);
+        if (pref != null) {
+            pref.setOnPreferenceChangeListener(this);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        updateEnhancedBlockPref(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNREGISTERED);
+        updateEnhancedBlockPref(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PRIVATE);
+        updateEnhancedBlockPref(SystemContract.ENHANCED_SETTING_KEY_BLOCK_PAYPHONE);
+        updateEnhancedBlockPref(SystemContract.ENHANCED_SETTING_KEY_BLOCK_UNKNOWN);
+    }
+
+    /**
+     * Update preference checked status.
+     */
+    private void updateEnhancedBlockPref(String key) {
+        SwitchPreference pref = (SwitchPreference) findPreference(key);
+        if (pref != null) {
+            pref.setChecked(BlockedNumbersUtil.getEnhancedBlockSetting(getActivity(), key));
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object objValue) {
+        BlockedNumbersUtil.setEnhancedBlockSetting(getActivity(), preference.getKey(),
+                (boolean) objValue);
+        return true;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.xml.layout_customized_listview,
+                container, false);
+    }
+}
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index 9851674..7fee263 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -66,6 +66,7 @@
 
 import java.lang.Override;
 import java.lang.String;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -134,7 +135,7 @@
     // Used to track the number of missed calls.
     private ConcurrentMap<UserHandle, AtomicInteger> mMissedCallCounts;
 
-    private UserHandle userToLoadAfterBootComplete;
+    private List<UserHandle> mUsersToLoadAfterBootComplete = new ArrayList<>();
 
     public MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
             DefaultDialerCache defaultDialerCache) {
@@ -271,7 +272,7 @@
     }
 
     private void showMissedCallNotification(@NonNull CallInfo callInfo, UserHandle userHandle) {
-        Log.i(this, "showMissedCallNotification()");
+        Log.i(this, "showMissedCallNotification: userHandle=%d", userHandle.getIdentifier());
         mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0));
         int missCallCounts = mMissedCallCounts.get(userHandle).incrementAndGet();
 
@@ -517,7 +518,7 @@
             UserHandle userHandle) {
         Intent intent = new Intent(action, data, mContext, TelecomBroadcastReceiver.class);
         intent.putExtra(TelecomBroadcastIntentProcessor.EXTRA_USERHANDLE, userHandle);
-        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     /**
@@ -537,10 +538,14 @@
     @Override
     public void reloadAfterBootComplete(final CallerInfoLookupHelper callerInfoLookupHelper,
             CallInfoFactory callInfoFactory) {
-        if (userToLoadAfterBootComplete != null) {
-            reloadFromDatabase(callerInfoLookupHelper,
-                    callInfoFactory, userToLoadAfterBootComplete);
-            userToLoadAfterBootComplete = null;
+        if (!mUsersToLoadAfterBootComplete.isEmpty()) {
+            for (UserHandle handle : mUsersToLoadAfterBootComplete) {
+                Log.i(this, "reloadAfterBootComplete: user=%d", handle.getIdentifier());
+                reloadFromDatabase(callerInfoLookupHelper, callInfoFactory, handle);
+            }
+            mUsersToLoadAfterBootComplete.clear();
+        } else {
+            Log.i(this, "reloadAfterBootComplete: no user(s) to check; skipping reload.");
         }
     }
     /**
@@ -549,11 +554,12 @@
     @Override
     public void reloadFromDatabase(final CallerInfoLookupHelper callerInfoLookupHelper,
             CallInfoFactory callInfoFactory, final UserHandle userHandle) {
-        Log.d(this, "reloadFromDatabase()...");
+        Log.d(this, "reloadFromDatabase: user=%d", userHandle.getIdentifier());
         if (TelecomSystem.getInstance() == null || !TelecomSystem.getInstance().isBootComplete()) {
-            Log.i(this, "Boot not yet complete -- call log db may not be available. Deferring " +
-                    "loading until boot complete.");
-            userToLoadAfterBootComplete = userHandle;
+            Log.i(this, "reloadFromDatabase: Boot not yet complete -- call log db may not be "
+                    + "available. Deferring loading until boot complete for user %d",
+                    userHandle.getIdentifier());
+            mUsersToLoadAfterBootComplete.add(userHandle);
             return;
         }
 
@@ -577,6 +583,7 @@
                                     || TextUtils.isEmpty(handleString)) {
                                 handle = null;
                             } else {
+                                // TODO: Remove the assumption that numbers are SIP or TEL only.
                                 handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(handleString) ?
                                         PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
                                                 handleString, null);
diff --git a/src/com/android/server/telecom/ui/NotificationChannelManager.java b/src/com/android/server/telecom/ui/NotificationChannelManager.java
index 46b6c28..70d4d0d 100644
--- a/src/com/android/server/telecom/ui/NotificationChannelManager.java
+++ b/src/com/android/server/telecom/ui/NotificationChannelManager.java
@@ -36,6 +36,7 @@
 
     public static final String CHANNEL_ID_MISSED_CALLS = "TelecomMissedCalls";
     public static final String CHANNEL_ID_INCOMING_CALLS = "TelecomIncomingCalls";
+    public static final String CHANNEL_ID_CALL_BLOCKING = "TelecomCallBlocking";
 
     private BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
         @Override
@@ -55,6 +56,7 @@
     private void createOrUpdateAll(Context context) {
         createOrUpdateChannel(context, CHANNEL_ID_MISSED_CALLS);
         createOrUpdateChannel(context, CHANNEL_ID_INCOMING_CALLS);
+        createOrUpdateChannel(context, CHANNEL_ID_CALL_BLOCKING);
     }
 
     private void createOrUpdateChannel(Context context, String channelId) {
@@ -88,6 +90,14 @@
                 vibration = true;
                 sound = silentRingtone;
                 break;
+            case CHANNEL_ID_CALL_BLOCKING:
+                name = context.getText(R.string.notification_channel_call_blocking);
+                importance = NotificationManager.IMPORTANCE_LOW;
+                canShowBadge = false;
+                lights = false;
+                vibration = false;
+                sound = null;
+                break;
         }
 
         NotificationChannel channel = new NotificationChannel(channelId, name, importance);
diff --git a/testapps/Android.mk b/testapps/Android.mk
index 74928d0..b600dca 100644
--- a/testapps/Android.mk
+++ b/testapps/Android.mk
@@ -25,6 +25,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := TelecomTestApps
+LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 
 LOCAL_MODULE_TAGS := tests
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index 83f021f..48451d1 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -22,6 +22,7 @@
         android:minSdkVersion="23"
         android:targetSdkVersion="23" />
 
+    <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
@@ -46,6 +47,13 @@
             </intent-filter>
         </service>
 
+        <receiver android:name=".TestConnectionServiceReceiver">
+            <intent-filter>
+                <action android:name="android.server.telecom.testapps.ACTION_SWITCH_PHONE_ACCOUNT"/>
+                <action android:name="android.server.telecom.testapps.ACTION_SWITCH_PHONE_ACCOUNT_WRONG"/>
+            </intent-filter>
+        </receiver>
+
         <service android:name="com.android.server.telecom.testapps.TestConnectionManager"
                  android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" >
             <intent-filter>
@@ -72,6 +80,8 @@
             </intent-filter>
         </receiver>
 
+
+
         <activity android:name="com.android.server.telecom.testapps.TestInCallUI"
                 android:process="com.android.server.telecom.testapps.TestInCallService"
                 android:label="@string/inCallUiAppLabel"
diff --git a/testapps/res/layout/incall_screen.xml b/testapps/res/layout/incall_screen.xml
index 36ffb27..452cb2b 100644
--- a/testapps/res/layout/incall_screen.xml
+++ b/testapps/res/layout/incall_screen.xml
@@ -29,7 +29,7 @@
     <GridLayout
         android:columnCount="3"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:orientation="horizontal">
         <Button
             android:id="@+id/end_call_button"
@@ -72,4 +72,48 @@
             android:layout_height="wrap_content"
             android:text="@string/handoverButton"/>
     </GridLayout>
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Spinner
+            android:id="@+id/available_bt_devices"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+        <Button
+            android:id="@+id/set_bt_device_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/setBtDeviceButton"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/earpiece_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/earpieceButton"/>
+        <Button
+            android:id="@+id/speaker_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/speakerButton"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <TextView
+            android:id="@+id/current_route_label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/currentRouteLabel"/>
+        <TextView
+            android:id="@+id/current_audio_route"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"/>
+    </LinearLayout>
 </LinearLayout>
diff --git a/testapps/res/layout/self_managed_call_list_item.xml b/testapps/res/layout/self_managed_call_list_item.xml
index f3be4ad..66b5b21 100644
--- a/testapps/res/layout/self_managed_call_list_item.xml
+++ b/testapps/res/layout/self_managed_call_list_item.xml
@@ -50,6 +50,11 @@
         <Button
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:text="Missed"
+            android:id="@+id/missedButton" />
+        <Button
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
             android:text="Disconnect"
             android:id="@+id/disconnectButton" />
     </LinearLayout>
@@ -67,5 +72,10 @@
             android:layout_height="wrap_content"
             android:text="Earpiece"
             android:id="@+id/earpieceButton" />
+        <CheckBox
+            android:id="@+id/holdable"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="1"
+            android:text="Holdable"/>
     </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/testapps/res/layout/self_managed_sample_main.xml b/testapps/res/layout/self_managed_sample_main.xml
index e55de33..68ae65c 100644
--- a/testapps/res/layout/self_managed_sample_main.xml
+++ b/testapps/res/layout/self_managed_sample_main.xml
@@ -30,43 +30,42 @@
         android:layout_height="wrap_content"
         android:text="This app provides two sample implementations of the self-managed ConnectionService API.  Use this UI to add simulated self-managed calls:" />
 
-    <RadioGroup
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="horizontal">
-        <RadioButton
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Sample Source 1"
-            android:id="@+id/useAcct1Button"
-            android:background="@color/test_call_a_color"/>
-        <RadioButton
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Sample Source 2"
-            android:id="@+id/useAcct2Button"
-            android:background="@color/test_call_b_color"/>
-    </RadioGroup>
-
-    <RadioGroup
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-        <RadioButton
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Video Call"
-            android:id="@+id/videoCallButton"/>
-        <RadioButton
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Audio Call"
-            android:id="@+id/audioCallButton"/>
-    </RadioGroup>
+        <TextView
+            android:id="@+id/textView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" android:layout_weight="1"
+            android:text="Acct:"/>
+        <RadioGroup
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1" android:orientation="horizontal">
+            <RadioButton
+                android:id="@+id/useAcct1Button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@color/test_call_a_color"
+                android:checked="true" android:text="1"/>
+            <RadioButton
+                android:id="@+id/useAcct2Button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@color/test_call_b_color"
+                android:text="2"/>
+        </RadioGroup>
+        <TextView
+            android:id="@+id/hasFocus"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" android:layout_weight="1"
+            android:text="👎 No Focus 👎"/>
+    </LinearLayout>
 
     <LinearLayout android:orientation="horizontal"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content">
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -76,6 +75,16 @@
             android:layout_height="wrap_content"
             android:id="@+id/phoneNumber"
             android:text="tel:555-1212"/>
+        <CheckBox
+            android:id="@+id/holdable"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="1"
+            android:checked="true" android:text="Holdable"/>
+        <CheckBox
+            android:id="@+id/videoCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" android:layout_weight="1"
+            android:text="Video"/>
     </LinearLayout>
 
     <LinearLayout android:orientation="horizontal"
@@ -83,20 +92,20 @@
         android:layout_height="wrap_content">
 
         <Button
+            android:id="@+id/placeOutgoingCallButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="Outgoing Call"
-            android:id="@+id/placeOutgoingCallButton" />
+            android:text="Outgoing"/>
         <Button
+            android:id="@+id/placeIncomingCallButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="Incoming Call"
-            android:id="@+id/placeIncomingCallButton" />
+            android:text="Incoming"/>
         <Button
+            android:id="@+id/handoverFrom"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="Handover From"
-            android:id="@+id/handoverFrom" />
+            android:text="Accept Handover"/>
     </LinearLayout>
 
     <ListView
diff --git a/testapps/res/values/donottranslate_strings.xml b/testapps/res/values/donottranslate_strings.xml
index a0485d0..abd2949 100644
--- a/testapps/res/values/donottranslate_strings.xml
+++ b/testapps/res/values/donottranslate_strings.xml
@@ -68,6 +68,13 @@
 
     <string name="getKeyButton">Get Key Json</string>
 
+    <string name="earpieceButton">Earpiece/Wired</string>
+
+    <string name="speakerButton">Speakerphone</string>
+
+    <string name="setBtDeviceButton">Set BT device</string>
+
+    <string name="currentRouteLabel">Current audio route</string>
     <!-- String for button in SelfManagedCallingActivity. -->
     <string name="checkIfPermittedBeforeCallingButton">Check if calls permitted before calling</string>
 
diff --git a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
index ba58655..758ae4f 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
@@ -35,7 +35,9 @@
 import android.widget.Toast;
 
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Class used to create, update and cancel the notification used to display and update call state
@@ -44,11 +46,16 @@
 public class CallServiceNotifier {
     private static final CallServiceNotifier INSTANCE = new CallServiceNotifier();
 
-    static final String CALL_PROVIDER_ID = "testapps_TestConnectionService_CALL_PROVIDER_ID";
-    static final String SIM_SUBSCRIPTION_ID = "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID";
-    static final String CONNECTION_MANAGER_ID =
+    public static final String CALL_PROVIDER_ID = "testapps_TestConnectionService_CALL_PROVIDER_ID";
+    public static final String SIM_SUBSCRIPTION_ID =
+            "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID";
+    public static final String SIM_SUBSCRIPTION_ID2 =
+            "testapps_TestConnectionService_SIM_SUBSCRIPTION_ID2";
+    public static final String CONNECTION_MANAGER_ID =
             "testapps_TestConnectionService_CONNECTION_MANAGER_ID";
 
+    private Map<String, PhoneAccountHandle> mPhoneAccountMap = new HashMap<>();
+
     /**
      * Static notification IDs.
      */
@@ -110,10 +117,11 @@
         testBundle.putString("EXTRA_STR1", "Hello");
         testBundle.putString("EXTRA_STR2", "There");
 
+        PhoneAccountHandle handle1 = new PhoneAccountHandle(
+                new ComponentName(context, TestConnectionService.class), CALL_PROVIDER_ID);
+        mPhoneAccountMap.put(CALL_PROVIDER_ID, handle1);
         telecomManager.registerPhoneAccount(PhoneAccount.builder(
-                new PhoneAccountHandle(
-                        new ComponentName(context, TestConnectionService.class),
-                        CALL_PROVIDER_ID),
+                handle1,
                 "TelecomTestApp Call Provider")
                 .setAddress(Uri.parse("tel:555-TEST"))
                 .setSubscriptionAddress(Uri.parse("tel:555-TEST"))
@@ -130,10 +138,11 @@
                 .setExtras(testBundle)
                 .build());
 
+        PhoneAccountHandle handle2 = new PhoneAccountHandle(
+                new ComponentName(context, TestConnectionService.class), SIM_SUBSCRIPTION_ID);
+        mPhoneAccountMap.put(SIM_SUBSCRIPTION_ID, handle2);
         telecomManager.registerPhoneAccount(PhoneAccount.builder(
-                new PhoneAccountHandle(
-                        new ComponentName(context, TestConnectionService.class),
-                        SIM_SUBSCRIPTION_ID),
+                handle2,
                 "TelecomTestApp SIM Subscription")
                 .setAddress(Uri.parse("tel:555-TSIM"))
                 .setSubscriptionAddress(Uri.parse("tel:555-TSIM"))
@@ -149,11 +158,31 @@
                 .setShortDescription("a short description for the sim subscription")
                 .build());
 
+        PhoneAccountHandle handle3 = new PhoneAccountHandle(
+                new ComponentName(context, TestConnectionService.class), SIM_SUBSCRIPTION_ID2);
+        mPhoneAccountMap.put(SIM_SUBSCRIPTION_ID2, handle3);
         telecomManager.registerPhoneAccount(PhoneAccount.builder(
-                        new PhoneAccountHandle(
-                                new ComponentName(context, TestConnectionManager.class),
-                                CONNECTION_MANAGER_ID),
-                        "TelecomTestApp CONNECTION MANAGER")
+                handle3,
+                "TelecomTestApp SIM Subscription 2")
+                .setAddress(Uri.parse("tel:555-TSI2"))
+                .setSubscriptionAddress(Uri.parse("tel:555-TSI2"))
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                        PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+                        PhoneAccount.CAPABILITY_VIDEO_CALLING |
+                        PhoneAccount.CAPABILITY_RTT |
+                        PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)
+                .setIcon(Icon.createWithResource(
+                        context.getResources(), R.drawable.stat_sys_phone_call))
+                .setHighlightColor(Color.CYAN)
+                .setShortDescription("a short description for the sim subscription")
+                .build());
+
+        PhoneAccountHandle handle4 = new PhoneAccountHandle(
+                new ComponentName(context, TestConnectionManager.class), CONNECTION_MANAGER_ID);
+        mPhoneAccountMap.put(CONNECTION_MANAGER_ID, handle4);
+        telecomManager.registerPhoneAccount(PhoneAccount.builder(
+                handle4,
+                "TelecomTestApp CONNECTION MANAGER")
                 .setAddress(Uri.parse("tel:555-CMGR"))
                 .setSubscriptionAddress(Uri.parse("tel:555-CMGR"))
                 .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
@@ -164,6 +193,10 @@
                 .build());
     }
 
+    public PhoneAccountHandle getPhoneAccountHandle(String id) {
+        return mPhoneAccountMap.get(id);
+    }
+
     /**
      * Displays all phone accounts registered with telecom.
      */
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java
index f9bce35..4b5fa57 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallList.java
@@ -40,12 +40,15 @@
         public void onCreateIncomingConnectionFailed(ConnectionRequest request) {};
         public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {};
         public void onConnectionListChanged() {};
+        public void onConnectionServiceFocusLost() {};
+        public void onConnectionServiceFocusGained() {};
     }
 
     public static String SELF_MANAGED_ACCOUNT_1 = "1";
     public static String SELF_MANAGED_ACCOUNT_2 = "2";
     public static String SELF_MANAGED_NAME_1 = "SuperCall";
     public static String SELF_MANAGED_NAME_2 = "Mega Call";
+    public static String CUSTOM_URI_SCHEME = "custom";
 
     private static SelfManagedCallList sInstance;
     private static ComponentName COMPONENT_NAME = new ComponentName(
@@ -110,6 +113,7 @@
         PhoneAccount.Builder builder = PhoneAccount.builder(handle, name)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
+                .addSupportedUriScheme(CUSTOM_URI_SCHEME)
                 .setAddress(address)
                 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
                         PhoneAccount.CAPABILITY_VIDEO_CALLING |
@@ -136,6 +140,18 @@
         }
     }
 
+    public void notifyConnectionServiceFocusGained() {
+        if (mListener != null) {
+            mListener.onConnectionServiceFocusGained();
+        }
+    }
+
+    public void notifyConnectionServiceFocusLost() {
+        if (mListener != null) {
+            mListener.onConnectionServiceFocusLost();
+        }
+    }
+
     public void addConnection(SelfManagedConnection connection) {
         Log.i(this, "addConnection %s", connection);
         mConnections.add(connection);
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
index b46d5e1..8eaa282 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallListAdapter.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom.testapps;
 
 import android.telecom.CallAudioState;
+import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
 import android.util.Log;
@@ -24,6 +25,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
+import android.widget.CheckBox;
 import android.widget.TextView;
 
 import com.android.server.telecom.testapps.R;
@@ -60,8 +62,18 @@
     };
 
     /**
-     * Listener used to handle tap of the "held" button for a connection.
+     * Listener used to handle tap of the "missed" button for a connection.
      */
+    private View.OnClickListener mMissedListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            View parent = (View) v.getParent().getParent();
+            SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+            connection.setConnectionDisconnected(DisconnectCause.MISSED);
+            SelfManagedCallList.getInstance().removeConnection(connection);
+        }
+    };
+
     private View.OnClickListener mHeldListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -92,6 +104,22 @@
         }
     };
 
+    private View.OnClickListener mHoldableListener = new View.OnClickListener() {
+        @Override
+        public void onClick (View v) {
+            View parent = (View) v.getParent().getParent();
+            SelfManagedConnection connection = (SelfManagedConnection) parent.getTag();
+            int capabilities = connection.getConnectionCapabilities();
+            if ((capabilities & Connection.CAPABILITY_HOLD) == Connection.CAPABILITY_HOLD) {
+                capabilities &= ~(Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD);
+            } else {
+                capabilities |= (Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD);
+            }
+            connection.setConnectionCapabilities(capabilities);
+            notifyDataSetChanged();
+        }
+    };
+
     private final LayoutInflater mLayoutInflater;
 
     private List<SelfManagedConnection> mConnections;
@@ -165,7 +193,8 @@
         }
         setInfoForRow(result, phoneAccountHandle.getId(), connection.getAddress().toString(),
                 android.telecom.Connection.stateToString(connection.getState()), audioRoute,
-                callType);
+                callType, connection.getState() == android.telecom.Connection.STATE_RINGING, 
+                connection.isHoldable());
         result.setTag(connection);
         return result;
     }
@@ -177,7 +206,8 @@
     }
 
     private void setInfoForRow(View view, String accountName, String number,
-                               String status, String audioRoute, String callType) {
+                               String status, String audioRoute, String callType,
+            boolean isRinging, boolean isHoldable) {
 
         TextView numberTextView = (TextView) view.findViewById(R.id.phoneNumber);
         TextView statusTextView = (TextView) view.findViewById(R.id.callState);
@@ -191,6 +221,14 @@
         speakerButton.setOnClickListener(mSpeakerListener);
         View earpieceButton = view.findViewById(R.id.earpieceButton);
         earpieceButton.setOnClickListener(mEarpieceListener);
+        View missedButton = view.findViewById(R.id.missedButton);
+        missedButton.setOnClickListener(mMissedListener);
+        missedButton.setVisibility(isRinging ? View.VISIBLE : View.GONE);
+        setHeldButton.setVisibility(!isRinging ? View.VISIBLE : View.GONE);
+        disconnectButton.setVisibility(!isRinging ? View.VISIBLE : View.GONE);
+        CheckBox holdableCheckbox = view.findViewById(R.id.holdable);
+        holdableCheckbox.setOnClickListener(mHoldableListener);
+        holdableCheckbox.setChecked(isHoldable);
         numberTextView.setText(accountName + " - " + number + " (" + audioRoute + ")");
         statusTextView.setText(callType + " - Status: " + status);
     }
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
index 6139e33..a7b1350 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
@@ -31,6 +31,7 @@
 import android.widget.EditText;
 import android.widget.ListView;
 import android.widget.RadioButton;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.server.telecom.testapps.R;
@@ -50,10 +51,12 @@
     private Button mHandoverFrom;
     private RadioButton mUseAcct1Button;
     private RadioButton mUseAcct2Button;
-    private RadioButton mVideoCallButton;
-    private RadioButton mAudioCallButton;
+    private CheckBox mHoldableCheckbox;
+    private CheckBox mVideoCallCheckbox;
     private EditText mNumber;
     private ListView mListView;
+    private TextView mHasFocus;
+
     private SelfManagedCallListAdapter mListAdapter;
 
     private SelfManagedCallList.Listener mCallListListener = new SelfManagedCallList.Listener() {
@@ -76,6 +79,16 @@
             Log.i(TAG, "onConnectionListChanged");
             mListAdapter.updateConnections();
         };
+
+        @Override
+        public void onConnectionServiceFocusLost() {
+            mHasFocus.setText("\uD83D\uDC4E No Focus \uD83D\uDC4E");
+        };
+
+        @Override
+        public void onConnectionServiceFocusGained() {
+            mHasFocus.setText("\uD83D\uDC4D Has Focus \uD83D\uDC4D");
+        };
     };
 
     @Override
@@ -109,10 +122,11 @@
             placeIncomingCall(true /* isHandoverFrom */);
         }));
 
-        mUseAcct1Button = (RadioButton) findViewById(R.id.useAcct1Button);
-        mUseAcct2Button = (RadioButton) findViewById(R.id.useAcct2Button);
-        mVideoCallButton = (RadioButton) findViewById(R.id.videoCallButton);
-        mAudioCallButton = (RadioButton) findViewById(R.id.audioCallButton);
+        mUseAcct1Button = findViewById(R.id.useAcct1Button);
+        mUseAcct2Button = findViewById(R.id.useAcct2Button);
+        mHasFocus = findViewById(R.id.hasFocus);
+        mVideoCallCheckbox = findViewById(R.id.videoCall);
+        mHoldableCheckbox = findViewById(R.id.holdable);
         mNumber = (EditText) findViewById(R.id.phoneNumber);
         mListView = (ListView) findViewById(R.id.callList);
         mCallList.setListener(mCallListListener);
@@ -146,10 +160,14 @@
         Bundle extras = new Bundle();
         extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                 getSelectedPhoneAccountHandle());
-        if (mVideoCallButton.isChecked()) {
+        if (mVideoCallCheckbox.isChecked()) {
             extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                     VideoProfile.STATE_BIDIRECTIONAL);
         }
+        Bundle clientExtras = new Bundle();
+        clientExtras.putBoolean(SelfManagedConnectionService.EXTRA_HOLDABLE,
+                mHoldableCheckbox.isChecked());
+        extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, clientExtras);
         tm.placeCall(Uri.parse(mNumber.getText().toString()), extras);
     }
 
@@ -167,7 +185,9 @@
         Bundle extras = new Bundle();
         extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
                 Uri.parse(mNumber.getText().toString()));
-        if (mVideoCallButton.isChecked()) {
+        extras.putBoolean(SelfManagedConnectionService.EXTRA_HOLDABLE,
+                mHoldableCheckbox.isChecked());
+        if (mVideoCallCheckbox.isChecked()) {
             extras.putInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE,
                     VideoProfile.STATE_BIDIRECTIONAL);
         }
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
index 82967c4..8d0af04 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnection.java
@@ -138,11 +138,17 @@
 
     @Override
     public void onHold() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.pause();
+        }
         setOnHold();
     }
 
     @Override
     public void onUnhold() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer.start();
+        }
         setActive();
     }
 
@@ -183,6 +189,9 @@
     }
 
     public void setConnectionDisconnected(int cause) {
+        NotificationManager notificationManager = mContext.getSystemService(
+                NotificationManager.class);
+        notificationManager.cancel(CALL_NOTIFICATION, mCallId);
         mMediaPlayer.stop();
         setDisconnected(new DisconnectCause(cause));
         destroy();
@@ -215,6 +224,10 @@
         return mIsHandover;
     }
 
+    public boolean isHoldable() {
+        return (getConnectionCapabilities() & Connection.CAPABILITY_HOLD) != 0;
+    }
+
     private MediaPlayer createMediaPlayer(Context context) {
         int audioToPlay = (Math.random() > 0.5f) ? R.raw.sample_audio : R.raw.sample_audio2;
         MediaPlayer mediaPlayer = MediaPlayer.create(context, audioToPlay);
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
index bb34530..d5d79af 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedConnectionService.java
@@ -35,6 +35,7 @@
  * See {@link android.telecom} for more information on self-managed {@link ConnectionService}s.
  */
 public class SelfManagedConnectionService extends ConnectionService {
+    public static final String EXTRA_HOLDABLE = "com.android.server.telecom.testapps.HOLDABLE";
     private static final String[] TEST_NAMES = {"Tom Smith", "Jane Appleseed", "Joseph Engleton",
             "Claudia McPherson", "Chris P. Bacon", "Seymour Butz", "Hugh Mungus", "Anita Bath"};
     private final SelfManagedCallList mCallList = SelfManagedCallList.getInstance();
@@ -65,6 +66,17 @@
         mCallList.notifyCreateOutgoingConnectionFailed(request);
     }
 
+    @Override
+    public void onConnectionServiceFocusLost() {
+        mCallList.notifyConnectionServiceFocusLost();
+        connectionServiceFocusReleased();
+    }
+
+    @Override
+    public void onConnectionServiceFocusGained() {
+        mCallList.notifyConnectionServiceFocusGained();
+    }
+
     private Connection createSelfManagedConnection(ConnectionRequest request, boolean isIncoming) {
         SelfManagedConnection connection = new SelfManagedConnection(mCallList,
                 getApplicationContext(), isIncoming);
@@ -79,14 +91,21 @@
         connection.setExtras(request.getExtras());
         if (isIncoming) {
             connection.setIsIncomingCallUiShowing(request.shouldShowIncomingCallUi());
+            connection.setRinging();
         }
         Bundle requestExtras = request.getExtras();
         if (requestExtras != null) {
-            Log.i(this, "createConnection: isHandover=%b, handoverFrom=%s",
+            boolean isHoldable = requestExtras.getBoolean(EXTRA_HOLDABLE, false);
+            Log.i(this, "createConnection: isHandover=%b, handoverFrom=%s, holdable=%b",
                     requestExtras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER),
-                    requestExtras.getString(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT));
+                    requestExtras.getString(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT),
+                    isHoldable);
             connection.setIsHandover(requestExtras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER,
                     false));
+            if (isHoldable) {
+                connection.setConnectionCapabilities(connection.getConnectionCapabilities() |
+                        Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD);
+            }
             if (!isIncoming && connection.isHandover()) {
                 Intent intent = new Intent(Intent.ACTION_MAIN, null);
                 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
index ac239b9..52dcadb 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionService.java
@@ -43,6 +43,8 @@
 import java.util.List;
 import java.util.Random;
 
+import static com.android.server.telecom.testapps.CallServiceNotifier.SIM_SUBSCRIPTION_ID2;
+
 /**
  * Service which provides fake calls to test the ConnectionService interface.
  * TODO: Rename all classes in the directory to Dummy* (e.g., DummyConnectionService).
@@ -56,6 +58,9 @@
     public static final String EXTRA_HANDLE = "extra_handle";
 
     private static final String LOG_TAG = TestConnectionService.class.getSimpleName();
+
+    private static TestConnectionService INSTANCE;
+
     /**
      * Random number generator used to generate phone numbers.
      */
@@ -332,6 +337,11 @@
     private MediaPlayer mMediaPlayer;
 
     @Override
+    public void onCreate() {
+        INSTANCE = this;
+    }
+
+    @Override
     public boolean onUnbind(Intent intent) {
         log("onUnbind");
         mMediaPlayer = null;
@@ -479,6 +489,36 @@
         }
     }
 
+    public static TestConnectionService getInstance() {
+        return INSTANCE;
+    }
+
+    public void switchPhoneAccount() {
+        if (!mCalls.isEmpty()) {
+            TestConnection c = mCalls.get(0);
+            c.notifyPhoneAccountChanged(CallServiceNotifier.getInstance()
+                    .getPhoneAccountHandle(SIM_SUBSCRIPTION_ID2));
+        } else {
+            Log.i(this, "Couldn't switch PhoneAccount, call is null!");
+        }
+    }
+    public void switchPhoneAccountWrong() {
+        PhoneAccountHandle pah = new PhoneAccountHandle(
+                new ComponentName("com.android.phone",
+                "com.android.services.telephony.TelephonyConnectionService"), "TEST");
+        if (!mCalls.isEmpty()) {
+            TestConnection c = mCalls.get(0);
+            try {
+                c.notifyPhoneAccountChanged(pah);
+            } catch (SecurityException e) {
+                Toast.makeText(getApplicationContext(), "SwitchPhoneAccount: Pass",
+                        Toast.LENGTH_SHORT).show();
+            }
+        } else {
+            Log.i(this, "Couldn't switch PhoneAccount, call is null!");
+        }
+    }
+
     private void addVideoProvider(TestConnection connection) {
         TestVideoProvider testVideoCallProvider =
                 new TestVideoProvider(getApplicationContext(), connection);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestConnectionServiceReceiver.java b/testapps/src/com/android/server/telecom/testapps/TestConnectionServiceReceiver.java
new file mode 100644
index 0000000..aaa5e2c
--- /dev/null
+++ b/testapps/src/com/android/server/telecom/testapps/TestConnectionServiceReceiver.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.testapps;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class TestConnectionServiceReceiver extends BroadcastReceiver {
+    private static final String TAG = "TestConnectionServiceBR";
+
+    // Switches the PhoneAccount to the second test SIM account.
+    public static final String INTENT_SWITCH_PHONE_ACCOUNT =
+            "android.server.telecom.testapps.ACTION_SWITCH_PHONE_ACCOUNT";
+
+    // Creates a PhoneAccount that is incorrect and should create a SecurityException
+    public static final String INTENT_SWITCH_PHONE_ACCOUNT_WRONG =
+            "android.server.telecom.testapps.ACTION_SWITCH_PHONE_ACCOUNT_WRONG";
+
+    /**
+     * Handles broadcasts directed at the {@link TestInCallServiceImpl}.
+     *
+     * @param context The Context in which the receiver is running.
+     * @param intent  The Intent being received.
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        Log.v(TAG, "onReceive: " + action);
+
+        TestConnectionService cs = TestConnectionService.getInstance();
+        if (cs == null) {
+            Log.i(TAG, "null TestConnectionService");
+            return;
+        }
+
+        if (INTENT_SWITCH_PHONE_ACCOUNT.equals(action)) {
+            cs.switchPhoneAccount();
+        } else if (INTENT_SWITCH_PHONE_ACCOUNT_WRONG.equals(action)) {
+            cs.switchPhoneAccountWrong();
+        }
+    }
+}
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java b/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java
index 03ca3d0..78b8bcc 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallServiceImpl.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.telecom.Call;
+import android.telecom.CallAudioState;
 import android.telecom.InCallService;
 import android.telecom.Phone;
 import android.util.Log;
@@ -32,6 +33,7 @@
  */
 public class TestInCallServiceImpl extends InCallService {
     private static final String TAG = "TestInCallServiceImpl";
+    public static TestInCallServiceImpl sInstance;
 
     private Phone mPhone;
 
@@ -63,14 +65,24 @@
     }
 
     @Override
-    public void onPhoneDestroyed(Phone phone) {
+    public boolean onUnbind(Intent intent) {
         Log.i(TAG, "onPhoneDestroyed");
         mPhone.removeListener(mPhoneListener);
         mPhone = null;
         TestCallList.getInstance().clearCalls();
+        sInstance = null;
+        return super.onUnbind(intent);
+    }
+
+    @Override
+    public void onCallAudioStateChanged(CallAudioState cas) {
+        if (TestInCallUI.sInstance != null) {
+            TestInCallUI.sInstance.updateCallAudioState(cas);
+        }
     }
 
     private void startInCallUI() {
+        sInstance = this;
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
         intent.setClass(this, TestInCallUI.class);
diff --git a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
index d99798c..9851253 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestInCallUI.java
@@ -17,9 +17,11 @@
 package com.android.server.telecom.testapps;
 
 import android.app.Activity;
+import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
 import android.os.Bundle;
 import android.telecom.Call;
+import android.telecom.CallAudioState;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -27,21 +29,50 @@
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
 import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
 import android.widget.Toast;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 
 public class TestInCallUI extends Activity {
+    private class BluetoothDeviceAdapter extends ArrayAdapter<BluetoothDevice> {
+        public BluetoothDeviceAdapter() {
+            super(TestInCallUI.this, android.R.layout.simple_spinner_item);
+            setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        }
 
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            BluetoothDevice info = getItem(position);
+            TextView result = new TextView(TestInCallUI.this);
+            result.setText(info.getName());
+            return result;
+        }
+
+        public void update(Collection<BluetoothDevice> devices) {
+            clear();
+            addAll(devices);
+        }
+    }
+
+    public static TestInCallUI sInstance;
     private ListView mListView;
     private TestCallList mCallList;
+    private Spinner mBtDeviceList;
+    private BluetoothDeviceAdapter mBluetoothDeviceAdapter;
+    private TextView mCurrentRouteDisplay;
 
     /** ${inheritDoc} */
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        sInstance = this;
 
         setContentView(R.layout.incall_screen);
 
@@ -90,6 +121,13 @@
         View startRttButton = findViewById(R.id.start_rtt_button);
         View acceptRttButton = findViewById(R.id.accept_rtt_button);
         View handoverButton = findViewById(R.id.request_handover_button);
+        View setBtDeviceButton = findViewById(R.id.set_bt_device_button);
+        View earpieceButton = findViewById(R.id.earpiece_button);
+        View speakerButton = findViewById(R.id.speaker_button);
+        mBtDeviceList = findViewById(R.id.available_bt_devices);
+        mBluetoothDeviceAdapter = new BluetoothDeviceAdapter();
+        mBtDeviceList.setAdapter(mBluetoothDeviceAdapter);
+        mCurrentRouteDisplay = findViewById(R.id.current_audio_route);
 
         endCallButton.setOnClickListener(new OnClickListener() {
             @Override
@@ -146,6 +184,22 @@
             }
         });
 
+        earpieceButton.setOnClickListener(view -> {
+            TestInCallServiceImpl.sInstance.setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
+        });
+
+        speakerButton.setOnClickListener(view -> {
+            TestInCallServiceImpl.sInstance.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+        });
+
+        setBtDeviceButton.setOnClickListener(view -> {
+            if (mBtDeviceList.getSelectedItem() != null
+                    && TestInCallServiceImpl.sInstance != null) {
+                TestInCallServiceImpl.sInstance.requestBluetoothAudio(
+                        (BluetoothDevice) mBtDeviceList.getSelectedItem());
+            }
+        });
+
         acceptRttButton.setOnClickListener(view -> {
             Call call = mCallList.getCall(0);
             if (!call.isRttActive()) {
@@ -163,9 +217,33 @@
         });
     }
 
+    public void updateCallAudioState(CallAudioState cas) {
+        mBluetoothDeviceAdapter.update(cas.getSupportedBluetoothDevices());
+        String routeText;
+        switch (cas.getRoute()) {
+            case CallAudioState.ROUTE_EARPIECE:
+                routeText = "Earpiece";
+                break;
+            case CallAudioState.ROUTE_SPEAKER:
+                routeText = "Speaker";
+                break;
+            case CallAudioState.ROUTE_WIRED_HEADSET:
+                routeText = "Wired";
+                break;
+            case CallAudioState.ROUTE_BLUETOOTH:
+                BluetoothDevice activeDevice = cas.getActiveBluetoothDevice();
+                routeText = activeDevice == null ? "null bt" : activeDevice.getName();
+                break;
+            default:
+                routeText = "unknown: " + cas.getRoute();
+        }
+        mCurrentRouteDisplay.setText(routeText);
+    }
+
     /** ${inheritDoc} */
     @Override
     protected void onDestroy() {
+        sInstance = null;
         super.onDestroy();
     }
 
diff --git a/tests/Android.mk b/tests/Android.mk
index 77abcad..5f083de 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -18,17 +18,22 @@
 include $(CLEAR_VARS)
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-ex-camera2 \
-        android-support-v4 \
-        android-support-test \
-        guava \
-        mockito-target \
-        platform-test-annotations
+    android-ex-camera2 \
+    guava \
+    mockito-target \
+    android-support-test \
+    platform-test-annotations
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    android-support-core-ui \
+    android-support-core-utils \
+    android-support-compat \
+    android-support-fragment
 
 LOCAL_SRC_FILES := \
-        $(call all-java-files-under, src) \
-        $(call all-java-files-under, ../src) \
-        $(call all-proto-files-under, ../proto)
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, ../src) \
+    $(call all-proto-files-under, ../proto)
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../proto/
@@ -36,23 +41,26 @@
 
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
-    $(LOCAL_PATH)/../res \
-    $(SUPPORT_LIBRARY_ROOT)/compat/res
+    $(LOCAL_PATH)/../res
 
 LOCAL_JAVA_LIBRARIES := \
-        android.test.mock \
-        legacy-android-test \
-        telephony-common
+    android.test.mock \
+    android.test.base \
+    android.test.runner \
+    telephony-common
+
+LOCAL_USE_AAPT2 := true
 
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
-    --extra-packages com.android.server.telecom:android.support.compat
+    --extra-packages com.android.server.telecom
 
 LOCAL_JACK_FLAGS := --multi-dex native
 
 LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_PACKAGE_NAME := TelecomUnitTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 
 LOCAL_MODULE_TAGS := tests
@@ -60,6 +68,7 @@
 LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.server.telecom.*
 LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := com.android.server.telecom.tests.*
 
+LOCAL_COMPATIBILITY_SUITE := device-tests
 include frameworks/base/packages/SettingsLib/common.mk
 
 include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index eccd513..0e79fce 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -51,7 +51,7 @@
         adb shell am instrument -w -e class com.android.server.telecom.tests.unit.FooUnitTest \
                                com.android.server.telecom.tests/android.test.InstrumentationTestRunner
     -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.server.telecom.tests"
             android:label="Telecomm application tests"
             android:debuggable="true"/>
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
new file mode 100644
index 0000000..d3db2e3
--- /dev/null
+++ b/tests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Telecom Test Cases.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="TelecomUnitTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="TelecomUnitTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.server.telecom.tests" />
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
index e70a63a..9d64f18 100644
--- a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
+++ b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
@@ -42,6 +42,12 @@
 import com.android.server.telecom.LogUtils;
 import com.android.server.telecom.nano.TelecomLogClass;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
@@ -53,13 +59,32 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 
+@RunWith(JUnit4.class)
 public class AnalyticsTests extends TelecomSystemTest {
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @MediumTest
+    @Test
     public void testAnalyticsSingleCall() throws Exception {
         IdPair testCall = startAndMakeActiveIncomingCall(
                 "650-555-1212",
@@ -101,6 +126,7 @@
 
     @FlakyTest
     @MediumTest
+    @Test
     public void testAnalyticsDumping() throws Exception {
         Analytics.reset();
         IdPair testCall = startAndMakeActiveIncomingCall(
@@ -144,6 +170,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAnalyticsTwoCalls() throws Exception {
         IdPair testCall1 = startAndMakeActiveIncomingCall(
                 "650-555-1212",
@@ -196,6 +223,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAnalyticsVideo() throws Exception {
         Analytics.reset();
         IdPair callIds = startAndMakeActiveOutgoingCall(
@@ -245,6 +273,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAnalyticsRounding() {
         long[] testVals = {0, -1, -10, -100, -57836, 1, 10, 100, 1000, 458457};
         long[] expected = {0, -1, -10, -100, -60000, 1, 10, 100, 1000, 500000};
@@ -254,6 +283,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAnalyticsLogSessionTiming() throws Exception {
         long minTime = 50;
         Log.startSession(LogUtils.Sessions.CSW_ADD_CONFERENCE_CALL);
@@ -268,6 +298,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAnalyticsDumpToProto() throws Exception {
         Analytics.reset();
         IdPair testCall = startAndMakeActiveIncomingCall(
@@ -315,6 +346,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAnalyticsAudioRoutes() throws Exception {
         Analytics.reset();
         IdPair testCall = startAndMakeActiveIncomingCall(
@@ -327,12 +359,20 @@
                 mTelecomSystem.getCallsManager().getCallAudioManager()
                         .getCallAudioRouteStateMachine().getHandler(),
                 TEST_TIMEOUT);
+        waitForHandlerAction(
+                mTelecomSystem.getCallsManager().getCallAudioManager()
+                        .getCallAudioModeStateMachine().getHandler(),
+                TEST_TIMEOUT);
         audioRoutes.add(mInCallServiceFixtureX.mCallAudioState.getRoute());
-        mInCallServiceFixtureX.getInCallAdapter().setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+        mInCallServiceFixtureX.getInCallAdapter().setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
         waitForHandlerAction(
                 mTelecomSystem.getCallsManager().getCallAudioManager()
                         .getCallAudioRouteStateMachine().getHandler(),
                 TEST_TIMEOUT);
+        waitForHandlerAction(
+                mTelecomSystem.getCallsManager().getCallAudioManager()
+                        .getCallAudioModeStateMachine().getHandler(),
+                TEST_TIMEOUT);
         audioRoutes.add(CallAudioState.ROUTE_SPEAKER);
 
         Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData();
@@ -347,6 +387,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAnalyticsConnectionProperties() throws Exception {
         Analytics.reset();
         IdPair testCall = startAndMakeActiveIncomingCall(
@@ -382,6 +423,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAnalyticsMaxSize() throws Exception {
         Analytics.reset();
         for (int i = 0; i < Analytics.MAX_NUM_CALLS_TO_STORE * 2; i++) {
diff --git a/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java b/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java
index 79d711b..9b2b3fb 100644
--- a/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java
@@ -18,14 +18,24 @@
 
 import android.content.Context;
 import android.net.Uri;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.Call;
+import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.concurrent.CountDownLatch;
@@ -37,11 +47,14 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class AsyncBlockCheckFilterTest extends TelecomTestCase {
     @Mock private Context mContext;
     @Mock private BlockCheckerAdapter mBlockCheckerAdapter;
     @Mock private Call mCall;
     @Mock private CallFilterResultCallback mCallback;
+    @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper;
+    @Mock private CarrierConfigManager mCarrierConfigManager;
 
     private AsyncBlockCheckFilter mFilter;
     private static final CallFilteringResult BLOCK_RESULT = new CallFilteringResult(
@@ -62,40 +75,112 @@
     private static final int TEST_TIMEOUT = 100;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         when(mCall.getHandle()).thenReturn(TEST_HANDLE);
-        mFilter = new AsyncBlockCheckFilter(mContext, mBlockCheckerAdapter);
+        mFilter = new AsyncBlockCheckFilter(mContext, mBlockCheckerAdapter,
+                mCallerInfoLookupHelper);
     }
 
     @SmallTest
+    @Test
     public void testBlockNumber() {
         final CountDownLatch latch = new CountDownLatch(1);
         doAnswer(invocation -> {
             latch.countDown();
             return true;
         }).when(mBlockCheckerAdapter)
-                .isBlocked(any(Context.class), eq(TEST_HANDLE.getSchemeSpecificPart()));
+                .isBlocked(any(Context.class), eq(TEST_HANDLE.getSchemeSpecificPart()),
+                        any(Bundle.class));
+
+        setEnhancedBlockingEnabled(false);
         mFilter.startFilterLookup(mCall, mCallback);
+
         waitOnLatch(latch);
         verify(mCallback, timeout(TEST_TIMEOUT))
                 .onCallFilteringComplete(eq(mCall), eq(BLOCK_RESULT));
     }
 
     @SmallTest
+    @Test
+    public void testBlockNumber_enhancedBlockingEnabled() {
+        final CountDownLatch latch = new CountDownLatch(1);
+        doAnswer(invocation -> {
+            latch.countDown();
+            return true;
+        }).when(mBlockCheckerAdapter)
+                .isBlocked(any(Context.class), eq(TEST_HANDLE.getSchemeSpecificPart()),
+                        any(Bundle.class));
+
+        setEnhancedBlockingEnabled(true);
+        CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyEnhancedLookupStart();
+        queryListener.onCallerInfoQueryComplete(TEST_HANDLE, null);
+
+        waitOnLatch(latch);
+        verify(mCallback, timeout(TEST_TIMEOUT))
+                .onCallFilteringComplete(eq(mCall), eq(BLOCK_RESULT));
+    }
+
+    @SmallTest
+    @Test
     public void testDontBlockNumber() {
         final CountDownLatch latch = new CountDownLatch(1);
         doAnswer(invocation -> {
             latch.countDown();
             return false;
         }).when(mBlockCheckerAdapter)
-                .isBlocked(any(Context.class), eq(TEST_HANDLE.getSchemeSpecificPart()));
+                .isBlocked(any(Context.class), eq(TEST_HANDLE.getSchemeSpecificPart()),
+                        any(Bundle.class));
+
+        setEnhancedBlockingEnabled(false);
         mFilter.startFilterLookup(mCall, mCallback);
+
         waitOnLatch(latch);
         verify(mCallback, timeout(TEST_TIMEOUT))
                 .onCallFilteringComplete(eq(mCall), eq(PASS_RESULT));
     }
 
+    @SmallTest
+    @Test
+    public void testDontBlockNumber_enhancedBlockingEnabled() {
+        final CountDownLatch latch = new CountDownLatch(1);
+        doAnswer(invocation -> {
+            latch.countDown();
+            return false;
+        }).when(mBlockCheckerAdapter)
+                .isBlocked(any(Context.class), eq(TEST_HANDLE.getSchemeSpecificPart()),
+                        any(Bundle.class));
+
+        setEnhancedBlockingEnabled(true);
+        CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyEnhancedLookupStart();
+        queryListener.onCallerInfoQueryComplete(TEST_HANDLE, null);
+
+        waitOnLatch(latch);
+        verify(mCallback, timeout(TEST_TIMEOUT))
+                .onCallFilteringComplete(eq(mCall), eq(PASS_RESULT));
+    }
+
+    private void setEnhancedBlockingEnabled(Boolean value) {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_SUPPORT_ENHANCED_CALL_BLOCKING_BOOL,
+                value);
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(mCarrierConfigManager);
+        when(mCarrierConfigManager.getConfig()).thenReturn(bundle);
+    }
+
+    private CallerInfoLookupHelper.OnQueryCompleteListener verifyEnhancedLookupStart() {
+        // The enhanced lookup will only be excuted when enhanced blocking enabled and the
+        // presentation is PRESENTATION_ALLOWED.
+        when(mCall.getHandlePresentation()).thenReturn(TelecomManager.PRESENTATION_ALLOWED);
+        mFilter.startFilterLookup(mCall, mCallback);
+        ArgumentCaptor<CallerInfoLookupHelper.OnQueryCompleteListener> captor =
+                ArgumentCaptor.forClass(CallerInfoLookupHelper.OnQueryCompleteListener.class);
+        verify(mCallerInfoLookupHelper).startLookup(eq(TEST_HANDLE), captor.capture());
+        return captor.getValue();
+    }
+
     private void waitOnLatch(CountDownLatch latch) {
         while (latch.getCount() > 0) {
             try {
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index e18ffb2..e304d34 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -16,6 +16,10 @@
 
 package com.android.server.telecom.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -23,6 +27,7 @@
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
@@ -56,9 +61,15 @@
 
 import com.google.common.base.Predicate;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import java.util.List;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
@@ -69,11 +80,25 @@
 /**
  * Performs various basic call tests in Telecom.
  */
+@RunWith(JUnit4.class)
 public class BasicCallTests extends TelecomSystemTest {
     private static final String TEST_BUNDLE_KEY = "android.telecom.extra.TEST";
     private static final String TEST_EVENT = "android.telecom.event.TEST";
 
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @LargeTest
+    @Test
     public void testSingleOutgoingCallLocalDisconnect() throws Exception {
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -102,6 +127,7 @@
     }
 
     @LargeTest
+    @Test
     public void testSingleOutgoingCallRemoteDisconnect() throws Exception {
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -123,6 +149,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testTelecomManagerAcceptRingingCall() throws Exception {
         IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 mConnectionServiceFixtureA);
@@ -149,6 +176,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
         IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
@@ -177,6 +205,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
         IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
@@ -204,6 +233,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
         IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
@@ -225,6 +255,7 @@
     }
 
     @LargeTest
+    @Test
     public void testSingleIncomingCallLocalDisconnect() throws Exception {
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -242,6 +273,7 @@
     }
 
     @LargeTest
+    @Test
     public void testSingleIncomingCallRemoteDisconnect() throws Exception {
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -256,6 +288,7 @@
     }
 
     @LargeTest
+    @Test
     public void testIncomingEmergencyCallback() throws Exception {
         // Make an outgoing emergency call
         String phoneNumber = "650-555-1212";
@@ -279,15 +312,16 @@
                 .createConnection(any(PhoneAccountHandle.class), anyString(),
                         connectionRequestCaptor.capture(), eq(true), eq(false), any());
 
-        assert(connectionRequestCaptor.getValue().getExtras().containsKey(
+        assertTrue(connectionRequestCaptor.getValue().getExtras().containsKey(
             android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS));
         assertTrue(connectionRequestCaptor.getValue().getExtras().getLong(
             android.telecom.Call.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0) > 0);
-        assert(connectionRequestCaptor.getValue().getExtras().containsKey(
+        assertTrue(connectionRequestCaptor.getValue().getExtras().containsKey(
             TelecomManager.EXTRA_INCOMING_CALL_ADDRESS));
     }
 
     @LargeTest
+    @Test
     public void testOutgoingCallAndSelectPhoneAccount() throws Exception {
         // Remove default PhoneAccount so that the Call moves into the correct
         // SELECT_PHONE_ACCOUNT state.
@@ -317,6 +351,7 @@
     }
 
     @LargeTest
+    @Test
     public void testIncomingCallFromContactWithSendToVoicemailIsRejected() throws Exception {
         Bundle extras = new Bundle();
         extras.putParcelable(
@@ -359,6 +394,7 @@
     }
 
     @LargeTest
+    @Test
     public void testIncomingCallCallerInfoLookupTimesOutIsAllowed() throws Exception {
         when(mClockProxy.currentTimeMillis()).thenReturn(TEST_CREATE_TIME);
         when(mClockProxy.elapsedRealtime()).thenReturn(TEST_CREATE_ELAPSED_TIME);
@@ -405,6 +441,8 @@
     }
 
     @LargeTest
+    @Test
+    @FlakyTest
     public void testIncomingCallFromBlockedNumberIsRejected() throws Exception {
         String phoneNumber = "650-555-1212";
         blockNumber(phoneNumber);
@@ -443,6 +481,7 @@
     }
 
     @LargeTest
+    @Test
     public void testIncomingCallBlockCheckTimesoutIsAllowed() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         String phoneNumber = "650-555-1212";
@@ -490,20 +529,8 @@
                 });
     }
 
-    @MediumTest
-    public void testDeadlockOnOutgoingCall() throws Exception {
-        for (int i = 0; i < 100; i++) {
-            BasicCallTests test = new BasicCallTests();
-            test.setContext(getContext());
-            test.setTestContext(getTestContext());
-            test.setName(getName());
-            test.setUp();
-            test.do_testDeadlockOnOutgoingCall();
-            test.tearDown();
-        }
-    }
-
     @LargeTest
+    @Test
     public void testIncomingThenOutgoingCalls() throws Exception {
         // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
         IdPair incoming = startAndMakeActiveIncomingCall("650-555-2323",
@@ -516,6 +543,7 @@
     }
 
     @LargeTest
+    @Test
     public void testOutgoingThenIncomingCalls() throws Exception {
         // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
         IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1212",
@@ -537,6 +565,7 @@
     }
 
     @LargeTest
+    @Test
     public void testAudioManagerOperations() throws Exception {
         AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble()
                 .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
@@ -555,10 +584,10 @@
         verify(mAudioService, timeout(TEST_TIMEOUT))
                 .setMicrophoneMute(eq(false), any(String.class), any(Integer.class));
 
-        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
         verify(audioManager, timeout(TEST_TIMEOUT))
                 .setSpeakerphoneOn(true);
-        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
+        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE, null);
         verify(audioManager, timeout(TEST_TIMEOUT))
                 .setSpeakerphoneOn(false);
 
@@ -598,11 +627,13 @@
     }
 
     @MediumTest
+    @Test
     public void testBasicConferenceCall() throws Exception {
         makeConferenceCall();
     }
 
     @MediumTest
+    @Test
     public void testAddCallToConference1() throws Exception {
         ParcelableCall conferenceCall = makeConferenceCall();
         IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
@@ -620,6 +651,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAddCallToConference2() throws Exception {
         ParcelableCall conferenceCall = makeConferenceCall();
         IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
@@ -642,6 +674,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testPullNonExternalCall() throws Exception {
         // TODO: Revisit this unit test once telecom support for filtering external calls from
         // InCall services is implemented.
@@ -662,6 +695,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testSendConnectionEventNull() throws Exception {
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -677,6 +711,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testSendConnectionEventNotNull() throws Exception {
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -698,12 +733,13 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testSendCallEventNull() throws Exception {
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
 
-        mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT, null);
+        mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT, 26, null);
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
                 .sendCallEvent(eq(ids.mConnectionId), eq(TEST_EVENT), isNull(Bundle.class), any());
     }
@@ -714,6 +750,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testSendCallEventNonNull() throws Exception {
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
@@ -723,7 +760,7 @@
         testBundle.putString(TEST_BUNDLE_KEY, "TEST");
 
         ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
-        mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT,
+        mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT, 26,
                 testBundle);
         verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
                 .sendCallEvent(eq(ids.mConnectionId), eq(TEST_EVENT),
@@ -776,6 +813,7 @@
      * property set.
      */
     @MediumTest
+    @Test
     public void testCdmaEnhancedPrivacyVoiceCall() throws Exception {
         mConnectionServiceFixtureA.mConnectionServiceDelegate.mProperties =
                 Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY;
@@ -794,6 +832,7 @@
      * when the Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY property is removed from the Connection.
      */
     @MediumTest
+    @Test
     public void testDropCdmaEnhancedPrivacyVoiceCall() throws Exception {
         mConnectionServiceFixtureA.mConnectionServiceDelegate.mProperties =
                 Connection.PROPERTY_HAS_CDMA_VOICE_PRIVACY;
@@ -815,6 +854,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testPullExternalCall() throws Exception {
         // TODO: Revisit this unit test once telecom support for filtering external calls from
         // InCall services is implemented.
@@ -841,6 +881,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testPullNonPullableExternalCall() throws Exception {
         // TODO: Revisit this unit test once telecom support for filtering external calls from
         // InCall services is implemented.
@@ -858,32 +899,13 @@
                 .pullExternalCall(eq(ids.mConnectionId), any());
     }
 
-    @LargeTest
-    public void testEmergencyCallFailMoveToSecondSim() throws Exception {
-        IdPair ids = startAndMakeDialingEmergencyCall("650-555-1212",
-                mPhoneAccountE0.getAccountHandle(), mConnectionServiceFixtureA);
-        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
-        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
-
-        // The Emergency Call has failed on the default SIM with an ERROR Disconnect Cause. Retry
-        // with the other SIM PhoneAccount
-        IdPair newIds = triggerEmergencyRedial(mPhoneAccountE1.getAccountHandle(),
-                mConnectionServiceFixtureA, ids);
-
-        // Call should be active on the E1 PhoneAccount
-        mConnectionServiceFixtureA.sendSetActive(newIds.mConnectionId);
-        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(newIds.mCallId).getState());
-        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(newIds.mCallId).getState());
-        assertEquals(mInCallServiceFixtureX.getCall(ids.mCallId).getAccountHandle(),
-                mPhoneAccountE1.getAccountHandle());
-    }
-
     /**
      * Test scenario where the user starts an outgoing video call with no selected PhoneAccount, and
      * then subsequently selects a PhoneAccount which supports video calling.
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testOutgoingCallSelectPhoneAccountVideo() throws Exception {
         startOutgoingPhoneCallPendingCreateConnection("650-555-1212",
                 null, mConnectionServiceFixtureA,
@@ -906,6 +928,7 @@
      */
     @FlakyTest
     @LargeTest
+    @Test
     public void testOutgoingCallSelectPhoneAccountNoVideo() throws Exception {
         startOutgoingPhoneCallPendingCreateConnection("650-555-1212",
                 null, mConnectionServiceFixtureA,
@@ -926,6 +949,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testSelfManagedOutgoing() throws Exception {
         PhoneAccountHandle phoneAccountHandle = mPhoneAccountSelfManaged.getAccountHandle();
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212", phoneAccountHandle,
@@ -940,6 +964,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testSelfManagedIncoming() throws Exception {
         PhoneAccountHandle phoneAccountHandle = mPhoneAccountSelfManaged.getAccountHandle();
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212", phoneAccountHandle,
@@ -955,24 +980,158 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testIsOutgoingCallPermitted() throws Exception {
         assertTrue(mTelecomSystem.getTelecomServiceImpl().getBinder()
                 .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
     }
 
     /**
-     * Basic test to ensure that when there are other calls, we do not permit outgoing calls by a
-     * self managed CS.
+     * Ensure if there is a holdable call ongoing we'll be able to place another call.
      * @throws Exception
      */
     @LargeTest
-    public void testIsOutgoingCallPermittedOngoing() throws Exception {
-        // Start a regular call; the self-managed CS can't make a call now.
+    @Test
+    public void testIsOutgoingCallPermittedOngoingHoldable() throws Exception {
+        // Start a regular call; the self-managed CS can make a call now since ongoing call can be
+        // held
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
 
-        assertFalse(mTelecomSystem.getTelecomServiceImpl().getBinder()
+        assertTrue(mTelecomSystem.getTelecomServiceImpl().getBinder()
                 .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
     }
+
+    /**
+     * Ensure if there is an unholdable call we can't place another call.
+     * @throws Exception
+     */
+    @LargeTest
+    @Test
+    public void testIsOutgoingCallPermittedOngoingUnHoldable() throws Exception {
+        // Start a regular call; the self-managed CS can't make a call now because the ongoing call
+        // can't be held.
+        mConnectionServiceFixtureA.mConnectionServiceDelegate.mCapabilities = 0;
+        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
+                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+
+        assertTrue(mTelecomSystem.getTelecomServiceImpl().getBinder()
+                .isOutgoingCallPermitted(mPhoneAccountSelfManaged.getAccountHandle()));
+    }
+
+    /**
+     * Basic to verify audio route gets reset to baseline when emergency call placed while a
+     * self-managed call is underway.
+     * @throws Exception
+     */
+    @LargeTest
+    @Test
+    @FlakyTest
+    public void testDisconnectSelfManaged() throws Exception {
+        // Add a self-managed call.
+        PhoneAccountHandle phoneAccountHandle = mPhoneAccountSelfManaged.getAccountHandle();
+        startAndMakeActiveIncomingCall("650-555-1212", phoneAccountHandle,
+                mConnectionServiceFixtureA);
+        Connection connection = mConnectionServiceFixtureA.mLatestConnection;
+
+        // Route self-managed call to speaker.
+        connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        // Place an emergency call.
+        startAndMakeDialingEmergencyCall("650-555-1212", mPhoneAccountE0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        // Should have reverted back to earpiece.
+        assertEquals(CallAudioState.ROUTE_EARPIECE,
+                mInCallServiceFixtureX.mCallAudioState.getRoute());
+    }
+
+    /**
+     * Tests the {@link Call#deflect} API.  Verifies that if a call is incoming,
+     * and deflect API is called, then request is made to the connection service.
+     *
+     * @throws Exception
+     */
+    @LargeTest
+    @Test
+    public void testDeflectCallWhenIncoming() throws Exception {
+        Uri deflectAddress = Uri.parse("tel:650-555-1214");
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+        // Attempt to deflect the call and verify the API call makes it through
+        mInCallServiceFixtureX.mInCallAdapter.deflectCall(ids.mCallId, deflectAddress);
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .deflect(eq(ids.mConnectionId), eq(deflectAddress), any());
+        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
+    }
+
+    /**
+     * Tests the {@link Call#deflect} API.  Verifies that if a call is outgoing,
+     * and deflect API is called, then request is not made to the connection service.
+     * Ideally, deflect option should be displayed only if call is incoming/waiting.
+     *
+     * @throws Exception
+     */
+    @LargeTest
+    @Test
+    public void testDeflectCallWhenOutgoing() throws Exception {
+        Uri deflectAddress = Uri.parse("tel:650-555-1214");
+        IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA, Process.myUserHandle());
+        // Attempt to deflect the call and verify the API call does not make it through
+        mInCallServiceFixtureX.mInCallAdapter.deflectCall(ids.mCallId, deflectAddress);
+        verify(mConnectionServiceFixtureA.getTestDouble(), never())
+                .deflect(eq(ids.mConnectionId), eq(deflectAddress), any());
+        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
+    }
+
+    /**
+     * Test to make sure to unmute automatically when making an emergency call and keep unmute
+     * during the emergency call.
+     * @throws Exception
+     */
+    @LargeTest
+    @Test
+    public void testUnmuteDuringEmergencyCall() throws Exception {
+        // Make an outgoing call and turn ON mute.
+        IdPair outgoingCall = startAndMakeActiveOutgoingCall("650-555-1212",
+                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
+        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(outgoingCall.mCallId)
+                .getState());
+        mInCallServiceFixtureX.mInCallAdapter.mute(true);
+        waitForHandlerAction(mTelecomSystem.getCallsManager().getCallAudioManager()
+                .getCallAudioRouteStateMachine().getHandler(), TEST_TIMEOUT);
+        assertTrue(mTelecomSystem.getCallsManager().getAudioState().isMuted());
+
+        // Make an emergency call.
+        IdPair emergencyCall = startAndMakeDialingEmergencyCall("650-555-1213",
+                mPhoneAccountE0.getAccountHandle(), mConnectionServiceFixtureA);
+        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(emergencyCall.mCallId)
+                .getState());
+        waitForHandlerAction(mTelecomSystem.getCallsManager().getCallAudioManager()
+                .getCallAudioRouteStateMachine().getHandler(), TEST_TIMEOUT);
+        // Should be unmute automatically.
+        assertFalse(mTelecomSystem.getCallsManager().getAudioState().isMuted());
+
+        // Toggle mute during an emergency call.
+        mTelecomSystem.getCallsManager().getCallAudioManager().toggleMute();
+        waitForHandlerAction(mTelecomSystem.getCallsManager().getCallAudioManager()
+                .getCallAudioRouteStateMachine().getHandler(), TEST_TIMEOUT);
+        // Should keep unmute.
+        assertFalse(mTelecomSystem.getCallsManager().getAudioState().isMuted());
+
+        ArgumentCaptor<Boolean> muteValueCaptor = ArgumentCaptor.forClass(Boolean.class);
+        verify(mAudioService, times(2)).setMicrophoneMute(muteValueCaptor.capture(),
+                any(String.class), any(Integer.class));
+        List<Boolean> muteValues = muteValueCaptor.getAllValues();
+        // Check mute status was changed twice with true and false.
+        assertTrue(muteValues.get(0));
+        assertFalse(muteValues.get(1));
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 68e76b8..cb7671a 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -30,13 +30,22 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 
+@RunWith(JUnit4.class)
 public class BluetoothDeviceManagerTest extends TelecomTestCase {
     @Mock BluetoothRouteManager mRouteManager;
     @Mock BluetoothHeadsetProxy mHeadsetProxy;
@@ -50,6 +59,8 @@
     private BluetoothDevice device2;
     private BluetoothDevice device3;
 
+    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         device1 = makeBluetoothDevice("00:00:00:00:00:01");
@@ -67,19 +78,14 @@
                 serviceCaptor.capture(), eq(BluetoothProfile.HEADSET));
         serviceListenerUnderTest = serviceCaptor.getValue();
 
-        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        ArgumentCaptor<IntentFilter> intentFilterCaptor =
-                ArgumentCaptor.forClass(IntentFilter.class);
-        verify(mContext).registerReceiver(receiverCaptor.capture(), intentFilterCaptor.capture());
-        assertTrue(intentFilterCaptor.getValue().hasAction(
-                BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));
-        receiverUnderTest = receiverCaptor.getValue();
+        receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager,
+                null /* route mgr not needed here */);
 
         mBluetoothDeviceManager.setHeadsetServiceForTesting(mHeadsetProxy);
     }
 
     @SmallTest
+    @Test
     public void testSingleDeviceConnectAndDisconnect() {
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1));
@@ -93,6 +99,18 @@
     }
 
     @SmallTest
+    @Test
+    public void testAddDeviceFailsWhenServicesAreNull() {
+        mBluetoothDeviceManager.setHeadsetServiceForTesting(null);
+
+        receiverUnderTest.onReceive(mContext,
+                buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1));
+
+        assertEquals(0, mBluetoothDeviceManager.getNumConnectedDevices());
+    }
+    
+    @SmallTest
+    @Test
     public void testMultiDeviceConnectAndDisconnect() {
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1));
@@ -115,6 +133,7 @@
     }
 
     @SmallTest
+    @Test
     public void testExclusionaryGetRecentDevices() {
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1));
@@ -132,6 +151,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetServiceDisconnect() {
         receiverUnderTest.onReceive(mContext,
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device1));
@@ -139,8 +159,8 @@
                 buildConnectionActionIntent(BluetoothHeadset.STATE_CONNECTED, device2));
         serviceListenerUnderTest.onServiceDisconnected(0);
 
-        verify(mRouteManager).onDeviceLost(device1);
-        verify(mRouteManager).onDeviceLost(device2);
+        verify(mRouteManager).onDeviceLost(device1.getAddress());
+        verify(mRouteManager).onDeviceLost(device2.getAddress());
         assertNull(mBluetoothDeviceManager.getHeadsetService());
         assertEquals(0, mBluetoothDeviceManager.getNumConnectedDevices());
     }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothManagerTest.java
deleted file mode 100644
index d9a9e14..0000000
--- a/tests/src/com/android/server/telecom/tests/BluetoothManagerTest.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.telecom.tests;
-
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.telecom.BluetoothAdapterProxy;
-import com.android.server.telecom.BluetoothHeadsetProxy;
-import com.android.server.telecom.BluetoothManager;
-
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-
-import java.util.Collections;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class BluetoothManagerTest extends TelecomTestCase {
-    @Mock BluetoothManager.BluetoothStateListener mListener;
-    @Mock BluetoothHeadsetProxy mHeadsetProxy;
-    @Mock BluetoothAdapterProxy mAdapterProxy;
-
-    BluetoothManager mBluetoothManager;
-    BluetoothProfile.ServiceListener serviceListenerUnderTest;
-    BroadcastReceiver receiverUnderTest;
-
-    private BluetoothDevice device1;
-
-    public void setUp() throws Exception {
-        super.setUp();
-        initializeDevice();
-        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-        mBluetoothManager = new BluetoothManager(mContext, mAdapterProxy);
-
-        ArgumentCaptor<BluetoothProfile.ServiceListener> serviceCaptor =
-                ArgumentCaptor.forClass(BluetoothProfile.ServiceListener.class);
-        verify(mAdapterProxy).getProfileProxy(eq(mContext),
-                serviceCaptor.capture(), eq(BluetoothProfile.HEADSET));
-        serviceListenerUnderTest = serviceCaptor.getValue();
-
-        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mContext).registerReceiver(receiverCaptor.capture(), any(IntentFilter.class));
-        receiverUnderTest = receiverCaptor.getValue();
-
-        mBluetoothManager.setBluetoothStateListener(mListener);
-        mBluetoothManager.setBluetoothHeadsetForTesting(mHeadsetProxy);
-    }
-
-    @SmallTest
-    public void testIsBluetoothAvailableWithNoDevices() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.<BluetoothDevice>emptyList());
-        assertFalse(mBluetoothManager.isBluetoothAvailable());
-    }
-
-    @SmallTest
-    public void testIsBluetoothAvailable() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        assertTrue(mBluetoothManager.isBluetoothAvailable());
-    }
-
-    @SmallTest
-    public void testIsAudioConnectedWithNoDevices() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.<BluetoothDevice>emptyList());
-        assertFalse(mBluetoothManager.isBluetoothAudioConnected());
-    }
-
-    @SmallTest
-    public void testIsAudioConnectedWhenAudioNotOn() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(false);
-        assertFalse(mBluetoothManager.isBluetoothAudioConnected());
-    }
-
-    @SmallTest
-    public void testIsAudioConnectedWhenAudioOn() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(true);
-        assertTrue(mBluetoothManager.isBluetoothAudioConnected());
-    }
-
-    @SmallTest
-    public void testShouldBePendingAfterConnectAudio() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(false);
-        mBluetoothManager.connectBluetoothAudio();
-        verify(mHeadsetProxy).connectAudio();
-        assertFalse(mBluetoothManager.isBluetoothAudioConnected());
-        assertTrue(mBluetoothManager.isBluetoothAudioConnectedOrPending());
-    }
-
-    @SmallTest
-    public void testDisconnectAudioWhenHeadsetServiceConnected() {
-        mBluetoothManager.disconnectBluetoothAudio();
-        verify(mHeadsetProxy).disconnectAudio();
-    }
-
-    @SmallTest
-    public void testDisconnectAudioWithNoHeadsetService() {
-        mBluetoothManager.setBluetoothHeadsetForTesting(null);
-        mBluetoothManager.disconnectBluetoothAudio();
-        verify(mHeadsetProxy, never()).disconnectAudio();
-    }
-
-    @SmallTest
-    public void testConnectServiceWhenUninitialized1() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.<BluetoothDevice>emptyList());
-        serviceListenerUnderTest.onServiceConnected(BluetoothProfile.HEALTH, null);
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_UNINITIALIZED,
-                BluetoothManager.BLUETOOTH_DISCONNECTED);
-    }
-
-    @SmallTest
-    public void testConnectServiceWhenUninitialized2() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(false);
-        serviceListenerUnderTest.onServiceConnected(BluetoothProfile.HEALTH, null);
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_UNINITIALIZED,
-                BluetoothManager.BLUETOOTH_DEVICE_CONNECTED);
-    }
-
-    @SmallTest
-    public void testConnectServiceWhenUninitialized3() {
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(true);
-        serviceListenerUnderTest.onServiceConnected(BluetoothProfile.HEALTH, null);
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_UNINITIALIZED,
-                BluetoothManager.BLUETOOTH_AUDIO_CONNECTED);
-    }
-
-    @SmallTest
-    public void testReceiveAudioDisconnectWhenConnected() {
-        mBluetoothManager.setInternalBluetoothState(BluetoothManager.BLUETOOTH_AUDIO_CONNECTED);
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(false);
-        receiverUnderTest.onReceive(mContext,
-                buildAudioActionIntent(BluetoothHeadset.STATE_AUDIO_DISCONNECTED));
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_AUDIO_CONNECTED,
-                BluetoothManager.BLUETOOTH_DEVICE_CONNECTED);
-    }
-
-    @SmallTest
-    public void testReceiveAudioConnectWhenDisconnected() {
-        mBluetoothManager.setInternalBluetoothState(BluetoothManager.BLUETOOTH_DEVICE_CONNECTED);
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(true);
-        receiverUnderTest.onReceive(mContext,
-                buildAudioActionIntent(BluetoothHeadset.STATE_AUDIO_CONNECTED));
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_DEVICE_CONNECTED,
-                BluetoothManager.BLUETOOTH_AUDIO_CONNECTED);
-    }
-
-    @SmallTest
-    public void testReceiveAudioConnectWhenPending() {
-        mBluetoothManager.setInternalBluetoothState(BluetoothManager.BLUETOOTH_AUDIO_PENDING);
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(true);
-        receiverUnderTest.onReceive(mContext,
-                buildAudioActionIntent(BluetoothHeadset.STATE_AUDIO_CONNECTED));
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_AUDIO_PENDING,
-                BluetoothManager.BLUETOOTH_AUDIO_CONNECTED);
-    }
-
-    @SmallTest
-    public void testReceiveAudioDisconnectWhenPending() {
-        mBluetoothManager.setInternalBluetoothState(BluetoothManager.BLUETOOTH_AUDIO_PENDING);
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(false);
-        receiverUnderTest.onReceive(mContext,
-                buildAudioActionIntent(BluetoothHeadset.STATE_AUDIO_DISCONNECTED));
-        verify(mListener).onBluetoothStateChange(BluetoothManager.BLUETOOTH_AUDIO_PENDING,
-                BluetoothManager.BLUETOOTH_DEVICE_CONNECTED);
-    }
-
-    @SmallTest
-    public void testReceiveAudioConnectingWhenPending() {
-        mBluetoothManager.setInternalBluetoothState(BluetoothManager.BLUETOOTH_AUDIO_PENDING);
-        when(mHeadsetProxy.getConnectedDevices()).thenReturn(
-                Collections.singletonList(device1));
-        when(mHeadsetProxy.isAudioConnected(eq(device1))).thenReturn(false);
-        receiverUnderTest.onReceive(mContext,
-                buildAudioActionIntent(BluetoothHeadset.STATE_AUDIO_CONNECTING));
-        verify(mListener, never()).onBluetoothStateChange(anyInt(), anyInt());
-    }
-
-    private Intent buildAudioActionIntent(int state) {
-        Intent i = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
-        i.putExtra(BluetoothHeadset.EXTRA_STATE, state);
-        return i;
-    }
-
-    private void initializeDevice() {
-        Parcel p1 = Parcel.obtain();
-        p1.writeString("00:01:02:03:04:05");
-        p1.setDataPosition(0);
-        device1 = BluetoothDevice.CREATOR.createFromParcel(p1);
-        p1.recycle();
-    }
-}
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
index 300415a..a5feab7 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
@@ -22,12 +22,10 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Debug;
 import android.telecom.Connection;
 import android.telecom.GatewayInfo;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -42,12 +40,18 @@
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -64,6 +68,7 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 
+@RunWith(JUnit4.class)
 public class BluetoothPhoneServiceTest extends TelecomTestCase {
 
     private static final int TEST_DTMF_TONE = 0;
@@ -78,6 +83,7 @@
     private static final int CALL_STATE_INCOMING = 4;
     private static final int CALL_STATE_WAITING = 5;
     private static final int CALL_STATE_IDLE = 6;
+    private static final int CALL_STATE_DISCONNECTED = 7;
     // Terminate all held or set UDUB("busy") to a waiting call
     private static final int CHLD_TYPE_RELEASEHELD = 0;
     // Terminate all active calls and accepts a waiting/held call
@@ -96,6 +102,7 @@
     @Mock BluetoothHeadsetProxy mMockBluetoothHeadset;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
@@ -110,6 +117,7 @@
         doReturn(null).when(mMockCallsManager).getHeldCall();
         doReturn(null).when(mMockCallsManager).getOutgoingCall();
         doReturn(0).when(mMockCallsManager).getNumHeldCalls();
+        doReturn(false).when(mMockCallsManager).hasOnlyDisconnectedCalls();
         mBluetoothPhoneService = new BluetoothPhoneServiceImpl(mContext, mLock, mMockCallsManager,
                 mock(BluetoothAdapterProxy.class), mMockPhoneAccountRegistrar);
 
@@ -118,6 +126,7 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
 
         mBluetoothPhoneService = null;
@@ -125,6 +134,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetAnswerCall() throws Exception {
         Call mockCall = createRingingCall();
 
@@ -135,6 +145,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetAnswerCallNull() throws Exception {
         when(mMockCallsManager.getRingingCall()).thenReturn(null);
 
@@ -145,6 +156,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetHangupCall() throws Exception {
         Call mockCall = createForegroundCall();
 
@@ -155,6 +167,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetHangupCallNull() throws Exception {
         when(mMockCallsManager.getForegroundCall()).thenReturn(null);
 
@@ -165,6 +178,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetSendDTMF() throws Exception {
         Call mockCall = createForegroundCall();
 
@@ -176,6 +190,7 @@
     }
 
     @SmallTest
+    @Test
     public void testHeadsetSendDTMFNull() throws Exception {
         when(mMockCallsManager.getForegroundCall()).thenReturn(null);
 
@@ -187,6 +202,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetNetworkOperator() throws Exception {
         Call mockCall = createForegroundCall();
         PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
@@ -199,6 +215,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetNetworkOperatorNoPhoneAccount() throws Exception {
         when(mMockCallsManager.getForegroundCall()).thenReturn(null);
 
@@ -208,6 +225,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetSubscriberNumber() throws Exception {
         Call mockCall = createForegroundCall();
         PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
@@ -220,6 +238,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetSubscriberNumberFallbackToTelephony() throws Exception {
         Call mockCall = createForegroundCall();
         String fakeNumber = "8675309";
@@ -235,6 +254,7 @@
     }
 
     @MediumTest
+    @Test
     public void testListCurrentCallsOneCall() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         Call activeCall = createActiveCall();
@@ -252,6 +272,7 @@
     }
 
     @MediumTest
+    @Test
     public void testConferenceInProgressCDMA() throws Exception {
         // If two calls are being conferenced and updateHeadsetWithCallState runs while this is
         // still occuring, it will look like there is an active and held call still while we are
@@ -302,6 +323,7 @@
     }
 
     @MediumTest
+    @Test
     public void testListCurrentCallsCdmaHold() throws Exception {
         // Call has been put into a CDMA "conference" with one call on hold.
         ArrayList<Call> calls = new ArrayList<>();
@@ -343,6 +365,7 @@
     }
 
     @MediumTest
+    @Test
     public void testListCurrentCallsCdmaConference() throws Exception {
         // Call is in a true CDMA conference
         ArrayList<Call> calls = new ArrayList<>();
@@ -384,6 +407,7 @@
     }
 
     @MediumTest
+    @Test
     public void testWaitingCallClccResponse() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         when(mMockCallsManager.getCalls()).thenReturn(calls);
@@ -406,6 +430,7 @@
     }
 
     @MediumTest
+    @Test
     public void testNewCallClccResponse() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         when(mMockCallsManager.getCalls()).thenReturn(calls);
@@ -421,6 +446,7 @@
     }
 
     @MediumTest
+    @Test
     public void testRingingCallClccResponse() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         when(mMockCallsManager.getCalls()).thenReturn(calls);
@@ -441,6 +467,7 @@
     }
 
     @MediumTest
+    @Test
     public void testCallClccCache() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         when(mMockCallsManager.getCalls()).thenReturn(calls);
@@ -475,6 +502,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAlertingCallClccResponse() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         when(mMockCallsManager.getCalls()).thenReturn(calls);
@@ -495,6 +523,7 @@
     }
 
     @MediumTest
+    @Test
     public void testHoldingCallClccResponse() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         when(mMockCallsManager.getCalls()).thenReturn(calls);
@@ -524,6 +553,7 @@
     }
 
     @MediumTest
+    @Test
     public void testListCurrentCallsImsConference() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         Call parentCall = createActiveCall();
@@ -542,6 +572,7 @@
     }
 
     @MediumTest
+    @Test
     public void testListCurrentCallsHeldImsCepConference() throws Exception {
         ArrayList<Call> calls = new ArrayList<>();
         Call parentCall = createHeldCall();
@@ -572,6 +603,7 @@
     }
 
     @MediumTest
+    @Test
     public void testQueryPhoneState() throws Exception {
         Call ringingCall = createRingingCall();
         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
@@ -583,6 +615,7 @@
     }
 
     @MediumTest
+    @Test
     public void testCDMAConferenceQueryState() throws Exception {
         Call parentConfCall = createActiveCall();
         final Call confCall1 = mock(Call.class);
@@ -603,6 +636,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldTypeReleaseHeldRinging() throws Exception {
         Call ringingCall = createRingingCall();
 
@@ -613,6 +647,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldTypeReleaseHeldHold() throws Exception {
         Call onHoldCall = createHeldCall();
 
@@ -623,6 +658,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldReleaseActiveRinging() throws Exception {
         Call activeCall = createActiveCall();
         Call ringingCall = createRingingCall();
@@ -636,6 +672,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldReleaseActiveHold() throws Exception {
         Call activeCall = createActiveCall();
         Call heldCall = createHeldCall();
@@ -649,6 +686,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldHoldActiveRinging() throws Exception {
         Call ringingCall = createRingingCall();
 
@@ -660,6 +698,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldHoldActiveUnhold() throws Exception {
         Call heldCall = createHeldCall();
 
@@ -671,6 +710,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldHoldActiveHold() throws Exception {
         Call activeCall = createActiveCall();
         addCallCapability(activeCall, Connection.CAPABILITY_HOLD);
@@ -683,6 +723,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldAddHeldToConfHolding() throws Exception {
         Call activeCall = createActiveCall();
         addCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
@@ -694,6 +735,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldAddHeldToConf() throws Exception {
         Call activeCall = createActiveCall();
         removeCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
@@ -709,6 +751,7 @@
     }
 
     @MediumTest
+    @Test
     public void testProcessChldHoldActiveSwapConference() throws Exception {
         // Create an active CDMA Call with a call on hold and simulate a swapConference().
         Call parentCall = createActiveCall();
@@ -734,6 +777,7 @@
 
     // Testing the CallsManager Listener Functionality on Bluetooth
     @MediumTest
+    @Test
     public void testOnCallAddedRinging() throws Exception {
         Call ringingCall = createRingingCall();
         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555000"));
@@ -746,6 +790,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnCallAddedCdmaActiveHold() throws Exception {
         // Call has been put into a CDMA "conference" with one call on hold.
         Call parentCall = createActiveCall();
@@ -767,6 +812,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnCallRemoved() throws Exception {
         Call activeCall = createActiveCall();
         mBluetoothPhoneService.mCallsManagerListener.onCallAdded(activeCall);
@@ -778,6 +824,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnCallStateChangedConnectingCall() throws Exception {
         Call activeCall = mock(Call.class);
         Call connectingCall = mock(Call.class);
@@ -795,6 +842,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnCallStateChangedDialing() throws Exception {
         Call activeCall = createActiveCall();
 
@@ -806,6 +854,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnCallStateChangedAlerting() throws Exception {
         Call outgoingCall = createOutgoingCall();
 
@@ -817,6 +866,27 @@
     }
 
     @MediumTest
+    @Test
+    public void testOnCallStateChangedDisconnected() throws Exception {
+        Call disconnectedCall = createDisconnectedCall();
+        doReturn(true).when(mMockCallsManager).hasOnlyDisconnectedCalls();
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(disconnectedCall,
+                CallState.DISCONNECTING, CallState.DISCONNECTED);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_DISCONNECTED),
+                eq(""), eq(128));
+
+        doReturn(false).when(mMockCallsManager).hasOnlyDisconnectedCalls();
+        mBluetoothPhoneService.mCallsManagerListener.onDisconnectedTonePlaying(true);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_DISCONNECTED),
+                eq(""), eq(128));
+
+        mBluetoothPhoneService.mCallsManagerListener.onDisconnectedTonePlaying(false);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+    }
+
+    @MediumTest
+    @Test
     public void testOnCallStateChanged() throws Exception {
         Call ringingCall = createRingingCall();
         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
@@ -837,6 +907,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnCallStateChangedGSMSwap() throws Exception {
         Call heldCall = createHeldCall();
         when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
@@ -849,6 +920,7 @@
     }
 
     @MediumTest
+    @Test
     public void testOnIsConferencedChanged() throws Exception {
         // Start with two calls that are being merged into a CDMA conference call. The
         // onIsConferencedChanged method will be called multiple times during the call. Make sure
@@ -885,6 +957,7 @@
     }
 
     @MediumTest
+    @Test
     public void testBluetoothAdapterReceiver() throws Exception {
         Call ringingCall = createRingingCall();
         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
@@ -929,6 +1002,12 @@
         return call;
     }
 
+    private Call createDisconnectedCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getFirstCallWithState(CallState.DISCONNECTED)).thenReturn(call);
+        return call;
+    }
+
     private Call createForegroundCall() {
         Call call = mock(Call.class);
         when(mMockCallsManager.getForegroundCall()).thenReturn(call);
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 9b94f63..5b45828 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -17,426 +17,143 @@
 package com.android.server.telecom.tests;
 
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
 import android.content.ContentResolver;
 import android.os.Parcel;
 import android.telecom.Log;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Pair;
 
 import com.android.internal.os.SomeArgs;
 import com.android.server.telecom.BluetoothHeadsetProxy;
-import com.android.server.telecom.CallAudioModeStateMachine;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Objects;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-public class BluetoothRouteManagerTest extends StateMachineTestBase<BluetoothRouteManager> {
-    private static class BluetoothRouteTestParametersBuilder {
-        private String name;
-        private String initialBluetoothState;
-        private BluetoothDevice initialDevice;
-        private BluetoothDevice audioOnDevice;
-        private int messageType;
-        private String messageDevice;
-        private Pair<Integer, Integer> expectedListenerUpdate;
-        private int expectedBluetoothInteraction;
-        private String expectedConnectionAddress;
-        private String expectedFinalStateName;
-        private BluetoothDevice[] connectedDevices;
-
-        public BluetoothRouteTestParametersBuilder setName(String name) {
-            this.name = name;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setInitialBluetoothState(
-                String initialBluetoothState) {
-            this.initialBluetoothState = initialBluetoothState;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setInitialDevice(BluetoothDevice
-                initialDevice) {
-            this.initialDevice = initialDevice;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setMessageType(int messageType) {
-            this.messageType = messageType;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setMessageDevice(String messageDevice) {
-            this.messageDevice = messageDevice;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setExpectedListenerUpdate(Pair<Integer,
-                Integer> expectedListenerUpdate) {
-            this.expectedListenerUpdate = expectedListenerUpdate;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setExpectedBluetoothInteraction(
-                int expectedBluetoothInteraction) {
-            this.expectedBluetoothInteraction = expectedBluetoothInteraction;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setExpectedConnectionAddress(String
-                expectedConnectionAddress) {
-            this.expectedConnectionAddress = expectedConnectionAddress;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setExpectedFinalStateName(
-                String expectedFinalStateName) {
-            this.expectedFinalStateName = expectedFinalStateName;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setConnectedDevices(
-                BluetoothDevice... connectedDevices) {
-            this.connectedDevices = connectedDevices;
-            return this;
-        }
-
-        public BluetoothRouteTestParametersBuilder setAudioOnDevice(BluetoothDevice device) {
-            this.audioOnDevice = device;
-            return this;
-        }
-
-        public BluetoothRouteTestParameters build() {
-            return new BluetoothRouteTestParameters(name,
-                    initialBluetoothState,
-                    initialDevice,
-                    messageType,
-                    expectedListenerUpdate,
-                    expectedBluetoothInteraction,
-                    expectedConnectionAddress,
-                    expectedFinalStateName,
-                    connectedDevices,
-                    messageDevice,
-                    audioOnDevice);
-        }
-    }
-
-    private static class BluetoothRouteTestParameters extends TestParameters {
-        public String name;
-        public String initialBluetoothState; // One of the state names or prefixes from BRM.
-        public BluetoothDevice initialDevice; // null if we start from AudioOff
-        public BluetoothDevice audioOnDevice; // The device (if any) that is active
-        public int messageType; // Any of the commands from the state machine
-        public String messageDevice; // The device that should be specified in the message.
-        // TODO: Change this when refactoring CARSM.
-        public Pair<Integer, Integer> expectedListenerUpdate; // (old state, new state)
-        public int expectedBluetoothInteraction; // NONE, CONNECT, or DISCONNECT
-        // TODO: this will always be none for now. Change once BT changes their API.
-        public String expectedConnectionAddress; // Expected device to connect to.
-        public String expectedFinalStateName; // Expected name of the final state.
-        public BluetoothDevice[] connectedDevices; // array of connected devices
-
-        public BluetoothRouteTestParameters(String name, String initialBluetoothState,
-                BluetoothDevice initialDevice, int messageType, Pair<Integer, Integer>
-                expectedListenerUpdate, int expectedBluetoothInteraction, String
-                expectedConnectionAddress, String expectedFinalStateName,
-                BluetoothDevice[] connectedDevices, String messageDevice,
-                BluetoothDevice audioOnDevice) {
-            this.name = name;
-            this.initialBluetoothState = initialBluetoothState;
-            this.initialDevice = initialDevice;
-            this.messageType = messageType;
-            this.expectedListenerUpdate = expectedListenerUpdate;
-            this.expectedBluetoothInteraction = expectedBluetoothInteraction;
-            this.expectedConnectionAddress = expectedConnectionAddress;
-            this.expectedFinalStateName = expectedFinalStateName;
-            this.connectedDevices = connectedDevices;
-            this.messageDevice = messageDevice;
-            this.audioOnDevice = audioOnDevice;
-        }
-
-        @Override
-        public String toString() {
-            return "BluetoothRouteTestParameters{" +
-                    "name='" + name + '\'' +
-                    ", initialBluetoothState='" + initialBluetoothState + '\'' +
-                    ", initialDevice=" + initialDevice +
-                    ", messageType=" + messageType +
-                    ", messageDevice='" + messageDevice + '\'' +
-                    ", expectedListenerUpdate=" + expectedListenerUpdate +
-                    ", expectedBluetoothInteraction=" + expectedBluetoothInteraction +
-                    ", expectedConnectionAddress='" + expectedConnectionAddress + '\'' +
-                    ", expectedFinalStateName='" + expectedFinalStateName + '\'' +
-                    ", connectedDevices=" + Arrays.toString(connectedDevices) +
-                    '}';
-        }
-    }
-
-    private static final int NONE = 1;
-    private static final int CONNECT = 2;
-    private static final int DISCONNECT = 3;
+@RunWith(JUnit4.class)
+public class BluetoothRouteManagerTest extends TelecomTestCase {
+    private static final int TEST_TIMEOUT = 1000;
+    static final BluetoothDevice DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01");
+    static final BluetoothDevice DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02");
+    static final BluetoothDevice DEVICE3 = makeBluetoothDevice("00:00:00:00:00:03");
 
     @Mock private BluetoothDeviceManager mDeviceManager;
     @Mock private BluetoothHeadsetProxy mHeadsetProxy;
     @Mock private Timeouts.Adapter mTimeoutsAdapter;
     @Mock private BluetoothRouteManager.BluetoothStateListener mListener;
 
-    private BluetoothDevice device1;
-    private BluetoothDevice device2;
-    private BluetoothDevice device3;
-
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
-        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-
-        device1 = makeBluetoothDevice("00:00:00:00:00:01");
-        device2 = makeBluetoothDevice("00:00:00:00:00:02");
-        device3 = makeBluetoothDevice("00:00:00:00:00:03");
-    }
-
-    @LargeTest
-    public void testTransitions() throws Throwable {
-        List<BluetoothRouteTestParameters> testCases = generateTestCases();
-        parametrizedTestStateMachine(testCases);
     }
 
     @SmallTest
+    @Test
     public void testConnectHfpRetryWhileNotConnected() {
         BluetoothRouteManager sm = setupStateMachine(
                 BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
-        setupConnectedDevices(new BluetoothDevice[]{device1}, null);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(0L);
         when(mHeadsetProxy.connectAudio()).thenReturn(false);
-        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, null);
-        // Wait 3 times: for the first connection attempt, the retry attempt, and once more to
-        // make sure there are only two attempts.
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        // TODO: verify address
-        verify(mHeadsetProxy, times(2)).connectAudio();
+        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE1.getAddress());
+        // Wait 3 times: for the first connection attempt, the retry attempt,
+        // the second retry, and once more to make sure there are only three attempts.
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        verifyConnectionAttempt(DEVICE1, 3);
         assertEquals(BluetoothRouteManager.AUDIO_OFF_STATE_NAME, sm.getCurrentState().getName());
         sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
         sm.quitNow();
     }
 
     @SmallTest
+    @Test
     public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
         BluetoothRouteManager sm = setupStateMachine(
-                BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, device1);
-        setupConnectedDevices(new BluetoothDevice[]{device1, device2}, null);
+                BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(0L);
         when(mHeadsetProxy.connectAudio()).thenReturn(false);
-        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device2.getAddress());
+        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE2.getAddress());
         // Wait 3 times: the first connection attempt is accounted for in executeRoutingAction,
-        // so wait for the retry attempt, again to make sure there are only two attempts, and
-        // once more for good luck.
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        // TODO: verify address of device2
-        verify(mHeadsetProxy, times(2)).connectAudio();
+        // so wait twice for the retry attempt, again to make sure there are only three attempts,
+        // and once more for good luck.
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        verifyConnectionAttempt(DEVICE2, 3);
         assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress(),
+                        + ":" + DEVICE1.getAddress(),
                 sm.getCurrentState().getName());
         sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
         sm.quitNow();
     }
 
-    @SmallTest
-    public void testProperFallbackOrder1() {
-        // Device 1, 2, 3 are connected in that order. Device 1 is activated, then device 2.
-        // Disconnect device 2, verify fallback to device 1. Disconnect device 1, fallback to
-        // device 3.
-        BluetoothRouteManager sm = setupStateMachine(
-                BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
-        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, null);
-        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device1.getAddress());
-        // TODO: verify address
-        verify(mHeadsetProxy, times(1)).connectAudio();
-
-        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, device1);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device1.getAddress());
-
-        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device2.getAddress());
-        // TODO: verify address
-        verify(mHeadsetProxy, times(2)).connectAudio();
-
-        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, device2);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device2.getAddress());
-        // Disconnect device 2
-        setupConnectedDevices(new BluetoothDevice[]{device3, device1}, null);
-        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device2.getAddress());
-        // Verify that we've fallen back to device 1
-        verify(mHeadsetProxy, times(3)).connectAudio();
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress(),
-                sm.getCurrentState().getName());
-        setupConnectedDevices(new BluetoothDevice[]{device3, device1}, device1);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device1.getAddress());
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress(),
-                sm.getCurrentState().getName());
-
-        // Disconnect device 1
-        setupConnectedDevices(new BluetoothDevice[]{device3}, null);
-        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device1.getAddress());
-        // Verify that we've fallen back to device 3
-        verify(mHeadsetProxy, times(4)).connectAudio();
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress(),
-                sm.getCurrentState().getName());
-        setupConnectedDevices(new BluetoothDevice[]{device3}, device3);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device3.getAddress());
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress(),
-                sm.getCurrentState().getName());
-
-        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
-        sm.quitNow();
-    }
-
-    @SmallTest
-    public void testProperFallbackOrder2() {
-        // Device 1, 2, 3 are connected in that order. Device 3 is activated.
-        // Disconnect device 3, verify fallback to device 2. Disconnect device 2, fallback to
-        // device 1.
-        BluetoothRouteManager sm = setupStateMachine(
-                BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
-        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, null);
-        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device3.getAddress());
-        // TODO: verify address
-        verify(mHeadsetProxy, times(1)).connectAudio();
-
-        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, device3);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device3.getAddress());
-
-        // Disconnect device 2
-        setupConnectedDevices(new BluetoothDevice[]{device2, device1}, null);
-        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device3.getAddress());
-        // Verify that we've fallen back to device 2
-        verify(mHeadsetProxy, times(2)).connectAudio();
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device2.getAddress(),
-                sm.getCurrentState().getName());
-        setupConnectedDevices(new BluetoothDevice[]{device2, device1}, device2);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device2.getAddress());
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device2.getAddress(),
-                sm.getCurrentState().getName());
-
-        // Disconnect device 2
-        setupConnectedDevices(new BluetoothDevice[]{device1}, null);
-        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device2.getAddress());
-        // Verify that we've fallen back to device 1
-        verify(mHeadsetProxy, times(3)).connectAudio();
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress(),
-                sm.getCurrentState().getName());
-        setupConnectedDevices(new BluetoothDevice[]{device1}, device1);
-        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device1.getAddress());
-        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress(),
-                sm.getCurrentState().getName());
-
-        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
-        sm.quitNow();
-    }
-
-    @Override
-    protected void runParametrizedTestCase(TestParameters _params) {
-        BluetoothRouteTestParameters params = (BluetoothRouteTestParameters) _params;
-        BluetoothRouteManager sm = setupStateMachine(
-                params.initialBluetoothState, params.initialDevice);
-
-        setupConnectedDevices(params.connectedDevices, params.audioOnDevice);
-        executeRoutingAction(sm, params.messageType, params.messageDevice);
-
-        assertEquals(params.expectedFinalStateName, sm.getCurrentState().getName());
-
-        if (params.expectedListenerUpdate != null) {
-            verify(mListener).onBluetoothStateChange(params.expectedListenerUpdate.first,
-                    params.expectedListenerUpdate.second);
-        } else {
-            verify(mListener, never()).onBluetoothStateChange(anyInt(), anyInt());
-        }
-        // TODO: work the address in here
-        switch (params.expectedBluetoothInteraction) {
-            case NONE:
-                verify(mHeadsetProxy, never()).connectAudio();
-                verify(mHeadsetProxy, never()).disconnectAudio();
-                break;
-            case CONNECT:
-                verify(mHeadsetProxy).connectAudio();
-                verify(mHeadsetProxy, never()).disconnectAudio();
-                break;
-            case DISCONNECT:
-                verify(mHeadsetProxy, never()).connectAudio();
-                verify(mHeadsetProxy).disconnectAudio();
-                break;
-        }
-
-        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
-        sm.quitNow();
-    }
-
     private BluetoothRouteManager setupStateMachine(String initialState,
             BluetoothDevice initialDevice) {
-        resetMocks(true);
+        resetMocks();
         BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
                 new TelecomSystem.SyncRoot() { }, mDeviceManager, mTimeoutsAdapter);
         sm.setListener(mListener);
         sm.setInitialStateForTesting(initialState, initialDevice);
-        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
-        resetMocks(false);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        resetMocks();
         return sm;
     }
 
     private void setupConnectedDevices(BluetoothDevice[] devices, BluetoothDevice activeDevice) {
         when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length);
+        when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         if (activeDevice != null) {
-            when(mHeadsetProxy.isAudioConnected(eq(activeDevice))).thenReturn(true);
+            when(mHeadsetProxy.getAudioState(eq(activeDevice)))
+                    .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
         }
         doAnswer(invocation -> {
             BluetoothDevice first = getFirstExcluding(devices,
                     (String) invocation.getArguments()[0]);
             return first == null ? null : first.getAddress();
         }).when(mDeviceManager).getMostRecentlyConnectedDevice(nullable(String.class));
+        for (BluetoothDevice device : devices) {
+            when(mDeviceManager.getDeviceFromAddress(device.getAddress())).thenReturn(device);
+        }
     }
 
-    private void executeRoutingAction(BluetoothRouteManager brm, int message, String device) {
+    static void executeRoutingAction(BluetoothRouteManager brm, int message, String
+            device) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = Log.createSubsession();
         args.arg2 = device;
         brm.sendMessage(message, args);
-        waitForStateMachineActionCompletion(brm, CallAudioModeStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(brm.getHandler(), TEST_TIMEOUT);
     }
 
-    private BluetoothDevice makeBluetoothDevice(String address) {
+    public static BluetoothDevice makeBluetoothDevice(String address) {
         Parcel p1 = Parcel.obtain();
         p1.writeString(address);
         p1.setDataPosition(0);
@@ -445,22 +162,22 @@
         return device;
     }
 
-    private void resetMocks(boolean createNewMocks) {
+    private void resetMocks() {
         reset(mDeviceManager, mListener, mHeadsetProxy, mTimeoutsAdapter);
-        if (createNewMocks) {
-            mDeviceManager = mock(BluetoothDeviceManager.class);
-            mListener = mock(BluetoothRouteManager.BluetoothStateListener.class);
-            mHeadsetProxy = mock(BluetoothHeadsetProxy.class);
-            mTimeoutsAdapter = mock(Timeouts.Adapter.class);
-        }
         when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy);
         when(mHeadsetProxy.connectAudio()).thenReturn(true);
+        when(mHeadsetProxy.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true);
         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
                 nullable(ContentResolver.class))).thenReturn(100000L);
         when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
                 nullable(ContentResolver.class))).thenReturn(100000L);
     }
 
+    private void verifyConnectionAttempt(BluetoothDevice device, int numTimes) {
+        verify(mHeadsetProxy, times(numTimes)).setActiveDevice(device);
+        verify(mHeadsetProxy, atLeast(numTimes)).connectAudio();
+    }
+
     private static BluetoothDevice getFirstExcluding(
             BluetoothDevice[] devices, String excludeAddress) {
         for (BluetoothDevice x : devices) {
@@ -470,252 +187,4 @@
         }
         return null;
     }
-
-    private List<BluetoothRouteTestParameters> generateTestCases() {
-        List<BluetoothRouteTestParameters> result = new ArrayList<>();
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("New device connected while audio off")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .setInitialDevice(null)
-                .setConnectedDevices(device1)
-                .setMessageType(BluetoothRouteManager.NEW_DEVICE_CONNECTED)
-                .setMessageDevice(device1.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_DISCONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedConnectionAddress(null)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Nonspecific connection request while audio off.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .setInitialDevice(null)
-                .setConnectedDevices(device2, device1)
-                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
-                .setExpectedBluetoothInteraction(CONNECT)
-                .setExpectedConnectionAddress(device2.getAddress())
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device2.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Connection to a device succeeds after pending")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setAudioOnDevice(device2)
-                .setConnectedDevices(device2, device1)
-                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedConnectionAddress(null)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device2.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Device loses HFP audio but remains connected. No fallback.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2)
-                .setMessageType(BluetoothRouteManager.HFP_LOST)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedConnectionAddress(null)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Device loses HFP audio but remains connected. Fallback.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device1, device3)
-                .setMessageType(BluetoothRouteManager.HFP_LOST)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
-                .setExpectedBluetoothInteraction(CONNECT)
-                .setExpectedConnectionAddress(device1.getAddress())
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Switch active devices")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device1, device3)
-                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
-                .setMessageDevice(device3.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
-                .setExpectedBluetoothInteraction(CONNECT)
-                .setExpectedConnectionAddress(device3.getAddress())
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Switch to another device before first device has connected")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device1, device3)
-                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
-                .setMessageDevice(device3.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
-                .setExpectedBluetoothInteraction(CONNECT)
-                .setExpectedConnectionAddress(device3.getAddress())
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Device gets disconnected while active. No fallback.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices()
-                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_DISCONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedConnectionAddress(null)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Device gets disconnected while active. Fallback.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device3)
-                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
-                .setExpectedBluetoothInteraction(CONNECT)
-                .setExpectedConnectionAddress(device3.getAddress())
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Connection to device2 times out but device 1 still connected.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device1)
-                .setAudioOnDevice(device1)
-                .setMessageType(BluetoothRouteManager.CONNECTION_TIMEOUT)
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("device1 somehow becomes active when device2 is still pending.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device1)
-                .setAudioOnDevice(device1)
-                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
-                .setMessageDevice(device1.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device1.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Device gets disconnected while pending. Fallback.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device3)
-                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
-                .setExpectedBluetoothInteraction(CONNECT)
-                .setExpectedConnectionAddress(device3.getAddress())
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress())
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Device gets disconnected while pending. No fallback.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices()
-                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
-                .setMessageDevice(device2.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_DISCONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Audio routing requests HFP disconnection while a device is active")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device3)
-                .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
-                .setExpectedBluetoothInteraction(DISCONNECT)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Audio routing requests HFP disconnection while a device is pending")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
-                .setInitialDevice(device2)
-                .setConnectedDevices(device2, device3)
-                .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
-                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
-                .setExpectedBluetoothInteraction(DISCONNECT)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .build());
-
-        result.add(new BluetoothRouteTestParametersBuilder()
-                .setName("Bluetooth turns itself on.")
-                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
-                .setInitialDevice(null)
-                .setConnectedDevices(device2, device3)
-                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
-                .setMessageDevice(device3.getAddress())
-                .setExpectedListenerUpdate(Pair.create(
-                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED,
-                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
-                .setExpectedBluetoothInteraction(NONE)
-                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
-                        + ":" + device3.getAddress())
-                .build());
-
-        return result;
-    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
new file mode 100644
index 0000000..e7cd6ee
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -0,0 +1,578 @@
+/*
+ * 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.server.telecom.tests;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.content.ContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.BluetoothHeadsetProxy;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
+import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE1;
+import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE2;
+import static com.android.server.telecom.tests.BluetoothRouteManagerTest.DEVICE3;
+import static com.android.server.telecom.tests.BluetoothRouteManagerTest.executeRoutingAction;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(Parameterized.class)
+public class BluetoothRouteTransitionTests extends TelecomTestCase {
+    private enum ListenerUpdate {
+        DEVICE_LIST_CHANGED, ACTIVE_DEVICE_PRESENT, ACTIVE_DEVICE_GONE,
+        AUDIO_CONNECTED, AUDIO_DISCONNECTED
+    }
+
+    private static class BluetoothRouteTestParametersBuilder {
+        private String name;
+        private String initialBluetoothState;
+        private BluetoothDevice initialDevice;
+        private BluetoothDevice audioOnDevice;
+        private int messageType;
+        private BluetoothDevice messageDevice;
+        private ListenerUpdate[] expectedListenerUpdates;
+        private int expectedBluetoothInteraction;
+        private BluetoothDevice expectedConnectionDevice;
+        private String expectedFinalStateName;
+        private BluetoothDevice[] connectedDevices;
+        // the active device as returned by BluetoothHeadset#getActiveDevice
+        private BluetoothDevice activeDevice = null;
+
+        public BluetoothRouteTestParametersBuilder setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setInitialBluetoothState(
+                String initialBluetoothState) {
+            this.initialBluetoothState = initialBluetoothState;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setInitialDevice(BluetoothDevice
+                initialDevice) {
+            this.initialDevice = initialDevice;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setMessageType(int messageType) {
+            this.messageType = messageType;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setMessageDevice(BluetoothDevice messageDevice) {
+            this.messageDevice = messageDevice;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setExpectedListenerUpdates(
+                ListenerUpdate... expectedListenerUpdates) {
+            this.expectedListenerUpdates = expectedListenerUpdates;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setExpectedBluetoothInteraction(
+                int expectedBluetoothInteraction) {
+            this.expectedBluetoothInteraction = expectedBluetoothInteraction;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setExpectedConnectionDevice(
+                BluetoothDevice expectedConnectionDevice) {
+            this.expectedConnectionDevice = expectedConnectionDevice;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setExpectedFinalStateName(
+                String expectedFinalStateName) {
+            this.expectedFinalStateName = expectedFinalStateName;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setConnectedDevices(
+                BluetoothDevice... connectedDevices) {
+            this.connectedDevices = connectedDevices;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setAudioOnDevice(BluetoothDevice device) {
+            this.audioOnDevice = device;
+            return this;
+        }
+
+        public BluetoothRouteTestParametersBuilder setActiveDevice(BluetoothDevice device) {
+            this.activeDevice = device;
+            return this;
+        }
+
+        public BluetoothRouteTestParameters build() {
+            return new BluetoothRouteTestParameters(name,
+                    initialBluetoothState,
+                    initialDevice,
+                    messageType,
+                    expectedListenerUpdates,
+                    expectedBluetoothInteraction,
+                    expectedConnectionDevice,
+                    expectedFinalStateName,
+                    connectedDevices,
+                    messageDevice,
+                    audioOnDevice,
+                    activeDevice);
+
+        }
+    }
+
+    private static class BluetoothRouteTestParameters {
+        public String name;
+        public String initialBluetoothState; // One of the state names or prefixes from BRM.
+        public BluetoothDevice initialDevice; // null if we start from AudioOff
+        public BluetoothDevice audioOnDevice; // The device (if any) that is active
+        public int messageType; // Any of the commands from the state machine
+        public BluetoothDevice messageDevice; // The device that should be specified in the message.
+        public ListenerUpdate[] expectedListenerUpdates; // what the listener should expect.
+        public int expectedBluetoothInteraction; // NONE, CONNECT, or DISCONNECT
+        public BluetoothDevice expectedConnectionDevice; // Expected device to connect to.
+        public String expectedFinalStateName; // Expected name of the final state.
+        public BluetoothDevice[] connectedDevices; // array of connected devices
+        // the active device as returned by BluetoothHeadset#getActiveDevice
+        private BluetoothDevice activeDevice = null;
+
+        public BluetoothRouteTestParameters(String name, String initialBluetoothState,
+                BluetoothDevice initialDevice, int messageType, ListenerUpdate[]
+                expectedListenerUpdates, int expectedBluetoothInteraction, BluetoothDevice
+                expectedConnectionDevice, String expectedFinalStateName,
+                BluetoothDevice[] connectedDevices, BluetoothDevice messageDevice,
+                BluetoothDevice audioOnDevice, BluetoothDevice activeDevice) {
+            this.name = name;
+            this.initialBluetoothState = initialBluetoothState;
+            this.initialDevice = initialDevice;
+            this.messageType = messageType;
+            this.expectedListenerUpdates = expectedListenerUpdates;
+            this.expectedBluetoothInteraction = expectedBluetoothInteraction;
+            this.expectedConnectionDevice = expectedConnectionDevice;
+            this.expectedFinalStateName = expectedFinalStateName;
+            this.connectedDevices = connectedDevices;
+            this.messageDevice = messageDevice;
+            this.audioOnDevice = audioOnDevice;
+            this.activeDevice = activeDevice;
+        }
+
+        @Override
+        public String toString() {
+            return "BluetoothRouteTestParameters{" +
+                    "name='" + name + '\'' +
+                    ", initialBluetoothState='" + initialBluetoothState + '\'' +
+                    ", initialDevice=" + initialDevice +
+                    ", messageType=" + messageType +
+                    ", messageDevice='" + messageDevice + '\'' +
+                    ", expectedListenerUpdate=" + expectedListenerUpdates +
+                    ", expectedBluetoothInteraction=" + expectedBluetoothInteraction +
+                    ", expectedConnectionDevice='" + expectedConnectionDevice + '\'' +
+                    ", expectedFinalStateName='" + expectedFinalStateName + '\'' +
+                    ", connectedDevices=" + Arrays.toString(connectedDevices) +
+                    ", activeDevice='" + activeDevice + '\'' +
+                    '}';
+        }
+    }
+
+    private static final int NONE = 1;
+    private static final int CONNECT = 2;
+    private static final int DISCONNECT = 3;
+
+    private static final int TEST_TIMEOUT = 1000;
+
+    private final BluetoothRouteTestParameters mParams;
+    @Mock private BluetoothDeviceManager mDeviceManager;
+    @Mock private BluetoothHeadsetProxy mHeadsetProxy;
+    @Mock private Timeouts.Adapter mTimeoutsAdapter;
+    @Mock private BluetoothRouteManager.BluetoothStateListener mListener;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public BluetoothRouteTransitionTests(BluetoothRouteTestParameters params) {
+        mParams = params;
+    }
+
+    @Test
+    @SmallTest
+    public void testTransitions() {
+        BluetoothRouteManager sm = setupStateMachine(
+                mParams.initialBluetoothState, mParams.initialDevice);
+
+        setupConnectedDevices(mParams.connectedDevices,
+                mParams.audioOnDevice, mParams.activeDevice);
+        sm.setActiveDeviceCacheForTesting(mParams.activeDevice);
+
+        // Go through the utility methods for these two messages
+        if (mParams.messageType == BluetoothRouteManager.NEW_DEVICE_CONNECTED) {
+            sm.onDeviceAdded(mParams.messageDevice.getAddress());
+            sm.onActiveDeviceChanged(mParams.messageDevice);
+        } else if (mParams.messageType == BluetoothRouteManager.LOST_DEVICE) {
+            sm.onDeviceLost(mParams.messageDevice.getAddress());
+            sm.onActiveDeviceChanged(null);
+        } else {
+            executeRoutingAction(sm, mParams.messageType,
+                    mParams.messageDevice == null ? null : mParams.messageDevice.getAddress());
+        }
+
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        assertEquals(mParams.expectedFinalStateName, sm.getCurrentState().getName());
+
+        for (ListenerUpdate lu : mParams.expectedListenerUpdates) {
+            switch (lu) {
+                case DEVICE_LIST_CHANGED:
+                    verify(mListener).onBluetoothDeviceListChanged();
+                    break;
+                case ACTIVE_DEVICE_PRESENT:
+                    verify(mListener).onBluetoothActiveDevicePresent();
+                    break;
+                case ACTIVE_DEVICE_GONE:
+                    verify(mListener).onBluetoothActiveDeviceGone();
+                    break;
+                case AUDIO_CONNECTED:
+                    verify(mListener).onBluetoothAudioConnected();
+                    break;
+                case AUDIO_DISCONNECTED:
+                    verify(mListener).onBluetoothAudioDisconnected();
+                    break;
+            }
+        }
+
+        switch (mParams.expectedBluetoothInteraction) {
+            case NONE:
+                verify(mHeadsetProxy, never()).connectAudio();
+                verify(mHeadsetProxy, never()).setActiveDevice(nullable(BluetoothDevice.class));
+                verify(mHeadsetProxy, never()).disconnectAudio();
+                break;
+            case CONNECT:
+                verify(mHeadsetProxy).connectAudio();
+                verify(mHeadsetProxy).setActiveDevice(mParams.expectedConnectionDevice);
+                verify(mHeadsetProxy, never()).disconnectAudio();
+                break;
+            case DISCONNECT:
+                verify(mHeadsetProxy, never()).connectAudio();
+                verify(mHeadsetProxy, never()).setActiveDevice(nullable(BluetoothDevice.class));
+                verify(mHeadsetProxy).disconnectAudio();
+                break;
+        }
+
+        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
+        sm.quitNow();
+    }
+
+    private void setupConnectedDevices(BluetoothDevice[] devices,
+            BluetoothDevice audioOnDevice, BluetoothDevice activeDevice) {
+        when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length);
+        when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
+        when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
+        when(mHeadsetProxy.getActiveDevice()).thenReturn(activeDevice);
+        if (audioOnDevice != null) {
+            when(mHeadsetProxy.getAudioState(eq(audioOnDevice)))
+                    .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
+        }
+        doAnswer(invocation -> {
+            BluetoothDevice first = getFirstExcluding(devices,
+                    (String) invocation.getArguments()[0]);
+            return first == null ? null : first.getAddress();
+        }).when(mDeviceManager).getMostRecentlyConnectedDevice(nullable(String.class));
+        for (BluetoothDevice device : devices) {
+            when(mDeviceManager.getDeviceFromAddress(device.getAddress())).thenReturn(device);
+        }
+    }
+
+    private BluetoothRouteManager setupStateMachine(String initialState,
+            BluetoothDevice initialDevice) {
+        resetMocks();
+        BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
+                new TelecomSystem.SyncRoot() { }, mDeviceManager, mTimeoutsAdapter);
+        sm.setListener(mListener);
+        sm.setInitialStateForTesting(initialState, initialDevice);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        resetMocks();
+        return sm;
+    }
+
+    private void resetMocks() {
+        reset(mDeviceManager, mListener, mHeadsetProxy, mTimeoutsAdapter);
+        when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy);
+        when(mHeadsetProxy.connectAudio()).thenReturn(true);
+        when(mHeadsetProxy.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true);
+        when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
+                nullable(ContentResolver.class))).thenReturn(100000L);
+        when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
+                nullable(ContentResolver.class))).thenReturn(100000L);
+    }
+
+    private static BluetoothDevice getFirstExcluding(
+            BluetoothDevice[] devices, String excludeAddress) {
+        for (BluetoothDevice x : devices) {
+            if (!Objects.equals(excludeAddress, x.getAddress())) {
+                return x;
+            }
+        }
+        return null;
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<BluetoothRouteTestParameters> generateTestCases() {
+        List<BluetoothRouteTestParameters> result = new ArrayList<>();
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("New device connected while audio off")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .setInitialDevice(null)
+                .setConnectedDevices(DEVICE1)
+                .setMessageType(BluetoothRouteManager.NEW_DEVICE_CONNECTED)
+                .setMessageDevice(DEVICE1)
+                .setExpectedListenerUpdates(ListenerUpdate.DEVICE_LIST_CHANGED,
+                        ListenerUpdate.ACTIVE_DEVICE_PRESENT)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedConnectionDevice(null)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Nonspecific connection request while audio off with BT-active device")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .setInitialDevice(null)
+                .setConnectedDevices(DEVICE2, DEVICE1)
+                .setActiveDevice(DEVICE1)
+                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(CONNECT)
+                .setExpectedConnectionDevice(DEVICE1)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
+                        + ":" + DEVICE1)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Connection to a device succeeds after pending")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setAudioOnDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE1)
+                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedConnectionDevice(null)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+                        + ":" + DEVICE2)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Device loses HFP audio but remains connected. No fallback.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2)
+                .setMessageType(BluetoothRouteManager.HFP_LOST)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedConnectionDevice(null)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Device loses HFP audio but remains connected."
+                        + " No fallback even though other devices available.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
+                .setMessageType(BluetoothRouteManager.HFP_LOST)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedConnectionDevice(null)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Switch the device that audio is being routed to")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
+                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+                .setMessageDevice(DEVICE3)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(CONNECT)
+                .setExpectedConnectionDevice(DEVICE3)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
+                        + ":" + DEVICE3)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Switch to another device before first device has connected")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE1, DEVICE3)
+                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
+                .setMessageDevice(DEVICE3)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(CONNECT)
+                .setExpectedConnectionDevice(DEVICE3)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
+                        + ":" + DEVICE3)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Device gets disconnected while active. No fallback.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setActiveDevice(DEVICE2)
+                .setConnectedDevices()
+                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED,
+                        ListenerUpdate.DEVICE_LIST_CHANGED, ListenerUpdate.ACTIVE_DEVICE_GONE)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedConnectionDevice(null)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Device gets disconnected while active."
+                        + " No fallback even though other devices available.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE3)
+                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED,
+                        ListenerUpdate.DEVICE_LIST_CHANGED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedConnectionDevice(null)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Connection to DEVICE2 times out but device 1 still connected.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE1)
+                .setAudioOnDevice(DEVICE1)
+                .setMessageType(BluetoothRouteManager.CONNECTION_TIMEOUT)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+                        + ":" + DEVICE1)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("DEVICE1 somehow becomes active when DEVICE2 is still pending.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE1)
+                .setAudioOnDevice(DEVICE1)
+                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
+                .setMessageDevice(DEVICE1)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+                        + ":" + DEVICE1)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Device gets disconnected while pending."
+                        + " No fallback even though other devices available.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE3)
+                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED,
+                        ListenerUpdate.DEVICE_LIST_CHANGED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Device gets disconnected while pending. No fallback.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices()
+                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
+                .setMessageDevice(DEVICE2)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED,
+                        ListenerUpdate.DEVICE_LIST_CHANGED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Audio routing requests HFP disconnection while a device is active")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE3)
+                .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED)
+                .setExpectedBluetoothInteraction(DISCONNECT)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Audio routing requests HFP disconnection while a device is pending")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
+                .setInitialDevice(DEVICE2)
+                .setConnectedDevices(DEVICE2, DEVICE3)
+                .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_DISCONNECTED)
+                .setExpectedBluetoothInteraction(DISCONNECT)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .build());
+
+        result.add(new BluetoothRouteTestParametersBuilder()
+                .setName("Bluetooth turns itself on.")
+                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
+                .setInitialDevice(null)
+                .setConnectedDevices(DEVICE2, DEVICE3)
+                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
+                .setMessageDevice(DEVICE3)
+                .setExpectedListenerUpdates(ListenerUpdate.AUDIO_CONNECTED)
+                .setExpectedBluetoothInteraction(NONE)
+                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+                        + ":" + DEVICE3)
+                .build());
+
+        return result;
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
index 488ac29..5e23dcc 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
@@ -31,7 +31,12 @@
 import com.android.server.telecom.InCallTonePlayer;
 import com.android.server.telecom.RingbackPlayer;
 import com.android.server.telecom.Ringer;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
@@ -39,6 +44,8 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
@@ -49,6 +56,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class CallAudioManagerTest extends TelecomTestCase {
     @Mock private CallAudioRouteStateMachine mCallAudioRouteStateMachine;
     @Mock private CallsManager mCallsManager;
@@ -57,10 +65,12 @@
     @Mock private Ringer mRinger;
     @Mock private RingbackPlayer mRingbackPlayer;
     @Mock private DtmfLocalTonePlayer mDtmfLocalTonePlayer;
+    @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
 
     private CallAudioManager mCallAudioManager;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         doAnswer((invocation) -> {
@@ -78,10 +88,12 @@
                 mPlayerFactory,
                 mRinger,
                 mRingbackPlayer,
+                mBluetoothStateReceiver,
                 mDtmfLocalTonePlayer);
     }
 
     @MediumTest
+    @Test
     public void testUnmuteOfSecondIncomingCall() {
         // Start with a single incoming call.
         Call call = createIncomingCall();
@@ -145,6 +157,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSingleIncomingCallFlowWithoutMTSpeedUp() {
         Call call = createIncomingCall();
         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
@@ -180,6 +193,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSingleIncomingCallFlowWithMTSpeedUp() {
         Call call = createIncomingCall();
         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
@@ -213,6 +227,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSingleOutgoingCall() {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.CONNECTING);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
index af1d3a2..f253d19 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
@@ -17,97 +17,45 @@
 package com.android.server.telecom.tests;
 
 import android.media.AudioManager;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioModeStateMachine;
+import com.android.server.telecom.CallAudioRouteStateMachine;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
-import java.util.ArrayList;
-import java.util.List;
-
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-public class CallAudioModeStateMachineTest extends StateMachineTestBase<CallAudioModeStateMachine> {
-    private static class ModeTestParameters extends TestParameters {
-        public String name;
-        public int initialAudioState; // One of the explicit switch focus constants in CAMSM
-        public int messageType; // Any of the commands from the state machine
-        public CallAudioModeStateMachine.MessageArgs externalState;
-        public String expectedFinalStateName;
-        public int expectedFocus; // one of the FOCUS_* constants below
-        public int expectedMode; // NO_CHANGE, or an AudioManager.MODE_* constant
-        public int expectedRingingInteraction; // NO_CHANGE, ON, or OFF
-        public int expectedCallWaitingInteraction; // NO_CHANGE, ON, or OFF
-
-        public ModeTestParameters(String name, int initialAudioState, int messageType,
-                CallAudioModeStateMachine.MessageArgs externalState, String
-                expectedFinalStateName, int expectedFocus, int expectedMode, int
-                expectedRingingInteraction, int expectedCallWaitingInteraction) {
-            this.name = name;
-            this.initialAudioState = initialAudioState;
-            this.messageType = messageType;
-            this.externalState = externalState;
-            this.expectedFinalStateName = expectedFinalStateName;
-            this.expectedFocus = expectedFocus;
-            this.expectedMode = expectedMode;
-            this.expectedRingingInteraction = expectedRingingInteraction;
-            this.expectedCallWaitingInteraction = expectedCallWaitingInteraction;
-        }
-
-        @Override
-        public String toString() {
-            return "ModeTestParameters{" +
-                    "name='" + name + '\'' +
-                    ", initialAudioState=" + initialAudioState +
-                    ", messageType=" + messageType +
-                    ", externalState=" + externalState +
-                    ", expectedFinalStateName='" + expectedFinalStateName + '\'' +
-                    ", expectedFocus=" + expectedFocus +
-                    ", expectedMode=" + expectedMode +
-                    ", expectedRingingInteraction=" + expectedRingingInteraction +
-                    ", expectedCallWaitingInteraction=" + expectedCallWaitingInteraction +
-                    '}';
-        }
-    }
-
-    private static final int FOCUS_NO_CHANGE = 0;
-    private static final int FOCUS_RING = 1;
-    private static final int FOCUS_VOICE = 2;
-    private static final int FOCUS_OFF = 3;
-
-    private static final int NO_CHANGE = -1;
-    private static final int ON = 0;
-    private static final int OFF = 1;
+@RunWith(JUnit4.class)
+public class CallAudioModeStateMachineTest extends TelecomTestCase {
+    private static final int TEST_TIMEOUT = 1000;
 
     @Mock private AudioManager mAudioManager;
     @Mock private CallAudioManager mCallAudioManager;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
-        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-    }
-
-    @LargeTest
-    public void testTransitions() throws Throwable {
-        List<ModeTestParameters> testCases = generateTestCases();
-        parametrizedTestStateMachine(testCases);
     }
 
     @SmallTest
+    @Test
     public void testNoFocusWhenRingerSilenced() throws Throwable {
         CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
         sm.setCallAudioManager(mCallAudioManager);
         sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
-        waitForStateMachineActionCompletion(sm, CallAudioModeStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
         resetMocks();
         when(mCallAudioManager.startRinging()).thenReturn(false);
@@ -121,7 +69,7 @@
                         false, // foregroundCallIsVoip
                         null // session
                 ));
-        waitForStateMachineActionCompletion(sm, CallAudioModeStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
         assertEquals(CallAudioModeStateMachine.RING_STATE_NAME, sm.getCurrentStateName());
 
@@ -133,481 +81,51 @@
         verify(mCallAudioManager).stopCallWaiting();
     }
 
-    private List<ModeTestParameters> generateTestCases() {
-        List<ModeTestParameters> result = new ArrayList<>();
-        result.add(new ModeTestParameters(
-                "New active/dialing call with no other calls when unfocused",
-                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "New active/dialing voip call with no other calls when unfocused",
-                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        true, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_COMMUNICATION, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "New ringing call with no other calls when unfocused",
-                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
-                FOCUS_RING, // expectedFocus
-                AudioManager.MODE_RINGTONE, // expectedMode
-                ON, // expectedRingingInteraction
-                OFF // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "New ringing call coming in on top of active/dialing call",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                ON // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Ringing call becomes active, part 1",
-                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                OFF, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Ringing call becomes active, part 2",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Active call disconnects, but tone is playing",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Tone stops playing, with no active calls",
-                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.TONE_STOPPED_PLAYING, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
-                FOCUS_OFF, // expectedFocus
-                AudioManager.MODE_NORMAL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Ringing call disconnects",
-                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
-                FOCUS_OFF, // expectedFocus
-                AudioManager.MODE_NORMAL, // expectedMode
-                OFF, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call-waiting call disconnects",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                OFF // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call is placed on hold - 1",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call is placed on hold - 2",
-                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_HOLDING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
-                FOCUS_NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call is taken off hold - 1",
-                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call is taken off hold - 2",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Active call disconnects while there's a call-waiting call",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
-                FOCUS_RING, // expectedFocus
-                AudioManager.MODE_RINGTONE, // expectedMode
-                ON, // expectedRingingInteraction
-                OFF // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "New dialing call when there's a call on hold",
-                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Ringing call disconnects with a holding call in the background",
-                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_NORMAL, // expectedMode -- we're expecting this because
-                                          // mMostRecentMode hasn't been set properly.
-                OFF, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Foreground call transitions from sim to voip",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        true, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_COMMUNICATION, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Foreground call transitions from voip to sim",
-                CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_VOICE, // expectedFocus
-                AudioManager.MODE_IN_CALL, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                NO_CHANGE // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call-waiting hangs up before being answered, with another sim call in " +
-                        "foreground",
-                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
-                FOCUS_NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                OFF // expectedCallWaitingInteraction
-        ));
-
-        result.add(new ModeTestParameters(
-                "Call-waiting hangs up before being answered, with another voip call in " +
-                        "foreground",
-                CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
-                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        true, // foregroundCallIsVoip
-                        null // session
-                ),
-                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
-                FOCUS_NO_CHANGE, // expectedFocus
-                NO_CHANGE, // expectedMode
-                NO_CHANGE, // expectedRingingInteraction
-                OFF // expectedCallWaitingInteraction
-        ));
-
-        return result;
-    }
-
-    @Override
-    protected void runParametrizedTestCase(TestParameters _params) {
-        ModeTestParameters params = (ModeTestParameters) _params;
+    @SmallTest
+    @Test
+    public void testRegainFocusWhenHfpIsConnectedSilenced() throws Throwable {
         CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
         sm.setCallAudioManager(mCallAudioManager);
-        sm.sendMessage(params.initialAudioState);
-        waitForStateMachineActionCompletion(sm, CallAudioModeStateMachine.RUN_RUNNABLE);
+        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
         resetMocks();
+        when(mCallAudioManager.startRinging()).thenReturn(false);
+
+        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL,
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        true, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ));
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+        assertEquals(CallAudioModeStateMachine.RING_STATE_NAME, sm.getCurrentStateName());
+
+        verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
+        verify(mAudioManager, never()).setMode(anyInt());
+
+        verify(mCallAudioManager, never()).stopRinging();
+
+        verify(mCallAudioManager).stopCallWaiting();
+
         when(mCallAudioManager.startRinging()).thenReturn(true);
 
-        sm.sendMessage(params.messageType, params.externalState);
-        waitForStateMachineActionCompletion(sm, CallAudioModeStateMachine.RUN_RUNNABLE);
+        sm.sendMessage(CallAudioModeStateMachine.RINGER_MODE_CHANGE);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
-        assertEquals(params.expectedFinalStateName, sm.getCurrentStateName());
-
-        switch (params.expectedFocus) {
-            case FOCUS_NO_CHANGE:
-                verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
-                break;
-            case FOCUS_OFF:
-                verify(mAudioManager).abandonAudioFocusForCall();
-                break;
-            case FOCUS_RING:
-                verify(mAudioManager).requestAudioFocusForCall(
-                        eq(AudioManager.STREAM_RING), anyInt());
-                break;
-            case FOCUS_VOICE:
-                verify(mAudioManager).requestAudioFocusForCall(
-                        eq(AudioManager.STREAM_VOICE_CALL), anyInt());
-                break;
-        }
-
-        if (params.expectedMode != NO_CHANGE) {
-            verify(mAudioManager).setMode(eq(params.expectedMode));
-        } else {
-            verify(mAudioManager, never()).setMode(anyInt());
-        }
-
-        switch (params.expectedRingingInteraction) {
-            case NO_CHANGE:
-                verify(mCallAudioManager, never()).startRinging();
-                verify(mCallAudioManager, never()).stopRinging();
-                break;
-            case ON:
-                verify(mCallAudioManager).startRinging();
-                break;
-            case OFF:
-                verify(mCallAudioManager).stopRinging();
-                break;
-        }
-
-        switch (params.expectedCallWaitingInteraction) {
-            case NO_CHANGE:
-                verify(mCallAudioManager, never()).startCallWaiting();
-                verify(mCallAudioManager, never()).stopCallWaiting();
-                break;
-            case ON:
-                verify(mCallAudioManager).startCallWaiting();
-                break;
-            case OFF:
-                verify(mCallAudioManager).stopCallWaiting();
-                break;
-        }
-
-        sm.quitNow();
+        verify(mCallAudioManager).startRinging();
+        verify(mAudioManager).requestAudioFocusForCall(AudioManager.STREAM_RING,
+                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+        verify(mAudioManager).setMode(AudioManager.MODE_RINGTONE);
+        verify(mCallAudioManager).setCallAudioRouteFocusState(
+                CallAudioRouteStateMachine.RINGING_FOCUS);
     }
 
+
     private void resetMocks() {
         reset(mCallAudioManager, mAudioManager);
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
new file mode 100644
index 0000000..b8b4859
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
@@ -0,0 +1,591 @@
+/*
+ * 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.server.telecom.tests;
+
+import android.media.AudioManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.CallAudioManager;
+import com.android.server.telecom.CallAudioModeStateMachine;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(Parameterized.class)
+public class CallAudioModeTransitionTests extends TelecomTestCase {
+    private static class ModeTestParameters {
+        public String name;
+        public int initialAudioState; // One of the explicit switch focus constants in CAMSM
+        public int messageType; // Any of the commands from the state machine
+        public CallAudioModeStateMachine.MessageArgs externalState;
+        public String expectedFinalStateName;
+        public int expectedFocus; // one of the FOCUS_* constants below
+        public int expectedMode; // NO_CHANGE, or an AudioManager.MODE_* constant
+        public int expectedRingingInteraction; // NO_CHANGE, ON, or OFF
+        public int expectedCallWaitingInteraction; // NO_CHANGE, ON, or OFF
+
+        public ModeTestParameters(String name, int initialAudioState, int messageType,
+                CallAudioModeStateMachine.MessageArgs externalState, String
+                expectedFinalStateName, int expectedFocus, int expectedMode, int
+                expectedRingingInteraction, int expectedCallWaitingInteraction) {
+            this.name = name;
+            this.initialAudioState = initialAudioState;
+            this.messageType = messageType;
+            this.externalState = externalState;
+            this.expectedFinalStateName = expectedFinalStateName;
+            this.expectedFocus = expectedFocus;
+            this.expectedMode = expectedMode;
+            this.expectedRingingInteraction = expectedRingingInteraction;
+            this.expectedCallWaitingInteraction = expectedCallWaitingInteraction;
+        }
+
+        @Override
+        public String toString() {
+            return "ModeTestParameters{" +
+                    "name='" + name + '\'' +
+                    ", initialAudioState=" + initialAudioState +
+                    ", messageType=" + messageType +
+                    ", externalState=" + externalState +
+                    ", expectedFinalStateName='" + expectedFinalStateName + '\'' +
+                    ", expectedFocus=" + expectedFocus +
+                    ", expectedMode=" + expectedMode +
+                    ", expectedRingingInteraction=" + expectedRingingInteraction +
+                    ", expectedCallWaitingInteraction=" + expectedCallWaitingInteraction +
+                    '}';
+        }
+    }
+
+    private static final int FOCUS_NO_CHANGE = 0;
+    private static final int FOCUS_RING = 1;
+    private static final int FOCUS_VOICE = 2;
+    private static final int FOCUS_OFF = 3;
+
+    private static final int NO_CHANGE = -1;
+    private static final int ON = 0;
+    private static final int OFF = 1;
+
+    private static final int TEST_TIMEOUT = 1000;
+
+    @Mock private AudioManager mAudioManager;
+    @Mock private CallAudioManager mCallAudioManager;
+    private final ModeTestParameters mParams;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public CallAudioModeTransitionTests(ModeTestParameters params) {
+        mParams = params;
+    }
+
+    @Test
+    @SmallTest
+    public void modeTransitionTest() {
+        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
+        sm.setCallAudioManager(mCallAudioManager);
+        sm.sendMessage(mParams.initialAudioState);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+        resetMocks();
+        when(mCallAudioManager.startRinging()).thenReturn(true);
+
+        sm.sendMessage(mParams.messageType, mParams.externalState);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+        assertEquals(mParams.expectedFinalStateName, sm.getCurrentStateName());
+
+        switch (mParams.expectedFocus) {
+            case FOCUS_NO_CHANGE:
+                verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
+                break;
+            case FOCUS_OFF:
+                verify(mAudioManager).abandonAudioFocusForCall();
+                break;
+            case FOCUS_RING:
+                verify(mAudioManager).requestAudioFocusForCall(
+                        eq(AudioManager.STREAM_RING), anyInt());
+                break;
+            case FOCUS_VOICE:
+                verify(mAudioManager).requestAudioFocusForCall(
+                        eq(AudioManager.STREAM_VOICE_CALL), anyInt());
+                break;
+        }
+
+        if (mParams.expectedMode != NO_CHANGE) {
+            verify(mAudioManager).setMode(eq(mParams.expectedMode));
+        } else {
+            verify(mAudioManager, never()).setMode(anyInt());
+        }
+
+        switch (mParams.expectedRingingInteraction) {
+            case NO_CHANGE:
+                verify(mCallAudioManager, never()).startRinging();
+                verify(mCallAudioManager, never()).stopRinging();
+                break;
+            case ON:
+                verify(mCallAudioManager).startRinging();
+                break;
+            case OFF:
+                verify(mCallAudioManager).stopRinging();
+                break;
+        }
+
+        switch (mParams.expectedCallWaitingInteraction) {
+            case NO_CHANGE:
+                verify(mCallAudioManager, never()).startCallWaiting();
+                verify(mCallAudioManager, never()).stopCallWaiting();
+                break;
+            case ON:
+                verify(mCallAudioManager).startCallWaiting();
+                break;
+            case OFF:
+                verify(mCallAudioManager).stopCallWaiting();
+                break;
+        }
+
+        sm.quitNow();
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<ModeTestParameters> generateTestCases() {
+        List<ModeTestParameters> result = new ArrayList<>();
+        result.add(new ModeTestParameters(
+                "New active/dialing call with no other calls when unfocused",
+                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "New active/dialing voip call with no other calls when unfocused",
+                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        true, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_COMMUNICATION, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "New ringing call with no other calls when unfocused",
+                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        true, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
+                FOCUS_RING, // expectedFocus
+                AudioManager.MODE_RINGTONE, // expectedMode
+                ON, // expectedRingingInteraction
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "New ringing call coming in on top of active/dialing call",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        true, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                ON // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Ringing call becomes active, part 1",
+                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                OFF, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Ringing call becomes active, part 2",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Active call disconnects, but tone is playing",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        true, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Tone stops playing, with no active calls",
+                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.TONE_STOPPED_PLAYING, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
+                FOCUS_OFF, // expectedFocus
+                AudioManager.MODE_NORMAL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Ringing call disconnects",
+                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
+                FOCUS_OFF, // expectedFocus
+                AudioManager.MODE_NORMAL, // expectedMode
+                OFF, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call-waiting call disconnects",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        true, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call is placed on hold - 1",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        true, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call is placed on hold - 2",
+                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_HOLDING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        true, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
+                FOCUS_NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call is taken off hold - 1",
+                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call is taken off hold - 2",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Active call disconnects while there's a call-waiting call",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        true, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        true, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
+                FOCUS_RING, // expectedFocus
+                AudioManager.MODE_RINGTONE, // expectedMode
+                ON, // expectedRingingInteraction
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "New dialing call when there's a call on hold",
+                CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        true, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Ringing call disconnects with a holding call in the background",
+                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        true, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_NORMAL, // expectedMode -- we're expecting this because
+                                          // mMostRecentMode hasn't been set properly.
+                OFF, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Foreground call transitions from sim to voip",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        true, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_COMMUNICATION, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Foreground call transitions from voip to sim",
+                CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call-waiting hangs up before being answered, with another sim call in " +
+                        "foreground",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        true, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call-waiting hangs up before being answered, with another voip call in " +
+                        "foreground",
+                CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new CallAudioModeStateMachine.MessageArgs(
+                        true, // hasActiveOrDialingCalls
+                        false, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        true, // isTonePlaying
+                        true, // foregroundCallIsVoip
+                        null // session
+                ),
+                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
+                FOCUS_NO_CHANGE, // expectedFocus
+                NO_CHANGE, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        return result;
+    }
+
+    private void resetMocks() {
+        reset(mCallAudioManager, mAudioManager);
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index 6e09c90..9c90d3e 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -16,19 +16,16 @@
 
 package com.android.server.telecom.tests;
 
-import android.app.NotificationManager;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IAudioService;
-import android.os.Handler;
 import android.telecom.CallAudioState;
-import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.Call;
-import com.android.server.telecom.CallAudioModeStateMachine;
 import com.android.server.telecom.CallAudioRouteStateMachine;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ConnectionServiceWrapper;
@@ -37,23 +34,29 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.WiredHeadsetManager;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.same;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -62,66 +65,15 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
+public class CallAudioRouteStateMachineTest extends TelecomSystemTest {
 
-public class CallAudioRouteStateMachineTest
-        extends StateMachineTestBase<CallAudioRouteStateMachine> {
-    private static final int NONE = 0;
-    private static final int ON = 1;
-    private static final int OFF = 2;
-    private static final int OPTIONAL = 3;
-
-    static class RoutingTestParameters extends TestParameters {
-        public String name;
-        public int initialRoute;
-        public int availableRoutes; // may excl. speakerphone, because that's always available
-        public int speakerInteraction; // one of NONE, ON, or OFF
-        public int bluetoothInteraction; // one of NONE, ON, or OFF
-        public int action;
-        public int expectedRoute;
-        public int expectedAvailableRoutes; // also may exclude the speakerphone.
-        public boolean doesDeviceSupportEarpiece; // set to false in the case of Wear devices
-        public boolean shouldRunWithFocus;
-
-        public int callSupportedRoutes = CallAudioState.ROUTE_ALL;
-
-        public RoutingTestParameters(String name, int initialRoute,
-                int availableRoutes, int speakerInteraction,
-                int bluetoothInteraction, int action, int expectedRoute,
-                int expectedAvailableRoutes, boolean doesDeviceSupportEarpiece,
-                boolean shouldRunWithFocus) {
-            this.name = name;
-            this.initialRoute = initialRoute;
-            this.availableRoutes = availableRoutes;
-            this.speakerInteraction = speakerInteraction;
-            this.bluetoothInteraction = bluetoothInteraction;
-            this.action = action;
-            this.expectedRoute = expectedRoute;
-            this.expectedAvailableRoutes = expectedAvailableRoutes;
-            this.doesDeviceSupportEarpiece = doesDeviceSupportEarpiece;
-            this.shouldRunWithFocus = shouldRunWithFocus;
-        }
-
-        public RoutingTestParameters setCallSupportedRoutes(int routes) {
-            callSupportedRoutes = routes;
-            return this;
-        }
-
-        @Override
-        public String toString() {
-            return "RoutingTestParameters{" +
-                    "name='" + name + '\'' +
-                    ", initialRoute=" + initialRoute +
-                    ", availableRoutes=" + availableRoutes +
-                    ", speakerInteraction=" + speakerInteraction +
-                    ", bluetoothInteraction=" + bluetoothInteraction +
-                    ", action=" + action +
-                    ", expectedRoute=" + expectedRoute +
-                    ", expectedAvailableRoutes=" + expectedAvailableRoutes +
-                    ", doesDeviceSupportEarpiece=" + doesDeviceSupportEarpiece +
-                    ", shouldRunWithFocus=" + shouldRunWithFocus +
-                    '}';
-        }
-    }
+    private static final BluetoothDevice bluetoothDevice1 =
+            BluetoothRouteManagerTest.makeBluetoothDevice("00:00:00:00:00:01");
+    private static final BluetoothDevice bluetoothDevice2 =
+            BluetoothRouteManagerTest.makeBluetoothDevice("00:00:00:00:00:02");
+    private static final BluetoothDevice bluetoothDevice3 =
+            BluetoothRouteManagerTest.makeBluetoothDevice("00:00:00:00:00:03");
 
     @Mock CallsManager mockCallsManager;
     @Mock BluetoothRouteManager mockBluetoothRouteManager;
@@ -130,6 +82,7 @@
     @Mock WiredHeadsetManager mockWiredHeadsetManager;
     @Mock StatusBarNotifier mockStatusBarNotifier;
     @Mock Call fakeCall;
+    @Mock CallAudioManager mockCallAudioManager;
 
     private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
     private static final int TEST_TIMEOUT = 500;
@@ -137,6 +90,7 @@
     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
@@ -161,19 +115,26 @@
                 any(CallAudioState.class));
     }
 
-    @LargeTest
-    public void testStateMachineTransitionsWithFocus() throws Throwable {
-        List<RoutingTestParameters> paramList = generateTransitionTests(true);
-        parametrizedTestStateMachine(paramList);
-    }
+    @SmallTest
+    @Test
+    public void testEarpieceAutodetect() {
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT);
 
-    @LargeTest
-    public void testStateMachineTransitionsWithoutFocus() throws Throwable {
-        List<RoutingTestParameters> paramList = generateTransitionTests(false);
-        parametrizedTestStateMachine(paramList);
+        // Since we don't know if we're on a platform with an earpiece or not, all we can do
+        // is ensure the stateMachine construction didn't fail.  But at least we exercised the
+        // autodetection code...
+        assertNotNull(stateMachine);
     }
 
     @MediumTest
+    @Test
     public void testSpeakerPersistence() {
         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
                 mContext,
@@ -182,7 +143,7 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                true);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
@@ -208,14 +169,16 @@
         waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         verifyNewSystemCallAudioState(initState, expectedMiddleState);
-        resetMocks(true);
+        resetMocks();
 
         stateMachine.sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         verifyNewSystemCallAudioState(expectedMiddleState, initState);
     }
 
     @MediumTest
+    @Test
     public void testUserBluetoothSwitchOff() {
         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
                 mContext,
@@ -224,7 +187,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                true);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
@@ -236,25 +200,101 @@
 
         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
         stateMachine.sendMessageWithSessionInfo(
-                CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE);
+                CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
+                CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE);
         CallAudioState expectedEndState = new CallAudioState(false,
                 CallAudioState.ROUTE_EARPIECE,
                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
 
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         verifyNewSystemCallAudioState(initState, expectedEndState);
-        resetMocks(false);
+        resetMocks();
         stateMachine.sendMessageWithSessionInfo(
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH);
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE);
         stateMachine.sendMessageWithSessionInfo(
-                CallAudioRouteStateMachine.CONNECT_BLUETOOTH);
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT);
 
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState());
     }
 
     @MediumTest
+    @Test
+    public void testUserBluetoothSwitchOffAndOnAgain() {
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+        Collection<BluetoothDevice> availableDevices = Collections.singleton(bluetoothDevice1);
+
+        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices);
+
+        CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
+        stateMachine.initialize(initState);
+
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.ACTIVE_FOCUS);
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+
+        // Switch off the BT route explicitly.
+        stateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
+                CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE);
+        CallAudioState expectedMidState = new CallAudioState(false,
+                CallAudioState.ROUTE_EARPIECE,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+                null, availableDevices);
+
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        verifyNewSystemCallAudioState(initState, expectedMidState);
+        resetMocks();
+
+        // Now, switch back to BT explicitly
+        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices);
+        doAnswer(invocation -> {
+            when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                    .thenReturn(bluetoothDevice1);
+            stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+            return null;
+        }).when(mockBluetoothRouteManager).connectBluetoothAudio(nullable(String.class));
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH);
+        CallAudioState expectedEndState = new CallAudioState(false,
+                CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+                bluetoothDevice1, availableDevices);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        // second wait needed for the BT_AUDIO_CONNECTED message
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        verifyNewSystemCallAudioState(expectedMidState, expectedEndState);
+
+        stateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE);
+        when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                .thenReturn(null);
+        stateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT);
+
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        // second wait needed for the BT_AUDIO_CONNECTED message
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        // Verify that we're still on bluetooth.
+        assertEquals(expectedEndState, stateMachine.getCurrentCallAudioState());
+    }
+
+    @MediumTest
+    @Test
     public void testBluetoothRinging() {
         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
                 mContext,
@@ -263,10 +303,13 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                true);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockBluetoothRouteManager.getConnectedDevices())
+                .thenReturn(Collections.singletonList(bluetoothDevice1));
         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
 
         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
@@ -275,17 +318,18 @@
 
         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
                 CallAudioRouteStateMachine.RINGING_FOCUS);
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
 
-        verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
+        verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class));
 
         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
-        verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(null);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(nullable(String.class));
     }
 
     @MediumTest
+    @Test
     public void testConnectBluetoothDuringRinging() {
         CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
                 mContext,
@@ -294,8 +338,9 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                true);
-
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+        setInBandRing(false);
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(false);
         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
@@ -305,84 +350,196 @@
 
         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
                 CallAudioRouteStateMachine.RINGING_FOCUS);
+        // Wait for the state machine to finish transiting to ActiveEarpiece before hooking up
+        // bluetooth mocks
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
 
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
-        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.CONNECT_BLUETOOTH);
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
+        when(mockBluetoothRouteManager.getConnectedDevices())
+                .thenReturn(Collections.singletonList(bluetoothDevice1));
+        stateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED);
+        stateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
 
         verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
         CallAudioState expectedEndState = new CallAudioState(false,
                 CallAudioState.ROUTE_BLUETOOTH,
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH);
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+                null, Collections.singletonList(bluetoothDevice1));
         verifyNewSystemCallAudioState(initState, expectedEndState);
 
         stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(null);
+
+        when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                .thenReturn(bluetoothDevice1);
+        stateMachine.sendMessage(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        verify(mockCallAudioManager, times(1)).onRingerModeChange();
     }
 
     @SmallTest
+    @Test
+    public void testConnectSpecificBluetoothDevice() {
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+        List<BluetoothDevice> availableDevices =
+                Arrays.asList(bluetoothDevice1, bluetoothDevice2, bluetoothDevice3);
+
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
+        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices);
+        doAnswer(invocation -> {
+            when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                    .thenReturn(bluetoothDevice2);
+            stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+            return null;
+        }).when(mockBluetoothRouteManager).connectBluetoothAudio(bluetoothDevice2.getAddress());
+
+        CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, null,
+                availableDevices);
+        stateMachine.initialize(initState);
+
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.ACTIVE_FOCUS);
+
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH,
+                0, bluetoothDevice2.getAddress());
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        verify(mockBluetoothRouteManager).connectBluetoothAudio(bluetoothDevice2.getAddress());
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        CallAudioState expectedEndState = new CallAudioState(false,
+                CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+                bluetoothDevice2,
+                availableDevices);
+
+        verifyNewSystemCallAudioState(initState, expectedEndState);
+    }
+
+    @SmallTest
+    @Test
+    public void testFocusChangeWithAlreadyActiveBtDevice() {
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+        List<BluetoothDevice> availableDevices =
+                Arrays.asList(bluetoothDevice1, bluetoothDevice2);
+
+        // Set up a state where there's an HFP connected bluetooth device already.
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
+        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(true);
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices);
+        when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                .thenReturn(bluetoothDevice1);
+
+        // We want to be in the QuiescentBluetoothRoute because there's no call yet
+        CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+                bluetoothDevice1, availableDevices);
+        stateMachine.initialize(initState);
+
+        // Switch to active, pretending that a call came in.
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.ACTIVE_FOCUS);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        // Make sure that we've successfully switched to the active BT route without actually
+        // calling connectAudio.
+        verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class));
+        assertTrue(stateMachine.isInActiveState());
+    }
+
+    @SmallTest
+    @Test
     public void testInitializationWithEarpieceNoHeadsetNoBluetooth() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);
-        initializationTestHelper(expectedState, true);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithEarpieceAndHeadsetNoBluetooth() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET,
                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
-        initializationTestHelper(expectedState, true);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithEarpieceAndHeadsetAndBluetooth() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER
                 | CallAudioState.ROUTE_BLUETOOTH);
-        initializationTestHelper(expectedState, true);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithEarpieceAndBluetoothNoHeadset() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
                         | CallAudioState.ROUTE_BLUETOOTH);
-        initializationTestHelper(expectedState, true);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithNoEarpieceNoHeadsetNoBluetooth() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
                 CallAudioState.ROUTE_SPEAKER);
-        initializationTestHelper(expectedState, false);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithHeadsetNoBluetoothNoEarpiece() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET,
                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
-        initializationTestHelper(expectedState, false);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithHeadsetAndBluetoothNoEarpiece() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                 CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER
                 | CallAudioState.ROUTE_BLUETOOTH);
-        initializationTestHelper(expectedState, false);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED);
     }
 
     @SmallTest
+    @Test
     public void testInitializationWithBluetoothNoHeadsetNoEarpiece() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
                 CallAudioState.ROUTE_SPEAKER | CallAudioState.ROUTE_BLUETOOTH);
-        initializationTestHelper(expectedState, false);
+        initializationTestHelper(expectedState, CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED);
     }
 
     private void initializationTestHelper(CallAudioState expectedState,
-            boolean doesDeviceSupportEarpiece) {
+            int earpieceControl) {
         when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(
                 (expectedState.getSupportedRouteMask() & CallAudioState.ROUTE_WIRED_HEADSET) != 0);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
@@ -395,559 +552,11 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                doesDeviceSupportEarpiece);
+                earpieceControl);
         stateMachine.initialize();
         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
     }
 
-    private List<RoutingTestParameters> generateTransitionTests(boolean shouldRunWithFocus) {
-        List<RoutingTestParameters> params = new ArrayList<>();
-        params.add(new RoutingTestParameters(
-                "Connect headset during earpiece", // name
-                CallAudioState.ROUTE_EARPIECE, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Connect headset during bluetooth", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Connect headset during speakerphone", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OFF, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect headset during headset", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect headset during headset with bluetooth available", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                OPTIONAL, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect headset during bluetooth", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect headset during speakerphone", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect headset during speakerphone with bluetooth available", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Connect bluetooth during earpiece", // name
-                CallAudioState.ROUTE_EARPIECE, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Connect bluetooth during wired headset", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvai
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Connect bluetooth during speakerphone", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OFF, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect bluetooth during bluetooth without headset in", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect bluetooth during bluetooth without headset in, priority mode ", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect bluetooth during bluetooth with headset in", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                OPTIONAL, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect bluetooth during speakerphone", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect bluetooth during earpiece", // name
-                CallAudioState.ROUTE_EARPIECE, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to speakerphone from earpiece", // name
-                CallAudioState.ROUTE_EARPIECE, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                ON, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to speakerphone from headset", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
-                ON, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to speakerphone from bluetooth", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                ON, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to earpiece from bluetooth", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to earpiece from speakerphone", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OFF, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to earpiece from speakerphone, priority notifications", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OFF, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to earpiece from speakerphone, silent mode", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_EARPIECE, // availableRoutes
-                OFF, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
-                CallAudioState.ROUTE_EARPIECE, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to bluetooth from speakerphone", // name
-                CallAudioState.ROUTE_SPEAKER, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OFF, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to bluetooth from earpiece", // name
-                CallAudioState.ROUTE_EARPIECE, // initialRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                OPTIONAL, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch to bluetooth from wired headset", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
-                OPTIONAL, // speakerInteraction
-                ON, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
-                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
-                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Switch from bluetooth to wired/earpiece when neither are available", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                ON, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.SWITCH_BASELINE_ROUTE, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_BLUETOOTH, // expectedAvailableRoutes
-                false, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect wired headset when device does not support earpiece", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
-                ON, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
-                false, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect wired headset when call doesn't support earpiece", // name
-                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
-                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
-                ON, // speakerInteraction
-                NONE, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
-
-        params.add(new RoutingTestParameters(
-                "Disconnect bluetooth when call does not support earpiece", // name
-                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
-                CallAudioState.ROUTE_BLUETOOTH,  // availableRoutes
-                ON, // speakerInteraction
-                OFF, // bluetoothInteraction
-                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
-                CallAudioState.ROUTE_SPEAKER, // expectedRoute
-                CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
-                true, // doesDeviceSupportEarpiece
-                shouldRunWithFocus
-        ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
-
-        return params;
-    }
-
-    @Override
-    protected void runParametrizedTestCase(TestParameters _params) throws Throwable {
-        RoutingTestParameters params = (RoutingTestParameters) _params;
-        if (params.shouldRunWithFocus) {
-            runParametrizedTestCaseWithFocus(params);
-        } else {
-            runParametrizedTestCaseWithoutFocus(params);
-        }
-    }
-
-    private void runParametrizedTestCaseWithFocus(final RoutingTestParameters params)
-            throws Throwable {
-        resetMocks(true);
-
-        // Construct a fresh state machine on every case
-        final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
-                mContext,
-                mockCallsManager,
-                mockBluetoothRouteManager,
-                mockWiredHeadsetManager,
-                mockStatusBarNotifier,
-                mAudioServiceFactory,
-                params.doesDeviceSupportEarpiece);
-
-        setupMocksForParams(params);
-
-        // Set the initial CallAudioState object
-        final CallAudioState initState = new CallAudioState(false,
-                params.initialRoute, (params.availableRoutes | CallAudioState.ROUTE_SPEAKER));
-        stateMachine.initialize(initState);
-
-        // Make the state machine have focus so that we actually do something
-        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
-                CallAudioRouteStateMachine.ACTIVE_FOCUS);
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
-
-        // Reset mocks one more time to discard stuff from initialization
-        resetMocks(false);
-        setupMocksForParams(params);
-        stateMachine.sendMessageWithSessionInfo(params.action);
-
-        waitForStateMachineActionCompletion(stateMachine, CallAudioRouteStateMachine.RUN_RUNNABLE);
-
-        Handler h = stateMachine.getHandler();
-        waitForHandlerAction(h, TEST_TIMEOUT);
-        stateMachine.quitStateMachine();
-
-        // Verify interactions with the speakerphone and bluetooth systems
-        switch (params.bluetoothInteraction) {
-            case NONE:
-                verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
-                verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
-                break;
-            case ON:
-                verify(mockBluetoothRouteManager).connectBluetoothAudio(null);
-
-                verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
-                break;
-            case OFF:
-                verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
-                verify(mockBluetoothRouteManager).disconnectBluetoothAudio();
-                break;
-            case OPTIONAL:
-                // optional, don't test
-                break;
-        }
-
-        switch (params.speakerInteraction) {
-            case NONE:
-                verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
-                break;
-            case ON: // fall through
-            case OFF:
-                verify(mockAudioManager).setSpeakerphoneOn(params.speakerInteraction == ON);
-                break;
-            case OPTIONAL:
-                // optional, don't test
-                break;
-        }
-
-        // Verify the end state
-        CallAudioState expectedState = new CallAudioState(false, params.expectedRoute,
-                params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER);
-        verifyNewSystemCallAudioState(initState, expectedState);
-    }
-
-    private void setupMocksForParams(RoutingTestParameters params) {
-        // Set up bluetooth and speakerphone state
-        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(
-                params.initialRoute == CallAudioState.ROUTE_BLUETOOTH);
-        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
-                (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
-                        || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
-        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
-                params.initialRoute == CallAudioState.ROUTE_SPEAKER);
-        when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
-    }
-
-    private void runParametrizedTestCaseWithoutFocus(final RoutingTestParameters params)
-            throws Throwable {
-        resetMocks(true);
-
-        // Construct a fresh state machine on every case
-        final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
-                mContext,
-                mockCallsManager,
-                mockBluetoothRouteManager,
-                mockWiredHeadsetManager,
-                mockStatusBarNotifier,
-                mAudioServiceFactory,
-                params.doesDeviceSupportEarpiece);
-
-        // Set up bluetooth and speakerphone state
-        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
-                (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
-                || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
-        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
-                params.initialRoute == CallAudioState.ROUTE_SPEAKER);
-        when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
-
-        // Set the initial CallAudioState object
-        CallAudioState initState = new CallAudioState(false,
-                params.initialRoute, (params.availableRoutes | CallAudioState.ROUTE_SPEAKER));
-        stateMachine.initialize(initState);
-        // Omit the focus-getting statement
-        stateMachine.sendMessageWithSessionInfo(params.action);
-
-        waitForStateMachineActionCompletion(stateMachine, CallAudioModeStateMachine.RUN_RUNNABLE);
-
-        Handler h = stateMachine.getHandler();
-        waitForHandlerAction(h, TEST_TIMEOUT);
-        stateMachine.quitStateMachine();
-
-        // Verify that no substantive interactions have taken place with the
-        // rest of the system
-        verifyNoSystemAudioChanges();
-
-        // Verify the end state
-        CallAudioState expectedState = new CallAudioState(false, params.expectedRoute,
-                params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER);
-        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
-    }
-
-    private void verifyNoSystemAudioChanges() {
-        verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
-        verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(null);
-        verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
-        verify(mockCallsManager, never()).onCallAudioStateChanged(any(CallAudioState.class),
-                any(CallAudioState.class));
-        verify(mockConnectionServiceWrapper, never()).onCallAudioStateChanged(
-                any(Call.class), any(CallAudioState.class));
-    }
-
     private void verifyNewSystemCallAudioState(CallAudioState expectedOldState,
             CallAudioState expectedNewState) {
         ArgumentCaptor<CallAudioState> oldStateCaptor = ArgumentCaptor.forClass(
@@ -961,14 +570,19 @@
         verify(mockConnectionServiceWrapper, timeout(TEST_TIMEOUT).atLeastOnce())
                 .onCallAudioStateChanged(same(fakeCall), newStateCaptor2.capture());
 
-        assertTrue(oldStateCaptor.getValue().equals(expectedOldState));
+        assertTrue(oldStateCaptor.getAllValues().get(0).equals(expectedOldState));
         assertTrue(newStateCaptor1.getValue().equals(expectedNewState));
         assertTrue(newStateCaptor2.getValue().equals(expectedNewState));
     }
 
-    private void resetMocks(boolean resetNotificationFilter) {
+    private void setInBandRing(boolean enabled) {
+        when(mockBluetoothRouteManager.isInbandRingingEnabled()).thenReturn(enabled);
+    }
+
+    private void resetMocks() {
         reset(mockAudioManager, mockBluetoothRouteManager, mockCallsManager,
-                mockConnectionServiceWrapper, fakeCall);
+                mockConnectionServiceWrapper);
+        fakeCall = mock(Call.class);
         when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
         when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
         when(fakeCall.isAlive()).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
new file mode 100644
index 0000000..19630b1
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -0,0 +1,792 @@
+/*
+ * 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.server.telecom.tests;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.IAudioService;
+import android.os.Handler;
+import android.telecom.CallAudioState;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallAudioManager;
+import com.android.server.telecom.CallAudioModeStateMachine;
+import com.android.server.telecom.CallAudioRouteStateMachine;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ConnectionServiceWrapper;
+import com.android.server.telecom.StatusBarNotifier;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.WiredHeadsetManager;
+import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(Parameterized.class)
+public class CallAudioRouteTransitionTests extends TelecomTestCase {
+    private static final int NONE = 0;
+    private static final int ON = 1;
+    private static final int OFF = 2;
+    private static final int OPTIONAL = 3;
+
+    // This is used to simulate the first bluetooth device getting connected --
+    // it requires two messages: BT device list changed and active device present
+    private static final int SPECIAL_CONNECT_BT_ACTION = 998;
+    // Same, but for disconnection
+    private static final int SPECIAL_DISCONNECT_BT_ACTION = 999;
+
+    static class RoutingTestParameters {
+        public String name;
+        public int initialRoute;
+        public BluetoothDevice initialBluetoothDevice = null;
+        public int availableRoutes; // may excl. speakerphone, because that's always available
+        public List<BluetoothDevice> availableBluetoothDevices = Collections.emptyList();
+        public int speakerInteraction; // one of NONE, ON, or OFF
+        public int bluetoothInteraction; // one of NONE, ON, or OFF
+        public int action;
+        public int expectedRoute;
+        public BluetoothDevice expectedBluetoothDevice = null;
+        public int expectedAvailableRoutes; // also may exclude the speakerphone.
+        public int earpieceControl; // Allows disabling the earpiece to simulate Wear or Car
+
+        public int callSupportedRoutes = CallAudioState.ROUTE_ALL;
+
+        public RoutingTestParameters(String name, int initialRoute,
+                int availableRoutes, int speakerInteraction,
+                int bluetoothInteraction, int action, int expectedRoute,
+                int expectedAvailableRoutes, int earpieceControl) {
+            this.name = name;
+            this.initialRoute = initialRoute;
+            this.availableRoutes = availableRoutes;
+            this.speakerInteraction = speakerInteraction;
+            this.bluetoothInteraction = bluetoothInteraction;
+            this.action = action;
+            this.expectedRoute = expectedRoute;
+            this.expectedAvailableRoutes = expectedAvailableRoutes;
+            this.earpieceControl = earpieceControl;
+        }
+
+        public RoutingTestParameters setCallSupportedRoutes(int routes) {
+            callSupportedRoutes = routes;
+            return this;
+        }
+
+        public RoutingTestParameters setInitialBluetoothDevice(BluetoothDevice device) {
+            initialBluetoothDevice = device;
+            return this;
+        }
+
+        public RoutingTestParameters setAvailableBluetoothDevices(BluetoothDevice... devices) {
+            availableBluetoothDevices = Arrays.asList(devices);
+            return this;
+        }
+
+        public RoutingTestParameters setExpectedBluetoothDevice(BluetoothDevice device) {
+            expectedBluetoothDevice = device;
+            return this;
+        }
+
+        @Override
+        public String toString() {
+            return "RoutingTestParameters{" +
+                    "name='" + name + '\'' +
+                    ", initialRoute=" + initialRoute +
+                    ", availableRoutes=" + availableRoutes +
+                    ", speakerInteraction=" + speakerInteraction +
+                    ", bluetoothInteraction=" + bluetoothInteraction +
+                    ", action=" + action +
+                    ", expectedRoute=" + expectedRoute +
+                    ", expectedAvailableRoutes=" + expectedAvailableRoutes +
+                    ", earpieceControl=" + earpieceControl +
+                    '}';
+        }
+    }
+
+    private final RoutingTestParameters mParams;
+    @Mock CallsManager mockCallsManager;
+    @Mock BluetoothRouteManager mockBluetoothRouteManager;
+    @Mock IAudioService mockAudioService;
+    @Mock ConnectionServiceWrapper mockConnectionServiceWrapper;
+    @Mock WiredHeadsetManager mockWiredHeadsetManager;
+    @Mock StatusBarNotifier mockStatusBarNotifier;
+    @Mock Call fakeCall;
+    @Mock CallAudioManager mockCallAudioManager;
+    private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
+    private static final int TEST_TIMEOUT = 500;
+    private AudioManager mockAudioManager;
+    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
+
+    public CallAudioRouteTransitionTests(RoutingTestParameters params) {
+        mParams = params;
+    }
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+        mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+
+        mAudioServiceFactory = new CallAudioManager.AudioServiceFactory() {
+            @Override
+            public IAudioService getAudioService() {
+                return mockAudioService;
+            }
+        };
+
+        when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
+        when(mockCallsManager.getLock()).thenReturn(mLock);
+        when(mockCallsManager.hasVideoCall()).thenReturn(false);
+        when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
+        when(fakeCall.isAlive()).thenReturn(true);
+        when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL);
+
+        doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
+                any(CallAudioState.class));
+    }
+
+    private void setupMocksForParams(final CallAudioRouteStateMachine sm,
+            RoutingTestParameters params) {
+        // Set up bluetooth and speakerphone state
+        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(
+                params.initialRoute == CallAudioState.ROUTE_BLUETOOTH);
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
+                (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
+                        || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
+        when(mockBluetoothRouteManager.getConnectedDevices())
+                .thenReturn(params.availableBluetoothDevices);
+        if (params.initialBluetoothDevice != null) {
+            when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                    .thenReturn(params.initialBluetoothDevice);
+        }
+
+
+        doAnswer(invocation -> {
+            sm.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+            return null;
+        }).when(mockBluetoothRouteManager).connectBluetoothAudio(nullable(String.class));
+
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
+                params.initialRoute == CallAudioState.ROUTE_SPEAKER);
+        when(fakeCall.getSupportedAudioRoutes()).thenReturn(params.callSupportedRoutes);
+    }
+
+    private void sendActionToStateMachine(CallAudioRouteStateMachine sm) {
+        switch (mParams.action) {
+            case SPECIAL_CONNECT_BT_ACTION:
+                sm.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED);
+                sm.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT);
+                break;
+            case SPECIAL_DISCONNECT_BT_ACTION:
+                sm.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.BLUETOOTH_DEVICE_LIST_CHANGED);
+                sm.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE);
+                break;
+            default:
+                sm.sendMessageWithSessionInfo(mParams.action);
+                break;
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testActiveTransition() {
+        final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                mParams.earpieceControl);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+
+        setupMocksForParams(stateMachine, mParams);
+
+        // Set the initial CallAudioState object
+        final CallAudioState initState = new CallAudioState(false,
+                mParams.initialRoute, (mParams.availableRoutes | CallAudioState.ROUTE_SPEAKER),
+                mParams.initialBluetoothDevice, mParams.availableBluetoothDevices);
+        stateMachine.initialize(initState);
+
+        // Make the state machine have focus so that we actually do something
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.ACTIVE_FOCUS);
+        // Tell the state machine that BT is on, if that's what the mParams say.
+        if (mParams.initialRoute == CallAudioState.ROUTE_BLUETOOTH) {
+            stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+        }
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        // Reset mocks to discard stuff from initialization
+        resetMocks();
+        setupMocksForParams(stateMachine, mParams);
+
+        sendActionToStateMachine(stateMachine);
+
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        Handler h = stateMachine.getHandler();
+        waitForHandlerAction(h, TEST_TIMEOUT);
+        stateMachine.quitStateMachine();
+
+        // Verify interactions with the speakerphone and bluetooth systems
+        switch (mParams.bluetoothInteraction) {
+            case NONE:
+                verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
+                verify(mockBluetoothRouteManager, never())
+                        .connectBluetoothAudio(nullable(String.class));
+                break;
+            case ON:
+                if (mParams.expectedBluetoothDevice == null) {
+                    verify(mockBluetoothRouteManager).connectBluetoothAudio(null);
+                } else {
+                    verify(mockBluetoothRouteManager).connectBluetoothAudio(
+                            mParams.expectedBluetoothDevice.getAddress());
+                }
+                verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
+                break;
+            case OFF:
+                verify(mockBluetoothRouteManager, never())
+                        .connectBluetoothAudio(nullable(String.class));
+                verify(mockBluetoothRouteManager).disconnectBluetoothAudio();
+                break;
+            case OPTIONAL:
+                // optional, don't test
+                break;
+        }
+
+        switch (mParams.speakerInteraction) {
+            case NONE:
+                verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
+                break;
+            case ON: // fall through
+            case OFF:
+                verify(mockAudioManager).setSpeakerphoneOn(mParams.speakerInteraction == ON);
+                break;
+            case OPTIONAL:
+                // optional, don't test
+                break;
+        }
+
+        // Verify the end state
+        CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute,
+                mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
+                mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
+        verifyNewSystemCallAudioState(initState, expectedState);
+    }
+
+    @Test
+    @SmallTest
+    public void testQuiescentTransition() {
+        final CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                mParams.earpieceControl);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+
+        // Set up bluetooth and speakerphone state
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(
+                (mParams.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
+                || (mParams.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
+        when(mockBluetoothRouteManager.getConnectedDevices())
+                .thenReturn(mParams.availableBluetoothDevices);
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
+                mParams.initialRoute == CallAudioState.ROUTE_SPEAKER);
+        when(fakeCall.getSupportedAudioRoutes()).thenReturn(mParams.callSupportedRoutes);
+
+        // Set the initial CallAudioState object
+        CallAudioState initState = new CallAudioState(false,
+                mParams.initialRoute, (mParams.availableRoutes | CallAudioState.ROUTE_SPEAKER),
+                mParams.initialBluetoothDevice, mParams.availableBluetoothDevices);
+        stateMachine.initialize(initState);
+        // Omit the focus-getting statement
+        sendActionToStateMachine(stateMachine);
+
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        stateMachine.quitStateMachine();
+
+        // Verify that no substantive interactions have taken place with the
+        // rest of the system
+        verifyNoSystemAudioChanges();
+
+        // Verify the end state
+        CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute,
+                mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
+                mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
+        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<RoutingTestParameters> testParametersCollection() {
+        List<RoutingTestParameters> params = new ArrayList<>();
+
+        params.add(new RoutingTestParameters(
+                "Connect headset during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Connect headset during bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Connect headset during speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OFF, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect headset during headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect headset during headset with bluetooth available", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                OPTIONAL, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect headset during bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect headset during speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect headset during speakerphone with bluetooth available", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Connect bluetooth during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                ON, // bluetoothInteraction
+                SPECIAL_CONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ).setAvailableBluetoothDevices(BluetoothRouteManagerTest.DEVICE1));
+
+        params.add(new RoutingTestParameters(
+                "Connect bluetooth during wired headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                ON, // bluetoothInteraction
+                SPECIAL_CONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvai
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ).setAvailableBluetoothDevices(BluetoothRouteManagerTest.DEVICE1));
+
+        params.add(new RoutingTestParameters(
+                "Connect bluetooth during speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OFF, // speakerInteraction
+                ON, // bluetoothInteraction
+                SPECIAL_CONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ).setAvailableBluetoothDevices(BluetoothRouteManagerTest.DEVICE1));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect bluetooth during bluetooth without headset in", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                OFF, // bluetoothInteraction
+                SPECIAL_DISCONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect bluetooth during bluetooth with headset in", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                OPTIONAL, // speakerInteraction
+                OFF, // bluetoothInteraction
+                SPECIAL_DISCONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect bluetooth during speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                SPECIAL_DISCONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect bluetooth during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                NONE, // bluetoothInteraction
+                SPECIAL_DISCONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to speakerphone from earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                ON, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to speakerphone from headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                ON, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to speakerphone from bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                ON, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_SPEAKER, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to earpiece from bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to earpiece from speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OFF, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to earpiece from speakerphone, priority notifications", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OFF, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to earpiece from speakerphone, silent mode", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                OFF, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to bluetooth from speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OFF, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to bluetooth from earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailable
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch to bluetooth from wired headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                OPTIONAL, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // expectedAvai
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Switch from bluetooth to wired/earpiece when neither are available", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                ON, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_BASELINE_ROUTE, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_BLUETOOTH, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect wired headset when device does not support earpiece", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                ON, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_DISABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect wired headset when call doesn't support earpiece", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                ON, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
+
+        params.add(new RoutingTestParameters(
+                "Disconnect bluetooth when call does not support earpiece", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_BLUETOOTH,  // availableRoutes
+                ON, // speakerInteraction
+                OFF, // bluetoothInteraction
+                SPECIAL_DISCONNECT_BT_ACTION, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_SPEAKER, // expectedAvailableRoutes
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ).setCallSupportedRoutes(CallAudioState.ROUTE_ALL & ~CallAudioState.ROUTE_EARPIECE));
+
+        params.add(new RoutingTestParameters(
+                "Active device deselected during BT", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_GONE, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        params.add(new RoutingTestParameters(
+                "Active device selected during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                OPTIONAL, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.BT_ACTIVE_DEVICE_PRESENT, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // expectedAvailabl
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED // earpieceControl
+        ));
+
+        return params;
+    }
+
+    private void verifyNewSystemCallAudioState(CallAudioState expectedOldState,
+            CallAudioState expectedNewState) {
+        ArgumentCaptor<CallAudioState> oldStateCaptor = ArgumentCaptor.forClass(
+                CallAudioState.class);
+        ArgumentCaptor<CallAudioState> newStateCaptor1 = ArgumentCaptor.forClass(
+                CallAudioState.class);
+        ArgumentCaptor<CallAudioState> newStateCaptor2 = ArgumentCaptor.forClass(
+                CallAudioState.class);
+        verify(mockCallsManager, timeout(TEST_TIMEOUT).atLeastOnce()).onCallAudioStateChanged(
+                oldStateCaptor.capture(), newStateCaptor1.capture());
+        verify(mockConnectionServiceWrapper, timeout(TEST_TIMEOUT).atLeastOnce())
+                .onCallAudioStateChanged(same(fakeCall), newStateCaptor2.capture());
+
+        assertEquals(expectedOldState, oldStateCaptor.getAllValues().get(0));
+        assertEquals(expectedNewState, newStateCaptor1.getValue());
+        assertEquals(expectedNewState, newStateCaptor2.getValue());
+    }
+
+    private void verifyNoSystemAudioChanges() {
+        verify(mockBluetoothRouteManager, never()).disconnectBluetoothAudio();
+        verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class));
+        verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
+        verify(mockCallsManager, never()).onCallAudioStateChanged(any(CallAudioState.class),
+                any(CallAudioState.class));
+        verify(mockConnectionServiceWrapper, never()).onCallAudioStateChanged(
+                any(Call.class), any(CallAudioState.class));
+    }
+
+    private void resetMocks() {
+        reset(mockAudioManager, mockBluetoothRouteManager, mockCallsManager,
+                mockConnectionServiceWrapper);
+        fakeCall = mock(Call.class);
+        when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
+        when(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
+        when(fakeCall.isAlive()).thenReturn(true);
+        when(fakeCall.getSupportedAudioRoutes()).thenReturn(CallAudioState.ROUTE_ALL);
+        when(mockCallsManager.getLock()).thenReturn(mLock);
+        doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
+                any(CallAudioState.class));
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallExtrasTest.java b/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
index e9cd733..44578c5 100644
--- a/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallExtrasTest.java
@@ -25,12 +25,24 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.MediumTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 /**
  * Tests the {@link Connection} and {@link Call} extras functionality.
  */
+@RunWith(JUnit4.class)
 public class CallExtrasTest extends TelecomSystemTest {
 
     public final static String EXTRA_KEY_STR = "STRINGKEY";
@@ -41,6 +53,18 @@
     public final static String EXTRA_VALUE2_STR = "mozzarella";
     public final static int EXTRA_VALUE_INT = 1234;
 
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     /**
      * Tests setting extras on the connection side and ensuring they are propagated through to
      * the InCallService.
@@ -48,6 +72,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsPutExtras() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -74,6 +99,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsPutBooleanExtra() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -95,6 +121,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsPutIntExtra() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -116,6 +143,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsPutStringExtra() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -138,6 +166,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsRemoveExtra() throws Exception {
         // Get a call up and running."STRING"
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -168,6 +197,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsUpdateExisting() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -204,6 +234,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testCsSetExtras() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -243,6 +274,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testICSPutExtras() throws Exception {
         Bundle extras = new Bundle();
         extras.putString(EXTRA_KEY_STR, EXTRA_VALUE_STR);
@@ -270,6 +302,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testExtrasBidirectional() throws Exception {
         // Get a call up and running.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -322,6 +355,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testConferenceSetExtras() throws Exception {
         ParcelableCall call = makeConferenceCall();
         String conferenceId = call.getId();
@@ -364,6 +398,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testConferenceExtraOperations() throws Exception {
         ParcelableCall call = makeConferenceCall();
         String conferenceId = call.getId();
@@ -399,6 +434,7 @@
      * @throws Exception
      */
     @LargeTest
+    @Test
     public void testConferenceICS() throws Exception {
         ParcelableCall call = makeConferenceCall();
         String conferenceId = call.getId();
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index 3bf044c..690a38a 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.IContentProvider;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.location.Country;
 import android.location.CountryDetector;
 import android.location.CountryListener;
@@ -46,11 +47,15 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallLogManager;
 import com.android.server.telecom.CallState;
+import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
-import com.android.server.telecom.R;
 import com.android.server.telecom.TelephonyUtil;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
@@ -61,14 +66,18 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
-import java.util.Locale;
 
+@RunWith(JUnit4.class)
 public class CallLogManagerTest extends TelecomTestCase {
 
     private CallLogManager mCallLogManager;
@@ -76,6 +85,7 @@
     private PhoneAccountHandle mDefaultAccountHandle;
     private PhoneAccountHandle mOtherUserAccountHandle;
     private PhoneAccountHandle mManagedProfileAccountHandle;
+    private PhoneAccountHandle mSelfManagedAccountHandle;
 
     private static final Uri TEL_PHONEHANDLE = Uri.parse("tel:5555551234");
 
@@ -88,6 +98,7 @@
     private static final String POST_DIAL_STRING = ";12345";
     private static final String VIA_NUMBER_STRING = "5555555678";
     private static final String TEST_PHONE_ACCOUNT_ID= "testPhoneAccountId";
+    private static final String TEST_SELF_MGD_PHONE_ACCOUNT_ID= "testPhoneAccountId";
 
     private static final int TEST_TIMEOUT_MILLIS = 200;
     private static final int CURRENT_USER_ID = 0;
@@ -103,6 +114,7 @@
     MissedCallNotifier mMissedCallNotifier;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -128,6 +140,12 @@
                 UserHandle.of(MANAGED_USER_ID)
         );
 
+        mSelfManagedAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerSelfMgdTest"),
+                TEST_SELF_MGD_PHONE_ACCOUNT_ID,
+                UserHandle.of(CURRENT_USER_ID)
+        );
+
         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         UserInfo userInfo = new UserInfo(CURRENT_USER_ID, "test", 0);
         UserInfo otherUserInfo = new UserInfo(OTHER_USER_ID, "test2", 0);
@@ -153,6 +171,7 @@
     }
 
     @MediumTest
+    @Test
     public void testDontLogCancelledCall() {
         Call fakeCall = makeFakeCall(
                 DisconnectCause.CANCELED,
@@ -174,6 +193,7 @@
     }
 
     @MediumTest
+    @Test
     public void testDontLogChoosingAccountCall() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -196,6 +216,58 @@
     }
 
     @MediumTest
+    @Test
+    public void testDontLogUnloggableNumbers() {
+        // Set up the carrier config source
+        String number1 = "90000";
+        String number2 = "80000";
+        CarrierConfigManager mockCarrierConfigManager =
+                (CarrierConfigManager) mComponentContextFixture.getTestDouble()
+                        .getApplicationContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putStringArray(CarrierConfigManager.KEY_UNLOGGABLE_NUMBERS_STRING_ARRAY,
+                new String[] {number1});
+        when(mockCarrierConfigManager.getConfig()).thenReturn(bundle);
+
+        Resources mockResources = mContext.getResources();
+        when(mockResources.getStringArray(com.android.internal.R.array.unloggable_phone_numbers))
+                .thenReturn(new String[] {number2});
+
+        Call fakeCall1 = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                Uri.parse("tel:" + number1),
+                EMERGENCY_ACCT_HANDLE, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                VIA_NUMBER_STRING, // viaNumber
+                UserHandle.of(CURRENT_USER_ID)
+        );
+
+        Call fakeCall2 = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                Uri.parse("tel:" + number2),
+                EMERGENCY_ACCT_HANDLE, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                VIA_NUMBER_STRING, // viaNumber
+                UserHandle.of(CURRENT_USER_ID)
+        );
+
+        mCallLogManager.onCallStateChanged(fakeCall1, CallState.ACTIVE, CallState.DISCONNECTED);
+        mCallLogManager.onCallStateChanged(fakeCall2, CallState.ACTIVE, CallState.DISCONNECTED);
+        verifyNoInsertion();
+    }
+
+    @MediumTest
+    @Test
     public void testDontLogCallsFromEmergencyAccount() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(EMERGENCY_ACCT_HANDLE, 0));
@@ -224,6 +296,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionOutgoing() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -248,6 +321,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionIncoming() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -272,6 +346,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionMissed() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -300,6 +375,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionRejected() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -325,6 +401,7 @@
     }
 
     @MediumTest
+    @Test
     public void testCreationTimeAndAge() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -352,6 +429,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogPhoneAccountId() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -375,6 +453,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCorrectPhoneNumber() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -401,6 +480,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallVideoFeatures() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -425,6 +505,7 @@
 
     @MediumTest
     @FlakyTest
+    @Test
     public void testLogCallDirectionOutgoingWithMultiUserCapability() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
@@ -457,6 +538,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionIncomingWithMultiUserCapability() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
@@ -489,6 +571,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionOutgoingWithMultiUserCapabilityFromManagedProfile() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle,
@@ -519,6 +602,7 @@
 
     @MediumTest
     @FlakyTest
+    @Test
     public void testLogCallDirectionOutgoingFromManagedProfile() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
@@ -548,6 +632,7 @@
     }
 
     @MediumTest
+    @Test
     public void testLogCallDirectionIngoingFromManagedProfile() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
@@ -580,6 +665,7 @@
      * Ensure call data usage is persisted to the call log when present in the call.
      */
     @MediumTest
+    @Test
     public void testLogCallDataUsageSet() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -606,6 +692,7 @@
      * Ensures call data usage is null in the call log when not set on the call.
      */
     @MediumTest
+    @Test
     public void testLogCallDataUsageNotSet() {
         when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
                 .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
@@ -628,7 +715,39 @@
         assertNull(insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
     }
 
+    /**
+     * Ensures missed self-managed calls are marked as read..
+     */
+    @MediumTest
+    @Test
+    public void testLogMissedSelfManaged() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle,
+                        PhoneAccount.CAPABILITY_SELF_MANAGED));
+        Call fakeMissedCall = makeFakeCall(
+                DisconnectCause.MISSED, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mSelfManagedAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                VIA_NUMBER_STRING, // viaNumber
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        when(fakeMissedCall.isSelfManaged()).thenReturn(true);
+        when(fakeMissedCall.isLoggedSelfManaged()).thenReturn(true);
+        when(fakeMissedCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_NONE);
+        mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(1, insertedValues.getAsInteger(Calls.IS_READ).intValue());
+    }
+
     @SmallTest
+    @Test
     public void testCountryIso_setCache() {
         Country testCountry = new Country(TEST_ISO, Country.COUNTRY_SOURCE_LOCALE);
         CountryDetector mockDetector = (CountryDetector) mContext.getSystemService(
@@ -641,6 +760,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCountryIso_newCountryDetected() {
         Country testCountry = new Country(TEST_ISO, Country.COUNTRY_SOURCE_LOCALE);
         Country testCountry2 = new Country(TEST_ISO_2, Country.COUNTRY_SOURCE_LOCALE);
diff --git a/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
new file mode 100644
index 0000000..eca374b
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.server.telecom.tests;
+
+import static com.android.server.telecom.tests.TelecomSystemTest.TEST_TIMEOUT;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecordingConfiguration;
+import android.media.MediaRecorder;
+import android.os.Handler;
+import android.os.Looper;
+import android.telecom.PhoneAccountHandle;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallRecordingTonePlayer;
+import com.android.server.telecom.TelecomSystem;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for the {@link com.android.server.telecom.CallRecordingTonePlayer} class.
+ */
+@RunWith(JUnit4.class)
+public class CallRecordingTonePlayerTest extends TelecomTestCase {
+
+    private static final String PHONE_ACCOUNT_PACKAGE = "com.android.telecom.test";
+    private static final String PHONE_ACCOUNT_CLASS = "MyFancyConnectionService";
+    private static final String PHONE_ACCOUNT_ID = "1";
+    private static final String RECORDING_APP_PACKAGE = "com.recording.app";
+
+    private static final PhoneAccountHandle TEST_PHONE_ACCOUNT = new PhoneAccountHandle(
+            new ComponentName(PHONE_ACCOUNT_PACKAGE, PHONE_ACCOUNT_CLASS), PHONE_ACCOUNT_ID);
+
+    private CallRecordingTonePlayer mCallRecordingTonePlayer;
+    private TelecomSystem.SyncRoot mSyncRoot = new TelecomSystem.SyncRoot() { };
+    @Mock private AudioManager mAudioManager;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mCallRecordingTonePlayer = new CallRecordingTonePlayer(
+                mComponentContextFixture.getTestDouble().getApplicationContext(),
+                mAudioManager, mSyncRoot);
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn(null);
+    }
+
+    /**
+     * Ensures that child calls are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testChildCall() {
+        Call childCall = Mockito.mock(Call.class);
+        Call parentcall = Mockito.mock(Call.class);
+        when(childCall.getParentCall()).thenReturn(parentcall);
+        mCallRecordingTonePlayer.onCallAdded(childCall);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that external calls are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddExternalCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(true);
+        mCallRecordingTonePlayer.onCallAdded(call);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that emergency calls are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddEmergencyCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(false);
+        when(call.isEmergencyCall()).thenReturn(true);
+        mCallRecordingTonePlayer.onCallAdded(call);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that calls which don't use the recording tone are not tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddIneligibleCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(false);
+        when(call.isEmergencyCall()).thenReturn(false);
+        when(call.isUsingCallRecordingTone()).thenReturn(false);
+        mCallRecordingTonePlayer.onCallAdded(call);
+
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Ensures that an eligible call is tracked.
+     */
+    @MediumTest
+    @Test
+    public void testAddEligibleCall() {
+        Call call = addValidCall();
+
+        mCallRecordingTonePlayer.onCallRemoved(call);
+        assertFalse(mCallRecordingTonePlayer.hasCalls());
+    }
+
+    /**
+     * Verifies registration and unregistration of the recording callback.
+     */
+    @MediumTest
+    @Test
+    public void testRecordingCallbackRegistered() {
+        Call call = addValidCall();
+
+        // Ensure we got a request for the first set of recordings.
+        verify(mAudioManager).getActiveRecordingConfigurations();
+
+        // Ensure that we registered an audio recording callback.
+        verify(mAudioManager).registerAudioRecordingCallback(
+                any(AudioManager.AudioRecordingCallback.class), any());
+
+        mCallRecordingTonePlayer.onCallRemoved(call);
+
+        // Ensure we unregistered the audio recording callback after the last call was removed.
+        verify(mAudioManager).unregisterAudioRecordingCallback(
+                any(AudioManager.AudioRecordingCallback.class));
+    }
+
+    /**
+     * Verify that we are in a recording state when we add a call and there is a recording taking
+     * place prior to the call starting.
+     */
+    @MediumTest
+    @Test
+    public void testIsRecordingInitial() {
+        // Return an active recording configuration when we add the first call.
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn(
+                getAudioRecordingConfig(RECORDING_APP_PACKAGE));
+
+        addValidCall();
+
+        // Ensure we got a request for the first set of recordings.
+        verify(mAudioManager).getActiveRecordingConfigurations();
+
+        assertTrue(mCallRecordingTonePlayer.isRecording());
+    }
+
+    /**
+     * Verify that we are in a recording state when we add a call and a recording start after the
+     * call starts.
+     */
+    @MediumTest
+    @Test
+    public void testIsRecordingLater() {
+        // Return no active recording configuration when we add the first call.
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn( null);
+
+        addValidCall();
+
+        // Capture the registered callback so we can pass back test data via it.
+        ArgumentCaptor<AudioManager.AudioRecordingCallback> callbackCaptor =
+                ArgumentCaptor.forClass(AudioManager.AudioRecordingCallback.class);
+        verify(mAudioManager).registerAudioRecordingCallback(callbackCaptor.capture(), any());
+
+        // Pass back some test configuration data.
+        callbackCaptor.getValue().onRecordingConfigChanged(getAudioRecordingConfig(
+                RECORDING_APP_PACKAGE));
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        assertTrue(mCallRecordingTonePlayer.isRecording());
+    }
+
+    /**
+     * Verifies that we are not in a recording state if the PhoneAccount associated with the call is
+     * the recording app.
+     */
+    @MediumTest
+    @Test
+    public void testNotRecordingApp() {
+        // Return no active recording configuration when we add the first call.
+        when(mAudioManager.getActiveRecordingConfigurations()).thenReturn( null);
+
+        addValidCall();
+
+        // Capture the registered callback so we can pass back test data via it.
+        ArgumentCaptor<AudioManager.AudioRecordingCallback> callbackCaptor =
+                ArgumentCaptor.forClass(AudioManager.AudioRecordingCallback.class);
+        verify(mAudioManager).registerAudioRecordingCallback(callbackCaptor.capture(), any());
+
+        // Report that the recording app is the call's phone account.
+        callbackCaptor.getValue().onRecordingConfigChanged(getAudioRecordingConfig(
+                PHONE_ACCOUNT_PACKAGE));
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+
+        // Since the app which is recording is the phone account of the call, we should not be in
+        // a recording state.
+        assertFalse(mCallRecordingTonePlayer.isRecording());
+    }
+
+    /**
+     * @return Test audio recording configuration.
+     */
+    private List<AudioRecordingConfiguration> getAudioRecordingConfig(String packageName) {
+        List<AudioRecordingConfiguration> configs = new ArrayList<>();
+        configs.add(new AudioRecordingConfiguration(0, 0, MediaRecorder.AudioSource.MIC,
+                new AudioFormat.Builder().build(), new AudioFormat.Builder().build(),
+                0, packageName));
+        return configs;
+    }
+
+    private Call addValidCall() {
+        Call call = Mockito.mock(Call.class);
+        when(call.getParentCall()).thenReturn(null);
+        when(call.isExternalCall()).thenReturn(false);
+        when(call.isEmergencyCall()).thenReturn(false);
+        when(call.isUsingCallRecordingTone()).thenReturn(true);
+        when(call.getConnectionManagerPhoneAccount()).thenReturn(null);
+        when(call.getTargetPhoneAccount()).thenReturn(TEST_PHONE_ACCOUNT);
+        mCallRecordingTonePlayer.onCallAdded(call);
+        assertTrue(mCallRecordingTonePlayer.hasCalls());
+        return call;
+    }
+
+
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
index 6782d52..a319fad 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
@@ -15,6 +15,7 @@
  */
 
 package com.android.server.telecom.tests;
+
 import android.Manifest;
 import android.content.ComponentName;
 import android.content.Context;
@@ -37,29 +38,32 @@
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.ParcelableCallUtils;
 import com.android.server.telecom.PhoneAccountRegistrar;
-import com.android.server.telecom.TelecomServiceImpl;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 import com.android.server.telecom.callfiltering.CallScreeningServiceFilter;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.Collections;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class CallScreeningServiceFilterTest extends TelecomTestCase {
     @Mock Context mContext;
     @Mock CallsManager mCallsManager;
@@ -90,13 +94,14 @@
     );
 
     private CallScreeningServiceFilter mFilter;
+
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         when(mCallsManager.getCurrentUserHandle()).thenReturn(UserHandle.CURRENT);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mCall.getId()).thenReturn(CALL_ID);
-//        when(mBinder.queryLocalInterface(anyString())).thenReturn(mCallScreeningService);
         doReturn(mCallScreeningService).when(mBinder).queryLocalInterface(anyString());
 
         mResolveInfo =  new ResolveInfo() {{
@@ -120,6 +125,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNoDefaultDialer() {
         when(mDefaultDialerCache.getDefaultDialerApplication(eq(UserHandle.USER_CURRENT)))
                 .thenReturn(null);
@@ -128,6 +134,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNoResolveEntries() {
         when(mPackageManager.queryIntentServicesAsUser(nullable(Intent.class), anyInt(), anyInt()))
                 .thenReturn(Collections.emptyList());
@@ -136,6 +143,7 @@
     }
 
     @SmallTest
+    @Test
     public void testBadResolveEntry() {
         mResolveInfo.serviceInfo = null;
         mFilter.startFilterLookup(mCall, mCallback);
@@ -143,6 +151,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPermissionlessFilterService() {
         mResolveInfo.serviceInfo.permission = null;
         mFilter.startFilterLookup(mCall, mCallback);
@@ -150,6 +159,7 @@
     }
 
     @SmallTest
+    @Test
     public void testContextFailToBind() {
         when(mContext.bindServiceAsUser(nullable(Intent.class), nullable(ServiceConnection.class),
                 anyInt(), eq(UserHandle.CURRENT))).thenReturn(false);
@@ -158,6 +168,7 @@
     }
 
     @SmallTest
+    @Test
     public void testExceptionInScreeningService() throws Exception {
         doThrow(new RemoteException()).when(mCallScreeningService).screenCall(
                 nullable(ICallScreeningAdapter.class), nullable(ParcelableCall.class));
@@ -168,6 +179,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAllowCall() throws Exception {
         mFilter.startFilterLookup(mCall, mCallback);
         ServiceConnection serviceConnection = verifyBindingIntent();
@@ -178,6 +190,7 @@
     }
 
     @SmallTest
+    @Test
     public void testDisallowCall() throws Exception {
         mFilter.startFilterLookup(mCall, mCallback);
         ServiceConnection serviceConnection = verifyBindingIntent();
diff --git a/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java b/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java
index 2ae717d..74d07d8 100644
--- a/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java
@@ -21,36 +21,40 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.telecom.Logging.Session;
 
 import com.android.internal.telephony.CallerInfo;
 import com.android.internal.telephony.CallerInfoAsyncQuery;
-import com.android.server.telecom.Call;
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.io.FileNotFoundException;
 import java.io.InputStream;
-import java.net.URI;
 import java.util.concurrent.CountDownLatch;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class CallerInfoLookupHelperTest extends TelecomTestCase {
     @Mock Context mContext;
     @Mock CallerInfoAsyncQueryFactory mFactory;
@@ -72,6 +76,7 @@
     Bitmap mBitmap;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mCallerInfoLookupHelper = new CallerInfoLookupHelper(mContext,
@@ -85,7 +90,8 @@
         if (mBitmap == null) {
             InputStream is;
             try {
-                is = getTestContext().getContentResolver().openInputStream(CONTACTS_PHOTO_URI);
+                is = InstrumentationRegistry.getContext()
+                        .getContentResolver().openInputStream(CONTACTS_PHOTO_URI);
             } catch (FileNotFoundException e) {
                 return;
             }
@@ -96,6 +102,7 @@
     }
 
     @SmallTest
+    @Test
     public void testLookupWithEmptyHandle() {
         CallerInfoLookupHelper.OnQueryCompleteListener listener = mock(
                 CallerInfoLookupHelper.OnQueryCompleteListener.class);
@@ -105,6 +112,8 @@
         verifyProperCleanup();
     }
 
+    @SmallTest
+    @Test
     public void testSimpleLookup() {
         CallerInfoLookupHelper.OnQueryCompleteListener listener = mock(
                 CallerInfoLookupHelper.OnQueryCompleteListener.class);
@@ -140,6 +149,8 @@
         verifyProperCleanup();
     }
 
+    @SmallTest
+    @Test
     public void testLookupWithTwoListeners() {
         CallerInfoLookupHelper.OnQueryCompleteListener callListener = mock(
                 CallerInfoLookupHelper.OnQueryCompleteListener.class);
@@ -179,6 +190,8 @@
         verifyProperCleanup();
     }
 
+    @SmallTest
+    @Test
     public void testListenerAddedAfterCallerInfoBeforePhoto() {
         CallerInfoLookupHelper.OnQueryCompleteListener callListener = mock(
                 CallerInfoLookupHelper.OnQueryCompleteListener.class);
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
new file mode 100644
index 0000000..ed2f6b1
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyChar;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.telecom.Connection;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.VideoProfile;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.telecom.AsyncRingtonePlayer;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallAudioManager;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallerInfoAsyncQueryFactory;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.ConnectionServiceFocusManager;
+import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory;
+import com.android.server.telecom.ConnectionServiceWrapper;
+import com.android.server.telecom.ContactsAsyncHelper;
+import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.EmergencyCallHelper;
+import com.android.server.telecom.HeadsetMediaButton;
+import com.android.server.telecom.HeadsetMediaButtonFactory;
+import com.android.server.telecom.InCallController;
+import com.android.server.telecom.InCallControllerFactory;
+import com.android.server.telecom.InCallTonePlayer;
+import com.android.server.telecom.InCallWakeLockController;
+import com.android.server.telecom.InCallWakeLockControllerFactory;
+import com.android.server.telecom.MissedCallNotifier;
+import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.ProximitySensorManager;
+import com.android.server.telecom.ProximitySensorManagerFactory;
+import com.android.server.telecom.SystemStateProvider;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.WiredHeadsetManager;
+import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class CallsManagerTest extends TelecomTestCase {
+    private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
+            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
+    private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
+            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
+    private static final PhoneAccountHandle SELF_MANAGED_HANDLE = new PhoneAccountHandle(
+            ComponentName.unflattenFromString("com.foo/.Self"), "Self");
+    private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.Builder(SIM_1_HANDLE, "Sim1")
+            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+                    | PhoneAccount.CAPABILITY_CALL_PROVIDER)
+            .setIsEnabled(true)
+            .build();
+    private static final PhoneAccount SIM_2_ACCOUNT = new PhoneAccount.Builder(SIM_2_HANDLE, "Sim2")
+            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+                    | PhoneAccount.CAPABILITY_CALL_PROVIDER
+                    | PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING)
+            .setIsEnabled(true)
+            .build();
+    private static final PhoneAccount SELF_MANAGED_ACCOUNT = new PhoneAccount.Builder(
+            SELF_MANAGED_HANDLE, "Self")
+            .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
+            .setIsEnabled(true)
+            .build();
+    private static final Uri TEST_ADDRESS = Uri.parse("tel:555-1212");
+
+    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
+    @Mock private ContactsAsyncHelper mContactsAsyncHelper;
+    @Mock private CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
+    @Mock private MissedCallNotifier mMissedCallNotifier;
+    @Mock private PhoneAccountRegistrar mPhoneAccountRegistrar;
+    @Mock private HeadsetMediaButton mHeadsetMediaButton;
+    @Mock private HeadsetMediaButtonFactory mHeadsetMediaButtonFactory;
+    @Mock private ProximitySensorManager mProximitySensorManager;
+    @Mock private ProximitySensorManagerFactory mProximitySensorManagerFactory;
+    @Mock private InCallWakeLockController mInCallWakeLockController;
+    @Mock private ConnectionServiceFocusManagerFactory mConnSvrFocusManagerFactory;
+    @Mock private InCallWakeLockControllerFactory mInCallWakeLockControllerFactory;
+    @Mock private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
+    @Mock private BluetoothRouteManager mBluetoothRouteManager;
+    @Mock private WiredHeadsetManager mWiredHeadsetManager;
+    @Mock private SystemStateProvider mSystemStateProvider;
+    @Mock private DefaultDialerCache mDefaultDialerCache;
+    @Mock private Timeouts.Adapter mTimeoutsAdapter;
+    @Mock private AsyncRingtonePlayer mAsyncRingtonePlayer;
+    @Mock private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
+    @Mock private EmergencyCallHelper mEmergencyCallHelper;
+    @Mock private InCallTonePlayer.ToneGeneratorFactory mToneGeneratorFactory;
+    @Mock private ClockProxy mClockProxy;
+    @Mock private InCallControllerFactory mInCallControllerFactory;
+    @Mock private InCallController mInCallController;
+    @Mock private ConnectionServiceFocusManager mConnectionSvrFocusMgr;
+    @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
+
+    private CallsManager mCallsManager;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        when(mInCallWakeLockControllerFactory.create(any(), any())).thenReturn(
+                mInCallWakeLockController);
+        when(mHeadsetMediaButtonFactory.create(any(), any(), any())).thenReturn(
+                mHeadsetMediaButton);
+        when(mProximitySensorManagerFactory.create(any(), any())).thenReturn(
+                mProximitySensorManager);
+        when(mInCallControllerFactory.create(any(), any(), any(), any(), any(), any(),
+                any())).thenReturn(mInCallController);
+        when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
+        when(mClockProxy.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+        when(mConnSvrFocusManagerFactory.create(any(), any())).thenReturn(mConnectionSvrFocusMgr);
+        mCallsManager = new CallsManager(
+                mComponentContextFixture.getTestDouble().getApplicationContext(),
+                mLock,
+                mContactsAsyncHelper,
+                mCallerInfoAsyncQueryFactory,
+                mMissedCallNotifier,
+                mPhoneAccountRegistrar,
+                mHeadsetMediaButtonFactory,
+                mProximitySensorManagerFactory,
+                mInCallWakeLockControllerFactory,
+                mConnSvrFocusManagerFactory,
+                mAudioServiceFactory,
+                mBluetoothRouteManager,
+                mWiredHeadsetManager,
+                mSystemStateProvider,
+                mDefaultDialerCache,
+                mTimeoutsAdapter,
+                mAsyncRingtonePlayer,
+                mPhoneNumberUtilsAdapter,
+                mEmergencyCallHelper,
+                mToneGeneratorFactory,
+                mClockProxy,
+                mBluetoothStateReceiver,
+                mInCallControllerFactory);
+
+        when(mPhoneAccountRegistrar.getPhoneAccount(
+                eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
+        when(mPhoneAccountRegistrar.getPhoneAccount(
+                eq(SIM_1_HANDLE), any())).thenReturn(SIM_1_ACCOUNT);
+        when(mPhoneAccountRegistrar.getPhoneAccount(
+                eq(SIM_2_HANDLE), any())).thenReturn(SIM_2_ACCOUNT);
+    }
+
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccounts() throws Exception {
+        // Should be empty since the URI is null.
+        assertEquals(0, mCallsManager.constructPossiblePhoneAccounts(null, null, false).size());
+    }
+
+    /**
+     * Verify behavior for multisim devices where we want to ensure that the active sim is used for
+     * placing a new call.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive() throws Exception {
+        setupMsimAccounts();
+
+        Call ongoingCall = new Call(
+                "1", /* callId */
+                mComponentContextFixture.getTestDouble(),
+                mCallsManager,
+                mLock,
+                null /* ConnectionServiceRepository */,
+                mContactsAsyncHelper,
+                mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
+                TEST_ADDRESS,
+                null /* GatewayInfo */,
+                null /* connectionManagerPhoneAccountHandle */,
+                SIM_2_HANDLE,
+                Call.CALL_DIRECTION_INCOMING,
+                false /* shouldAttachToExistingConnection*/,
+                false /* isConference */,
+                mClockProxy);
+        ongoingCall.setState(CallState.ACTIVE, "just cuz");
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false);
+        assertEquals(1, phoneAccountHandles.size());
+        assertEquals(SIM_2_HANDLE, phoneAccountHandles.get(0));
+    }
+
+    /**
+     * Verify behavior for multisim devices when there are no calls active; expect both accounts.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimIdle() throws Exception {
+        setupMsimAccounts();
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false);
+        assertEquals(2, phoneAccountHandles.size());
+    }
+
+    /**
+     * Tests finding the outgoing call phone account where the call is being placed on a
+     * self-managed ConnectionService.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testFindOutgoingCallPhoneAccountSelfManaged() throws Exception {
+        List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+                SELF_MANAGED_HANDLE, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
+        assertEquals(1, accounts.size());
+        assertEquals(SELF_MANAGED_HANDLE, accounts.get(0));
+    }
+
+    /**
+     * Tests finding the outgoing calling account where the call has no associated phone account,
+     * but there is a user specified default which can be used.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testFindOutgoingCallAccountDefault() throws Exception {
+        when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+                SIM_1_HANDLE);
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt())).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+
+        List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+                null /* phoneAcct */, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
+
+        // Should have found just the default.
+        assertEquals(1, accounts.size());
+        assertEquals(SIM_1_HANDLE, accounts.get(0));
+    }
+
+    /**
+     * Tests finding the outgoing calling account where the call has no associated phone account,
+     * but there is no user specified default which can be used.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testFindOutgoingCallAccountNoDefault() throws Exception {
+        when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+                null);
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt())).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+
+        List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+                null /* phoneAcct */, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
+
+        assertEquals(2, accounts.size());
+        assertTrue(accounts.contains(SIM_1_HANDLE));
+        assertTrue(accounts.contains(SIM_2_HANDLE));
+    }
+
+    /**
+     * Tests that we will default to a video capable phone account if one is available for a video
+     * call.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testFindOutgoingCallAccountVideo() throws Exception {
+        when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+                null);
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), eq(PhoneAccount.CAPABILITY_VIDEO_CALLING))).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_2_HANDLE)));
+
+        List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+                null /* phoneAcct */, TEST_ADDRESS, true /* isVideo */, null /* userHandle */);
+
+        assertEquals(1, accounts.size());
+        assertTrue(accounts.contains(SIM_2_HANDLE));
+    }
+
+    /**
+     * Tests that we will default to a non-video capable phone account for a video call if no video
+     * capable phone accounts are available.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testFindOutgoingCallAccountVideoNotAvailable() throws Exception {
+        when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+                null);
+        // When querying for video capable accounts, return nothing.
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), eq(PhoneAccount.CAPABILITY_VIDEO_CALLING))).thenReturn(
+                Collections.emptyList());
+        // When querying for non-video capable accounts, return one.
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), eq(0 /* none specified */))).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE)));
+        List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+                null /* phoneAcct */, TEST_ADDRESS, true /* isVideo */, null /* userHandle */);
+
+        // Should have found one.
+        assertEquals(1, accounts.size());
+        assertTrue(accounts.contains(SIM_1_HANDLE));
+    }
+
+    /**
+     * Tests that we will use the provided target phone account if it exists.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testUseSpecifiedAccount() throws Exception {
+        when(mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(any(), any())).thenReturn(
+                null);
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt())).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+
+        List<PhoneAccountHandle> accounts = mCallsManager.findOutgoingCallPhoneAccount(
+                SIM_2_HANDLE, TEST_ADDRESS, false /* isVideo */, null /* userHandle */);
+
+        assertEquals(1, accounts.size());
+        assertTrue(accounts.contains(SIM_2_HANDLE));
+    }
+
+    /**
+     * Verifies that an active call will result in playing a DTMF tone when requested.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testPlayDtmfWhenActive() throws Exception {
+        Call callSpy = addSpyCall();
+        mCallsManager.playDtmfTone(callSpy, '1');
+        verify(callSpy).playDtmfTone(anyChar());
+    }
+
+    /**
+     * Verifies that DTMF requests are suppressed when a call is held.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testSuppessDtmfWhenHeld() throws Exception {
+        Call callSpy = addSpyCall();
+        callSpy.setState(CallState.ON_HOLD, "test");
+
+        mCallsManager.playDtmfTone(callSpy, '1');
+        verify(callSpy, never()).playDtmfTone(anyChar());
+    }
+
+    /**
+     * Verifies that DTMF requests are suppressed when a call is held.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testCancelDtmfWhenHeld() throws Exception {
+        Call callSpy = addSpyCall();
+        mCallsManager.playDtmfTone(callSpy, '1');
+        mCallsManager.markCallAsOnHold(callSpy);
+        verify(callSpy).stopDtmfTone();
+    }
+
+    @SmallTest
+    @Test
+    public void testUnholdCallWhenOngoingCallCanBeHeld() {
+        // GIVEN a CallsManager with ongoing call, and this call can be held
+        Call ongoingCall = addSpyCall();
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a held call
+        Call heldCall = addSpyCall();
+
+        // WHEN unhold the held call
+        mCallsManager.unholdCall(heldCall);
+
+        // THEN the ongoing call is held, and the focus request for incoming call is sent
+        verify(ongoingCall).hold(any());
+        verifyFocusRequestAndExecuteCallback(heldCall);
+
+        // and held call is unhold now
+        verify(heldCall).unhold(any());
+    }
+
+    @SmallTest
+    @Test
+    public void testUnholdCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
+        ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
+        ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a held call which has different ConnectionService
+        Call heldCall = addSpyCallWithConnectionService(connSvr2);
+
+        // WHEN unhold the held call
+        mCallsManager.unholdCall(heldCall);
+
+        // THEN the ongoing call is disconnected, and the focus request for incoming call is sent
+        verify(ongoingCall).disconnect(any());
+        verifyFocusRequestAndExecuteCallback(heldCall);
+
+        // and held call is unhold now
+        verify(heldCall).unhold(any());
+    }
+
+    @SmallTest
+    @Test
+    public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
+        ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a held call which has different ConnectionService
+        Call heldCall = addSpyCallWithConnectionService(connSvr);
+
+        // WHEN unhold the held call
+        mCallsManager.unholdCall(heldCall);
+
+        // THEN the ongoing call is held
+        verify(ongoingCall).hold(any());
+        verifyFocusRequestAndExecuteCallback(heldCall);
+
+        // and held call is unhold now
+        verify(heldCall).unhold(any());
+    }
+
+    @SmallTest
+    @Test
+    public void testAnswerCallWhenOngoingCallCanBeHeld() {
+        // GIVEN a CallsManager with ongoing call, and this call can be held
+        Call ongoingCall = addSpyCall();
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // WHEN answer an incoming call
+        Call incomingCall = addSpyCall();
+        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
+
+        // THEN the ongoing call is held and the focus request for incoming call is sent
+        verify(ongoingCall).hold();
+        verifyFocusRequestAndExecuteCallback(incomingCall);
+
+        // and the incoming call is answered.
+        verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @SmallTest
+    @Test
+    public void testAnswerCallWhenOngoingHasSameConnectionService() {
+        ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // WHEN answer an incoming call
+        Call incomingCall = addSpyCallWithConnectionService(connSvr);
+        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
+
+        // THEN nothing happened on the ongoing call and the focus request for incoming call is sent
+        verifyFocusRequestAndExecuteCallback(incomingCall);
+
+        // and the incoming call is answered.
+        verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @SmallTest
+    @Test
+    public void testAnswerCallWhenOngoingHasDifferentConnectionService() {
+        ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
+        ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // WHEN answer an incoming call
+        Call incomingCall = addSpyCallWithConnectionService(connSvr2);
+        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
+
+        // THEN the ongoing call is disconnected and the focus request for incoming call is sent
+        verify(ongoingCall).disconnect();
+        verifyFocusRequestAndExecuteCallback(incomingCall);
+
+        // and the incoming call is answered.
+        verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @SmallTest
+    @Test
+    public void testAnswerCallWhenMultipleHeldCallsExisted() {
+        ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
+        ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // Given an ongoing call and held call with the ConnectionService connSvr1. The
+        // ConnectionService connSvr1 can handle one held call
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        Call heldCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(CallState.ON_HOLD).when(heldCall).getState();
+
+        // and other held call has difference ConnectionService
+        Call heldCall2 = addSpyCallWithConnectionService(connSvr2);
+        doReturn(CallState.ON_HOLD).when(heldCall2).getState();
+
+        // WHEN answer an incoming call which ConnectionService is connSvr1
+        Call incomingCall = addSpyCallWithConnectionService(connSvr1);
+        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
+
+        // THEN the previous held call is disconnected
+        verify(heldCall).disconnect();
+
+        // and the ongoing call is held
+        verify(ongoingCall).hold();
+
+        // and the heldCall2 is not disconnected
+        verify(heldCall2, never()).disconnect();
+
+        // and the focus request is sent
+        verifyFocusRequestAndExecuteCallback(incomingCall);
+
+        // and the incoming call is answered
+        verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @SmallTest
+    @Test
+    public void testAnswerCallWhenNoOngoingCallExisted() {
+        // GIVEN a CallsManager with no ongoing call.
+
+        // WHEN answer an incoming call
+        Call incomingCall = addSpyCall();
+        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
+
+        // THEN the focus request for incoming call is sent
+        verifyFocusRequestAndExecuteCallback(incomingCall);
+
+        // and the incoming call is answered.
+        verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @SmallTest
+    @Test
+    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
+        ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
+        ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        doReturn(ongoingCall).when(mConnectionSvrFocusMgr).getCurrentFocusCall();
+
+        // and a new self-managed call which has different ConnectionService
+        Call newCall = addSpyCallWithConnectionService(connSvr2);
+        doReturn(true).when(newCall).isSelfManaged();
+
+        // WHEN active the new call
+        mCallsManager.markCallAsActive(newCall);
+
+        // THEN the ongoing call is disconnected, and the focus request for the new call is sent
+        verify(ongoingCall).disconnect();
+        verifyFocusRequestAndExecuteCallback(newCall);
+
+        // and the new call is active
+        assertEquals(CallState.ACTIVE, newCall.getState());
+    }
+
+    @SmallTest
+    @Test
+    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
+        ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a new self-managed call which has the same ConnectionService
+        Call newCall = addSpyCallWithConnectionService(connSvr);
+        doReturn(true).when(newCall).isSelfManaged();
+
+        // WHEN active the new call
+        mCallsManager.markCallAsActive(newCall);
+
+        // THEN the ongoing call isn't disconnected
+        verify(ongoingCall, never()).disconnect();
+        verifyFocusRequestAndExecuteCallback(newCall);
+
+        // and the new call is active
+        assertEquals(CallState.ACTIVE, newCall.getState());
+    }
+
+    @SmallTest
+    @Test
+    public void testSetActiveCallWhenOngoingCallCanBeHeld() {
+        // GIVEN a CallsManager with ongoing call, and this call can be held
+        Call ongoingCall = addSpyCall();
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        doReturn(ongoingCall).when(mConnectionSvrFocusMgr).getCurrentFocusCall();
+
+        // and a new self-managed call
+        Call newCall = addSpyCall();
+        doReturn(true).when(newCall).isSelfManaged();
+
+        // WHEN active the new call
+        mCallsManager.markCallAsActive(newCall);
+
+        // THEN the ongoing call is held
+        verify(ongoingCall).hold();
+        verifyFocusRequestAndExecuteCallback(newCall);
+
+        // and the new call is active
+        assertEquals(CallState.ACTIVE, newCall.getState());
+    }
+
+    private Call addSpyCallWithConnectionService(ConnectionServiceWrapper connSvr) {
+        Call call = addSpyCall();
+        doReturn(connSvr).when(call).getConnectionService();
+        return call;
+    }
+
+    private Call addSpyCall() {
+        Call ongoingCall = new Call("1", /* callId */
+                mComponentContextFixture.getTestDouble(),
+                mCallsManager,
+                mLock, /* ConnectionServiceRepository */
+                null,
+                mContactsAsyncHelper,
+                mCallerInfoAsyncQueryFactory,
+                mPhoneNumberUtilsAdapter,
+                TEST_ADDRESS,
+                null /* GatewayInfo */,
+                null /* connectionManagerPhoneAccountHandle */,
+                SIM_2_HANDLE,
+                Call.CALL_DIRECTION_INCOMING,
+                false /* shouldAttachToExistingConnection*/,
+                false /* isConference */,
+                mClockProxy);
+        ongoingCall.setState(CallState.ACTIVE, "just cuz");
+        Call callSpy = Mockito.spy(ongoingCall);
+
+        // Mocks some methods to not call the real method.
+        doNothing().when(callSpy).unhold();
+        doNothing().when(callSpy).hold();
+        doNothing().when(callSpy).disconnect();
+        doNothing().when(callSpy).answer(Matchers.anyInt());
+        doNothing().when(callSpy).setStartWithSpeakerphoneOn(Matchers.anyBoolean());
+
+        mCallsManager.addCall(callSpy);
+        return callSpy;
+    }
+
+    private void verifyFocusRequestAndExecuteCallback(Call call) {
+        ArgumentCaptor<CallsManager.RequestCallback> captor =
+                ArgumentCaptor.forClass(CallsManager.RequestCallback.class);
+        verify(mConnectionSvrFocusMgr).requestFocus(eq(call), captor.capture());
+        CallsManager.RequestCallback callback = captor.getValue();
+        callback.onRequestFocusDone(call);
+    }
+
+    private void setupMsimAccounts() {
+        TelephonyManager mockTelephonyManager = mComponentContextFixture.getTelephonyManager();
+        when(mockTelephonyManager.getMultiSimConfiguration()).thenReturn(
+                TelephonyManager.MultiSimVariants.DSDS);
+        when(mPhoneAccountRegistrar.getCallCapablePhoneAccounts(any(), anyBoolean(),
+                any(), anyInt())).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+        when(mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser()).thenReturn(
+                new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 1c74bfa..01d312b 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -436,6 +436,8 @@
     public ComponentContextFixture() {
         MockitoAnnotations.initMocks(this);
         when(mResources.getConfiguration()).thenReturn(mResourceConfiguration);
+        when(mResources.getString(anyInt())).thenReturn("");
+        when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
         mResourceConfiguration.setLocale(Locale.TAIWAN);
 
         // TODO: Move into actual tests
@@ -462,7 +464,9 @@
         when(mTelephonyManager.getSubIdForPhoneAccount((PhoneAccount) any())).thenReturn(1);
 
         when(mTelephonyManager.getNetworkOperatorName()).thenReturn("label1");
-
+        when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(
+                TelephonyManager.MultiSimVariants.UNKNOWN);
+        when(mResources.getBoolean(eq(R.bool.grant_location_permission_enabled))).thenReturn(false);
         doAnswer(new Answer<Void>(){
             @Override
             public Void answer(InvocationOnMock invocation) throws Throwable {
@@ -527,6 +531,10 @@
         mTelecomManager = telecomManager;
     }
 
+    public TelephonyManager getTelephonyManager() {
+        return mTelephonyManager;
+    }
+
     private void addService(String action, ComponentName name, IInterface service) {
         mComponentNamesByAction.put(action, name);
         mServiceByComponentName.put(name, service);
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 39f70c8..3154b7d 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -26,6 +26,7 @@
 import org.mockito.Mockito;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -76,6 +77,10 @@
         int mCapabilities = NOT_SPECIFIED;
         int mProperties = NOT_SPECIFIED;
 
+        public FakeConnectionServiceDelegate(Context base) {
+            attachBaseContext(base);
+        }
+
         @Override
         public Connection onCreateUnknownConnection(
                 PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
@@ -271,6 +276,10 @@
         public void answer(String callId, Session.Info info) throws RemoteException { }
 
         @Override
+        public void deflect(String callId, Uri address, Session.Info info)
+                throws RemoteException { }
+
+        @Override
         public void reject(String callId, Session.Info info) throws RemoteException {
             rejectedCallIds.add(callId);
         }
@@ -355,6 +364,14 @@
         }
 
         @Override
+        public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException {
+        }
+
+        @Override
+        public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException {
+        }
+
+        @Override
         public IBinder asBinder() {
             return this;
         }
@@ -363,12 +380,17 @@
         public IInterface queryLocalInterface(String descriptor) {
             return this;
         }
+
+        @Override
+        public void handoverFailed(String callId, ConnectionRequest request,
+                                   int error, Session.Info sessionInfo) {}
+
+        @Override
+        public void handoverComplete(String callId, Session.Info sessionInfo) {}
     }
 
-    FakeConnectionServiceDelegate mConnectionServiceDelegate =
-            new FakeConnectionServiceDelegate();
-    private IConnectionService mConnectionServiceDelegateAdapter =
-            IConnectionService.Stub.asInterface(mConnectionServiceDelegate.onBind(null));
+    FakeConnectionServiceDelegate mConnectionServiceDelegate;
+    private IConnectionService mConnectionServiceDelegateAdapter;
 
     FakeConnectionService mConnectionService = new FakeConnectionService();
     private IConnectionService.Stub mConnectionServiceSpy = Mockito.spy(mConnectionService);
@@ -423,7 +445,11 @@
     public final List<ComponentName> mRemoteConnectionServiceNames = new ArrayList<>();
     public final List<IBinder> mRemoteConnectionServices = new ArrayList<>();
 
-    public ConnectionServiceFixture() throws Exception { }
+    public ConnectionServiceFixture(Context context) throws Exception {
+        mConnectionServiceDelegate = new FakeConnectionServiceDelegate(context);
+        mConnectionServiceDelegateAdapter = IConnectionService.Stub.asInterface(
+                mConnectionServiceDelegate.onBind(null));
+    }
 
     @Override
     public IConnectionService getTestDouble() {
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java
new file mode 100644
index 0000000..3c2cc61
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import android.os.Looper;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ConnectionServiceFocusManager;
+import com.android.server.telecom.ConnectionServiceFocusManager.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(JUnit4.class)
+public class ConnectionServiceFocusManagerTest extends TelecomTestCase {
+
+    @Mock CallsManagerRequester mockCallsManagerRequester;
+    @Mock RequestFocusCallback mockRequestFocusCallback;
+
+    @Mock ConnectionServiceFocus mNewConnectionService;
+    @Mock ConnectionServiceFocus mActiveConnectionService;
+
+    private static final int CHECK_HANDLER_INTERVAL_MS = 10;
+
+    private ConnectionServiceFocusManager mFocusManagerUT;
+    private CallFocus mNewCall;
+    private CallFocus mActiveCall;
+    private CallsManager.CallsManagerListener mCallsManagerListener;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mFocusManagerUT = new ConnectionServiceFocusManager(
+                mockCallsManagerRequester, Looper.getMainLooper());
+        mNewCall = createFakeCall(mNewConnectionService, CallState.NEW);
+        mActiveCall = createFakeCall(mActiveConnectionService, CallState.ACTIVE);
+        ArgumentCaptor<CallsManager.CallsManagerListener> captor =
+                ArgumentCaptor.forClass(CallsManager.CallsManagerListener.class);
+        verify(mockCallsManagerRequester).setCallsManagerListener(captor.capture());
+        mCallsManagerListener = captor.getValue();
+    }
+
+    @SmallTest
+    @Test
+    public void testRequestFocusWithoutActiveFocusExisted() {
+        // GIVEN the ConnectionServiceFocusManager without focus ConnectionService.
+
+        // WHEN request calling focus for the given call.
+        requestFocus(mNewCall, mockRequestFocusCallback);
+
+        // THEN the request is done and the ConnectionService of the given call has gain the focus.
+        verifyRequestFocusDone(mFocusManagerUT, mNewCall, mockRequestFocusCallback, true);
+    }
+
+    @SmallTest
+    @Test
+    public void testRequestFocusWithActiveFocusExisted() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService.
+        requestFocus(mActiveCall, null);
+        ConnectionServiceFocusListener connSvrFocusListener =
+                getConnectionServiceFocusListener(mActiveConnectionService);
+
+        // WHEN request calling focus for the given call.
+        requestFocus(mNewCall, mockRequestFocusCallback);
+
+        // THEN the current focus ConnectionService is informed it has lose the focus.
+        verify(mActiveConnectionService).connectionServiceFocusLost();
+        // and the focus request is not done.
+        verify(mockRequestFocusCallback, never())
+                .onRequestFocusDone(any(CallFocus.class));
+
+        // WHEN the current focus released the call resource.
+        connSvrFocusListener.onConnectionServiceReleased(mActiveConnectionService);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the request is done and the ConnectionService of the given call has gain the focus.
+        verifyRequestFocusDone(mFocusManagerUT, mNewCall, mockRequestFocusCallback, true);
+
+        // and the timeout event of the focus released is canceled.
+        waitForHandlerActionDelayed(
+                mFocusManagerUT.getHandler(),
+                mFocusManagerUT.RELEASE_FOCUS_TIMEOUT_MS,
+                CHECK_HANDLER_INTERVAL_MS);
+        verify(mockCallsManagerRequester, never()).releaseConnectionService(
+                any(ConnectionServiceFocus.class));
+    }
+
+    @SmallTest
+    @Test
+    public void testRequestConnectionServiceSameAsFocusConnectionService() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService.
+        requestFocus(mActiveCall, null);
+        reset(mActiveConnectionService);
+
+        // WHEN request calling focus for the given call that has the same ConnectionService as the
+        // active call.
+        when(mNewCall.getConnectionServiceWrapper()).thenReturn(mActiveConnectionService);
+        requestFocus(mNewCall, mockRequestFocusCallback);
+
+        // THEN the request is done without any change on the focus ConnectionService.
+        verify(mNewConnectionService, never()).connectionServiceFocusLost();
+        verifyRequestFocusDone(mFocusManagerUT, mNewCall, mockRequestFocusCallback, false);
+    }
+
+    @SmallTest
+    @Test
+    public void testFocusConnectionServiceDoesNotRespondToFocusLost() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService.
+        requestFocus(mActiveCall, null);
+
+        // WHEN request calling focus for the given call.
+        requestFocus(mNewCall, mockRequestFocusCallback);
+
+        // THEN the current focus ConnectionService is informed it has lose the focus.
+        verify(mActiveConnectionService).connectionServiceFocusLost();
+        // and the focus request is not done.
+        verify(mockRequestFocusCallback, never())
+                .onRequestFocusDone(any(CallFocus.class));
+
+        // but the current focus ConnectionService didn't respond to the focus lost.
+        waitForHandlerActionDelayed(
+                mFocusManagerUT.getHandler(),
+                CHECK_HANDLER_INTERVAL_MS,
+                mFocusManagerUT.RELEASE_FOCUS_TIMEOUT_MS + 100);
+
+        // THEN the focusManager sends a request to disconnect the focus ConnectionService
+        verify(mockCallsManagerRequester).releaseConnectionService(mActiveConnectionService);
+        // THEN the request is done and the ConnectionService of the given call has gain the focus.
+        verifyRequestFocusDone(mFocusManagerUT, mNewCall, mockRequestFocusCallback, true);
+    }
+
+    @SmallTest
+    @Test
+    public void testNonFocusConnectionServiceReleased() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService
+        requestFocus(mActiveCall, null);
+        ConnectionServiceFocusListener connSvrFocusListener =
+                getConnectionServiceFocusListener(mActiveConnectionService);
+
+        // WHEN there is a released request for a non focus ConnectionService.
+        connSvrFocusListener.onConnectionServiceReleased(mNewConnectionService);
+
+        // THEN nothing changed.
+        assertEquals(mActiveCall, mFocusManagerUT.getCurrentFocusCall());
+        assertEquals(mActiveConnectionService, mFocusManagerUT.getCurrentFocusConnectionService());
+    }
+
+    @SmallTest
+    @Test
+    public void testFocusConnectionServiceReleased() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService
+        requestFocus(mActiveCall, null);
+        ConnectionServiceFocusListener connSvrFocusListener =
+                getConnectionServiceFocusListener(mActiveConnectionService);
+
+        // WHEN the focus ConnectionService request to release.
+        connSvrFocusListener.onConnectionServiceReleased(mActiveConnectionService);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN both focus call and ConnectionService are null.
+        assertNull(mFocusManagerUT.getCurrentFocusCall());
+        assertNull(mFocusManagerUT.getCurrentFocusConnectionService());
+    }
+
+    @SmallTest
+    @Test
+    public void testCallStateChangedAffectCallFocus() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService.
+        CallFocus activeCall = createFakeCall(mActiveConnectionService, CallState.ACTIVE);
+        CallFocus newActivateCall = createFakeCall(mActiveConnectionService, CallState.ACTIVE);
+        requestFocus(activeCall, null);
+
+        // WHEN hold the active call.
+        int previousState = activeCall.getState();
+        when(activeCall.getState()).thenReturn(CallState.ON_HOLD);
+        mCallsManagerListener.onCallStateChanged(
+                (Call) activeCall, previousState, activeCall.getState());
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the focus call is null
+        assertNull(mFocusManagerUT.getCurrentFocusCall());
+        // and the focus ConnectionService is not changed.
+        assertEquals(mActiveConnectionService, mFocusManagerUT.getCurrentFocusConnectionService());
+
+        // WHEN a new active call is added.
+        when(newActivateCall.getState()).thenReturn(CallState.ACTIVE);
+        mCallsManagerListener.onCallAdded((Call) newActivateCall);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the focus call changed as excepted.
+        assertEquals(newActivateCall, mFocusManagerUT.getCurrentFocusCall());
+    }
+
+    @SmallTest
+    @Test
+    public void testCallStateChangedDoesNotAffectCallFocusIfConnectionServiceIsDifferent() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService
+        requestFocus(mActiveCall, null);
+
+        // WHEN a new active call is added (actually this should not happen).
+        when(mNewCall.getState()).thenReturn(CallState.ACTIVE);
+        mCallsManagerListener.onCallAdded((Call) mNewCall);
+
+        // THEN the call focus isn't changed.
+        assertEquals(mActiveCall, mFocusManagerUT.getCurrentFocusCall());
+
+        // WHEN the hold the active call.
+        when(mActiveCall.getState()).thenReturn(CallState.ON_HOLD);
+        mCallsManagerListener.onCallStateChanged(
+                (Call) mActiveCall, CallState.ACTIVE, CallState.ON_HOLD);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the focus call is null.
+        assertNull(mFocusManagerUT.getCurrentFocusCall());
+    }
+
+    @SmallTest
+    @Test
+    public void testFocusCallIsNullWhenRemoveTheFocusCall() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService
+        requestFocus(mActiveCall, null);
+        assertEquals(mActiveCall, mFocusManagerUT.getCurrentFocusCall());
+
+        // WHEN remove the active call
+        mCallsManagerListener.onCallRemoved((Call) mActiveCall);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the focus call is null
+        assertNull(mFocusManagerUT.getCurrentFocusCall());
+    }
+
+    @SmallTest
+    @Test
+    public void testConnectionServiceFocusDeath() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService
+        requestFocus(mActiveCall, null);
+        ConnectionServiceFocusListener connSvrFocusListener =
+                getConnectionServiceFocusListener(mActiveConnectionService);
+
+        // WHEN the active connection service focus is death
+        connSvrFocusListener.onConnectionServiceDeath(mActiveConnectionService);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN both connection service focus and call focus are null.
+        assertNull(mFocusManagerUT.getCurrentFocusConnectionService());
+        assertNull(mFocusManagerUT.getCurrentFocusCall());
+    }
+
+    @SmallTest
+    @Test
+    public void testNonExternalCallChangedToExternalCall() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService.
+        requestFocus(mActiveCall, null);
+        assertTrue(mFocusManagerUT.getAllCall().contains(mActiveCall));
+
+        // WHEN the non-external call changed to external call
+        mCallsManagerListener.onExternalCallChanged((Call) mActiveCall, true);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the call should be removed as it's an external call now.
+        assertFalse(mFocusManagerUT.getAllCall().contains(mActiveCall));
+    }
+
+    @SmallTest
+    @Test
+    public void testExternalCallChangedToNonExternalCall() {
+        // GIVEN the ConnectionServiceFocusManager without focus ConnectionService
+
+        // WHEN an external call changed to external call
+        mCallsManagerListener.onExternalCallChanged((Call) mActiveCall, false);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+
+        // THEN the call should be added as it's a non-external call now.
+        assertTrue(mFocusManagerUT.getAllCall().contains(mActiveCall));
+    }
+
+    @SmallTest
+    @Test
+    public void testNonFocusableDoesntChangeFocus() {
+        // GIVEN the ConnectionServiceFocusManager with the focus ConnectionService
+        requestFocus(mActiveCall, null);
+
+        // WHEN a new non-focusable call is added.
+        when(mNewCall.isFocusable()).thenReturn(false);
+        mCallsManagerListener.onCallAdded((Call) mNewCall);
+
+        // THEN the call focus isn't changed.
+        assertEquals(mActiveCall, mFocusManagerUT.getCurrentFocusCall());
+    }
+
+    private void requestFocus(CallFocus call, RequestFocusCallback callback) {
+        mCallsManagerListener.onCallAdded((Call) call);
+        mFocusManagerUT.requestFocus(call, callback);
+        waitForHandlerAction(mFocusManagerUT.getHandler(), CHECK_HANDLER_INTERVAL_MS);
+    }
+
+    private static void verifyRequestFocusDone(
+            ConnectionServiceFocusManager focusManager,
+            CallFocus call,
+            RequestFocusCallback callback,
+            boolean isConnectionServiceFocusChanged) {
+        verify(callback).onRequestFocusDone(call);
+        verify(call.getConnectionServiceWrapper(), times(isConnectionServiceFocusChanged ? 1 : 0))
+                .connectionServiceFocusGained();
+        assertEquals(
+                call.getConnectionServiceWrapper(),
+                focusManager.getCurrentFocusConnectionService());
+    }
+
+    /**
+     * Returns the {@link ConnectionServiceFocusListener} of the ConnectionServiceFocusManager.
+     * Make sure the given parameter {@code ConnectionServiceFocus} is a mock object and
+     * {@link ConnectionServiceFocus#setConnectionServiceFocusListener(
+     * ConnectionServiceFocusListener)} is called.
+     */
+    private static ConnectionServiceFocusListener getConnectionServiceFocusListener(
+            ConnectionServiceFocus connSvrFocus) {
+        ArgumentCaptor<ConnectionServiceFocusListener> captor =
+                ArgumentCaptor.forClass(ConnectionServiceFocusListener.class);
+        verify(connSvrFocus).setConnectionServiceFocusListener(captor.capture());
+        return captor.getValue();
+    }
+
+    private static Call createFakeCall(ConnectionServiceFocus connSvr, int state) {
+        Call call = Mockito.mock(Call.class);
+        when(call.getConnectionServiceWrapper()).thenReturn(connSvr);
+        when(call.getState()).thenReturn(state);
+        when(call.isFocusable()).thenReturn(true);
+        return call;
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
index 960a170..81263ae 100644
--- a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
@@ -21,15 +21,21 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.support.test.InstrumentationRegistry;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.ContactsAsyncHelper;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyObject;
@@ -40,6 +46,7 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
+@RunWith(JUnit4.class)
 public class ContactsAsyncHelperTest extends TelecomTestCase {
     private static final Uri SAMPLE_CONTACT_PHOTO_URI = Uri.parse(
             "android.resource://com.android.server.telecom.tests/"
@@ -82,12 +89,14 @@
             };
 
     @Override
+    @Before
     public void setUp() throws Exception {
-        mContext = getTestContext();
         super.setUp();
+        mContext = InstrumentationRegistry.getTargetContext();
     }
 
     @SmallTest
+    @Test
     public void testEmptyUri() throws Exception {
         ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
         try {
@@ -101,6 +110,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNullReturnFromOpenInputStream() {
         ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
         cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI, mListener, COOKIE);
@@ -110,6 +120,7 @@
     }
 
     @SmallTest
+    @Test
     public void testImageScaling() {
         ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter);
         cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI, mListener, COOKIE);
@@ -129,6 +140,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNoScaling() {
         ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter);
         cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI_SMALL,
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index 350afa3..48319ad 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -16,11 +16,12 @@
 
 package com.android.server.telecom.tests;
 
+import static com.android.server.telecom.tests.TelecomSystemTest.TEST_TIMEOUT;
+
 import android.content.ComponentName;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Debug;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -28,29 +29,41 @@
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallIdMapper;
+import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ConnectionServiceRepository;
 import com.android.server.telecom.ConnectionServiceWrapper;
 import com.android.server.telecom.CreateConnectionProcessor;
 import com.android.server.telecom.CreateConnectionResponse;
 import com.android.server.telecom.PhoneAccountRegistrar;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 /**
  * Unit testing for CreateConnectionProcessor as well as CreateConnectionTimeout classes.
  */
+@RunWith(JUnit4.class)
 public class CreateConnectionProcessorTest extends TelecomTestCase {
 
     private static final String TEST_PACKAGE = "com.android.server.telecom.tests";
@@ -65,27 +78,48 @@
     CreateConnectionResponse mMockCreateConnectionResponse;
     @Mock
     Call mMockCall;
+    @Mock
+    ConnectionServiceFocusManager mConnectionServiceFocusManager;
 
     CreateConnectionProcessor mTestCreateConnectionProcessor;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
 
+        when(mMockCall.getConnectionServiceFocusManager()).thenReturn(
+                mConnectionServiceFocusManager);
+        doAnswer(new Answer<Void>() {
+                     @Override
+                     public Void answer(InvocationOnMock invocation) {
+                         Object[] args = invocation.getArguments();
+                         ConnectionServiceFocusManager.CallFocus focus =
+                                 (ConnectionServiceFocusManager.CallFocus) args[0];
+                         ConnectionServiceFocusManager.RequestFocusCallback callback =
+                                 (ConnectionServiceFocusManager.RequestFocusCallback) args[1];
+                         callback.onRequestFocusDone(focus);
+                         return null;
+                     }
+                 }
+                ).when(mConnectionServiceFocusManager).requestFocus(any(), any());
+
         mTestCreateConnectionProcessor = new CreateConnectionProcessor(mMockCall,
                 mMockConnectionServiceRepository, mMockCreateConnectionResponse,
                 mMockAccountRegistrar, mContext);
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         mTestCreateConnectionProcessor = null;
         super.tearDown();
     }
 
     @SmallTest
+    @Test
     public void testSimPhoneAccountSuccess() throws Exception {
         PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
         when(mMockCall.isEmergencyCall()).thenReturn(false);
@@ -106,6 +140,7 @@
     }
 
     @SmallTest
+    @Test
     public void testbadPhoneAccount() throws Exception {
         PhoneAccountHandle pAHandle = null;
         when(mMockCall.isEmergencyCall()).thenReturn(false);
@@ -124,6 +159,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConnectionManagerSuccess() throws Exception {
         PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
         when(mMockCall.isEmergencyCall()).thenReturn(false);
@@ -141,7 +177,8 @@
         verify(mMockCall).setConnectionManagerPhoneAccount(eq(callManagerPAHandle));
         verify(mMockCall).setTargetPhoneAccount(eq(pAHandle));
         verify(mMockCall).setConnectionService(eq(service));
-        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        verify(service).createConnection(eq(mMockCall),
+                any(CreateConnectionResponse.class));
         // Notify successful connection to call
         CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
         mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
@@ -149,6 +186,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConnectionManagerFailedFallToSim() throws Exception {
         PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
         when(mMockCall.isEmergencyCall()).thenReturn(false);
@@ -166,6 +204,8 @@
         reset(mMockCall);
         reset(service);
 
+        when(mMockCall.getConnectionServiceFocusManager()).thenReturn(
+                mConnectionServiceFocusManager);
         // Notify that the ConnectionManager has denied the call.
         when(mMockCall.getConnectionManagerPhoneAccount()).thenReturn(callManagerPAHandle);
         when(mMockCall.getConnectionService()).thenReturn(service);
@@ -184,6 +224,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConnectionManagerFailedDoNotFallToSim() throws Exception {
         PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
         when(mMockCall.isEmergencyCall()).thenReturn(false);
@@ -201,6 +242,8 @@
         reset(mMockCall);
         reset(service);
 
+        when(mMockCall.getConnectionServiceFocusManager()).thenReturn(
+                mConnectionServiceFocusManager);
         // Notify that the ConnectionManager has rejected the call.
         when(mMockCall.getConnectionManagerPhoneAccount()).thenReturn(callManagerPAHandle);
         when(mMockCall.getConnectionService()).thenReturn(service);
@@ -214,6 +257,7 @@
     }
 
     @SmallTest
+    @Test
     public void testEmergencyCallToSim() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
         // Put in a regular phone account to be sure it doesn't call that
@@ -237,6 +281,7 @@
     }
 
     @SmallTest
+    @Test
     public void testEmergencyCallSimFailToConnectionManager() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
         when(mMockCall.getHandle()).thenReturn(Uri.parse(""));
@@ -254,6 +299,9 @@
         mTestCreateConnectionProcessor.process();
         reset(mMockCall);
         reset(service);
+
+        when(mMockCall.getConnectionServiceFocusManager()).thenReturn(
+                mConnectionServiceFocusManager);
         when(mMockCall.isEmergencyCall()).thenReturn(true);
 
         // When Notify SIM connection fails, fall back to connection manager
diff --git a/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java b/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
index 82fee3e..94e6631 100644
--- a/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
+++ b/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
@@ -29,9 +29,14 @@
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
@@ -39,6 +44,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class DefaultDialerCacheTest extends TelecomTestCase {
 
     private static final String DIALER1 = "com.android.dialer";
@@ -55,6 +61,8 @@
 
     @Mock private DefaultDialerCache.DefaultDialerManagerAdapter mMockDefaultDialerManager;
 
+    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -88,6 +96,7 @@
     }
 
     @SmallTest
+    @Test
     public void testThreeUsers() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
@@ -107,6 +116,7 @@
     }
 
     @SmallTest
+    @Test
     public void testDialer1PackageChanged() {
         // Populate the caches first
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
@@ -129,6 +139,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRandomOtherPackageChanged() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
@@ -146,6 +157,7 @@
     }
 
     @SmallTest
+    @Test
     public void testUserRemoved() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
@@ -164,6 +176,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPackageRemovedWithoutReplace() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
@@ -183,6 +196,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPackageAdded() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
@@ -201,6 +215,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPackageRemovedWithReplace() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
@@ -220,6 +235,7 @@
     }
 
     @SmallTest
+    @Test
     public void testDefaultDialerSettingChanged() {
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
         assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
diff --git a/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java b/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java
index 2ebb6fc..af4c168 100644
--- a/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java
@@ -26,6 +26,10 @@
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
@@ -33,6 +37,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class DirectToVoicemailCallFilterTest extends TelecomTestCase {
     @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper;
     @Mock private CallFilterResultCallback mCallback;
@@ -40,11 +45,14 @@
 
     private static final Uri TEST_HANDLE = Uri.parse("tel:1235551234");
 
+    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
     }
 
     @SmallTest
+    @Test
     public void testSendToVoicemail() {
         CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyLookupStart();
 
@@ -62,6 +70,7 @@
     }
 
     @SmallTest
+    @Test
     public void testDontSendToVoicemail() {
         CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyLookupStart();
 
@@ -79,6 +88,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNullResponseFromLookupHelper() {
         CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyLookupStart(null);
 
diff --git a/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java
index 223cb58..24dd18f 100644
--- a/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java
+++ b/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java
@@ -15,7 +15,6 @@
 
 package com.android.server.telecom.tests;
 
-import android.media.AudioManager;
 import android.media.ToneGenerator;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -23,6 +22,10 @@
 import com.android.server.telecom.DtmfLocalTonePlayer;
 import com.android.server.telecom.R;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
 import static org.mockito.Matchers.anyInt;
@@ -31,6 +34,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class DtmfLocalTonePlayerTest extends TelecomTestCase {
     private static final int TIMEOUT = 2000;
     @Mock DtmfLocalTonePlayer.ToneGeneratorProxy mToneProxy;
@@ -38,6 +42,7 @@
 
     DtmfLocalTonePlayer mPlayer;
 
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -46,6 +51,7 @@
     }
 
     @SmallTest
+    @Test
     public void testSupportedStart() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(true);
         when(mToneProxy.isPresent()).thenReturn(true);
@@ -55,6 +61,7 @@
     }
 
     @SmallTest
+    @Test
     public void testUnsupportedStart() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(false);
         when(mToneProxy.isPresent()).thenReturn(true);
@@ -64,6 +71,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPlayToneWhenUninitialized() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(false);
         when(mToneProxy.isPresent()).thenReturn(false);
@@ -74,6 +82,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPlayToneWhenInitialized() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(true);
         when(mToneProxy.isPresent()).thenReturn(true);
@@ -84,6 +93,7 @@
     }
 
     @SmallTest
+    @Test
     public void testStopToneWhenUninitialized() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(false);
         when(mToneProxy.isPresent()).thenReturn(false);
@@ -94,6 +104,7 @@
     }
 
     @SmallTest
+    @Test
     public void testStopToneWhenInitialized() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(true);
         when(mToneProxy.isPresent()).thenReturn(true);
@@ -104,6 +115,7 @@
     }
 
     @SmallTest
+    @Test
     public void testProperTeardown() {
         when(mContext.getResources().getBoolean(R.bool.allow_local_dtmf_tones)).thenReturn(true);
         when(mToneProxy.isPresent()).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/EventManagerTest.java b/tests/src/com/android/server/telecom/tests/EventManagerTest.java
index bb07306..24394ec 100644
--- a/tests/src/com/android/server/telecom/tests/EventManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/EventManagerTest.java
@@ -19,14 +19,25 @@
 import android.telecom.Logging.EventManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.stream.Collectors;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
 /**
  * Unit tests for android.telecom.Logging.EventManager.
  */
-
+@RunWith(JUnit4.class)
 public class EventManagerTest extends TelecomTestCase {
 
     private EventManager mTestEventManager;
@@ -60,6 +71,7 @@
     }
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mTestEventManager = new EventManager(() -> "");
@@ -67,6 +79,7 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         mTestEventManager = null;
         mAddedEventRecord = null;
@@ -78,6 +91,7 @@
      * the eventRecordAdded callback is working.
      */
     @SmallTest
+    @Test
     public void testAddEventRecord() throws Exception {
         TestRecord testRecord = new TestRecord("testId", "testDescription");
         mTestEventManager.event(testRecord, TEST_EVENT, null);
@@ -94,6 +108,7 @@
      * the oldest entry is dropped.
      */
     @SmallTest
+    @Test
     public void testAddEventRecordOverflowMaxEvents() throws Exception {
         TestRecord oldestRecordEntry = new TestRecord("id0", "desc0");
         // Add the oldest record separately so that we can verify it is dropped later
@@ -119,6 +134,7 @@
      * If the queue is resized to be smaller, the oldest records are dropped.
      */
     @SmallTest
+    @Test
     public void testChangeQueueSize() throws Exception {
         TestRecord oldestRecordEntry = new TestRecord("id0", "desc0");
         // Add the oldest record separately so that we can verify it is dropped later
@@ -151,6 +167,7 @@
      * timing response is correct.
      */
     @SmallTest
+    @Test
     public void testExtractEventTimings() throws Exception {
         TestRecord testRecord = new TestRecord("testId", "testDesc");
         // Add unassociated event
@@ -179,6 +196,7 @@
      * Verify that adding events to different records does not create a valid TimedEventPair
      */
     @SmallTest
+    @Test
     public void testExtractEventTimingsDifferentRecords() throws Exception {
         TestRecord testRecord = new TestRecord("testId", "testDesc");
         TestRecord testRecord2 = new TestRecord("testId2", "testDesc2");
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index 0c6d3c7..0671a4e 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -54,6 +54,11 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -63,6 +68,8 @@
 import java.util.Collections;
 import java.util.LinkedList;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -76,6 +83,7 @@
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 
+@RunWith(JUnit4.class)
 public class InCallControllerTests extends TelecomTestCase {
     @Mock CallsManager mMockCallsManager;
     @Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar;
@@ -102,6 +110,7 @@
     private EmergencyCallHelper mEmergencyCallHelper;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
@@ -118,11 +127,13 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         super.tearDown();
     }
 
     @MediumTest
+    @Test
     public void testBindToService_NoServicesFound_IncomingCall() throws Exception {
         when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
@@ -150,6 +161,7 @@
     }
 
     @MediumTest
+    @Test
     public void testBindToService_NoServicesFound_OutgoingCall() throws Exception {
         Bundle callExtras = new Bundle();
         callExtras.putBoolean("whatever", true);
@@ -186,6 +198,7 @@
     }
 
     @MediumTest
+    @Test
     public void testBindToService_DefaultDialer_NoEmergency() throws Exception {
         Bundle callExtras = new Bundle();
         callExtras.putBoolean("whatever", true);
@@ -236,6 +249,7 @@
     }
 
     @MediumTest
+    @Test
     public void testBindToService_SystemDialer_Emergency() throws Exception {
         Bundle callExtras = new Bundle();
         callExtras.putBoolean("whatever", true);
@@ -303,6 +317,7 @@
     }
 
     @MediumTest
+    @Test
     public void testBindToService_DefaultDialer_FallBackToSystem() throws Exception {
         Bundle callExtras = new Bundle();
         callExtras.putBoolean("whatever", true);
@@ -391,6 +406,7 @@
      * supports external calls.
      */
     @MediumTest
+    @Test
     public void testBindToService_IncludeExternal() throws Exception {
         setupMocks(true /* isExternalCall */);
         setupMockPackageManager(true /* default */, true /* system */, true /* external calls */);
@@ -427,6 +443,7 @@
      * call gets connected soon after, the new call will still be sent to the in-call service.
      */
     @MediumTest
+    @Test
     public void testUnbindDueToCallDisconnect() throws Exception {
         when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
diff --git a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
index 7635427..69fcdd8 100644
--- a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
@@ -25,7 +25,6 @@
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
-import android.telecom.AudioState;
 import android.telecom.CallAudioState;
 import android.telecom.ParcelableCall;
 
@@ -49,7 +48,8 @@
     public boolean mShowDialpad;
     public boolean mCanAddCall;
     public boolean mSilenceRinger;
-    public CountDownLatch mLock = new CountDownLatch(1);
+    public CountDownLatch mUpdateCallLock = new CountDownLatch(1);
+    public CountDownLatch mAddCallLock = new CountDownLatch(1);
 
     public class FakeInCallService extends IInCallService.Stub {
         @Override
@@ -68,8 +68,9 @@
             if (mCallById.containsKey(call.getId())) {
                 throw new RuntimeException("Call " + call.getId() + " already added");
             }
-            mCallById.put(call.getId(), call);
             mLatestCallId = call.getId();
+            mCallById.put(call.getId(), call);
+            mAddCallLock.countDown();
         }
 
         @Override
@@ -77,9 +78,9 @@
             if (!mCallById.containsKey(call.getId())) {
                 throw new RuntimeException("Call " + call.getId() + " not added yet");
             }
-            mCallById.put(call.getId(), call);
             mLatestCallId = call.getId();
-            mLock.countDown();
+            mCallById.put(call.getId(), call);
+            mUpdateCallLock.countDown();
         }
 
         @Override
@@ -137,6 +138,12 @@
         public IInterface queryLocalInterface(String descriptor) {
             return this;
         }
+
+        @Override
+        public void onHandoverFailed(String callId, int error) {}
+
+        @Override
+        public void onHandoverComplete(String callId) {}
     }
 
     private IInCallService.Stub mInCallServiceFake = new FakeInCallService();
@@ -159,10 +166,23 @@
 
     public void waitForUpdate() {
         try {
-            mLock.await(5000, TimeUnit.MILLISECONDS);
+            mUpdateCallLock.await(5000, TimeUnit.MILLISECONDS);
         } catch (InterruptedException ie) {
             return;
         }
-        mLock = new CountDownLatch(1);
+        mUpdateCallLock = new CountDownLatch(1);
+    }
+
+    public void waitUntilNumCalls(int numCalls) {
+        if (mCallById.size() == numCalls) {
+            return;
+        }
+        mAddCallLock = new CountDownLatch(1);
+
+        try {
+            mAddCallLock.await(5000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException ie) {
+            return;
+        }
     }
 }
diff --git a/tests/src/com/android/server/telecom/tests/InCallWakeLockControllerTest.java b/tests/src/com/android/server/telecom/tests/InCallWakeLockControllerTest.java
index 3a8c2d9..fd581f3 100644
--- a/tests/src/com/android/server/telecom/tests/InCallWakeLockControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/InCallWakeLockControllerTest.java
@@ -30,8 +30,14 @@
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.TelecomWakeLock;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
+@RunWith(JUnit4.class)
 public class InCallWakeLockControllerTest extends TelecomTestCase {
 
     @Mock CallsManager mCallsManager;
@@ -40,6 +46,7 @@
     private InCallWakeLockController mInCallWakeLockController;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         TelecomWakeLock telecomWakeLock = new TelecomWakeLock(
@@ -50,12 +57,14 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         mInCallWakeLockController = null;
         super.tearDown();
     }
 
     @SmallTest
+    @Test
     public void testRingingCallAdded() throws Exception {
         when(mCallsManager.getRingingCall()).thenReturn(mCall);
         when(mWakeLockAdapter.isHeld()).thenReturn(false);
@@ -66,6 +75,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNonRingingCallAdded() throws Exception {
         when(mCallsManager.getRingingCall()).thenReturn(null);
         when(mWakeLockAdapter.isHeld()).thenReturn(false);
@@ -76,6 +86,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRingingCallTransition() throws Exception {
         when(mCallsManager.getRingingCall()).thenReturn(mCall);
         when(mWakeLockAdapter.isHeld()).thenReturn(false);
@@ -86,6 +97,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRingingCallRemoved() throws Exception {
         when(mCallsManager.getRingingCall()).thenReturn(null);
         when(mWakeLockAdapter.isHeld()).thenReturn(false);
@@ -96,6 +108,7 @@
     }
 
     @SmallTest
+    @Test
     public void testWakeLockReleased() throws Exception {
         when(mCallsManager.getRingingCall()).thenReturn(null);
         when(mWakeLockAdapter.isHeld()).thenReturn(true);
@@ -106,6 +119,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAcquireWakeLockWhenHeld() throws Exception {
         when(mCallsManager.getRingingCall()).thenReturn(mCall);
         when(mWakeLockAdapter.isHeld()).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java
index 6861ad5..d975ec2 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java
@@ -28,12 +28,17 @@
 import com.android.server.telecom.callfiltering.IncomingCallFilter;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atMost;
@@ -41,6 +46,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class IncomingCallFilterTest extends TelecomTestCase {
     @Mock private CallFilterResultCallback mResultCallback;
     @Mock private Call mCall;
@@ -82,6 +88,8 @@
 
     private static final CallFilteringResult DEFAULT_RESULT = RESULT1;
 
+    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -90,6 +98,7 @@
     }
 
     @SmallTest
+    @Test
     public void testSingleFilter() {
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
                 mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
@@ -102,6 +111,7 @@
     }
 
     @SmallTest
+    @Test
     public void testMultipleFilters() {
         List<IncomingCallFilter.CallFilter> filters =
                 new ArrayList<IncomingCallFilter.CallFilter>() {{
@@ -130,6 +140,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFilterTimeout() throws Exception {
         setTimeoutLength(SHORT_TIMEOUT);
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
@@ -145,6 +156,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFilterTimeoutDoesntTrip() throws Exception {
         setTimeoutLength(SHORT_TIMEOUT);
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
@@ -158,6 +170,7 @@
     }
 
     @SmallTest
+    @Test
     public void testToString() {
         assertEquals("[Allow, logged, notified]", RESULT1.toString());
         assertEquals("[Reject, notified]", RESULT2.toString());
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
index 1909259..6419bba 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
@@ -28,6 +28,10 @@
 import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
 import static org.mockito.Matchers.any;
@@ -41,6 +45,7 @@
 /**
  * Tests for the {@link com.android.server.telecom.ui.IncomingCallNotifier} class.
  */
+@RunWith(JUnit4.class)
 public class IncomingCallNotifierTest extends TelecomTestCase {
 
     @Mock private IncomingCallNotifier.CallsManagerProxy mCallsManagerProxy;
@@ -50,6 +55,8 @@
     private IncomingCallNotifier mIncomingCallNotifier;
     private NotificationManager mNotificationManager;
 
+    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -78,6 +85,7 @@
      * Add a call that isn't ringing.
      */
     @SmallTest
+    @Test
     public void testSingleCall() {
         mIncomingCallNotifier.onCallAdded(mAudioCall);
         verify(mNotificationManager, never()).notify(eq(IncomingCallNotifier.NOTIFICATION_TAG),
@@ -88,6 +96,7 @@
      * Add a ringing call when there is no other ongoing call.
      */
     @SmallTest
+    @Test
     public void testIncomingDuringOngoingCall() {
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(false);
         mIncomingCallNotifier.onCallAdded(mRingingCall);
@@ -99,6 +108,7 @@
      * Add a ringing call with another call ongoing, not from a different phone account.
      */
     @SmallTest
+    @Test
     public void testIncomingDuringOngoingCall2() {
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(false);
         when(mCallsManagerProxy.getNumCallsForOtherPhoneAccount(any())).thenReturn(0);
@@ -114,6 +124,7 @@
      * Remove ringing call with another call ongoing.
      */
     @SmallTest
+    @Test
     public void testCallRemoved() {
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(true);
         when(mCallsManagerProxy.getNumCallsForOtherPhoneAccount(any())).thenReturn(1);
@@ -132,6 +143,7 @@
      * Ensure notification doesn't show during handover.
      */
     @SmallTest
+    @Test
     public void testDontShowDuringHandover1() {
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(true);
         when(mCallsManagerProxy.getNumCallsForOtherPhoneAccount(any())).thenReturn(1);
@@ -150,6 +162,7 @@
      * Ensure notification doesn't show during handover.
      */
     @SmallTest
+    @Test
     public void testDontShowDuringHandover2() {
         when(mCallsManagerProxy.hasCallsForOtherPhoneAccount(any())).thenReturn(true);
         when(mCallsManagerProxy.getNumCallsForOtherPhoneAccount(any())).thenReturn(1);
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
index 950cb2c..a3bdb10 100644
--- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -51,6 +51,11 @@
 import com.android.server.telecom.ui.MissedCallNotifierImpl;
 import com.android.server.telecom.ui.MissedCallNotifierImpl.NotificationBuilderFactory;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -60,6 +65,8 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
@@ -74,6 +81,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class MissedCallNotifierImplTest extends TelecomTestCase {
 
     private static class MockMissedCallCursorBuilder {
@@ -149,6 +157,7 @@
     @Mock private DefaultDialerCache mDefaultDialerCache;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
@@ -174,17 +183,20 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         TelecomSystem.setInstance(null);
         when(mTelecomSystem.isBootComplete()).thenReturn(false);
     }
 
     @SmallTest
+    @Test
     public void testCancelNotificationInPrimaryUser() {
         cancelNotificationTestInternal(PRIMARY_USER);
     }
 
     @SmallTest
+    @Test
     public void testCancelNotificationInSecondaryUser() {
         cancelNotificationTestInternal(SECONARY_USER);
     }
@@ -217,6 +229,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNotifyMultipleMissedCalls() {
         Notification.Builder[] builders = new Notification.Builder[4];
 
@@ -280,18 +293,21 @@
     }
 
     @SmallTest
+    @Test
     public void testNotifySingleCallInPrimaryUser() {
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
         notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
     }
 
     @SmallTest
+    @Test
     public void testNotifySingleCallInSecondaryUser() {
         PhoneAccount phoneAccount = makePhoneAccount(SECONARY_USER, NO_CAPABILITY);
         notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
     }
 
     @SmallTest
+    @Test
     public void testNotifySingleCallInSecondaryUserWithMultiUserCapability() {
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER,
                 PhoneAccount.CAPABILITY_MULTI_USER);
@@ -299,12 +315,14 @@
     }
 
     @SmallTest
+    @Test
     public void testNotifySingleCallWhenCurrentUserIsSecondaryUser() {
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
         notifySingleCallTestInternal(phoneAccount, SECONARY_USER);
     }
 
     @SmallTest
+    @Test
     public void testNotifySingleCall() {
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
         notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
@@ -375,6 +393,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNoSmsBackAfterMissedSipCall() {
         Notification.Builder builder1 = makeNotificationBuilder("builder1");
         MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
@@ -410,6 +429,7 @@
     }
 
     @SmallTest
+    @Test
     public void testLoadOneCallFromDb() throws Exception {
         CallerInfoLookupHelper mockCallerInfoLookupHelper = mock(CallerInfoLookupHelper.class);
         MissedCallNotifier.CallInfoFactory mockCallInfoFactory =
@@ -474,6 +494,7 @@
     }
 
     @SmallTest
+    @Test
     public void testLoadTwoCallsFromDb() throws Exception {
         TelecomSystem.setInstance(mTelecomSystem);
         when(mTelecomSystem.isBootComplete()).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index ad262f1..4c0e76a 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -40,9 +40,16 @@
 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -57,6 +64,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class NewOutgoingCallIntentBroadcasterTest extends TelecomTestCase {
     private static class ReceiverIntentPair {
         public BroadcastReceiver receiver;
@@ -74,6 +82,7 @@
     private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapterSpy;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -83,6 +92,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNullHandle() {
         Intent intent = new Intent(Intent.ACTION_CALL, null);
         int result = processIntent(intent, true);
@@ -92,6 +102,7 @@
     }
 
     @SmallTest
+    @Test
     public void testVoicemailCall() {
         String voicemailNumber = "voicemail:18005551234";
         Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(voicemailNumber));
@@ -105,16 +116,19 @@
     }
 
     @SmallTest
+    @Test
     public void testVoicemailCallWithBadAction() {
         badCallActionHelper(Uri.parse("voicemail:18005551234"), DisconnectCause.OUTGOING_CANCELED);
     }
 
     @SmallTest
+    @Test
     public void testTelCallWithBadCallAction() {
         badCallActionHelper(Uri.parse("tel:6505551234"), DisconnectCause.INVALID_NUMBER);
     }
 
     @SmallTest
+    @Test
     public void testSipCallWithBadCallAction() {
         badCallActionHelper(Uri.parse("sip:testuser@testsite.com"), DisconnectCause.INVALID_NUMBER);
     }
@@ -130,6 +144,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAlreadyDisconnectedCall() {
         Uri handle = Uri.parse("tel:6505551234");
         doReturn(true).when(mCall).isDisconnected();
@@ -144,6 +159,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNoNumberSupplied() {
         Uri handle = Uri.parse("tel:");
         Intent intent = new Intent(Intent.ACTION_CALL, handle);
@@ -156,6 +172,7 @@
     }
 
     @SmallTest
+    @Test
     public void testEmergencyCallWithNonDefaultDialer() {
         Uri handle = Uri.parse("tel:6505551911");
         doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
@@ -185,6 +202,7 @@
     }
 
     @SmallTest
+    @Test
     public void testActionCallEmergencyCall() {
         Uri handle = Uri.parse("tel:6505551911");
         Intent intent = buildIntent(handle, Intent.ACTION_CALL, null);
@@ -192,6 +210,7 @@
     }
 
     @SmallTest
+    @Test
     public void testActionEmergencyWithEmergencyNumber() {
         Uri handle = Uri.parse("tel:6505551911");
         Intent intent = buildIntent(handle, Intent.ACTION_CALL_EMERGENCY, null);
@@ -199,6 +218,7 @@
     }
 
     @SmallTest
+    @Test
     public void testActionPrivCallWithEmergencyNumber() {
         Uri handle = Uri.parse("tel:6505551911");
         Intent intent = buildIntent(handle, Intent.ACTION_CALL_PRIVILEGED, null);
@@ -206,6 +226,7 @@
     }
 
     @SmallTest
+    @Test
     public void testEmergencyCallWithGatewayExtras() {
         Uri handle = Uri.parse("tel:6505551911");
         Bundle gatewayExtras = new Bundle();
@@ -218,6 +239,7 @@
     }
 
     @SmallTest
+    @Test
     public void testActionEmergencyWithNonEmergencyNumber() {
         Uri handle = Uri.parse("tel:6505551911");
         doReturn(false).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
@@ -254,6 +276,7 @@
     }
 
     @SmallTest
+    @Test
     public void testUnmodifiedRegularCall() {
         Uri handle = Uri.parse("tel:6505551234");
         Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
@@ -269,6 +292,7 @@
     }
 
     @SmallTest
+    @Test
     public void testUnmodifiedSipCall() {
         Uri handle = Uri.parse("sip:test@test.com");
         Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
@@ -286,6 +310,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCallWithGatewayInfo() {
         Uri handle = Uri.parse("tel:6505551234");
         Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
@@ -305,6 +330,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCallNumberModifiedToNull() {
         Uri handle = Uri.parse("tel:6505551234");
         Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
@@ -320,6 +346,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCallNumberModifiedToNullWithLongCustomTimeout() {
         Uri handle = Uri.parse("tel:6505551234");
         Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
@@ -339,6 +366,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCallModifiedToEmergency() {
         Uri handle = Uri.parse("tel:6505551234");
         Intent callIntent = buildIntent(handle, Intent.ACTION_CALL, null);
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index a98712c..f8acb9d 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -26,6 +26,8 @@
 import android.os.Parcel;
 import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
 import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -39,6 +41,11 @@
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneAccountRegistrar.DefaultPhoneAccountHandle;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -52,12 +59,20 @@
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class PhoneAccountRegistrarTest extends TelecomTestCase {
 
     private static final int MAX_VERSION = Integer.MAX_VALUE;
@@ -69,6 +84,7 @@
     @Mock private PhoneAccountRegistrar.AppLabelProxy mAppLabelProxy;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
@@ -87,6 +103,7 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         mRegistrar = null;
         new File(
@@ -97,6 +114,7 @@
     }
 
     @MediumTest
+    @Test
     public void testPhoneAccountHandle() throws Exception {
         PhoneAccountHandle input = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), "id0");
         PhoneAccountHandle result = roundTripXml(this, input,
@@ -111,6 +129,7 @@
     }
 
     @MediumTest
+    @Test
     public void testPhoneAccount() throws Exception {
         Bundle testBundle = new Bundle();
         testBundle.putInt("EXTRA_INT_1", 1);
@@ -133,9 +152,14 @@
     }
 
     @MediumTest
+    @Test
     public void testDefaultPhoneAccountHandleEmptyGroup() throws Exception {
         DefaultPhoneAccountHandle input = new DefaultPhoneAccountHandle(Process.myUserHandle(),
                 makeQuickAccountHandle("i1"), "");
+        when(UserManager.get(mContext).getSerialNumberForUser(input.userHandle))
+                .thenReturn(0L);
+        when(UserManager.get(mContext).getUserForSerialNumber(0L))
+                .thenReturn(input.userHandle);
         DefaultPhoneAccountHandle result = roundTripXml(this, input,
                 PhoneAccountRegistrar.sDefaultPhoneAcountHandleXml, mContext);
 
@@ -147,6 +171,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testPhoneAccountExtrasEdge() throws Exception {
         Bundle testBundle = new Bundle();
         // Ensure null values for string are not persisted.
@@ -176,6 +201,7 @@
     }
 
     @MediumTest
+    @Test
     public void testState() throws Exception {
         PhoneAccountRegistrar.State input = makeQuickState();
         PhoneAccountRegistrar.State result = roundTripXml(this, input,
@@ -190,6 +216,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAccounts() throws Exception {
         int i = 0;
 
@@ -220,11 +247,13 @@
     }
 
     @MediumTest
+    @Test
     public void testSimCallManager() throws Exception {
         // TODO
     }
 
     @MediumTest
+    @Test
     public void testDefaultOutgoing() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -273,6 +302,7 @@
     }
 
     @MediumTest
+    @Test
     public void testReplacePhoneAccountByGroup() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -317,6 +347,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAddSameDefault() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -361,6 +392,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAddSameGroup() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -406,6 +438,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAddSameGroupButDifferentComponent() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -444,6 +477,7 @@
     }
 
     @MediumTest
+    @Test
     public void testAddSameGroupButDifferentComponent2() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -496,6 +530,7 @@
     }
 
     @MediumTest
+    @Test
     public void testPhoneAccountParceling() throws Exception {
         PhoneAccountHandle handle = makeQuickAccountHandle("foo");
         roundTripPhoneAccount(new PhoneAccount.Builder(handle, null).build());
@@ -520,7 +555,7 @@
                         .setHighlightColor(0xf0f0f0)
                         .setIcon(Icon.createWithBitmap(
                                 BitmapFactory.decodeResource(
-                                        getContext().getResources(),
+                                        InstrumentationRegistry.getContext().getResources(),
                                         R.drawable.stat_sys_phone_call)))
                         .setShortDescription("short description")
                         .setSubscriptionAddress(Uri.parse("tel:2345678"))
@@ -535,6 +570,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testSelfManagedPhoneAccount() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -558,6 +594,7 @@
      * @throws Exception
      */
     @MediumTest
+    @Test
     public void testSelfManagedCapabilityOverride() throws Exception {
         mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                 Mockito.mock(IConnectionService.class));
@@ -579,6 +616,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSortSimFirst() throws Exception {
         ComponentName componentA = new ComponentName("a", "a");
         ComponentName componentB = new ComponentName("b", "b");
@@ -609,6 +647,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSortBySortOrder() throws Exception {
         ComponentName componentA = new ComponentName("a", "a");
         ComponentName componentB = new ComponentName("b", "b");
@@ -648,6 +687,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSortByLabel() throws Exception {
         ComponentName componentA = new ComponentName("a", "a");
         ComponentName componentB = new ComponentName("b", "b");
@@ -685,6 +725,7 @@
     }
 
     @MediumTest
+    @Test
     public void testSortAll() throws Exception {
         ComponentName componentA = new ComponentName("a", "a");
         ComponentName componentB = new ComponentName("b", "b");
@@ -764,6 +805,110 @@
         assertTrue(accounts.get(5).getLabel().toString().equals("b"));
     }
 
+    /**
+     * Tests {@link PhoneAccountRegistrar#getCallCapablePhoneAccounts(String, boolean, UserHandle)}
+     * to ensure disabled accounts are filtered out of results when requested.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testGetByEnabledState() throws Exception {
+        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
+                Mockito.mock(IConnectionService.class));
+        mRegistrar.registerPhoneAccount(makeQuickAccountBuilder("id1", 1)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .build());
+
+        assertEquals(0, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_TEL,
+                false /* includeDisabled */, Process.myUserHandle()).size());
+        assertEquals(1, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_TEL,
+                true /* includeDisabled */, Process.myUserHandle()).size());
+    }
+
+    /**
+     * Tests {@link PhoneAccountRegistrar#getCallCapablePhoneAccounts(String, boolean, UserHandle)}
+     * to ensure scheme filtering operates.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testGetByScheme() throws Exception {
+        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
+                Mockito.mock(IConnectionService.class));
+        registerAndEnableAccount(makeQuickAccountBuilder("id1", 1)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_SIP))
+                .build());
+        registerAndEnableAccount(makeQuickAccountBuilder("id2", 2)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_TEL))
+                .build());
+
+        assertEquals(1, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_SIP,
+                false /* includeDisabled */, Process.myUserHandle()).size());
+        assertEquals(1, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_TEL,
+                false /* includeDisabled */, Process.myUserHandle()).size());
+        assertEquals(2, mRegistrar.getCallCapablePhoneAccounts(null, false /* includeDisabled */,
+                Process.myUserHandle()).size());
+    }
+
+    /**
+     * Tests {@link PhoneAccountRegistrar#getCallCapablePhoneAccounts(String, boolean, UserHandle,
+     * int)} to ensure capability filtering operates.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testGetByCapability() throws Exception {
+        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
+                Mockito.mock(IConnectionService.class));
+        registerAndEnableAccount(makeQuickAccountBuilder("id1", 1)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER
+                        | PhoneAccount.CAPABILITY_VIDEO_CALLING)
+                .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_SIP))
+                .build());
+        registerAndEnableAccount(makeQuickAccountBuilder("id2", 2)
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_SIP))
+                .build());
+
+        assertEquals(1, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_SIP,
+                false /* includeDisabled */, Process.myUserHandle()).size(),
+                PhoneAccount.CAPABILITY_VIDEO_CALLING);
+        assertEquals(2, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_SIP,
+                false /* includeDisabled */, Process.myUserHandle()).size(), 0 /* none extra */);
+        assertEquals(0, mRegistrar.getCallCapablePhoneAccounts(PhoneAccount.SCHEME_SIP,
+                false /* includeDisabled */, Process.myUserHandle()).size(),
+                PhoneAccount.CAPABILITY_RTT);
+    }
+
+    /**
+     * Tests {@link PhoneAccount#equals(Object)} operator.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testPhoneAccountEquality() throws Exception {
+        PhoneAccountHandle handle = new PhoneAccountHandle(new ComponentName("foo", "bar"), "id");
+        PhoneAccount.Builder builder = new PhoneAccount.Builder(handle, "label");
+        builder.addSupportedUriScheme("tel");
+        builder.setAddress(Uri.fromParts("tel", "6505551212", null));
+        builder.setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER);
+        Bundle extras = new Bundle();
+        extras.putInt("INT", 1);
+        extras.putString("STR", "str");
+        builder.setExtras(extras);
+        builder.setGroupId("group");
+        builder.setHighlightColor(1);
+        builder.setShortDescription("short");
+        builder.setSubscriptionAddress(Uri.fromParts("tel", "6505551213", null));
+        builder.setSupportedAudioRoutes(2);
+
+        PhoneAccount account1 = builder.build();
+        PhoneAccount account2 = builder.build();
+        assertEquals(account1, account2);
+    }
+
     private static ComponentName makeQuickConnectionServiceComponentName() {
         return new ComponentName(
                 "com.android.server.telecom.tests",
@@ -919,9 +1064,10 @@
             PhoneAccountRegistrar.State a, PhoneAccountRegistrar.State b) {
         assertEquals(a.defaultOutgoingAccountHandles.size(),
                 b.defaultOutgoingAccountHandles.size());
-        for (int i = 0; i < a.defaultOutgoingAccountHandles.size(); i++) {
-            assertDefaultPhoneAccountHandleEquals(a.defaultOutgoingAccountHandles.get(i),
-                    b.defaultOutgoingAccountHandles.get(i));
+        for (Map.Entry<UserHandle, DefaultPhoneAccountHandle> e :
+                a.defaultOutgoingAccountHandles.entrySet()) {
+            assertDefaultPhoneAccountHandleEquals(e.getValue(),
+                    b.defaultOutgoingAccountHandles.get(e.getKey()));
         }
         assertEquals(a.accounts.size(), b.accounts.size());
         for (int i = 0; i < a.accounts.size(); i++) {
@@ -937,6 +1083,10 @@
         PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
                 new ComponentName("pkg0", "cls0"), "id0");
         UserHandle userHandle = phoneAccountHandle.getUserHandle();
+        when(UserManager.get(mContext).getSerialNumberForUser(userHandle))
+                .thenReturn(0L);
+        when(UserManager.get(mContext).getUserForSerialNumber(0L))
+                .thenReturn(userHandle);
         s.defaultOutgoingAccountHandles
                 .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle,
                         "testGroup"));
diff --git a/tests/src/com/android/server/telecom/tests/ProximitySensorManagerTest.java b/tests/src/com/android/server/telecom/tests/ProximitySensorManagerTest.java
index 01bd09e..eafaa53 100644
--- a/tests/src/com/android/server/telecom/tests/ProximitySensorManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/ProximitySensorManagerTest.java
@@ -24,6 +24,11 @@
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.TelecomWakeLock;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
 import java.util.ArrayList;
@@ -33,6 +38,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class ProximitySensorManagerTest extends TelecomTestCase{
 
     @Mock CallsManager mCallsManager;
@@ -41,6 +47,7 @@
     private ProximitySensorManager mProximitySensorManager;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         TelecomWakeLock telecomWakeLock = new TelecomWakeLock(
@@ -51,12 +58,14 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         mProximitySensorManager = null;
         super.tearDown();
     }
 
     @SmallTest
+    @Test
     public void testTurnOnProximityWithCallsActive() throws Exception {
         when(mCallsManager.getCalls()).thenReturn(new ArrayList<Call>(){{
             add(mCall);
@@ -69,6 +78,7 @@
     }
 
     @SmallTest
+    @Test
     public void testTurnOnProximityWithNoCallsActive() throws Exception {
         when(mCallsManager.getCalls()).thenReturn(new ArrayList<Call>());
         when(mWakeLockAdapter.isHeld()).thenReturn(false);
@@ -80,6 +90,7 @@
     }
 
     @SmallTest
+    @Test
     public void testTurnOffProximityExplicitly() throws Exception {
         when(mWakeLockAdapter.isHeld()).thenReturn(true);
 
@@ -89,6 +100,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCallRemovedFromCallsManagerCallsActive() throws Exception {
         when(mCallsManager.getCalls()).thenReturn(new ArrayList<Call>(){{
             add(mCall);
@@ -101,6 +113,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCallRemovedFromCallsManagerNoCallsActive() throws Exception {
         when(mCallsManager.getCalls()).thenReturn(new ArrayList<Call>());
         when(mWakeLockAdapter.isHeld()).thenReturn(true);
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 8b269a9..25460de 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -18,11 +18,15 @@
 
 import android.app.NotificationManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.Ringtone;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.telecom.TelecomManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.AsyncRingtonePlayer;
@@ -33,16 +37,26 @@
 import com.android.server.telecom.RingtoneFactory;
 import com.android.server.telecom.SystemSettingsUtil;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class RingerTest extends TelecomTestCase {
+    private static final Uri FAKE_RINGTONE_URI = Uri.parse("content://media/fake/audio/1729");
+
     @Mock InCallTonePlayer.Factory mockPlayerFactory;
     @Mock SystemSettingsUtil mockSystemSettingsUtil;
     @Mock AsyncRingtonePlayer mockRingtonePlayer;
@@ -56,6 +70,9 @@
 
     Ringer mRingerUnderTest;
     AudioManager mockAudioManager;
+
+    @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -71,6 +88,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNoActionInTheaterMode() {
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
@@ -78,11 +96,28 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
+    public void testNoActionWithExternalRinger() {
+        Bundle externalRingerExtra = new Bundle();
+        externalRingerExtra.putBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, true);
+        when(mockCall1.getIntentExtras()).thenReturn(externalRingerExtra);
+        when(mockCall2.getIntentExtras()).thenReturn(externalRingerExtra);
+        // Start call waiting to make sure that it doesn't stop when we start ringing
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+        verify(mockTonePlayer, never()).stopTone();
+        verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
+    }
+
+    @SmallTest
+    @Test
     public void testNoActionWhenDialerRings() {
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
@@ -90,11 +125,12 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testAudioFocusStillAcquiredWhenDialerRings() {
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
@@ -103,11 +139,12 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testNoActionWhenCallIsSelfManaged() {
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
@@ -116,11 +153,12 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
         verify(mockTonePlayer, never()).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testCallWaitingButNoRingForSpecificContacts() {
         NotificationManager notificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -132,11 +170,12 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testVibrateButNoRingForNullRingtone() {
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(null);
@@ -145,11 +184,12 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
+                any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testVibrateButNoRingForSilentRingtone() {
         mRingerUnderTest.startCallWaiting(mockCall1);
         Ringtone mockRingtone = mock(Ringtone.class);
@@ -160,11 +200,30 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
+                any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
+    public void testCustomVibrationForRingtone() {
+        Resources resources = mContext.getResources();
+        when(resources.getStringArray(com.android.internal.R.array.config_ringtoneEffectUris))
+                .thenReturn(new String[] { FAKE_RINGTONE_URI.toString() });
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        Ringtone mockRingtone = mock(Ringtone.class);
+        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
+        when(mockRingtone.getUri()).thenReturn(FAKE_RINGTONE_URI);
+        enableVibrationWhenRinging();
+        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+        verify(mockTonePlayer).stopTone();
+        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
+        verify(mockVibrator).vibrate(eq(VibrationEffect.get(FAKE_RINGTONE_URI, mContext)),
+                any(AudioAttributes.class));
+    }
+
+    @SmallTest
+    @Test
     public void testRingAndNoVibrate() {
         mRingerUnderTest.startCallWaiting(mockCall1);
         ensureRingerIsAudible();
@@ -172,11 +231,12 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testSilentRingWithHfpStillAcquiresFocus1() {
         mRingerUnderTest.startCallWaiting(mockCall1);
         Ringtone mockRingtone = mock(Ringtone.class);
@@ -187,11 +247,12 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     @SmallTest
+    @Test
     public void testSilentRingWithHfpStillAcquiresFocus2() {
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(null);
@@ -201,8 +262,8 @@
         assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator, never()).vibrate(
-                any(long[].class), anyInt(), any(AudioAttributes.class));
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
 
     private void ensureRingerIsAudible() {
diff --git a/tests/src/com/android/server/telecom/tests/SessionManagerTest.java b/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
index 2d9b0c6..85a9bff 100644
--- a/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
@@ -16,16 +16,29 @@
 
 package com.android.server.telecom.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
 import android.telecom.Logging.Session;
 import android.telecom.Logging.SessionManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
 import java.lang.ref.WeakReference;
 
 /**
  * Unit tests for android.telecom.Logging.SessionManager
  */
 
+@RunWith(JUnit4.class)
 public class SessionManagerTest extends TelecomTestCase {
 
     private static final String TEST_PARENT_NAME = "testParent";
@@ -40,6 +53,7 @@
     private String mFullSessionMethodName = "";
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mTestSessionManager = new SessionManager();
@@ -52,6 +66,7 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         mFullSessionMethodName = "";
         mfullSessionCompleteTime = Session.UNDEFINED;
@@ -63,6 +78,7 @@
      * Starts a Session on the current thread and verifies that it exists in the HashMap
      */
     @SmallTest
+    @Test
     public void testStartSession() {
         assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
 
@@ -81,6 +97,7 @@
      * session and the second session will be attached to that thread ID.
      */
     @SmallTest
+    @Test
     public void testStartInvisibleChildSession() {
         assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
 
@@ -105,6 +122,7 @@
      * End the active Session and verify that it is completed and removed from mSessionMapper.
      */
     @SmallTest
+    @Test
     public void testEndSession() {
         assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
         // Set the thread Id to 0
@@ -129,6 +147,7 @@
      * into mSessionMapper.
      */
     @SmallTest
+    @Test
     public void testEndInvisibleChildSession() {
         assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
         // Set the thread Id to 0 for the parent
@@ -154,6 +173,7 @@
      * in a different thread.
      */
     @SmallTest
+    @Test
     public void testCreateSubsession() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -176,6 +196,7 @@
      * marked as completed and never added to mSessionMapper.
      */
     @SmallTest
+    @Test
     public void testCancelSubsession() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -196,6 +217,7 @@
      * its parent are in mSessionMapper.
      */
     @SmallTest
+    @Test
     public void testContinueSubsession() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -219,6 +241,7 @@
      * no longer exists in mSessionMapper.
      */
     @SmallTest
+    @Test
     public void testEndSubsession() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -241,6 +264,7 @@
      * sessions that are complete as well and note the completion time of the entire chain.
      */
     @SmallTest
+    @Test
     public void testEndSubsessionWithParentComplete() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -276,6 +300,7 @@
      * correctly generates the child session.
      */
     @SmallTest
+    @Test
     public void testStartExternalSession() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -298,6 +323,7 @@
      * external session from mSessionMapper.
      */
     @SmallTest
+    @Test
     public void testEndExternalSession() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -323,6 +349,7 @@
      * the external Session, but the one subsession underneath.
      */
     @SmallTest
+    @Test
     public void testEndExternalSessionListenerCallback() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -346,6 +373,7 @@
      * Verifies that the recursive method for getting the full ID works correctly.
      */
     @SmallTest
+    @Test
     public void testFullMethodPath() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
@@ -364,6 +392,7 @@
      * correctly to ensure that there are no dangling sessions.
      */
     @SmallTest
+    @Test
     public void testStaleSessionCleanupTimer() {
         mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
         mTestSessionManager.startSession(TEST_PARENT_NAME, null);
diff --git a/tests/src/com/android/server/telecom/tests/StateMachineTestBase.java b/tests/src/com/android/server/telecom/tests/StateMachineTestBase.java
deleted file mode 100644
index 358b960..0000000
--- a/tests/src/com/android/server/telecom/tests/StateMachineTestBase.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.telecom.tests;
-
-import com.android.internal.util.StateMachine;
-
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-public abstract class StateMachineTestBase<T extends StateMachine> extends TelecomTestCase {
-    abstract static class TestParameters {}
-
-    protected final void waitForStateMachineActionCompletion(T stateMachine, int runnableCode) {
-        final CountDownLatch lock = new CountDownLatch(1);
-        Runnable actionComplete = new Runnable() {
-            @Override
-            public void run() {
-                lock.countDown();
-            }
-        };
-        stateMachine.sendMessage(runnableCode, actionComplete);
-        while (lock.getCount() > 0) {
-            try {
-                lock.await();
-            } catch (InterruptedException e) {
-                // do nothing
-            }
-        }
-    }
-
-    protected final void parametrizedTestStateMachine(
-            List<? extends TestParameters> paramList) throws Throwable {
-        for (TestParameters params : paramList) {
-            try {
-                runParametrizedTestCase(params);
-            } catch (Throwable e) {
-                String newMessage = "Failed at parameters: \n" + params.toString() + '\n'
-                        + e.getMessage();
-                Throwable t = new Throwable(newMessage, e);
-                t.setStackTrace(e.getStackTrace());
-                throw t;
-            }
-        }
-    }
-
-    protected abstract void runParametrizedTestCase(TestParameters params) throws Throwable;
-}
diff --git a/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java b/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java
index ad740f4..033f929 100644
--- a/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java
+++ b/tests/src/com/android/server/telecom/tests/SystemStateProviderTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.telecom.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -31,6 +34,11 @@
 import com.android.server.telecom.SystemStateProvider;
 import com.android.server.telecom.SystemStateProvider.SystemStateListener;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -38,10 +46,9 @@
 /**
  * Unit tests for SystemStateProvider
  */
+@RunWith(JUnit4.class)
 public class SystemStateProviderTest extends TelecomTestCase {
 
-    SystemStateProvider mSystemStateProvider;
-
     @Mock Context mContext;
     @Mock SystemStateListener mSystemStateListener;
     @Mock UiModeManager mUiModeManager;
@@ -49,17 +56,20 @@
     @Mock Intent mIntentExit;
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         super.tearDown();
     }
 
     @SmallTest
+    @Test
     public void testListeners() throws Exception {
         SystemStateProvider systemStateProvider = new SystemStateProvider(mContext);
 
@@ -70,6 +80,7 @@
     }
 
     @SmallTest
+    @Test
     public void testQuerySystemForCarMode_True() {
         when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager);
         when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
@@ -77,6 +88,7 @@
     }
 
     @SmallTest
+    @Test
     public void testQuerySystemForCarMode_False() {
         when(mContext.getSystemService(Context.UI_MODE_SERVICE)).thenReturn(mUiModeManager);
         when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_NORMAL);
@@ -84,6 +96,7 @@
     }
 
     @SmallTest
+    @Test
     public void testReceiverAndIntentFilter() {
         ArgumentCaptor<IntentFilter> intentFilter = ArgumentCaptor.forClass(IntentFilter.class);
         new SystemStateProvider(mContext);
@@ -95,6 +108,7 @@
     }
 
     @SmallTest
+    @Test
     public void testOnEnterExitCarMode() {
         ArgumentCaptor<BroadcastReceiver> receiver =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
diff --git a/tests/src/com/android/server/telecom/tests/TODO b/tests/src/com/android/server/telecom/tests/TODO
deleted file mode 100644
index f412b4a..0000000
--- a/tests/src/com/android/server/telecom/tests/TODO
+++ /dev/null
@@ -1,14 +0,0 @@
-* Implement acquireProvider("settings")
-* Implement acquireUnstableProvider("com.android.contacts")
-* ComponentContextFixture to have "setUp/tearDown" to check for things like un-released providers
-
-Bluetooth
-Speakerphone and audio modes
-Video calling
-Connection managers
-DTMF tones
-Call logging
-Ringback
-Missed calls
-Respond via SMS
-Permissions in registering PhoneAccounts
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 305475d..cbca5e1 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom.tests;
 
 import static android.Manifest.permission.CALL_PHONE;
+import static android.Manifest.permission.CALL_PRIVILEGED;
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
@@ -53,10 +54,13 @@
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
-import org.mockito.compat.ArgumentMatcher;
-import org.mockito.internal.matchers.VarargMatcher;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -64,6 +68,11 @@
 
 import static android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -81,7 +90,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+@RunWith(JUnit4.class)
 public class TelecomServiceImplTest extends TelecomTestCase {
+
+    public static final String TEST_PACKAGE = "com.test";
+
     public static class CallIntentProcessAdapterFake implements CallIntentProcessor.Adapter {
         @Override
         public void processOutgoingCallIntent(Context context, CallsManager callsManager,
@@ -108,25 +121,18 @@
         }
     }
 
-    private static class AnyStringIn extends ArgumentMatcher<String> {
+    private static class AnyStringIn implements ArgumentMatcher<String> {
         private Collection<String> mStrings;
         public AnyStringIn(Collection<String> strings) {
             this.mStrings = strings;
         }
 
         @Override
-        public boolean matchesObject(Object string) {
+        public boolean matches(String string) {
             return mStrings.contains(string);
         }
     }
 
-    private static class IntVarArgMatcher extends ArgumentMatcher<int[]> implements VarargMatcher {
-        @Override
-        public boolean matchesObject(Object argument) {
-            return true;
-        }
-    }
-
     private ITelecomService.Stub mTSIBinder;
     private AppOpsManager mAppOpsManager;
     private UserManager mUserManager;
@@ -156,6 +162,7 @@
             new ComponentName("test", "sipComponentName"), "3", Binder.getCallingUserHandle());
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -194,6 +201,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetDefaultOutgoingPhoneAccount() throws RemoteException {
         when(mFakePhoneAccountRegistrar
                 .getOutgoingPhoneAccountForScheme(eq("tel"), any(UserHandle.class)))
@@ -213,6 +221,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetDefaultOutgoingPhoneAccountFailure() throws RemoteException {
         // make sure that the list of user profiles doesn't include anything the PhoneAccountHandles
         // are associated with
@@ -233,6 +242,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetUserSelectedOutgoingPhoneAccount() throws RemoteException {
         when(mFakePhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount(any(UserHandle.class)))
                 .thenReturn(TEL_PA_HANDLE_16);
@@ -245,6 +255,7 @@
     }
 
     @SmallTest
+    @Test
     public void testSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
         mTSIBinder.setUserSelectedOutgoingPhoneAccount(TEL_PA_HANDLE_16);
         verify(mFakePhoneAccountRegistrar)
@@ -252,6 +263,7 @@
     }
 
     @SmallTest
+    @Test
     public void testSetUserSelectedOutgoingPhoneAccountFailure() throws RemoteException {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                 anyString(), nullable(String.class));
@@ -266,6 +278,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetCallCapablePhoneAccounts() throws RemoteException {
         List<PhoneAccountHandle> fullPHList = new ArrayList<PhoneAccountHandle>() {{
             add(TEL_PA_HANDLE_16);
@@ -292,6 +305,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetCallCapablePhoneAccountsFailure() throws RemoteException {
         List<String> enforcedPermissions = new ArrayList<String>() {{
             add(READ_PHONE_STATE);
@@ -312,6 +326,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetPhoneAccountsSupportingScheme() throws RemoteException {
         List<PhoneAccountHandle> sipPHList = new ArrayList<PhoneAccountHandle>() {{
             add(SIP_PA_HANDLE_17);
@@ -335,6 +350,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetPhoneAccountsForPackage() throws RemoteException {
         List<PhoneAccountHandle> phoneAccountHandleList = new ArrayList<PhoneAccountHandle>() {{
             add(TEL_PA_HANDLE_16);
@@ -350,6 +366,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetPhoneAccount() throws RemoteException {
         makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
         assertEquals(TEL_PA_HANDLE_16, mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16)
@@ -359,6 +376,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetAllPhoneAccounts() throws RemoteException {
         List<PhoneAccount> phoneAccountList = new ArrayList<PhoneAccount>() {{
             add(makePhoneAccount(TEL_PA_HANDLE_16).build());
@@ -371,6 +389,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRegisterPhoneAccount() throws RemoteException {
         String packageNameToUse = "com.android.officialpackage";
         PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
@@ -383,6 +402,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRegisterPhoneAccountWithoutModifyPermission() throws RemoteException {
         // tests the case where the package does not have MODIFY_PHONE_STATE but is
         // registering its own phone account as a third-party connection service
@@ -400,6 +420,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRegisterPhoneAccountWithoutModifyPermissionFailure() throws RemoteException {
         // tests the case where the third party package should not be allowed to register a phone
         // account due to the lack of modify permission.
@@ -417,6 +438,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRegisterPhoneAccountWithoutSimSubscriptionPermissionFailure()
             throws RemoteException {
         String packageNameToUse = "com.thirdparty.connectionservice";
@@ -436,6 +458,7 @@
     }
 
     @SmallTest
+    @Test
     public void testRegisterPhoneAccountWithoutMultiUserPermissionFailure()
             throws Exception {
         String packageNameToUse = "com.thirdparty.connectionservice";
@@ -474,6 +497,7 @@
     }
 
     @SmallTest
+    @Test
     public void testUnregisterPhoneAccount() throws RemoteException {
         String packageNameToUse = "com.android.officialpackage";
         PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
@@ -488,6 +512,7 @@
     }
 
     @SmallTest
+    @Test
     public void testUnregisterPhoneAccountFailure() throws RemoteException {
         String packageNameToUse = "com.thirdparty.connectionservice";
         PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
@@ -510,6 +535,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAddNewIncomingCall() throws Exception {
         PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT).build();
         phoneAccount.setIsEnabled(true);
@@ -525,6 +551,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAddNewIncomingCallFailure() throws Exception {
         try {
             mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, null);
@@ -546,6 +573,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAddNewUnknownCall() throws Exception {
         PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT).build();
         phoneAccount.setIsEnabled(true);
@@ -561,6 +589,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAddNewUnknownCallFailure() throws Exception {
         try {
             mTSIBinder.addNewUnknownCall(TEL_PA_HANDLE_16, null);
@@ -611,6 +640,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPlaceCallWithNonEmergencyPermission() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
@@ -619,12 +649,15 @@
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
         doReturn(PackageManager.PERMISSION_GRANTED)
                 .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
 
         mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE);
         placeCallTestHelper(handle, extras, true);
     }
 
     @SmallTest
+    @Test
     public void testPlaceCallWithAppOpsOff() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
@@ -633,12 +666,15 @@
                 .thenReturn(AppOpsManager.MODE_IGNORED);
         doReturn(PackageManager.PERMISSION_GRANTED)
                 .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
 
         mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE);
         placeCallTestHelper(handle, extras, false);
     }
 
     @SmallTest
+    @Test
     public void testPlaceCallWithNoCallingPermission() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
@@ -647,6 +683,8 @@
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
         doReturn(PackageManager.PERMISSION_DENIED)
                 .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
 
         mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE);
         placeCallTestHelper(handle, extras, false);
@@ -656,7 +694,7 @@
             boolean shouldNonEmergencyBeAllowed) {
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mUserCallIntentProcessor).processIntent(intentCaptor.capture(), anyString(),
-                eq(shouldNonEmergencyBeAllowed));
+                eq(shouldNonEmergencyBeAllowed), eq(true));
         Intent capturedIntent = intentCaptor.getValue();
         assertEquals(Intent.ACTION_CALL, capturedIntent.getAction());
         assertEquals(expectedHandle, capturedIntent.getData());
@@ -664,6 +702,7 @@
     }
 
     @SmallTest
+    @Test
     public void testPlaceCallFailure() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
@@ -678,10 +717,11 @@
         }
 
         verify(mUserCallIntentProcessor, never())
-                .processIntent(any(Intent.class), anyString(), anyBoolean());
+                .processIntent(any(Intent.class), anyString(), anyBoolean(), eq(true));
     }
 
     @SmallTest
+    @Test
     public void testSetDefaultDialer() throws Exception {
         String packageName = "sample.package";
         int currentUser = ActivityManager.getCurrentUser();
@@ -702,6 +742,7 @@
     }
 
     @SmallTest
+    @Test
     public void testSetDefaultDialerNoModifyPhoneStatePermission() throws Exception {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                 eq(MODIFY_PHONE_STATE), nullable(String.class));
@@ -709,6 +750,7 @@
     }
 
     @SmallTest
+    @Test
     public void testSetDefaultDialerNoWriteSecureSettingsPermission() throws Exception {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                 eq(WRITE_SECURE_SETTINGS), nullable(String.class));
@@ -728,6 +770,7 @@
     }
 
     @SmallTest
+    @Test
     public void testIsVoicemailNumber() throws Exception {
         String vmNumber = "010";
         makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_CURRENT);
@@ -739,6 +782,7 @@
     }
 
     @SmallTest
+    @Test
     public void testIsVoicemailNumberAccountNotVisibleFailure() throws Exception {
         String vmNumber = "010";
 
@@ -752,6 +796,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetVoicemailNumberWithNullAccountHandle() throws Exception {
         when(mFakePhoneAccountRegistrar.getPhoneAccount(isNull(PhoneAccountHandle.class),
                 eq(Binder.getCallingUserHandle())))
@@ -768,6 +813,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetVoicemailNumberWithNonNullAccountHandle() throws Exception {
         when(mFakePhoneAccountRegistrar.getPhoneAccount(eq(TEL_PA_HANDLE_CURRENT),
                 eq(Binder.getCallingUserHandle())))
@@ -786,6 +832,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetLine1Number() throws Exception {
         int subId = 58374;
         String line1Number = "9482752023479";
@@ -801,39 +848,44 @@
     }
 
     @SmallTest
+    @Test
     public void testEndCallWithRingingForegroundCall() throws Exception {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.RINGING);
         when(mFakeCallsManager.getForegroundCall()).thenReturn(call);
-        assertTrue(mTSIBinder.endCall());
-        verify(call).reject(false, null);
+        assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
+        verify(call).reject(eq(false), isNull(), eq(TEST_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testEndCallWithNonRingingForegroundCall() throws Exception {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.ACTIVE);
         when(mFakeCallsManager.getForegroundCall()).thenReturn(call);
-        assertTrue(mTSIBinder.endCall());
-        verify(call).disconnect();
+        assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
+        verify(call).disconnect(eq(0L), eq(TEST_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testEndCallWithNoForegroundCall() throws Exception {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.ACTIVE);
-        when(mFakeCallsManager.getFirstCallWithState(argThat(new IntVarArgMatcher())))
+        when(mFakeCallsManager.getFirstCallWithState(any()))
                 .thenReturn(call);
-        assertTrue(mTSIBinder.endCall());
-        verify(call).disconnect();
+        assertTrue(mTSIBinder.endCall(TEST_PACKAGE));
+        verify(call).disconnect(eq(0L), eq(TEST_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testEndCallWithNoCalls() throws Exception {
-        assertFalse(mTSIBinder.endCall());
+        assertFalse(mTSIBinder.endCall(null));
     }
 
     @SmallTest
+    @Test
     public void testAcceptRingingCall() throws Exception {
         Call call = mock(Call.class);
         when(mFakeCallsManager.getFirstCallWithState(anyInt())).thenReturn(call);
@@ -846,6 +898,7 @@
     }
 
     @SmallTest
+    @Test
     public void testAcceptRingingCallWithValidVideoState() throws Exception {
         Call call = mock(Call.class);
         when(mFakeCallsManager.getFirstCallWithState(anyInt())).thenReturn(call);
@@ -859,18 +912,21 @@
     }
 
     @SmallTest
+    @Test
     public void testIsInCall() throws Exception {
         when(mFakeCallsManager.hasOngoingCalls()).thenReturn(true);
         assertTrue(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testNotIsInCall() throws Exception {
         when(mFakeCallsManager.hasOngoingCalls()).thenReturn(false);
         assertFalse(mTSIBinder.isInCall(DEFAULT_DIALER_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testIsInCallFail() throws Exception {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                 anyString(), any());
@@ -884,18 +940,21 @@
     }
 
     @SmallTest
+    @Test
     public void testIsInManagedCall() throws Exception {
         when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(true);
         assertTrue(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testNotIsInManagedCall() throws Exception {
         when(mFakeCallsManager.hasOngoingManagedCalls()).thenReturn(false);
         assertFalse(mTSIBinder.isInManagedCall(DEFAULT_DIALER_PACKAGE));
     }
 
     @SmallTest
+    @Test
     public void testIsInManagedCallFail() throws Exception {
         doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                 anyString(), any());
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 6b2cf4f..4cf7644 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -17,6 +17,11 @@
 package com.android.server.telecom.tests;
 
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.isNotNull;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -35,7 +40,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -69,6 +73,7 @@
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.CallsManagerListenerBase;
 import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
@@ -443,6 +448,14 @@
                         return mBluetoothPhoneServiceImpl;
                     }
                 },
+                new ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory() {
+                    @Override
+                    public ConnectionServiceFocusManager create(
+                            ConnectionServiceFocusManager.CallsManagerRequester requester,
+                            Looper looper) {
+                        return new ConnectionServiceFocusManager(requester, looper);
+                    }
+                },
                 mTimeoutsAdapter,
                 mAsyncRingtonePlayer,
                 mPhoneNumberUtilsAdapter,
@@ -467,8 +480,8 @@
     }
 
     private void setupConnectionServices() throws Exception {
-        mConnectionServiceFixtureA = new ConnectionServiceFixture();
-        mConnectionServiceFixtureB = new ConnectionServiceFixture();
+        mConnectionServiceFixtureA = new ConnectionServiceFixture(mContext);
+        mConnectionServiceFixtureB = new ConnectionServiceFixture(mContext);
 
         mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameA,
                 mConnectionServiceFixtureA.getTestDouble());
@@ -581,6 +594,9 @@
         startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
                 connectionServiceFixture, initiatingUser, videoState);
 
+        verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
+                .createConnectionComplete(anyString(), any());
+
         return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
                 phoneAccountHandle, connectionServiceFixture);
     }
@@ -657,7 +673,7 @@
         final UserHandle userHandle = initiatingUser;
         Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
         new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
-                actionCallIntent, null, true /* hasCallAppOp*/);
+                actionCallIntent, null, true /* hasCallAppOp*/, false /* isLocal */);
         // UserCallIntentProcessor's mContext.sendBroadcastAsUser(...) will call to an empty method
         // as to not actually try to send an intent to PrimaryCallReceiver. We verify that it was
         // called correctly in order to continue.
@@ -746,13 +762,16 @@
             int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
             ConnectionServiceFixture connectionServiceFixture) throws Exception {
 
-        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
+        // Wait for the focus tracker.
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
 
         verify(connectionServiceFixture.getTestDouble())
                 .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
                         eq(false)/*isIncoming*/, anyBoolean(), any());
         // Wait for handleCreateConnectionComplete
         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
+
         // Wait for the callback in ConnectionService#onAdapterAttached to execute.
         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
 
@@ -821,7 +840,6 @@
         verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
                 .createConnectionComplete(anyString(), any());
 
-
         // Process the CallerInfo lookup reply
         mCallerInfoAsyncQueryFactoryFixture.mRequests.forEach(
                 CallerInfoAsyncQueryFactoryFixture.Request::reply);
@@ -835,41 +853,42 @@
                 anyString(),
                 eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
                 eq(number),
-                isNull(Bundle.class));
+                isNotNull(Bundle.class));
 
         // For the case of incoming calls, Telecom connecting the InCall services and adding the
         // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
         // is added, future interactions as triggered by the ConnectionService, through the various
         // test fixtures, will be synchronous.
 
-        if (!hasInCallAdapter
-                && phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
-            verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
-                    .setInCallAdapter(any(IInCallAdapter.class));
-            verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
-                    .setInCallAdapter(any(IInCallAdapter.class));
+        if (phoneAccountHandle != mPhoneAccountSelfManaged.getAccountHandle()) {
+            if (!hasInCallAdapter) {
+                verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
+                        .setInCallAdapter(any(IInCallAdapter.class));
+                verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
+                        .setInCallAdapter(any(IInCallAdapter.class));
 
-            // Give the InCallService time to respond
-            assertTrueWithTimeout(new Predicate<Void>() {
-                @Override
-                public boolean apply(Void v) {
-                    return mInCallServiceFixtureX.mInCallAdapter != null;
-                }
-            });
+                // Give the InCallService time to respond
+                assertTrueWithTimeout(new Predicate<Void>() {
+                    @Override
+                    public boolean apply(Void v) {
+                        return mInCallServiceFixtureX.mInCallAdapter != null;
+                    }
+                });
 
-            assertTrueWithTimeout(new Predicate<Void>() {
-                @Override
-                public boolean apply(Void v) {
-                    return mInCallServiceFixtureY.mInCallAdapter != null;
-                }
-            });
+                assertTrueWithTimeout(new Predicate<Void>() {
+                    @Override
+                    public boolean apply(Void v) {
+                        return mInCallServiceFixtureY.mInCallAdapter != null;
+                    }
+                });
 
-            verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
-                    .addCall(any(ParcelableCall.class));
-            verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
-                    .addCall(any(ParcelableCall.class));
+                verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
+                        .addCall(any(ParcelableCall.class));
+                verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
+                        .addCall(any(ParcelableCall.class));
 
-            // Give the InCallService time to respond
+                // Give the InCallService time to respond
+            }
 
             assertTrueWithTimeout(new Predicate<Void>() {
                 @Override
@@ -878,18 +897,11 @@
                             connectionServiceFixture.mConnectionById.size();
                 }
             });
-            assertTrueWithTimeout(new Predicate<Void>() {
-                @Override
-                public boolean apply(Void v) {
-                    return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
-                }
-            });
-            assertTrueWithTimeout(new Predicate<Void>() {
-                @Override
-                public boolean apply(Void v) {
-                    return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
-                }
-            });
+
+            mInCallServiceFixtureX.waitUntilNumCalls(startingNumCalls + 1);
+            mInCallServiceFixtureY.waitUntilNumCalls(startingNumCalls + 1);
+            assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
+            assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
 
             assertEquals(mInCallServiceFixtureX.mLatestCallId,
                     mInCallServiceFixtureY.mLatestCallId);
@@ -962,10 +974,10 @@
                     .answerCall(ids.mCallId, videoState);
 
             if (!VideoProfile.isVideo(videoState)) {
-                verify(connectionServiceFixture.getTestDouble())
+                verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
                         .answer(eq(ids.mConnectionId), any());
             } else {
-                verify(connectionServiceFixture.getTestDouble())
+                verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT))
                         .answerVideo(eq(ids.mConnectionId), eq(videoState), any());
             }
         }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index b735df9..c356cb4 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -18,36 +18,37 @@
 
 import org.mockito.MockitoAnnotations;
 
+import android.content.Context;
 import android.os.Handler;
+import android.support.test.InstrumentationRegistry;
 import android.telecom.Log;
-import android.test.AndroidTestCase;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-public abstract class TelecomTestCase extends AndroidTestCase {
+public abstract class TelecomTestCase {
     protected static final String TESTING_TAG = "Telecom-TEST";
+    protected Context mContext;
 
     MockitoHelper mMockitoHelper = new MockitoHelper();
     ComponentContextFixture mComponentContextFixture;
 
-    @Override
     public void setUp() throws Exception {
         Log.setTag(TESTING_TAG);
-        mMockitoHelper.setUp(getContext(), getClass());
+        mMockitoHelper.setUp(InstrumentationRegistry.getContext(), getClass());
         mComponentContextFixture = new ComponentContextFixture();
+        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
         Log.setSessionContext(mComponentContextFixture.getTestDouble().getApplicationContext());
         Log.getSessionManager().mCleanStaleSessions = null;
         MockitoAnnotations.initMocks(this);
     }
 
-    @Override
     public void tearDown() throws Exception {
         mComponentContextFixture = null;
         mMockitoHelper.tearDown();
     }
 
-    protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
+    protected static void waitForHandlerAction(Handler h, long timeoutMillis) {
         final CountDownLatch lock = new CountDownLatch(1);
         h.post(lock::countDown);
         while (lock.getCount() > 0) {
@@ -58,4 +59,16 @@
             }
         }
     }
+
+    protected final void waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs) {
+        final CountDownLatch lock = new CountDownLatch(1);
+        h.postDelayed(lock::countDown, delayMs);
+        while (lock.getCount() > 0) {
+            try {
+                lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/VideoCallTests.java b/tests/src/com/android/server/telecom/tests/VideoCallTests.java
index c91f86e..97e71d1 100644
--- a/tests/src/com/android/server/telecom/tests/VideoCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/VideoCallTests.java
@@ -16,6 +16,11 @@
 
 package com.android.server.telecom.tests;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 
 import android.os.Process;
@@ -31,6 +36,10 @@
 
 import java.util.List;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 
@@ -39,13 +48,27 @@
  * TODO: Add unit tests which ensure that auto-speakerphone does not occur when using a wired
  * headset or a bluetooth headset.
  */
+@RunWith(JUnit4.class)
 public class VideoCallTests extends TelecomSystemTest {
 
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     /**
      * Tests to ensure an incoming video-call is automatically routed to the speakerphone when
      * the call is answered and neither a wired headset nor bluetooth headset are connected.
      */
     @MediumTest
+    @Test
     public void testAutoSpeakerphoneIncomingBidirectional() throws Exception {
         // Start an incoming video call.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -62,6 +85,7 @@
      * party dialer to answer an incoming video call a a one-way video call.
      */
     @MediumTest
+    @Test
     public void testAutoSpeakerphoneIncomingReceiveOnly() throws Exception {
         // Start an incoming video call.
         IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
@@ -76,6 +100,7 @@
      * in speaker mode.
      */
     @MediumTest
+    @Test
     public void testAutoSpeakerphoneOutgoingBidirectional() throws Exception {
         // Start an incoming video call.
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
@@ -91,6 +116,7 @@
      * APIs do and a third party incall UI could choose to support that.
      */
     @MediumTest
+    @Test
     public void testAutoSpeakerphoneOutgoingTransmitOnly() throws Exception {
         // Start an incoming video call.
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
@@ -106,6 +132,7 @@
      * APIs do and a third party incall UI could choose to support that.
      */
     @MediumTest
+    @Test
     public void testNoAutoSpeakerphoneOnOutgoing() throws Exception {
         // Start an incoming video call.
         IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
@@ -119,6 +146,7 @@
      * Tests to ensure an incoming audio-only call is routed to the earpiece.
      */
     @MediumTest
+    @Test
     public void testNoAutoSpeakerphoneOnIncoming() throws Exception {
 
         // Start an incoming video call.
@@ -134,6 +162,7 @@
      * video calling. This is important for the call log.
      */
     @LargeTest
+    @Test
     public void testIncomingVideoCallMissedCheckVideoHistory() throws Exception {
         IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
@@ -150,6 +179,7 @@
      * video calling. This is important for the call log.
      */
     @LargeTest
+    @Test
     public void testIncomingVideoCallRejectedCheckVideoHistory() throws Exception {
         IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
@@ -167,6 +197,7 @@
      * video calling. This is important for the call log.
      */
     @LargeTest
+    @Test
     public void testOutgoingVideoCallCanceledCheckVideoHistory() throws Exception {
         IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 mConnectionServiceFixtureA, Process.myUserHandle(),
@@ -184,6 +215,7 @@
      * video calling. This is important for the call log.
      */
     @LargeTest
+    @Test
     public void testOutgoingVideoCallRejectedCheckVideoHistory() throws Exception {
         IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 mConnectionServiceFixtureA, Process.myUserHandle(),
@@ -201,6 +233,7 @@
      * shows that the call was audio only. This is important for the call log.
      */
     @LargeTest
+    @Test
     public void testOutgoingVideoCallAnsweredAsAudio() throws Exception {
         IdPair ids = startOutgoingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
                 mConnectionServiceFixtureA, Process.myUserHandle(),
diff --git a/tests/src/com/android/server/telecom/tests/VideoProfileTest.java b/tests/src/com/android/server/telecom/tests/VideoProfileTest.java
index 8e972e6..6acadf7 100644
--- a/tests/src/com/android/server/telecom/tests/VideoProfileTest.java
+++ b/tests/src/com/android/server/telecom/tests/VideoProfileTest.java
@@ -16,15 +16,24 @@
 
 package com.android.server.telecom.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.telecom.VideoProfile;
-import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
 /**
  * Unit tests for the {@link android.telecom.VideoProfile} class.
  */
-public class VideoProfileTest extends AndroidTestCase {
+@RunWith(JUnit4.class)
+public class VideoProfileTest extends TelecomTestCase {
     @SmallTest
+    @Test
     public void testToString() {
         assertEquals("Audio Only", VideoProfile.videoStateToString(VideoProfile.STATE_AUDIO_ONLY));
         assertEquals("Audio Tx", VideoProfile.videoStateToString(VideoProfile.STATE_TX_ENABLED));
@@ -42,6 +51,7 @@
     }
 
     @SmallTest
+    @Test
     public void testIsAudioOnly() {
         assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_RX_ENABLED));
         assertFalse(VideoProfile.isAudioOnly(VideoProfile.STATE_TX_ENABLED));
diff --git a/tests/src/com/android/server/telecom/tests/VideoProviderTest.java b/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
index 3234c1f..75dc36f 100644
--- a/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
+++ b/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
@@ -16,10 +16,13 @@
 
 package com.android.server.telecom.tests;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.internal.exceptions.ExceptionIncludingMockitoWarnings;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -46,7 +49,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-import static android.test.MoreAsserts.assertEquals;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
@@ -56,17 +59,16 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 /**
  * Performs tests of the {@link VideoProvider} and {@link VideoCall} APIs.  Ensures that requests
  * sent from an InCallService are routed through Telecom to a VideoProvider, and that callbacks are
  * correctly routed.
  */
+@RunWith(JUnit4.class)
 public class VideoProviderTest extends TelecomSystemTest {
     private static final int ORIENTATION_0 = 0;
     private static final int ORIENTATION_90 = 90;
@@ -89,6 +91,7 @@
     };
 
     @Override
+    @Before
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -121,6 +124,7 @@
     }
 
     @Override
+    @After
     public void tearDown() throws Exception {
         super.tearDown();
     }
@@ -131,6 +135,7 @@
      * APIS.
      */
     @MediumTest
+    @Test
     public void testCameraChange() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)
@@ -165,6 +170,7 @@
      * change from a non-permitted caller is ignored.
      */
     @MediumTest
+    @Test
     public void testCameraChangePermissionFail() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
@@ -193,6 +199,7 @@
      * change from a non-permitted caller is ignored.
      */
     @MediumTest
+    @Test
     public void testCameraChangeAppOpsFail() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
@@ -222,6 +229,7 @@
      * of a CAMERA_PERMISSION_ERROR.
      */
     @MediumTest
+    @Test
     public void testCameraChangeAppOpsBelowNMR1Fail() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
@@ -251,6 +259,7 @@
      * change from a background user is not permitted.
      */
     @MediumTest
+    @Test
     public void testCameraChangeUserFail() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
@@ -278,6 +287,7 @@
      * caller can null out the camera, even if they do not have camera permission.
      */
     @MediumTest
+    @Test
     public void testCameraChangeNullNoPermission() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback).onCallSessionEvent(anyInt());
@@ -306,6 +316,7 @@
      * {@link VideoProvider#onSetPreviewSurface(Surface)} APIs.
      */
     @MediumTest
+    @Test
     public void testSetPreviewSurface() throws Exception {
         final Surface surface = new Surface(new SurfaceTexture(1));
         mVideoCall.setPreviewSurface(surface);
@@ -332,6 +343,7 @@
      * {@link VideoProvider#onSetDisplaySurface(Surface)} APIs.
      */
     @MediumTest
+    @Test
     public void testSetDisplaySurface() throws Exception {
         final Surface surface = new Surface(new SurfaceTexture(1));
         mVideoCall.setDisplaySurface(surface);
@@ -358,6 +370,7 @@
      * {@link VideoProvider#onSetDeviceOrientation(int)} APIs.
      */
     @MediumTest
+    @Test
     public void testSetDeviceOrientation() throws Exception {
         mVideoCall.setDeviceOrientation(ORIENTATION_0);
 
@@ -382,6 +395,7 @@
      * Tests the {@link VideoCall#setZoom(float)} and {@link VideoProvider#onSetZoom(float)} APIs.
      */
     @MediumTest
+    @Test
     public void testSetZoom() throws Exception {
         mVideoCall.setZoom(ZOOM_LEVEL);
 
@@ -404,6 +418,7 @@
      * peer accepts as-is.
      */
     @MediumTest
+    @Test
     public void testSessionModifyRequest() throws Exception {
         VideoProfile requestProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
 
@@ -442,6 +457,7 @@
      * and {@link VideoProvider#onSendSessionModifyResponse(VideoProfile)} APIs.
      */
     @MediumTest
+    @Test
     public void testSessionModifyResponse() throws Exception {
         VideoProfile sessionModifyResponse = new VideoProfile(VideoProfile.STATE_TX_ENABLED);
 
@@ -463,6 +479,7 @@
      * {@link VideoCall.Callback#onCameraCapabilitiesChanged(CameraCapabilities)} APIs.
      */
     @MediumTest
+    @Test
     public void testRequestCameraCapabilities() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)
@@ -481,6 +498,7 @@
      * {@link VideoProvider#onSetPauseImage(Uri)} APIs.
      */
     @MediumTest
+    @Test
     public void testSetPauseImage() throws Exception {
         final Uri testUri = Uri.fromParts("file", "test.jpg", null);
         mVideoCall.setPauseImage(testUri);
@@ -500,6 +518,7 @@
      * {@link VideoCall.Callback#onCallDataUsageChanged(long)} APIs.
      */
     @MediumTest
+    @Test
     public void testRequestDataUsage() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)
@@ -518,6 +537,7 @@
      * {@link VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)} APIs.
      */
     @MediumTest
+    @Test
     public void testReceiveSessionModifyRequest() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)
@@ -541,6 +561,7 @@
      * {@link VideoCall.Callback#onCallSessionEvent(int)} APIs.
      */
     @MediumTest
+    @Test
     public void testSessionEvent() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)
@@ -560,6 +581,7 @@
      * {@link VideoCall.Callback#onPeerDimensionsChanged(int, int)} APIs.
      */
     @MediumTest
+    @Test
     public void testPeerDimensionChange() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)
@@ -580,6 +602,7 @@
      * {@link VideoCall.Callback#onVideoQualityChanged(int)} APIs.
      */
     @MediumTest
+    @Test
     public void testVideoQualityChange() throws Exception {
         // Wait until the callback has been received before performing verification.
         doAnswer(mVerification).when(mVideoCallCallback)