Merge "Import translations. DO NOT MERGE" into mnc-dr2-dev am: 01fd2ff631  -s ours
am: 43ef2dd855  -s ours

* commit '43ef2dd855976c621fb8e88e4c9c5483cffbb5f3':
  Import translations. DO NOT MERGE
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 56a594b..5b0f624 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,6 +20,10 @@
         coreApp="true"
         android:sharedUserId="android.uid.system">
 
+    <uses-sdk
+        android:minSdkVersion="23"
+        android:targetSdkVersion="23" />
+
 
     <protected-broadcast android:name="android.intent.action.SHOW_MISSED_CALLS_NOTIFICATION" />
 
@@ -72,7 +76,9 @@
             android:allowBackup="false"
             android:supportsRtl="true"
             android:process="system"
-            android:usesCleartextTraffic="false">
+            android:usesCleartextTraffic="false"
+            android:forceDeviceEncrypted="true"
+            android:encryptionAware="true">
 
         <!-- CALL vs CALL_PRIVILEGED vs CALL_EMERGENCY
              We have three different intents through which a call can be initiated each with its
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index b9aa6b9..0e0b5b0 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Foon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Foonoproepbestuur"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Bel"</string>
     <string name="unknown" msgid="6878797917991465859">"Onbekend"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Gemiste oproep"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Gemiste oproepe"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Vinnige antwoord"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Boodskap gestuur na <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Net noodoproepe word deur die toesteleienaar toegelaat"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Oproeprekeninge"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Hierdie program kan nie uitgaande oproepe maak sonder die foon se toestemming nie."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Voer \'n geldige nommer in om \'n oproep te maak."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Oproep kan nie op die oomblik bygevoeg word nie."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Deaktiveer TTY-modus om video-oproepe te maak."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Vermiste stemboodskapnommer"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Geen stemboodskapnommer is op die SIM-kaart gestoor nie."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Voeg nommer by"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Verander verstekbellerprogram?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Gebruik <xliff:g id="NEW_APP">%1$s</xliff:g> eerder as <xliff:g id="CURRENT_APP">%2$s</xliff:g> as jou verstekbellerprogram?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Gebruik <xliff:g id="NEW_APP">%s</xliff:g> as jou verstekbellerprogram?"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 758b795..853729d 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ስልክ"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"የስልክ ጥሪ አስተዳደር"</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_missedCallsTitle" msgid="1361677948941502522">"ያመለጡ ጥሪዎች"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"የአስቸኳይ አደጋ ጥሪዎች ብቻ ናቸው በባለቤቱ የተፈቀዱት"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"የመደወያ መለያዎች"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"የቪዲዮ ጥሪዎችን ለማድረግ እባክዎ የTTY ሁነታን ያሰናክሉ።"</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="4430590714918044425">"ነባሪ መደወያ መተግበሪያ ይለወጥ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g>ን በ<xliff:g id="CURRENT_APP">%2$s</xliff:g> ምትክ እንደ የእርስዎ ነባሪ መደወያ መተግበሪያ ይጠቀም?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g>ን እንደ የእርስዎ ነባሪ መደወያ መተግበሪያ ይጠቀም?"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 3d31dc6..ae8b985 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"الهاتف"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"إدارة المكالمات الهاتفية"</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_missedCallsTitle" msgid="1361677948941502522">"المكالمات الفائتة"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"مسموح لمالك الجهاز بمكالمات الطوارئ فقط"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"‏يُرجى تعطيل وضع TTY لإجراء مكالمات فيديو."</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="4430590714918044425">"هل تريد تغيير التطبيق الافتراضي لبرنامج الاتصال؟"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"هل تريد استخدام <xliff:g id="NEW_APP">%1$s</xliff:g> بدلاً من <xliff:g id="CURRENT_APP">%2$s</xliff:g> تطبيقًا افتراضيًا لبرنامج الاتصال؟"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"هل تريد استخدام <xliff:g id="NEW_APP">%s</xliff:g> تطبيقًا افتراضيًا لبرنامج الاتصال؟"</string>
 </resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
new file mode 100644
index 0000000..2082e05
--- /dev/null
+++ b/res/values-az-rAZ/strings.xml
@@ -0,0 +1,51 @@
+<?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="9166784827254469057">"Telefon Zənglərin İdarə Olunması"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
+    <string name="unknown" msgid="6878797917991465859">"Naməlum"</string>
+    <string name="notification_missedCallTitle" msgid="7554385905572364535">"Buraxılmış zəng"</string>
+    <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Buraxılmış zənglər"</string>
+    <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> buraxılmış zənglər"</string>
+    <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> tərəfindən zəng buraxılıb"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Geriyə zəng"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"Mesaj"</string>
+    <string name="accessibility_call_muted" msgid="2776111226185342220">"Səssiz zəng edin."</string>
+    <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Spikerfon aktivdir."</string>
+    <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_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_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>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Hesabların çağırılması"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Yalnız təcili zənglərə icazə verilir."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Bu proqram Telefon icazəsi olmadan zəng edə bilməz."</string>
+    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Zəngi yerləşdirmək üçün düzgün nömrə daxil edin."</string>
+    <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hazırda çağrı edilə bilməz."</string>
+    <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Video zəng etmək üçün TTY Rejimini deaktiv edin."</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"Səsli poçt nömrəsi çatışmır"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartda heç bir səsli poçt nömrəsi yoxdur."</string>
+    <string name="add_vm_number_str" msgid="4676479471644687453">"Nömrə əlavə edin"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Defolt nömrə yığan tətbiqi dəyişdirilsin?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Defolt nömrə yığan tətbiqi kimi <xliff:g id="CURRENT_APP">%2$s</xliff:g> əvəzinə <xliff:g id="NEW_APP">%1$s</xliff:g> işlədilsin?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Defolt nömrə yığan tətbiqi kimi <xliff:g id="NEW_APP">%s</xliff:g> işlədilsin?"</string>
+</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 63d32b1..49005ff 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Телефон"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Управление на телефонните обаждания"</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_missedCallsTitle" msgid="1361677948941502522">"Пропуснати обаждания"</string>
@@ -31,15 +32,20 @@
     <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>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Собственикът на устройството разрешава само спешни обаждания"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Моля, деактивирайте режима на TTY, за да извършвате видеообаждания."</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="4430590714918044425">"Да се промени ли основното приложение за дайлер?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g> да се използва ли вместо <xliff:g id="CURRENT_APP">%2$s</xliff:g> като основното ви приложение за дайлер?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Да се използва ли <xliff:g id="NEW_APP">%s</xliff:g> като основното ви приложение за дайлер?"</string>
 </resources>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 6922524..ff406db 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ফোন"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ফোন কল ব্যবস্থাপনা"</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_missedCallsTitle" msgid="1361677948941502522">"মিসড কলগুলি"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"ডিভাইসের মালিক শুধুমাত্র জরুরি কলগুলিতে অনুমতি দিয়েছেন"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"কলিং অ্যাকাউন্টগুলি"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"অনুগ্রহ করে ভিডিও কলগুলি করতে TTY মোড অক্ষম করুন৷"</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="4430590714918044425">"ডিফল্ট ডায়ালার অ্যাপ পরিবর্তন করবেন?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"আপনার ডিফল্ট ডায়লার অ্যাপ হিসেবে <xliff:g id="CURRENT_APP">%2$s</xliff:g> পরিবর্তে <xliff:g id="NEW_APP">%1$s</xliff:g> ব্যবহার করবেন?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"আপনার ডিফল্ট ডায়লার অ্যাপ হিসেবে <xliff:g id="NEW_APP">%s</xliff:g> ব্যবহার করবেন?"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index e3cf515..f297dab 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telèfon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gestió de trucades telefòniques"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telèfon"</string>
     <string name="unknown" msgid="6878797917991465859">"Desconegut"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Trucada perduda"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Trucades perdudes"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta ràpida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Missatge enviat a <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"El propietari del dispositiu només permet les trucades d\'emergència."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Comptes de trucades"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Aquesta aplicació no pot fer trucades sortints sense el permís del telèfon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Per realitzar una trucada, introdueix un número vàlid."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"En aquest moment no es pot afegir la trucada."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Desactiva el mode de TTY per fer videotrucades."</string>
-    <string name="no_vm_number" msgid="4164780423805688336">"Falta el número de correu de veu"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"No hi ha cap número de correu de veu emmagatzemat a la targeta SIM."</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"Falta el número de la bústia de veu"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"No hi ha cap número de bústia de veu emmagatzemat a la targeta SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Afegeix número"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Vols canviar l\'aplicació de marcador predeterminada?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Vols fer servir <xliff:g id="NEW_APP">%1$s</xliff:g> en lloc de <xliff:g id="CURRENT_APP">%2$s</xliff:g> com a aplicació de marcador predeterminada?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Vols fer servir <xliff:g id="NEW_APP">%s</xliff:g> com a aplicació de marcador predeterminada?"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 259cca9..56c5c76 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Správa telefonních hovorů"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Neznámý volající"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Zmeškaný hovor"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Zmeškané hovory"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Rychlá odpověď"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Zpráva byla odeslána na číslo <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vlastník zařízení povolil pouze tísňová volání."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Účty pro volání"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Tato aplikace nemůže provádět odchozí hovory bez oprávnění k použití telefonu."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Chcete-li uskutečnit hovor, zadejte platné telefonní číslo."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hovor aktuálně nelze přidat."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Chcete-li vést videohovory, vypněte režim TTY."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Chybí číslo hlasové schránky"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Na SIM kartě není uloženo žádné číslo hlasové schránky."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Přidat číslo"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Změnit výchozí aplikaci vytáčení?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Chcete aplikaci <xliff:g id="NEW_APP">%1$s</xliff:g> použít jako výchozí aplikaci vytáčení místo aplikace <xliff:g id="CURRENT_APP">%2$s</xliff:g>?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Chcete aplikaci <xliff:g id="NEW_APP">%s</xliff:g> použít jako výchozí aplikaci vytáčení?"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 83be7ed..5d8fa36 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Opkaldsstyring"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Opkald"</string>
     <string name="unknown" msgid="6878797917991465859">"Ukendt"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Ubesvarede opkald"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Ubesvarede opkald"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Enhedens ejer tillader kun nødopkald."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Opkaldskonti"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Opkaldet kan ikke tilføjes på nuværende tidspunkt."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Deaktiver TTY-tilstanden for at foretage videoopkald."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Telefonsvarernummer mangler"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Der er ikke gemt noget telefonsvarernummer på SIM-kortet."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Tilføj nummer"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Vil du skifte standardappen til opkald?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Brug <xliff:g id="NEW_APP">%1$s</xliff:g> i stedet for <xliff:g id="CURRENT_APP">%2$s</xliff:g> som din standardapp til opkald?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Brug <xliff:g id="NEW_APP">%s</xliff:g> som din standardapp til opkald?"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 29aecb2..fc8e9ae 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Anrufverwaltung"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Unbekannt"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Entgangener Anruf"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Entgangene Anrufe"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Kurzantwort"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Nachricht an <xliff:g id="PHONE_NUMBER">%s</xliff:g> gesendet"</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Der Geräteinhaber hat nur Notrufe zugelassen."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Anrufkonten"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Diese App darf ohne die Berechtigung \"Standard-App für Telefonie\" keine ausgehenden Anrufe tätigen."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Geben Sie eine gültige Nummer ein."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Der Anruf kann momentan nicht hinzugefügt werden."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Bitte deaktivieren Sie den TTY-Modus, um Videoanrufe tätigen zu können."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Fehlende Mailbox-Nummer"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Auf der SIM-Karte ist keine Mailbox-Nummer gespeichert."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Nummer hinzufügen"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Standard-App für Telefonie ändern?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g> statt <xliff:g id="CURRENT_APP">%2$s</xliff:g> als Standard-App für Telefonie verwenden?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> als Standard-App für Telefonie verwenden?"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index a4249bb..1d0d773 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Τηλέφωνο"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Διαχείριση τηλεφωνικών κλήσεων"</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_missedCallsTitle" msgid="1361677948941502522">"Αναπάντητες κλήσεις"</string>
@@ -31,15 +32,21 @@
     <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>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Από τον κάτοχο της συσκευής επιτρέπονται μόνο κλήσεις έκτακτης ανάγκης"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Λογαριασμοί κλήσης"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Απενεργοποιήστε τη λειτουργία TTY για να πραγματοποιήσετε βιντεοκλήσεις."</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="4430590714918044425">"Αλλαγή της προεπιλεγμένης εφαρμογής Dialer;"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Χρήση της εφαρμογής <xliff:g id="NEW_APP">%1$s</xliff:g> αντί για την εφαρμογή <xliff:g id="CURRENT_APP">%2$s</xliff:g> ως προεπιλεγμένης εφαρμογής Dialer;"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Χρήση της εφαρμογής <xliff:g id="NEW_APP">%s</xliff:g> ως προεπιλεγμένης εφαρμογής Dialer;"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 97d4c9c..78e374e 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Phone"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Phone Call Management"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telephone"</string>
     <string name="unknown" msgid="6878797917991465859">"Unknown"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Missed call"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Missed calls"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Only emergency calls are allowed by the device owner"</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 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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Please disable TTY Mode to make video calls."</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="4430590714918044425">"Change default Dialer app?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Use <xliff:g id="NEW_APP">%1$s</xliff:g> instead of <xliff:g id="CURRENT_APP">%2$s</xliff:g> as your default dialler app?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Use <xliff:g id="NEW_APP">%s</xliff:g> as your default dialler app?"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 97d4c9c..78e374e 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Phone"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Phone Call Management"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telephone"</string>
     <string name="unknown" msgid="6878797917991465859">"Unknown"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Missed call"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Missed calls"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Only emergency calls are allowed by the device owner"</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 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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Please disable TTY Mode to make video calls."</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="4430590714918044425">"Change default Dialer app?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Use <xliff:g id="NEW_APP">%1$s</xliff:g> instead of <xliff:g id="CURRENT_APP">%2$s</xliff:g> as your default dialler app?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Use <xliff:g id="NEW_APP">%s</xliff:g> as your default dialler app?"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 97d4c9c..78e374e 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Phone"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Phone Call Management"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telephone"</string>
     <string name="unknown" msgid="6878797917991465859">"Unknown"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Missed call"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Missed calls"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Only emergency calls are allowed by the device owner"</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 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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Please disable TTY Mode to make video calls."</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="4430590714918044425">"Change default Dialer app?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Use <xliff:g id="NEW_APP">%1$s</xliff:g> instead of <xliff:g id="CURRENT_APP">%2$s</xliff:g> as your default dialler app?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Use <xliff:g id="NEW_APP">%s</xliff:g> as your default dialler app?"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 3b6bd73..4285c28 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -16,17 +16,18 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Teléfono"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Administración de llamadas telefónicas"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Teléfono"</string>
     <string name="unknown" msgid="6878797917991465859">"Desconocida"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Llamada perdida"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Llamadas perdidas"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> llamadas perdidas"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Se perdieron las llamadas de <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Devolver llamada"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Llamar"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Mensaje"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Llamada silenciada"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Altavoz habilitado"</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"No puedo hablar ahora. ¿Qué pasa?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"No puedo hablar ahora. ¿Todo bien?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Te llamo enseguida."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Te llamo más tarde."</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"No puedo hablar ahora. ¿Me llamas más tarde?"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respuesta rápida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaje enviado a <xliff:g id="PHONE_NUMBER">%s</xliff:g>"</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"El propietario del dispositivo solo permite las llamadas de emergencia."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Cuentas telefónicas"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Esta aplicación no puede realizar llamadas salientes sin permiso del teléfono."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar una llamada, ingresa un número válido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No se puede agregar la llamada en este momento."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Inhabilita el modo TTY para realizar una videollamada."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Falta el número de correo de voz"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"No hay un número de correo de voz almacenado en la tarjeta SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Agregar número"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"¿Quieres cambiar la aplicación Marcador predeterminada?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"¿Quieres usar <xliff:g id="NEW_APP">%1$s</xliff:g> en lugar de <xliff:g id="CURRENT_APP">%2$s</xliff:g> como la aplicación de marcado predeterminada?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"¿Quieres usar <xliff:g id="NEW_APP">%s</xliff:g> como la aplicación de marcado predeterminada?"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 7287095..071ddba 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Teléfono"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gestión de llamadas del teléfono"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Teléfono"</string>
     <string name="unknown" msgid="6878797917991465859">"Desconocido"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Llamada perdida"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Llamadas perdidas"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respuestas rápidas"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaje enviado a <xliff:g id="PHONE_NUMBER">%s</xliff:g>"</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"El propietario del dispositivo solo permite llamadas de emergencia"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Cuentas de llamadas"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Esta aplicación no puede hacer llamadas sin permiso del teléfono."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar una llamada, introduce un número válido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No se puede añadir la llamada en este momento."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Inhabilita el modo TTY para hacer videollamadas."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Falta el número del buzón de voz."</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"No se ha almacenado ningún número de buzón de voz en la tarjeta SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Añadir número"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"¿Cambiar el marcador predeterminado?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"¿Quieres utilizar <xliff:g id="NEW_APP">%1$s</xliff:g> en lugar de <xliff:g id="CURRENT_APP">%2$s</xliff:g> como marcador predeterminado?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"¿Quieres utilizar <xliff:g id="NEW_APP">%s</xliff:g> como marcador predeterminado?"</string>
 </resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 47627f8..b6090f3 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefonikõnede haldus"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Tundmatu"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Vastamata kõne"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Vastamata kõned"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Kiirvastus"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Sõnum on saadetud numbrile <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Seadme omanik lubab ainult hädaabikõnesid"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Kõnekontod"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"See rakendus ei saa ilma telefoni kasutamise loata välja helistada."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Helistamiseks sisestage kehtiv number."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Kõnet ei saa praegu lisada."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Videokõnede tegemiseks keelake TTY-režiim."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Puudub kõnepostinumber"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM-kaardile pole salvestatud ühtegi kõnepostinumbrit."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Lisa number"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Kas muuta helistamise vaikerakendust?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Kas kasutada rakendust <xliff:g id="NEW_APP">%1$s</xliff:g> rakenduse <xliff:g id="CURRENT_APP">%2$s</xliff:g> asemel helistamise vaikerakendusena?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Kas kasutada rakendust <xliff:g id="NEW_APP">%s</xliff:g> helistamise vaikerakendusena?"</string>
 </resources>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index e35b3fe..654f115 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefonoa"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefono-deien kudeaketa"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefonoa"</string>
     <string name="unknown" msgid="6878797917991465859">"Ezezaguna"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Dei galdua"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Dei galduak"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Erantzun bizkorra"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mezua bidali da <xliff:g id="PHONE_NUMBER">%s</xliff:g> zenbakira."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Gailuaren jabeak larrialdi-deiak bakarrik egitea onartzen du"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Deiak egiteko kontuak"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Aplikazioak irteerako deiak egin ahal izan ditzan, telefonoaren eginbidea erabiltzeko baimena behar du."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Deitzeko, idatzi balio duen zenbaki bat."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Une honetan ezin da deirik gehitu."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Desgaitu TTY modua bideo-deiak egiteko."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Erantzungailuaren zenbakia falta da"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Ez da erantzungailuaren zenbakirik gorde SIM txartelean."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Gehitu zenbakia"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Telefono-aplikazio lehenetsia aldatu nahi duzu?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> aplikazioaren ordez <xliff:g id="NEW_APP">%1$s</xliff:g> erabili nahi duzu telefono-aplikazio lehenetsi gisa?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> telefono-aplikazio lehenetsi gisa erabili nahi duzu?"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 61d4ae1..25e380a 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"تلفن"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"مدیریت تماس تلفنی"</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_missedCallsTitle" msgid="1361677948941502522">"تماس‌های بی پاسخ"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"مالک دستگاه فقط تماس‌های اضطراری را مجاز کرده است"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"حساب‌های تماس"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"‏لطفاً حالت TTY را برای برقراری تماس‌های ویدیویی غیرفعال کنید."</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="4430590714918044425">"برنامه شماره‌گیر پیش‌فرض تغییر کند؟"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"از <xliff:g id="NEW_APP">%1$s</xliff:g> به جای <xliff:g id="CURRENT_APP">%2$s</xliff:g> به عنوان برنامه شماره‌گیر پیش‌فرض استفاده شود؟"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"از <xliff:g id="NEW_APP">%s</xliff:g> به عنوان برنامه شماره‌گیر پیش‌فرض استفاده شود؟"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 1ff2b0b..f253000 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -16,30 +16,37 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Puhelin"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Puhelujen hallinta"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Puhelin"</string>
     <string name="unknown" msgid="6878797917991465859">"Tuntematon"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Vastaamatta jäänyt puhelu"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Vastaamattomat puhelut"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> vastaamatonta puhelua"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Vastaamatta jäänyt puhelu numerosta <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Soita takaisin"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Soita"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Viesti"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Puhelu mykistetty."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Kaiutin käytössä."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"En voi puhua nyt. Mikä hätänä?"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Soitan sinulle heti takaisin."</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"En voi vastata - mitä asiaa?"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Soitan sinulle pian."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Soitan sinulle myöhemmin."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"En voi puhua nyt. Soita myöhemmin?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"En voi vastata. Soitatko myöhemmin?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Pikavastaukset"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Muokkaa pikavastausta"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Pikavastaukset"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Viesti lähetetty numeroon <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Laitteen omistaja on sallinut vain hätäpuhelut"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Puhelutilit"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Tämä sovellus ei voi soittaa puheluita ilman Puhelin-lupaa."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Soita antamalla kelvollinen numero."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Puhelua ei voi lisätä juuri nyt."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Poista TTY-tila käytöstä, jos haluat soittaa videopuheluita."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Puhelinvastaajan numero puuttuu"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM-kortille ei ole tallennettu puhelinvastaajan numeroa."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Lisää numero"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Vaihdetaanko oletuspuhelusovellus?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Asetetaanko oletuspuhelusovellukseksi <xliff:g id="NEW_APP">%1$s</xliff:g> sovelluksen <xliff:g id="CURRENT_APP">%2$s</xliff:g> sijaan?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Asetetaanko <xliff:g id="NEW_APP">%s</xliff:g> oletuspuhelusovellukseksi?"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 1aa0bdb..bffa229 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Téléphone"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gestion des appels téléphoniques"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Téléphone"</string>
     <string name="unknown" msgid="6878797917991465859">"Inconnu"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Appel manqué"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Appels manqués"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Réponse rapide"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Message envoyé à <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Seuls les appels d\'urgence sont autorisés par le propriétaire de l\'appareil"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Comptes d\'appel"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Cette application ne peut pas faire d\'appels sans l\'autorisation de l\'application Téléphone."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Pour faire un appel, entrez un numéro valide."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Impossible d\'ajouter l\'appel pour le moment."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Veuillez désactiver le mode ATS pour faire un appel vidéo."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Numéro de messagerie vocale manquant"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Aucun numéro de messagerie vocale n\'est enregistré sur la carte SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Ajouter un numéro"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Changer l\'application de composition par défaut?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Utiliser <xliff:g id="NEW_APP">%1$s</xliff:g> au lieu de <xliff:g id="CURRENT_APP">%2$s</xliff:g> comme application de composition par défaut?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Utiliser <xliff:g id="NEW_APP">%s</xliff:g> comme application de composition par défaut?"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f5f8afd..e8ee088 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Téléphone"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gestion des appels téléphoniques"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Téléphone"</string>
     <string name="unknown" msgid="6878797917991465859">"Inconnu"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Appel manqué"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Appels manqués"</string>
@@ -31,15 +32,21 @@
     <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_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_title_2" msgid="6104662227299493906">"Modifier réponses rapides"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Réponse rapide"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Message envoyé à <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Le propriétaire de l\'appareil n\'autorise que les appels d\'urgence."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Comptes téléphoniques"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Cette application ne peut pas passer d\'appels sortants sans l\'autorisation de l\'application Téléphone."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Pour émettre un appel, veuillez saisir un numéro valide."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Impossible d\'ajouter un appel pour le moment."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Veuillez désactiver le mode TTY pour passer des appels vidéo."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Numéro de messagerie vocale manquant"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Aucun numéro de messagerie vocale n\'est enregistré sur la carte SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Ajouter un numéro"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Modifier l\'application de clavier par défaut ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Utiliser <xliff:g id="NEW_APP">%1$s</xliff:g> et non plus <xliff:g id="CURRENT_APP">%2$s</xliff:g> comme application de clavier par défaut ?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Utiliser <xliff:g id="NEW_APP">%s</xliff:g> comme application de clavier par défaut ?"</string>
 </resources>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 628caa9..f64da11 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Teléfono"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Xestión de chamadas do teléfono"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Teléfono"</string>
     <string name="unknown" msgid="6878797917991465859">"Descoñecido"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Chamada perdida"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Chamadas perdidas"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensaxe enviada ao <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"O propietario do dispositivo só permite as chamadas de emerxencia"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Contas de chamadas"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Desactiva o modo TTY para realizar videochamadas."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Falta o número de correo de voz"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Non hai ningún número de correo de voz almacenado na tarxeta SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Engadir número"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Queres cambiar a aplicación predeterminada do marcador?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Queres usar <xliff:g id="NEW_APP">%1$s</xliff:g> en lugar de <xliff:g id="CURRENT_APP">%2$s</xliff:g> como a túa aplicación de marcador predeterminada?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Queres usar <xliff:g id="NEW_APP">%s</xliff:g> como a túa aplicación de marcador predeterminada?"</string>
 </resources>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 8d0ad60..12e163c 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ફોન"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ફોન કૉલ સંચાલન"</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_missedCallsTitle" msgid="1361677948941502522">"છૂટેલા કૉલ્સ"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"ઉપકરણના માલિક દ્વારા ફક્ત કટોકટીના કૉલ્સને મંજૂરી અપાયેલ છે"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"કૉલિંગ એકાઉન્ટ્સ"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"કૃપા કરીને વિડિઓ કૉલ્સ કરવા માટે TTY મોડ અક્ષમ કરો."</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="4430590714918044425">"ડિફોલ્ટ ડાયલર એપ્લિકેશન બદલીએ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"તમારી ડિફોલ્ટ ડાયલર એપ્લિકેશન તરીકે <xliff:g id="CURRENT_APP">%2$s</xliff:g> ને બદલે <xliff:g id="NEW_APP">%1$s</xliff:g> નો ઉપયોગ કરીએ?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"તમારી ડિફોલ્ટ ડાયલર એપ્લિકેશન તરીકે <xliff:g id="NEW_APP">%s</xliff:g> નો ઉપયોગ કરીએ?"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 760fe63..09c654b 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"फ़ोन"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"फ़ोन कॉल प्रबंधन"</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_missedCallsTitle" msgid="1361677948941502522">"छूटी कॉल"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"डिवाइस स्‍वामी द्वारा केवल आपातकालीन कॉल करने की अनुमति है"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"कृपया वीडियो कॉल करने के लिए TTY मोड अक्षम करें."</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="4430590714918044425">"डिफ़ॉल्‍ट डायलर ऐप को बदलें?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"अपने डिफ़ॉल्‍ट डायलर ऐप के रूप में <xliff:g id="CURRENT_APP">%2$s</xliff:g> के बजाय <xliff:g id="NEW_APP">%1$s</xliff:g> का उपयोग करें?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"अपने डिफ़ॉल्‍ट डायलर ऐप के रूप में <xliff:g id="NEW_APP">%s</xliff:g> का उपयोग करें?"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 5242229..cf9a5e9 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Upravljanje telefonskim pozivima"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Nepoznato"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Propušteni poziv"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Propušteni pozivi"</string>
@@ -29,17 +30,23 @@
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Sada ne mogu razgovarati. Što ima?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Nazvat ću vas odmah."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Zvat ću vas kasnije."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Sada ne mogu razgovarati. Nazovite me kasnije?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Sada ne mogu razgovarati. Nazovite 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">"Uređivanje brzih odgovora"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Brzi odgovor"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Poruka poslana na broj <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vlasnik uređaja dopušta samo hitne pozive"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Računi za pozivanje"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Ova aplikacija ne može uspostavljati odlazne pozive bez dopuštenja za telefon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Unesite važeći broj da biste uspostavili poziv."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Poziv trenutačno nije moguć."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Onemogućite TTY način da biste omogućili videopozive."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Nedostaje broj govorne pošte"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Na SIM kartici nije spremljen broj govorne pošte."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Dodaj broj"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Želite li promijeniti zadanu aplikaciju brojčanika?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Želite li upotrebljavati <xliff:g id="NEW_APP">%1$s</xliff:g> umjesto aplikacije <xliff:g id="CURRENT_APP">%2$s</xliff:g> kao zadanu aplikaciju brojčanika?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Želite li upotrebljavati <xliff:g id="NEW_APP">%s</xliff:g> kao zadanu aplikaciju brojčanika?"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 69fb335..4796321 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Híváskezelés"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Ismeretlen"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Nem fogadott hívás"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Nem fogadott hívások"</string>
@@ -35,11 +36,16 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Gyors válasz"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Üzenet elküldve ide: <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Az eszköz tulajdonosa csak a segélyhívásokat engedélyezte"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Telefonos fiókok"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Csak vészhívás engedélyezett."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Az alkalmazásból nem lehet kimenő hívást kezdeményezni a Telefon (Phone) engedély nélkül."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Hívásindításhoz adjon meg egy érvényes számot."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Jelenleg nem lehet videohívást hozzáadni."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Videohívások indításához kapcsolja ki a TTY módot."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Hiányzik a hangposta száma"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Nincs hangpostaszám a SIM kártyán."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Szám hozzáadása"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Módosítja az alapértelmezett tárcsázó alkalmazást?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Legyen a(z) <xliff:g id="NEW_APP">%1$s</xliff:g> az alapértelmezett tárcsázó alkalmazás a(z) <xliff:g id="CURRENT_APP">%2$s</xliff:g> helyett?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Legyen a(z) <xliff:g id="NEW_APP">%s</xliff:g> az alapértelmezett tárcsázó alkalmazás?"</string>
 </resources>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index c38c6fc..05a7746 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Հեռախոս"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Հեռախոսային զանգերի կառավարում"</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_missedCallsTitle" msgid="1361677948941502522">"Բաց թողնված զանգեր"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Սարքի սեփականատերը թույլատրում է միայն արտակարգ իրավիճակի զանգերը"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Հաշիվներ զանգերի համար"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Տեսազանգեր կատարելու համար անջատեք TTY ռեժիմը:"</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="4430590714918044425">"Փոխե՞լ կանխադրված Համարհավաքի հավելվածը:"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Օգտագործե՞լ <xliff:g id="NEW_APP">%1$s</xliff:g> հավելվածը <xliff:g id="CURRENT_APP">%2$s</xliff:g>-ի փոխարեն որպես համարհավաքի կանխադրված հավելված:"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Դարձնե՞լ <xliff:g id="NEW_APP">%s</xliff:g> հավելվածը համարհավաքի կանխադրված հավելված:"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 7ad8e00..59c3ca9 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -16,13 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telepon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Pengelolaan Panggilan Telepon"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telepon"</string>
     <string name="unknown" msgid="6878797917991465859">"Tidak diketahui"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Panggilan tak terjawab"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Panggilan tak terjawab"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> panggilan tak terjawab"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Panggilan tak terjawab dari <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Hubungi kembali"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Telepon"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Pesan"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Panggilan disenyapkan."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Pengeras suara ponsel diaktifkan."</string>
@@ -30,16 +31,21 @@
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Saya segera telepon balik."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Nanti saya telepon balik."</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Tak bisa bicara skrg. Tlp lg nanti?"</string>
-    <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Tanggapan cepat"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit tanggapan cepat"</string>
+    <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Respons cepat"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit respons cepat"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
-    <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Tanggapan cepat"</string>
+    <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respons cepat"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Pesan dikirim ke <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Hanya panggilan darurat yang diizinkan oleh pemilik perangkat"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Akun pemanggil"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Hanya panggilan darurat yang diizinkan."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Aplikasi ini tidak dapat melakukan panggilan keluar tanpa izin Telepon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Untuk melakukan panggilan telepon, masukkan nomor yang valid."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Panggilan tidak dapat ditambahkan untuk saat ini."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Nonaktifkan Mode TTY untuk melakukan panggilan video."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Nomor kotak pesan hilang"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Tidak ada nomor kotak pesan tersimpan pada kartu SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Tambahkan nomor"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Ubah aplikasi Pemanggil default?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Gunakan <xliff:g id="NEW_APP">%1$s</xliff:g>, bukan <xliff:g id="CURRENT_APP">%2$s</xliff:g> sebagai aplikasi pemanggil default?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Gunakan <xliff:g id="NEW_APP">%s</xliff:g> sebagai aplikasi pemanggil default?"</string>
 </resources>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 13827b9..59e37ac 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Sími"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Símtalastjórnun"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Sími"</string>
     <string name="unknown" msgid="6878797917991465859">"Óþekkt"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Ósvarað símtal"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Ósvöruð símtöl"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Snarsvar"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Skilaboð send til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Eigandi tækisins leyfir aðeins neyðarsímtöl"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Símtalareikningar"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Þetta forrit getur ekki hringt án heimildar í símanum."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Sláðu inn gilt númer til að hringja símtal."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Ekki er hægt að bæta símtali við sem stendur."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Slökktu á fjarritastillingu til að hringja myndsímtöl."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Talhólfsnúmer vantar"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Ekkert talhólfsnúmer er vistað á SIM-kortinu."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Bæta númeri við"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Viltu skipta um sjálfgefið hringiforrit?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Nota <xliff:g id="NEW_APP">%1$s</xliff:g> í stað <xliff:g id="CURRENT_APP">%2$s</xliff:g> sem sjálfgefið hringiforrit?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Nota <xliff:g id="NEW_APP">%s</xliff:g> sem sjálfgefið hringiforrit?"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 0505e37..aa42c41 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefono"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Phone Call Management"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefono"</string>
     <string name="unknown" msgid="6878797917991465859">"Sconosciuto"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Chiamata senza risposta"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Chiamate senza risposta"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Risposta rapida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Messaggio inviato a <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Il proprietario del dispositivo consente soltanto chiamate di emergenza"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Account di chiamata"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Non è possibile effettuare chiamate tramite questa applicazione senza l\'autorizzazione sul telefono."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Per effettuare una chiamata, inserisci un numero valido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Al momento non è possibile aggiungere la chiamata."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Per poter fare videochiamate devi disattivare la modalità TTY."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Numero segreteria mancante"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Nessun numero di segreteria presente nella SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Aggiungi numero"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Modificare l\'app tastiera predefinita?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Utilizzare <xliff:g id="NEW_APP">%1$s</xliff:g> invece di <xliff:g id="CURRENT_APP">%2$s</xliff:g> come app tastiera predefinita?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Utilizzare <xliff:g id="NEW_APP">%s</xliff:g> come app tastiera predefinita?"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index d8bdb1e..cc82904 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"טלפון"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ניהול שיחות טלפון"</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_missedCallsTitle" msgid="1361677948941502522">"שיחות שלא נענו"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"בעלי המכשיר מתיר לבצע שיחות חירום בלבד"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"‏השבת את מצב TTY כדי לבצע שיחות וידאו."</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="4430590714918044425">"האם לשנות את אפליקציית החייגן שבברירת מחדל?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"האם להשתמש ב-<xliff:g id="NEW_APP">%1$s</xliff:g> במקום ב-<xliff:g id="CURRENT_APP">%2$s</xliff:g> כאפליקציית החייגן שבברירת מחדל?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"האם להשתמש ב-<xliff:g id="NEW_APP">%s</xliff:g> כאפליקציית החייגן שבברירת מחדל?"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 551f281..0a9506f 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"電話"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"通話管理"</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_missedCallsTitle" msgid="1361677948941502522">"不在着信"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"端末の所有者に許可されているのは緊急通報のみです。"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"通話アカウント"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"ビデオハングアウトを行うにはTTYモードを無効にしてください。"</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="4430590714918044425">"既定の電話アプリを変更しますか?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g>を<xliff:g id="CURRENT_APP">%2$s</xliff:g>の代わりに既定の電話アプリとして使用しますか?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g>を既定の電話アプリとして使用しますか?"</string>
 </resources>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index ac1c20b..50d9375 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ტელეფონი"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ტელეფონის ზარების მართვა"</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_missedCallsTitle" msgid="1361677948941502522">"გამოტოვებული ზარები"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"მოწყობილობის მფლობელის მიერ ნებადართულია მხოლოდ საგანგებო ზარების განხორციელება"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"დარეკვის ანგარიშები"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"ვიდეოზარების განხორციელებისათვის, გთხოვთ, გამორთოთ TTY რეჟიმი"</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="4430590714918044425">"გსურთ ამკრეფის ნაგულისხმევი აპის შეცვლა?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"გსურთ ამკრეფის ნაგულისხმევ აპად <xliff:g id="CURRENT_APP">%2$s</xliff:g>-ის ნაცვლად <xliff:g id="NEW_APP">%1$s</xliff:g>-ის გამოყენება?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"გსურთ, გამოიყენოთ <xliff:g id="NEW_APP">%s</xliff:g>, როგორც ამკრეფის ნაგულისხმევი აპი?"</string>
 </resources>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 89be55c..7c69704 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -16,13 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Телефон"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Телефон қоңырауларын басқару"</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_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_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>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Құрылғы иесі тек жедел қоңырауларға рұқсат еткен"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Бейне қоңырау шалу үшін Телетайп режимін өшіріңіз."</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="4430590714918044425">"Әдепкі нөмір тергіш қолданбаны өзгерткіңіз келе ме?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> орнына <xliff:g id="NEW_APP">%1$s</xliff:g> қолданбасын әдепкі тергіш қолданба ретінде пайдалану қажет пе?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> қолданбасын әдепкі нөмір тергіш қолданба ретінде пайдалану қажет пе?"</string>
 </resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 1a019e8..cc0a951 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ទូរស័ព្ទ"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ការគ្រប់គ្រងការហៅទូរស័ព្ទ"</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_missedCallsTitle" msgid="1361677948941502522">"ខកខាន​ទទួល"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"ការហៅពេលមានអាសន្នដែលត្រូវបានអនុញ្ញាតដោយម្ចាស់ឧបករណ៍តែប៉ុណ្ណោះ។"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"សូមបិទដំណើរការរបៀប TTY ដើម្បីធ្វើការហៅជាវីដេអូ។"</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="4430590714918044425">"ប្តូរកម្មវិធីហៅទូរស័ព្ទលំនាំដើម?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"ប្រើ <xliff:g id="NEW_APP">%1$s</xliff:g> ជំនួសឲ្យ <xliff:g id="CURRENT_APP">%2$s</xliff:g> ជាកម្មវិធីហៅទូរស័ព្ទលំនាំដើមរបស់អ្នក?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"ប្រើ <xliff:g id="NEW_APP">%s</xliff:g> ជាកម្មវិធីហៅទូរស័ព្ទលំនាំដើមរបស់អ្នក?"</string>
 </resources>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index e84afc9..37da2d4 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ಫೋನ್"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ಫೋನ್ ಕರೆ ನಿರ್ವಹಣೆ"</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_missedCallsTitle" msgid="1361677948941502522">"ತಪ್ಪಿದ ಕರೆಗಳು"</string>
@@ -26,20 +27,26 @@
     <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_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="outgoing_call_not_allowed" msgid="1435394568102165287">"ಸಾಧನದ ಮಾಲೀಕರಿಂದ ತುರ್ತು ಕರೆಗಳನ್ನು ಮಾಡಲು ಮಾತ್ರ ಅವಕಾಶವಿದೆ"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"ಕರೆ ಮಾಡುವ ಖಾತೆಗಳು"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"ವೀಡಿಯೊ ಕರೆಗಳನ್ನು ಮಾಡಲು TTY ಮೋಡ್‌ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ."</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="4430590714918044425">"ಡೀಫಾಲ್ಟ್ ಡಯಲರ್ ಅಪ್ಲಿಕೇಶನ್ ಬದಲಾಯಿಸುವುದೇ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> ಬದಲಿಗೆ <xliff:g id="NEW_APP">%1$s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಡಯಲರ್ ಅಪ್ಲಿಕೇಶನ್ ಆಗಿ ಬಳಸುವುದೇ?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಡಯಲರ್ ಅಪ್ಲಿಕೇಶನ್ ಆಗಿ ಬಳಸುವುದೇ?"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index efb59cd..5080f15 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"전화"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"통화 관리"</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_missedCallsTitle" msgid="1361677948941502522">"부재중 통화"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"기기 소유자만 긴급 전화를 사용할 수 있습니다."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"통화 계정"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"화상 통화를 하려면 TTY 모드를 중지하세요."</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="4430590714918044425">"기본 다이얼러 앱을 변경하시겠습니까?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> 대신 <xliff:g id="NEW_APP">%1$s</xliff:g>을(를) 기본 다이얼러 앱으로 사용하시겠습니까?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g>을(를) 기본 다이얼러 앱으로 사용하시겠습니까?"</string>
 </resources>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 6b5e9c3..a65db25 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -16,42 +16,37 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Телефон"</string>
-    <!-- no translation found for unknown (6878797917991465859) -->
-    <skip />
-    <!-- no translation found for notification_missedCallTitle (7554385905572364535) -->
-    <skip />
-    <!-- no translation found for notification_missedCallsTitle (1361677948941502522) -->
-    <skip />
-    <!-- no translation found for notification_missedCallsMsg (4575787816055205600) -->
-    <skip />
-    <!-- no translation found for notification_missedCallTicker (504686252427747209) -->
-    <skip />
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Телефон чалууларын башкаруу"</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_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>
-    <!-- no translation found for respond_via_sms_canned_response_1 (2461606462788380215) -->
-    <skip />
-    <!-- no translation found for respond_via_sms_canned_response_2 (4074450431532859214) -->
-    <skip />
-    <!-- no translation found for respond_via_sms_canned_response_3 (3496079065723960450) -->
-    <skip />
-    <!-- no translation found for respond_via_sms_canned_response_4 (1698989243040062190) -->
-    <skip />
+    <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Түзмөк ээси шашылыш чалууларга гана уруксат берген"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Чалуу каттоо эсептери"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Видео чалууларды аткаруу үчүн Телетайп түзмөк режимин өчүрүңүз."</string>
-    <!-- no translation found for no_vm_number (4164780423805688336) -->
-    <skip />
-    <!-- no translation found for no_vm_number_msg (1300729501030053828) -->
-    <skip />
-    <!-- no translation found for add_vm_number_str (4676479471644687453) -->
-    <skip />
+    <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="4430590714918044425">"Демейки номер тергич колдонмо өзгөрүлсүнбү?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Демейки номер тергич колдонмо катары мурунку <xliff:g id="CURRENT_APP">%2$s</xliff:g> колдонмонун ордуна <xliff:g id="NEW_APP">%1$s</xliff:g> бул колдонмо колдонулсунбу?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Демейки номер тергич колдонмо катары <xliff:g id="NEW_APP">%s</xliff:g> колдонулсунбу?"</string>
 </resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 52479c3..6c41d65 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ໂທລະສັບ"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ການ​ຈັດ​ການການ​ໂທລະ​ສັບ"</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_missedCallsTitle" msgid="1361677948941502522">"ສາຍທີ່ບໍ່ໄດ້ຮັບ"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"ພຽງ​ແຕ່​ການ​ໂທ​ສຸກ​ເສີນ​ທີ່​ໄດ້​ຮັບ​ອະ​ນຸ​ຍາດ​ຈາກ​ເຈົ້າ​ຂອງ​ອຸ​ປະ​ກອນ​ເທົ່າ​ນັ້ນ"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"ບັນ​ຊີ​ໂທ"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"ກະ​ລຸ​ນາ​ປິດ​ໃຊ້​ງານ​ໂໝດ TTY ເພື່ອ​ໂທວິ​ດີ​ໂອ."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"ບໍ່ມີເບີຂໍ້ຄວາມສຽງ"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"ບໍ່ມີເບີຂໍ້ຄວາມສຽງຖືກບັນທຶກໃນ SIM card."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"ເພີ່ມໝາຍເລກ"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"ປ່ຽນ​ແປງ​ແອັບ​ແຜ່ນ​ກົດ​ມາດ​ຕະ​ຖານ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"ໃຊ້ <xliff:g id="NEW_APP">%1$s</xliff:g> ແທນ <xliff:g id="CURRENT_APP">%2$s</xliff:g> ເປັນ​ແອັບ​ແຜ່ນ​ກົດ​ມາດ​ຕະ​ຖານ​ຂອງ​ທ່ານ?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"ໃຊ້ <xliff:g id="NEW_APP">%s</xliff:g> ເປັນ​ແອັບ​ແຜ່ນ​ກົດ​ມາດ​ຕະ​ຖານ​ຂອງ​ທ່ານ?"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 8b0f066..d0b47cc 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefonas"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefono skambučių tvarkymas"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefonas"</string>
     <string name="unknown" msgid="6878797917991465859">"Nežinomas"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Praleistas skambutis"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Praleisti skambučiai"</string>
@@ -35,11 +36,16 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Greitas atsakas"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Pranešimas išsiųstas numeriu <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Įrenginio savininkas leidžia skambinti tik pagalbos numeriais"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Skambinimo paskyros"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Leidžiami tik skambučiai pagalbos numeriu."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Naudojant šią programą negalima skambinti be telefono leidimo."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Kad galėtumėte paskambinti, įveskite tinkamą numerį."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Šiuo metu dar vieno skambučio atlikti negalima."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Išjunkite TTY režimą, kad galėtumėte atlikti vaizdo skambučius."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Trūksta balso pašto numerio"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kortelėje nėra išsaugoto balso pašto numerio."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Pridėti numerį"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Pakeisti numatytąją numerio rinkiklio programą?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Naudoti „<xliff:g id="NEW_APP">%1$s</xliff:g>“ vietoje „<xliff:g id="CURRENT_APP">%2$s</xliff:g>“ kaip numatytąją numerio rinkiklio programą?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Naudoti „<xliff:g id="NEW_APP">%s</xliff:g>“ kaip numatytąją numerio rinkiklio programą?"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 43f0027..bac3368 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Tālrunis"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Tālruņa zvanu pārvaldība"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Tālrunis"</string>
     <string name="unknown" msgid="6878797917991465859">"Nezināms"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Neatbildēts zvans"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Neatbildētie zvani"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Ātrā atbilde"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Ziņojums nosūt. uz šādu tālr. nr.: <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Ierīces īpašnieks ļauj veikt tikai ārkārtas zvanus."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Zvanu konti"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Šajā lietojumprogrammā nevar veikt izejošos zvanus bez tālruņa atļaujas."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Lai veiktu zvanu, ievadiet derīgu numuru."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Šobrīd nevar pievienot zvanu."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Lai veiktu videozvanus, lūdzu, atspējojiet teksta tālruņa režīmu."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Trūkst balss pasta numura"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartē neviens balss pasta numurs nav saglabāts."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Pievienot numuru"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Vai mainīt numura sastādītāja noklusējuma lietotni?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Vai lietotnes <xliff:g id="CURRENT_APP">%2$s</xliff:g> vietā izmantot <xliff:g id="NEW_APP">%1$s</xliff:g> kā numura sastādītāja noklusējuma lietotni?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Vai izmantot <xliff:g id="NEW_APP">%s</xliff:g> kā numura sastādītāja noklusējuma lietotni?"</string>
 </resources>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 9654e0b..3c87169 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Телефон"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Управување со телефонски повици"</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_missedCallsTitle" msgid="1361677948941502522">"Пропуштени повици"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Дозволени се само итни повици од страна на сопственикот на уредот"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Сметки за повици"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Оневозможете го режимот TTY за да остварите видеоповици."</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="4430590714918044425">"Смени ја стандардната апликација Бирач?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Користи <xliff:g id="NEW_APP">%1$s</xliff:g> наместо <xliff:g id="CURRENT_APP">%2$s</xliff:g> како стандардна апликација за бирање?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Користи <xliff:g id="NEW_APP">%s</xliff:g> како стандардна апликација за бирање?"</string>
 </resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 38fc0e6..7bd5029 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ഫോണ്‍"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ഫോൺ കോൾ മാനേജ്‌മെന്റ്"</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_missedCallsTitle" msgid="1361677948941502522">"മിസ്‌ഡ് കോളുകൾ"</string>
@@ -31,15 +32,20 @@
     <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>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"ഉപകരണ ഉടമ അടിയന്തിര കോളുകൾ മാത്രമേ അനുവദിച്ചിട്ടുള്ളൂ"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"വീഡിയോ കോളുകൾ നടത്താൻ TTY മോഡ് പ്രവർത്തനരഹിതമാക്കുക."</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="4430590714918044425">"സ്ഥിര ഡയലർ ആപ്പ് മാറ്റണോ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> എന്നതിനുപകരം <xliff:g id="NEW_APP">%1$s</xliff:g> എന്നതിനെ നിങ്ങളുടെ സ്ഥിര ഡയലർ ആപ്പ് ആയി ഉപയോഗിക്കണോ?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> എന്നതിനെ നിങ്ങളുടെ സ്ഥിര ഡയലർ ആപ്പ് ആയി ഉപയോഗിക്കണോ?"</string>
 </resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 828f347..1ecabc4 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Утас"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Гар утасны Дуудлагын Удирдлага"</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_missedCallsTitle" msgid="1361677948941502522">"Аваагүй дуудлагууд"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Энэхүү төхөөрөмжийн эзэмшигч нь зөвхөн түргэн тусламжийн дуудлага хийх эрхтэй байна."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Дуудлагын эрхтэй бүртгэлүүд"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Видео дуудлага хийхийн тулд TTY горимыг идэвхгүй болгоно уу."</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="4430590714918044425">"Үндсэн залгагч апп-ыг өөрчлөх үү?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g>-ыг <xliff:g id="CURRENT_APP">%2$s</xliff:g>-ын оронд таны үндсэн залгагч апп болгон тохируулах уу?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g>-ыг таны үндсэн залгагч апп болгон тохируулах уу?"</string>
 </resources>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index c7c795c..c361736 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"फोन"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"फोन कॉल व्यवस्थापन"</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_missedCallsTitle" msgid="1361677948941502522">"सुटलेले कॉल"</string>
@@ -26,20 +27,25 @@
     <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>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"द्रुत प्रतिसाद"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"संदेश <xliff:g id="PHONE_NUMBER">%s</xliff:g> वर पाठविला."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"डिव्हाइस मालकाद्वारे केवळ आणीबाणी कॉलना अनुमती आहे"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"कृपया व्हिडिओ कॉल करण्‍यासाठी TTY मोड अक्षम करा."</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="4430590714918044425">"डीफॉल्ट डायलर अॅप बदलायचा?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"आपला डीफॉल्ट डायलर अॅप म्हणून <xliff:g id="CURRENT_APP">%2$s</xliff:g> ऐवजी <xliff:g id="NEW_APP">%1$s</xliff:g> वापरायचा?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"आपला डीफॉल्ट डायलर अॅप म्हणून <xliff:g id="NEW_APP">%s</xliff:g> वापरायचा?"</string>
 </resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index dc15ef3..d75cf36 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Pengurusan Panggilan Telefon"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Tidak diketahui"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Panggilan tidak dijawab"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Panggilan tidak dijawab"</string>
@@ -26,20 +27,26 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Mesej"</string>
     <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">"Tak boleh berckp skrg. Apa cerita?"</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_3" msgid="3496079065723960450">"Saya akan hubungi awak kemudian."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Tdk blh berckp skg. Tel saya nanti?"</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>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Edit respons pantas"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Respons pantas"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesej dihantar ke <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Hanya panggilan kecemasan dibenarkan oleh pemilik peranti"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Akaun panggilan"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Aplikasi ini tidak boleh membuat panggilan keluar tanpa kebenaran Telefon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Untuk membuat panggilan, masukkan nombor yang sah."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Panggilan tidak boleh ditambahkan pada masa ini."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Sila lumpuhkan Mod TTY untuk membuat panggilan video."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Nombor mel suara tiada"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Tidak ada nombor mel suara disimpan pada kad SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Tambah nombor"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Tukar apl Pendail lalai?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Gunakan <xliff:g id="NEW_APP">%1$s</xliff:g> dan bukannya <xliff:g id="CURRENT_APP">%2$s</xliff:g> sebagai apl pendail lalai anda?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Gunakan <xliff:g id="NEW_APP">%s</xliff:g> sebagai apl pendail lalai anda?"</string>
 </resources>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 2bb5771..4b6a0d3 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ဖုန်း"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ဖုန်းခေါ်ဆိုခြင်း စီမံမှု"</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_missedCallsTitle" msgid="1361677948941502522">"လွဲသွားသော ဖုန်းခေါ်မှုများ"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"အရေးပေါ်ဖုန်းများကိုသာ ခေါ်ဆိုနိုင်ရန် စက်ကိရိယာပိုင်ရှင်က ခွင့်ပြုထား၏"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"ဗီဒီယိုခေါ်ဆိုမှုများ ပြုလုပ်ရန် ကျေးဇူးပြု၍ TTY မုဒ်ကို ပိတ်ထားပါ။"</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="4430590714918044425">"စက်ရုံထုတ်ဖုန်းခေါ်အပ်ဖ်ကိုပြောင်းလိုပါသလား"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> အစား <xliff:g id="NEW_APP">%1$s</xliff:g> ကိုသင့်ရဲ့ စက်ရုံထုတ်ဖုန်းခေါ်အပ်ဖ်အဖြစ် သုံးလိုပါသလား"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> ကိုသင့်ရဲ့စက်ရုံထုတ်ဖုန်းခေါ်အပ်ဖ်အဖြစ် သုံးလိုပါသလား"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index c28a01d..7f37bf0 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Administrering av telefonsamtaler"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Ukjent"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Tapt anrop"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Tapte anrop"</string>
@@ -27,19 +28,25 @@
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Samtalelyd er kuttet."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Høyttaler er aktivert."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Kan ikke snakke nå. Hva skjer?"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Jeg ringer deg straks tilbake."</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Jeg ringer deg tilbake straks."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Jeg ringer deg senere."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Ikke nå. Ringer du meg senere?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Kan ikke nå. Ring meg senere."</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Hurtigsvar"</string>
     <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Rediger hurtigsvar"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hurtigsvar"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Melding er sendt til <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Eieren av enheten tillater bare nødanrop"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Ringekontoer"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Denne appen kan ikke ringe uten tillatelse fra telefonen."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Skriv inn et gyldig nummer for å plassere en samtale."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Anrop kan ikke legges til akkurat nå."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Slå av TTY-modus for å starte videosamtaler."</string>
-    <string name="no_vm_number" msgid="4164780423805688336">"Mangler nummer til telefonsvarer"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"Det er ikke lagret noe telefonsvarernummer på SIM-kortet."</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"Mangler nummer til talepostkasse"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"Det er ikke lagret noe nummer for talepostkasse på SIM-kortet."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Legg til nummer"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Vil du endre standard telefonapp?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Vil du bruke <xliff:g id="NEW_APP">%1$s</xliff:g> i stedet for <xliff:g id="CURRENT_APP">%2$s</xliff:g> som standard telefonapp?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Vil du bruke <xliff:g id="NEW_APP">%s</xliff:g> som standard telefonapp?"</string>
 </resources>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 1cac5f6..3e9ebe5 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"फोन"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"फोन कल व्यवस्थापन"</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_missedCallsTitle" msgid="1361677948941502522">"छुटेका कल"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"केवल आपतकालीन कलहरू मात्र यन्त्र मालिकद्वारा अनुमति दिइएको छ"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"कृपया भिडियो कलहरू गर्न TTY मोड निष्क्रिय गर्नुहोस्।"</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="4430590714918044425">"पूर्वनिर्धारित डायलर अनुप्रयोग फेर्ने हो?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g>को सट्टामा <xliff:g id="NEW_APP">%1$s</xliff:g>लाई पूर्वनिर्धारित डायलर अनुप्रयोगको रूपमा प्रयोग गर्ने हो?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g>लाई तपाईँको पूर्वनिर्धारित डायलर अनुप्रयोगको रूपमा प्रयोग गर्ने हो?"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index b762e9a..01f7810 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefoon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefoonoproepbeheer"</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_missedCallsTitle" msgid="1361677948941502522">"Gemiste oproepen"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Alleen noodoproepen zijn toegestaan door de apparaateigenaar"</string>
-    <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Als u wilt bellen, moet u een geldig nummer invoeren."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Oproepaccounts"</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_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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Schakel de TTY-modus uit om videogesprekken te voeren."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Voicemailnummer ontbreekt"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Er is geen voicemailnummer op de SIM-kaart opgeslagen."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Nummer toevoegen"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Standaard kiezerapp wijzigen?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g> in plaats van <xliff:g id="CURRENT_APP">%2$s</xliff:g> gebruiken als standaard kiezerapp?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> gebruiken als standaard kiezerapp?"</string>
 </resources>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 9e5cf0c..6e49d64 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ਫੋਨ"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ਫ਼ੋਨ - ਕਾਲ ਪ੍ਰਬੰਧਨ"</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_missedCallsTitle" msgid="1361677948941502522">"ਮਿਸਡ ਕਾਲਾਂ"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"ਡਿਵਾਈਸ ਦੇ ਮਾਲਕ ਵੱਲੋਂ ਕੇਵਲ ਐਮਰਜੈਂਸੀ ਕਾਲਾਂ ਦੀ ਆਗਿਆ ਹੈ।"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"ਕਾਲਿੰਗ ਖਾਤੇ"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"ਕਿਰਪਾ ਕਰਕੇ ਵੀਡੀਓ ਕਾਲਾਂ ਨੂੰ ਕਰਨ ਲਈ TTY ਮੋਡ ਅਸਮਰਥਿਤ ਕਰੋ."</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="4430590714918044425">"ਕੀ ਡਿਫ਼ੌਲਟ ਡਾਇਲਰ ਐਪ ਨੂੰ ਬਦਲਣਾ ਹੈ?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"ਕੀ <xliff:g id="CURRENT_APP">%2$s</xliff:g> ਦੀ ਬਜਾਏ <xliff:g id="NEW_APP">%1$s</xliff:g> ਨੂੰ ਆਪਣੀ ਡਿਫ਼ੌਲਟ ਡਾਇਲਰ ਐਪ ਦਾ ਉਪਯੋਗ ਕਰਨਾ ਹੈ?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"ਕੀ <xliff:g id="NEW_APP">%s</xliff:g> ਨੂੰ ਆਪਣੀ ਡਿਫ਼ੌਲਟ ਡਾਇਲਰ ਐਪ ਦਾ ਉਪਯੋਗ ਕਰਨਾ ਹੈ?"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 515190c..4d44cb1 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefon – zarządzanie połączeniami"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Nieznany"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Nieodebrane połączenie"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Połączenia nieodebrane"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Szybka odpowiedź"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Wiadomość wysłano na numer <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Właściciel urządzenia zezwala tylko na połączenia alarmowe"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Konta telefoniczne"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Ta aplikacja nie może wykonywać połączeń bez uprawnienia Telefon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Aby zadzwonić, wybierz prawidłowy numer."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Nie można w tej chwili dodać połączenia."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Wyłącz tryb TTY, by korzystać z rozmów wideo."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Brakuje numeru poczty głosowej"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Na karcie SIM nie ma zapisanego numeru poczty głosowej."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Dodaj numer"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Zmienić domyślną aplikację telefonu?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Zmienić <xliff:g id="CURRENT_APP">%2$s</xliff:g> na <xliff:g id="NEW_APP">%1$s</xliff:g> jako domyślną aplikację telefonu?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Ustawić <xliff:g id="NEW_APP">%s</xliff:g> jako domyślną aplikację telefonu?"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index d16d6d1..f3ec4f8 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telemóvel"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gestão de chamadas do telemóvel"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefone"</string>
     <string name="unknown" msgid="6878797917991465859">"Desconhecido"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Chamada não atendida"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Chamadas não atendidas"</string>
@@ -35,11 +36,16 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensagem enviada para <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Só são permitidas chamadas de emergência pelo proprietário do dispositivo"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Contas de chamadas"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Apenas são permitidas chamadas de emergência."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Esta aplicação não pode fazer chamadas sem a autorização do telefone."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para telefonar, introduza um número válido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Não é possível adicionar a chamada neste momento."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Desative o modo teletipo para efetuar videochamadas."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Número do correio de voz em falta"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Não existe um número de correio de voz armazenado no cartão SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Adicionar número"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Alterar aplicação de Telefone predefinida?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Utilizar <xliff:g id="NEW_APP">%1$s</xliff:g> em vez de <xliff:g id="CURRENT_APP">%2$s</xliff:g> como a aplicação de telefone predefinida?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Utilizar <xliff:g id="NEW_APP">%s</xliff:g> como a aplicação de telefone predefinida?"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 0c5f2dc..d167aff 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefone"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Gerenciamento de chamadas telefônicas"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefone"</string>
     <string name="unknown" msgid="6878797917991465859">"Desconhecido"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Chamada perdida"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Chamadas perdidas"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Resposta rápida"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mensagem enviada para <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Apenas chamadas de emergência são permitidas pelo proprietário do dispositivo"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Contas de chamadas"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Este aplicativo não pode fazer chamadas sem a permissão do smartphone."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Para realizar uma chamada, digite um número válido."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"No momento, não é possível adicionar a chamada."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Desative o modo TTD para realizar vídeo chamadas."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Número correio de voz ausente"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Não há um número correio de voz armazenado no cartão SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Adicionar número"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Alterar o app discador padrão?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Usar <xliff:g id="NEW_APP">%1$s</xliff:g> em vez de <xliff:g id="CURRENT_APP">%2$s</xliff:g> como seu app discador padrão?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Usar <xliff:g id="NEW_APP">%s</xliff:g> como seu app discador padrão?"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 090451e..c4464b8 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -16,13 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefon – Gestionarea apelurilor"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Necunoscut"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Apel nepreluat"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Apeluri nepreluate"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> (de) apeluri nepreluate"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Apel nepreluat de la <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Sunaţi înapoi"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Sunați"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Mesaj"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Apel cu sunet dezactivat."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Difuzor activat."</string>
@@ -31,15 +32,21 @@
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Te sun mai târziu."</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Nu pot acum. Vorbim mai târziu?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Răspunsuri rapide"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editaţi răspunsurile rapide"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Editați răspunsurile rapide"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Răspuns rapid"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesajul a fost trimis la <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Proprietarul dispozitivului permite numai apelurile de urgență"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Conturi pentru apelare"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Această aplicație nu poate efectua apeluri fără permisiunea Telefon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Pentru a apela, introduceți un număr valid."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Apelul nu poate fi adăugat în acest moment."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Dezactivați modul TTY pentru a iniția apeluri video."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Lipseşte numărul mesageriei vocale"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Niciun număr de mesagerie vocală nu este stocat pe cardul SIM."</string>
-    <string name="add_vm_number_str" msgid="4676479471644687453">"Adăugaţi numărul"</string>
+    <string name="add_vm_number_str" msgid="4676479471644687453">"Adăugați numărul"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Schimbați aplicația Telefon prestabilită?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Folosiți <xliff:g id="NEW_APP">%1$s</xliff:g> și nu <xliff:g id="CURRENT_APP">%2$s</xliff:g> ca aplicație de telefonie prestabilită?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Folosiți <xliff:g id="NEW_APP">%s</xliff:g> ca aplicație de telefonie prestabilită?"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 0145c3b..8d43a8c 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Телефон"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Телефон – управление звонками"</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_missedCallsTitle" msgid="1361677948941502522">"Пропущенные вызовы"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Владелец устройства разрешил только экстренные вызовы"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Аккаунты для звонков"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Чтобы делать видеозвонки, отключите режим телетайпа"</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="4430590714918044425">"Новое приложение для звонков"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"\"<xliff:g id="NEW_APP">%1$s</xliff:g>\" станет приложением для звонков по умолчанию вместо \"<xliff:g id="CURRENT_APP">%2$s</xliff:g>\". Продолжить?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"\"<xliff:g id="NEW_APP">%s</xliff:g>\" станет приложением для звонков по умолчанию. Продолжить?"</string>
 </resources>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index b8f7bc4..9c21e26 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"දුරකථනය"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"දුරකථන ඇමතුම් කළමනාකරණය"</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_missedCallsTitle" msgid="1361677948941502522">"මඟ හැරුණු ඇමතුම්"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"උපාංගයේ හිමිකරු විසින් හදිස්සි ඇමතුම් වලට පමණක් අවසර දෙයි"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"ඇමතීමේ ගිණුම්"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"වීඩියෝ ඇමතුම් ලබා ගැනීමට කරුණාකර TTY මෝඩය අබල කරන්න."</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="4430590714918044425">"සුපුරුදු දුරකථන යෙදුම වෙනස් කරන්න ද?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> වෙනුවට <xliff:g id="NEW_APP">%1$s</xliff:g> ඔබගේ සුපුරුදු දුරකථන යෙදුම ලෙස භාවිතා කරන්නද?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> ඔබගේ සුපුරුදු දුරකථන යෙදුම ලෙස භාවිතා කරන්නද?"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 030d517..4f1a354 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -16,14 +16,15 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefón"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Správa telefónnych hovorov"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefón"</string>
     <string name="unknown" msgid="6878797917991465859">"Neznáme"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Zmeškaný hovor"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Zmeškané hovory"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"Zmeškané hovory: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>."</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Zmeškaný hovor od volajúceho <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Zavolať späť"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"Správa"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Zavolať"</string>
+    <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>
@@ -35,11 +36,16 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Rýchla odpoveď"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Správa bola odoslaná na číslo <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vlastník zariadenia povolil iba tiesňové volania."</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Telefónne účty"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Povolené sú len tiesňové volania."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"V tejto aplikácii nie je možné uskutočňovať odchádzajúce hovory bez povolenia Telefón"</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ak chcete volať, zadajte platné číslo"</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hovor momentálne nie je možné pridať."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Ak chcete uskutočňovať videohovory, deaktivujte režim TTY."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Chýba číslo hlasovej schránky"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"Na karte SIM nie je uložené žiadne číslo hlasovej schránky."</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"Na SIM karte nie je uložené žiadne číslo hlasovej schránky."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Pridať číslo"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Chcete zmeniť predvolenú aplikáciu vytáčania?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Chcete použiť aplikáciu <xliff:g id="NEW_APP">%1$s</xliff:g> namiesto aplikácie <xliff:g id="CURRENT_APP">%2$s</xliff:g> ako predvolenú aplikáciu vytáčania?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Chcete použiť aplikáciu <xliff:g id="NEW_APP">%s</xliff:g> ako predvolenú aplikáciu vytáčania?"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 51f9de5..c8036ee 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Upravljanje telefonskih klicev"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Neznano"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Neodgovorjeni klic"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Neodgovorjeni klici"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hiter odgovor"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"SMS poslan na številko <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Lastnik naprave dovoljuje samo opravljanje klicev v sili"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Računi za klicanje"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Ta aplikacija lahko opravlja odhodne klice brez dovoljenja za telefon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Če želite opraviti klic, vnesite veljavno številko."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Klica trenutno ni mogoče dodati."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Če želite opravljati videoklice, onemogočite način TTY."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Manjkajoča številka glasovne pošte"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Na kartici SIM ni shranjena številka glasovne pošte."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Dodaj številko"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Želite spremeniti privzeto aplikacijo za klicanje?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Želite <xliff:g id="NEW_APP">%1$s</xliff:g> uporabljati kot privzeto aplikacijo za klicanje namesto <xliff:g id="CURRENT_APP">%2$s</xliff:g>?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Želite <xliff:g id="NEW_APP">%s</xliff:g> uporabljati kot privzeto aplikacijo za klicanje?"</string>
 </resources>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 75361a8..aa6b3c4 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -16,13 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefoni"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Menaxhimi i telefonatave"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefoni"</string>
     <string name="unknown" msgid="6878797917991465859">"I panjohur"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Telefonatë e humbur"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Telefonata të humbura"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> telefonata të humbura"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Telefonatë e humbur nga <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Ri-telefono"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Telefono"</string>
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Mesazh"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Telefonata kaloi në heshtje."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Altoparlanti u aktivizua."</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Përgjigje e shpejtë"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesazhi u dërgua te <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Vetëm telefonatat e urgjencës lejohen nga zotëruesi i pajisjes"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Llogaritë e telefonatave"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Ky aplikacion nuk mund të kryejë telefonata dalëse pa lejen e Telefonit."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Për të kryer një telefonatë, fut një numër të vlefshëm."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Telefonata nuk mund të shtohet këtë herë."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Çaktivizo modalitetin TTY për të kryer telefonata me video."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Mungon numri i postës zanore"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Nuk ka numër të ruajtur në kartën SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Shto numër"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Të ndryshohet apl. parazgjedhur i formuesit të numrave?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Të përdoret <xliff:g id="NEW_APP">%1$s</xliff:g> në vend të <xliff:g id="CURRENT_APP">%2$s</xliff:g> si aplikacioni i parazgjedhur i formuesit të numrave?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Të përdoret <xliff:g id="NEW_APP">%s</xliff:g> si aplikacioni i parazgjedhur i formuesit të numrave?"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 36d967c..ad9e9e3 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Телефон"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Управљање телефонским позивима"</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_missedCallsTitle" msgid="1361677948941502522">"Пропуштени позиви"</string>
@@ -26,7 +27,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>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Власник уређаја је дозволио само хитне позиве"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Налози за позивање"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Онемогућите TTY режим да бисте упућивали видео позиве."</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="4430590714918044425">"Променити подразумевану апликацију Телефон?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Желите ли да користите апликацију <xliff:g id="NEW_APP">%1$s</xliff:g> уместо апликације <xliff:g id="CURRENT_APP">%2$s</xliff:g> као подразумевану апликацију за позивање телефонских бројева?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Желите ли да користите апликацију <xliff:g id="NEW_APP">%s</xliff:g> као подразумевану апликацију за позивање телефонских бројева?"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index a2fa42d..6bbdeff 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -16,14 +16,15 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Samtalshantering"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Okänd"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Missat samtal"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Missade samtal"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> missade samtal"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Missat samtal från <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
     <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Ring upp"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"Meddelande"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"Sms:a"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Samtalets ljud avstängt."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Högtalartelefon aktiverad."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Kan inte prata nu. Läget?"</string>
@@ -35,11 +36,16 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Snabbsvar"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Meddelandet har skickats till <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Endast nödsamtal är tillåtna av enhetens ägare"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Konton för samtal"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Det går bara att ringa nödsamtal."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Den här appen kan inte göra utgående samtal utan behörigheten Telefon."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ange ett giltigt nummer om du vill ringa ett samtal."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Det går inte att lägga till samtalet just nu."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Inaktivera texttelefonläget om du vill ringa videosamtal."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Nummer till röstbrevlåda saknas"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Det finns inget nummer till röstbrevlådan sparat på SIM-kortet."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Lägg till nummer"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Vill du byta standardapp för uppringning?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Vill du använda <xliff:g id="NEW_APP">%1$s</xliff:g> i stället för <xliff:g id="CURRENT_APP">%2$s</xliff:g> som standardapp för uppringning?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Vill du använda <xliff:g id="NEW_APP">%s</xliff:g> som standardapp för uppringning?"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 9e71c93..6298b4f 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Nambari ya simu"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Usimamizi wa Mazunguzo ya Simu"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Simu"</string>
     <string name="unknown" msgid="6878797917991465859">"Haijulikani"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Simu isiyojibiwa"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Simu zisizojibiwa"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Majibu ya haraka"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Ujumbe uliotumwa kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g> ."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Ni simu za dharura pekee zinazoruhusiwa na mmiliki wa kifaa"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Akaunti za simu"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Programu hii haiwezi kupiga simu bila ruhusa ya Simu."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ili upige simu, weka nambari sahihi."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hangout ya video haiwezi kuongezwa kwa wakati huu."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Tafadhali zima Hali ya TTY ili uanzishe Hangout ya video."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Nambari ya sauti inayokosekana"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Hakuna nambari ya ujumbe wa sauti iliyohifadhiwa katika SIM kadi."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Ongeza nambari"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Ungependa kubadilisha programu chaguo-msingi ya simu?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Ungependa kutumia <xliff:g id="NEW_APP">%1$s</xliff:g> badala ya <xliff:g id="CURRENT_APP">%2$s</xliff:g> kama programu ya chaguo-msingi ya kupigia simu?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Ungependa kutumia <xliff:g id="NEW_APP">%s</xliff:g> kama programu ya chaguo-msingi ya kupigia simu?"</string>
 </resources>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 190cd0c..28f0b25 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -16,13 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ஃபோன்"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ஃபோன் அழைப்பு நிர்வாகம்"</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_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_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>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"சாதன உரிமையாளர் அவசர அழைப்புகளை மட்டுமே அனுமதித்துள்ளார்"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"வீடியோ அழைப்புகளை மேற்கொள்ள, TTY பயன்முறையை முடக்கவும்."</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="4430590714918044425">"இயல்புநிலை டயலர் பயன்பாட்டை மாற்றவா?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="NEW_APP">%1$s</xliff:g>ஐ <xliff:g id="CURRENT_APP">%2$s</xliff:g>க்குப் பதிலாக, இயல்புநிலை டயலர் பயன்பாடாகப் பயன்படுத்தவா?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g>ஐ இயல்புநிலை டயலர் பயன்பாடாகப் பயன்படுத்தவா?"</string>
 </resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index ee0c5b5..32d88d7 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -16,13 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"ఫోన్"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"ఫోన్ కాల్ నిర్వహణ"</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_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_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>
@@ -31,15 +32,20 @@
     <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>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"పరికరం యజమాని అత్యవసర కాల్‌లను మాత్రమే అనుమతించారు"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"దయచేసి వీడియో కాల్‌లు చేయడానికి TTY మోడ్ నిలిపివేయండి."</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="4430590714918044425">"డిఫాల్ట్ డయలర్ అనువర్తనాన్ని మార్చాలా?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"మీ డిఫాల్ట్ డయలర్ అనువర్తనంగా <xliff:g id="CURRENT_APP">%2$s</xliff:g> బదులు <xliff:g id="NEW_APP">%1$s</xliff:g>ని ఉపయోగించాలా?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"మీ డిఫాల్ట్ డయలర్ అనువర్తనంగా <xliff:g id="NEW_APP">%s</xliff:g>ని ఉపయోగించాలా?"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 2babae6..106a1b5 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"โทรศัพท์"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"การจัดการการโทรศัพท์"</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_missedCallsTitle" msgid="1361677948941502522">"สายที่ไม่ได้รับ"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"เจ้าของอุปกรณ์อนุญาตเฉพาะหมายเลขฉุกเฉิน"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"บัญชีการโทร"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"โปรดปิดใช้โหมด TTY เพื่อแฮงเอาท์วิดีโอ"</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="4430590714918044425">"เปลี่ยนแอปแป้นโทรศัพท์เริ่มต้นไหม"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"ใช้ <xliff:g id="NEW_APP">%1$s</xliff:g> เป็นแอปแป้นโทรศัพท์เริ่มต้นแทน <xliff:g id="CURRENT_APP">%2$s</xliff:g> ไหม"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"ใช้ <xliff:g id="NEW_APP">%s</xliff:g> เป็นแอปแป้นโทรศัพท์เริ่มต้นไหม"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index ca749b4..ea350ab 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telepono"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Pamamahala sa Tawag sa Telepono"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telepono"</string>
     <string name="unknown" msgid="6878797917991465859">"Di-kilala"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Hindi nasagot na tawag"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Mga hindi nasagot na tawag"</string>
@@ -26,20 +27,26 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Padalhan ng mensahe"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Naka-mute ang tawag."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Pinapagana ang speakerphone."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Di masagot ngayon. Ano meron?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Di masagot ngayon. Ano\'ng meron?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Tawagan kita ulit."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Tawagan kita mamaya."</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Di masagot ngayon. Tawag ka mamaya?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Mga mabilisang tugon"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"I-edit ang mga mabilisang tugon"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"I-edit ang mga quick response"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Mabilisang tugon"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Naipadala ang mensahe sa <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Ang mga emergency na tawag lang ang pinapayagan ng may-ari ng device"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Account sa pagtawag"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Hindi makakapagsagawa ng mga papalabas na tawag ang application na ito nang wala ang pahintulot ng Telepono."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Upang tumawag, maglagay ng wastong numero."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hindi maidadagdag ang tawag sa oras na ito."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Paki-disable ang TTY Mode upang makapag-video call."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Nawawala ang numero ng voicemail"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Walang nakaimbak na numero ng voicemail sa SIM card."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Magdagdag ng numero"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Baguhin ang iyong default na Dialer app?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Gamitin ang <xliff:g id="NEW_APP">%1$s</xliff:g> sa halip na <xliff:g id="CURRENT_APP">%2$s</xliff:g> bilang default na dialer app mo?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Gamitin ang <xliff:g id="NEW_APP">%s</xliff:g> bilang default na dialer app mo?"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 662c3fd..0ef8a03 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefon Çağrısı Yönetimi"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Bilinmiyor"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Cevapsız çağrı"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Cevapsız çağrılar"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Hızlı yanıt"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Mesaj, <xliff:g id="PHONE_NUMBER">%s</xliff:g> numaralı telefona gönderildi."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Cihaz sahibi sadece acil durum çağrılarına izin veriyor"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Çağrı hesapları"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Bu uygulama, Telefonun izni olmadan giden çağrılar yapamaz."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Telefon etmek için geçerli bir numara girin."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Çağrı şu anda eklenemiyor."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Video görüşmesi yapmak için lütfen TTY Modu\'nu devre dışı bırakın."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Eksik sesli mesaj numarası"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartta depolanan sesli mesaj numarası yok."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Numara ekle"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Varsayılan Numara Çeviri uygulaması değiştirilsin mi?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Varsayılan numara çevirici uygulamanız olarak <xliff:g id="CURRENT_APP">%2$s</xliff:g> yerine <xliff:g id="NEW_APP">%1$s</xliff:g> kullanılsın mı?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Varsayılan numara çevirici uygulamanız olarak <xliff:g id="NEW_APP">%s</xliff:g> kullanılsın mı?"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 16214b4..6182002 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -16,9 +16,10 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Номер телефону"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Керування дзвінками"</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_missedCallTitle" msgid="7554385905572364535">"Пропущений виклик"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Пропущ. дзвінки"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"Пропущ. дзвінк: <xliff:g id="NUM_MISSED_CALLS">%s</xliff:g>"</string>
     <string name="notification_missedCallTicker" msgid="504686252427747209">"Пропущ. виклик від <xliff:g id="MISSED_CALL_FROM">%s</xliff:g>"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"Власник пристрою дозволив лише екстрені виклики"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Щоб здійснювати відеодзвінки, вимкніть режим TTY."</string>
-    <string name="no_vm_number" msgid="4164780423805688336">"Відстун. номер голос. пошти"</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="4430590714918044425">"Змінити додаток для дзвінків за умовчанням?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Зробити <xliff:g id="NEW_APP">%1$s</xliff:g> додатком для дзвінків за умовчанням замість додатка <xliff:g id="CURRENT_APP">%2$s</xliff:g>?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Зробити <xliff:g id="NEW_APP">%s</xliff:g> додатком для дзвінків за умовчанням?"</string>
 </resources>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index ef9b16a..6144a70 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"فون"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"فون کال کا نظم"</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_missedCallsTitle" msgid="1361677948941502522">"چھوٹی ہوئی کالیں"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"آلہ کے مالک نے صرف ہنگامی کالز کی اجازت دی ہے۔"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"کالنگ اکاؤنٹس"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"‏براہ کرم ویڈیو کالز کرنے کیلئے TTY وض‏ع غیر فعال کریں۔"</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="4430590714918044425">"ڈیفالٹ ڈائلر ایپ تبدیل کریں"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"<xliff:g id="CURRENT_APP">%2$s</xliff:g> کی بجائے <xliff:g id="NEW_APP">%1$s</xliff:g> کو بطور اپنی ڈیفالٹ ڈائلر ایپ استعمال کریں؟"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> کو بطور اپنی ڈیفالٹ ڈائلر ایپ استعمال کریں؟"</string>
 </resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 1cd5da6..3ad9cf5 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -16,30 +16,37 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Telefon"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Telefon – qo‘ng‘iroqlarni boshqarish"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Telefon"</string>
     <string name="unknown" msgid="6878797917991465859">"Noma’lum"</string>
-    <string name="notification_missedCallTitle" msgid="7554385905572364535">"Javob berilmagan qo‘ng‘iroq"</string>
+    <string name="notification_missedCallTitle" msgid="7554385905572364535">"Javobsiz qo‘ng‘iroq"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Javobsiz qo‘ng‘iroqlar"</string>
     <string name="notification_missedCallsMsg" msgid="4575787816055205600">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> ta javobsiz qo‘ng‘iroq"</string>
-    <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>dan javobsiz qo‘ng‘iroq"</string>
-    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Teskari qo‘ng‘iroq"</string>
-    <string name="notification_missedCall_message" msgid="3049928912736917988">"Xabar"</string>
+    <string name="notification_missedCallTicker" msgid="504686252427747209">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g> qo‘ng‘irog‘i javobsiz qoldirildi"</string>
+    <string name="notification_missedCall_call_back" msgid="2684890353590890187">"Telefon"</string>
+    <string name="notification_missedCall_message" msgid="3049928912736917988">"SMS"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Qo‘ng‘iroq ovozi o‘chirildi."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Karnaychalar yoqildi."</string>
     <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Hozir gaplasholmayman. Tinchlikmi?"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Hozir qaytarib qo‘ng‘iroq qilaman."</string>
-    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Keyinroq qo‘ng‘iroq qilaman."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Bandman. Keyin qo‘n-q qilasizmi?"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Hozir telefon qilaman."</string>
+    <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Keyinroq telefon qilaman."</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Bandman, keyinroq telefon qiling."</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"Tezkor javoblar"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Tezkor javoblarni tahrirlash"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"Tezkor javoblar"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Tezkor javob"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Xabar <xliff:g id="PHONE_NUMBER">%s</xliff:g>ga jo‘natildi."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Qurilma egasi faqat favqulodda qo‘ng‘iroqlarga ruxsat bergan"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Qo‘ng‘iroq hisoblari"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Bu ilova Telefon ruxsatnomasisiz chiquvchi qo‘ng‘iroqlarni amalga oshira olmaydi."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Qo‘ng‘iroq qilish uchun raqamni to‘g‘ri kiriting."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Hozirgi vaqtda qo‘ng‘iroq qo‘shib bo‘lmaydi."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Video qo\'ng\'iroqlarni amalga oshirish uchun TTY rejimini o‘chirib qo‘yish kerak"</string>
-    <string name="no_vm_number" msgid="4164780423805688336">"Javobsiz ovozli xabar raqami"</string>
-    <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartada birorta ham ovozli xabar saqlanmagan."</string>
+    <string name="no_vm_number" msgid="4164780423805688336">"Ovozli pochta raqami ko‘rsatilmagan"</string>
+    <string name="no_vm_number_msg" msgid="1300729501030053828">"SIM kartada birorta ham ovozli pochta raqami yo‘q."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Raqam qo‘shish"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Birlamchi raqam terish ilovasi o‘zgartirilsinmi?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Raqam terish uchun birlamchi ilova sifatida <xliff:g id="CURRENT_APP">%2$s</xliff:g> o‘rniga <xliff:g id="NEW_APP">%1$s</xliff:g> ilovasi tanlansinmi?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"<xliff:g id="NEW_APP">%s</xliff:g> raqam terish uchun birlamchi ilova sifatida tanlansinmi?"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 3b7e1b4..6f5ccc5 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Điện thoại"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Quản lý cuộc gọi điện thoại"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Điện thoại"</string>
     <string name="unknown" msgid="6878797917991465859">"Không xác định"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Cuộc gọi nhỡ"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Cuộc gọi nhỡ"</string>
@@ -35,11 +36,16 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Trả lời nhanh"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Đã gửi tin nhắn tới <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Chỉ chủ sở hữu thiết bị mới được phép thực hiện cuộc gọi khẩn cấp"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Tài khoản gọi"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="6872406278300131364">"Chỉ được phép thực hiện cuộc gọi khẩn cấp."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Ứng dụng này không thể thực hiện cuộc gọi đi mà không có quyền của Điện thoại."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Để thực hiện cuộc gọi, hãy nhập một số hợp lệ."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Không thể thêm cuộc gọi tại thời điểm này."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Vui lòng tắt Chế độ TTY để thực hiện cuộc gọi điện video."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Thiếu số thư thoại"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Không có số thư thoại nào được lưu trữ trên thẻ SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Thêm số điện thoại"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Thay đổi ứng dụng Trình quay số mặc định?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Sử dụng <xliff:g id="NEW_APP">%1$s</xliff:g> làm ứng dụng trình quay số mặc định của bạn thay vì <xliff:g id="CURRENT_APP">%2$s</xliff:g>?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Sử dụng <xliff:g id="NEW_APP">%s</xliff:g> làm ứng dụng trình quay số mặc định của bạn?"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2d06096..c6752ad 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"电话"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"通话管理"</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_missedCallsTitle" msgid="1361677948941502522">"未接电话"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"设备机主仅允许拨打紧急呼救电话"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"通话帐号"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"要进行视频通话,请停用 TTY 模式。"</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="4430590714918044425">"要更改默认拨号器应用吗?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"要使用<xliff:g id="NEW_APP">%1$s</xliff:g>(而非<xliff:g id="CURRENT_APP">%2$s</xliff:g>)作为您的默认拨号器应用吗?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"要使用<xliff:g id="NEW_APP">%s</xliff:g>作为您的默认拨号器应用吗?"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index c00d0b4..d00073d 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"電話"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"手機通話管理"</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_missedCallsTitle" msgid="1361677948941502522">"未接來電"</string>
@@ -35,11 +36,16 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"只有裝置擁有者才可撥打緊急電話"</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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"請停用 TTY 模式以進行視像通話。"</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="4430590714918044425">"變更預設撥號器應用程式?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"使用 <xliff:g id="NEW_APP">%1$s</xliff:g> 取代 <xliff:g id="CURRENT_APP">%2$s</xliff:g> 作為預設撥號器應用程式?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"使用 <xliff:g id="NEW_APP">%s</xliff:g> 為預設撥號器應用程式?"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 231d938..a06b76f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"電話"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"通話管理"</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_missedCallsTitle" msgid="1361677948941502522">"未接來電"</string>
@@ -35,11 +36,17 @@
     <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="outgoing_call_not_allowed" msgid="1435394568102165287">"裝置擁有者限定只能撥打緊急電話"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"通話帳戶"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <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="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"如要進行視訊通話,請停用 TTY 模式。"</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="4430590714918044425">"要變更預設撥號應用程式嗎?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"要將 <xliff:g id="NEW_APP">%1$s</xliff:g> (而非 <xliff:g id="CURRENT_APP">%2$s</xliff:g>) 設為預設撥號應用程式嗎?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"要將 <xliff:g id="NEW_APP">%s</xliff:g> 設為預設撥號應用程式嗎?"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index acb00f3..70188be 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -16,7 +16,8 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="telecommAppLabel" product="default" msgid="3477737022166975496">"Ifoni"</string>
+    <string name="telecommAppLabel" product="default" msgid="9166784827254469057">"Ukuphathwa kwekholi yefoni"</string>
+    <string name="userCallActivityLabel" product="default" msgid="5415173590855187131">"Ifoni"</string>
     <string name="unknown" msgid="6878797917991465859">"Akwaziwa"</string>
     <string name="notification_missedCallTitle" msgid="7554385905572364535">"Ikholi ekulahlekele"</string>
     <string name="notification_missedCallsTitle" msgid="1361677948941502522">"Amakholi akuphuthele"</string>
@@ -35,11 +36,17 @@
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"Izimpendulo ezisheshayo"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"Umlayezo othunyelwe ku <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="outgoing_call_not_allowed" msgid="1435394568102165287">"Amakholi wesimo esiphuthumayo kuphela avunyelwe ngumnikazi wedivayisi"</string>
+    <string name="enable_account_preference_title" msgid="2021848090086481720">"Ama-akhawunti wokushaya"</string>
+    <!-- no translation found for outgoing_call_not_allowed_user_restriction (6872406278300131364) -->
+    <skip />
+    <string name="outgoing_call_not_allowed_no_permission" msgid="1996571596464271228">"Lolu hlelo lokusebenza alikwazi ukwenza amakhli aphumayo ngaphandle kwemvume yefoni."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="1940125199802007505">"Ukuze wenze ikholi, faka inombolo evumelekile."</string>
     <string name="duplicate_video_call_not_allowed" msgid="3749211605014548386">"Ikholi ayikwazi ukungezwa ngalesi sikhathi."</string>
     <string name="video_call_not_allowed_if_tty_enabled" msgid="7593649283571253283">"Sicela ukhubaze imodi ye-TTY ukuze wenze amakholi wevidiyo."</string>
     <string name="no_vm_number" msgid="4164780423805688336">"Inombolo engekho yomyalezo wezwi"</string>
     <string name="no_vm_number_msg" msgid="1300729501030053828">"Ayikho inombolo yomlayezo wezwi egcinwe ekhadini le-SIM."</string>
     <string name="add_vm_number_str" msgid="4676479471644687453">"Engeza inombolo"</string>
+    <string name="change_default_dialer_dialog_title" msgid="4430590714918044425">"Guqula uhlelo lwakho lokusebenza oluzenzakalelayo lokudayela?"</string>
+    <string name="change_default_dialer_with_previous_app_set_text" msgid="3213396537499337949">"Sebenzisa i-<xliff:g id="NEW_APP">%1$s</xliff:g> esikhundleni se-<xliff:g id="CURRENT_APP">%2$s</xliff:g> njengohlelo lwakho lokusebenza oluzenzakalelayo lokokudayela?"</string>
+    <string name="change_default_dialer_no_previous_app_set_text" msgid="7608426684114545221">"Sebenzisa i-<xliff:g id="NEW_APP">%s</xliff:g> njengohlelo lwakho lokusebenza oluzenzakalelayo lokokudayela?"</string>
 </resources>
diff --git a/src/com/android/server/telecom/Analytics.java b/src/com/android/server/telecom/Analytics.java
new file mode 100644
index 0000000..c86d6fc
--- /dev/null
+++ b/src/com/android/server/telecom/Analytics.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.telecom.DisconnectCause;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A class that collects and stores data on how calls are being made, in order to
+ * aggregate these into useful statistics.
+ */
+public class Analytics {
+   public static class CallInfo {
+        void setCallStartTime(long startTime) {
+        }
+
+        void setCallEndTime(long endTime) {
+        }
+
+        void setCallIsAdditional(boolean isAdditional) {
+        }
+
+        void setCallIsInterrupted(boolean isInterrupted) {
+        }
+
+        void setCallDisconnectCause(DisconnectCause disconnectCause) {
+        }
+
+        void addCallTechnology(int callTechnology) {
+        }
+
+        void setCreatedFromExistingConnection(boolean createdFromExistingConnection) {
+        }
+
+        void setCallConnectionService(String connectionServiceName) {
+        }
+    }
+
+    /**
+     * A class that holds data associated with a call.
+     */
+    @VisibleForTesting
+    public static class CallInfoImpl extends CallInfo {
+        public String callId;
+        public long startTime;  // start time in milliseconds since the epoch. 0 if not yet set.
+        public long endTime;  // end time in milliseconds since the epoch. 0 if not yet set.
+        public int callDirection;  // one of UNKNOWN_DIRECTION, INCOMING_DIRECTION,
+                                   // or OUTGOING_DIRECTION.
+        public boolean isAdditionalCall = false;  // true if the call came in while another call was
+                                                  // in progress or if the user dialed this call
+                                                  // while in the middle of another call.
+        public boolean isInterrupted = false;  // true if the call was interrupted by an incoming
+                                               // or outgoing call.
+        public int callTechnologies;  // bitmask denoting which technologies a call used.
+
+        // true if the Telecom Call object was created from an existing connection via
+        // CallsManager#createCallForExistingConnection, for example, by ImsConference.
+        public boolean createdFromExistingConnection = false;
+
+        public DisconnectCause callTerminationReason;
+        public String connectionService;
+        public boolean isEmergency = false;
+
+        CallInfoImpl(String callId, int callDirection) {
+            this.callId = callId;
+            startTime = 0;
+            endTime = 0;
+            this.callDirection = callDirection;
+            callTechnologies = 0;
+            connectionService = "";
+        }
+
+        CallInfoImpl(CallInfoImpl other) {
+            this.callId = other.callId;
+            this.startTime = other.startTime;
+            this.endTime = other.endTime;
+            this.callDirection = other.callDirection;
+            this.isAdditionalCall = other.isAdditionalCall;
+            this.isInterrupted = other.isInterrupted;
+            this.callTechnologies = other.callTechnologies;
+            this.createdFromExistingConnection = other.createdFromExistingConnection;
+            this.connectionService = other.connectionService;
+            this.isEmergency = other.isEmergency;
+
+            if (other.callTerminationReason != null) {
+                this.callTerminationReason = new DisconnectCause(
+                        other.callTerminationReason.getCode(),
+                        other.callTerminationReason.getLabel(),
+                        other.callTerminationReason.getDescription(),
+                        other.callTerminationReason.getReason(),
+                        other.callTerminationReason.getTone());
+            } else {
+                this.callTerminationReason = null;
+            }
+        }
+
+        @Override
+        public void setCallStartTime(long startTime) {
+            Log.d(TAG, "setting startTime for call " + callId + " to " + startTime);
+            this.startTime = startTime;
+        }
+
+        @Override
+        public void setCallEndTime(long endTime) {
+            Log.d(TAG, "setting endTime for call " + callId + " to " + endTime);
+            this.endTime = endTime;
+        }
+
+        @Override
+        public void setCallIsAdditional(boolean isAdditional) {
+            Log.d(TAG, "setting isAdditional for call " + callId + " to " + isAdditional);
+            this.isAdditionalCall = isAdditional;
+        }
+
+        @Override
+        public void setCallIsInterrupted(boolean isInterrupted) {
+            Log.d(TAG, "setting isInterrupted for call " + callId + " to " + isInterrupted);
+            this.isInterrupted = isInterrupted;
+        }
+
+        @Override
+        public void addCallTechnology(int callTechnology) {
+            Log.d(TAG, "adding callTechnology for call " + callId + ": " + callTechnology);
+            this.callTechnologies |= callTechnology;
+        }
+
+        @Override
+        public void setCallDisconnectCause(DisconnectCause disconnectCause) {
+            Log.d(TAG, "setting disconnectCause for call " + callId + " to " + disconnectCause);
+            this.callTerminationReason = disconnectCause;
+        }
+
+        @Override
+        public void setCreatedFromExistingConnection(boolean createdFromExistingConnection) {
+            Log.d(TAG, "setting createdFromExistingConnection for call " + callId + " to "
+                    + createdFromExistingConnection);
+            this.createdFromExistingConnection = createdFromExistingConnection;
+        }
+
+        @Override
+        public void setCallConnectionService(String connectionServiceName) {
+            Log.d(TAG, "setting connection service for call " + callId + ": "
+                    + connectionServiceName);
+            this.connectionService = connectionServiceName;
+        }
+
+        @Override
+        public String toString() {
+            return "{\n"
+                    + "    startTime: " + startTime + '\n'
+                    + "    endTime: " + endTime + '\n'
+                    + "    direction: " + getCallDirectionString() + '\n'
+                    + "    isAdditionalCall: " + isAdditionalCall + '\n'
+                    + "    isInterrupted: " + isInterrupted + '\n'
+                    + "    callTechnologies: " + getCallTechnologiesAsString() + '\n'
+                    + "    callTerminationReason: " + getCallDisconnectReasonString() + '\n'
+                    + "    connectionServices: " + connectionService + '\n'
+                    + "}\n";
+        }
+
+        private String getCallDirectionString() {
+            switch (callDirection) {
+                case UNKNOWN_DIRECTION:
+                    return "UNKNOWN";
+                case INCOMING_DIRECTION:
+                    return "INCOMING";
+                case OUTGOING_DIRECTION:
+                    return "OUTGOING";
+                default:
+                    return "UNKNOWN";
+            }
+        }
+
+        private String getCallTechnologiesAsString() {
+            StringBuilder s = new StringBuilder();
+            s.append('[');
+            if ((callTechnologies & CDMA_PHONE) != 0) s.append("CDMA ");
+            if ((callTechnologies & GSM_PHONE) != 0) s.append("GSM ");
+            if ((callTechnologies & SIP_PHONE) != 0) s.append("SIP ");
+            if ((callTechnologies & IMS_PHONE) != 0) s.append("IMS ");
+            if ((callTechnologies & THIRD_PARTY_PHONE) != 0) s.append("THIRD_PARTY ");
+            s.append(']');
+            return s.toString();
+        }
+
+        private String getCallDisconnectReasonString() {
+            if (callTerminationReason != null) {
+                return callTerminationReason.toString();
+            } else {
+                return "NOT SET";
+            }
+        }
+    }
+    public static final String TAG = "TelecomAnalytics";
+
+    // Constants for call direction
+    public static final int UNKNOWN_DIRECTION = 0;
+    public static final int INCOMING_DIRECTION = 1;
+    public static final int OUTGOING_DIRECTION = 2;
+
+    // Constants for call technology
+    public static final int CDMA_PHONE = 0x1;
+    public static final int GSM_PHONE = 0x2;
+    public static final int IMS_PHONE = 0x4;
+    public static final int SIP_PHONE = 0x8;
+    public static final int THIRD_PARTY_PHONE = 0x10;
+
+    private static final Object sLock = new Object(); // Coarse lock for all of analytics
+    private static final Map<String, CallInfoImpl> sCallIdToInfo = new HashMap<>();
+
+    public static CallInfo initiateCallAnalytics(String callId, int direction) {
+        Log.d(TAG, "Starting analytics for call " + callId);
+        CallInfoImpl callInfo = new CallInfoImpl(callId, direction);
+        synchronized (sLock) {
+            sCallIdToInfo.put(callId, callInfo);
+        }
+        return callInfo;
+    }
+
+    public static void dump(IndentingPrintWriter writer) {
+        synchronized (sLock) {
+            for (Map.Entry<String, CallInfoImpl> entry : sCallIdToInfo.entrySet()) {
+                writer.printf("Call %s: ", entry.getKey());
+                writer.println(entry.getValue().toString());
+            }
+        }
+    }
+
+    public static void reset() {
+        synchronized (sLock) {
+            sCallIdToInfo.clear();
+        }
+    }
+
+    /**
+     * Returns a deep copy of callIdToInfo that's safe to read/write without synchronization
+     */
+    public static Map<String, CallInfoImpl> cloneData() {
+        synchronized (sLock) {
+            Map<String, CallInfoImpl> result = new HashMap<>(sCallIdToInfo.size());
+            for (Map.Entry<String, CallInfoImpl> entry : sCallIdToInfo.entrySet()) {
+                result.put(entry.getKey(), new CallInfoImpl(entry.getValue()));
+            }
+            return result;
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 3030fea..f597cf1 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -16,23 +16,20 @@
 
 package com.android.server.telecom;
 
-import android.content.Context;
-import android.media.AudioManager;
 import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
-import android.provider.Settings;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 /**
  * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
  * used from the main thread.
  */
-class AsyncRingtonePlayer {
+@VisibleForTesting
+public class AsyncRingtonePlayer {
     // Message codes used with the ringtone thread.
     private static final int EVENT_PLAY = 1;
     private static final int EVENT_STOP = 2;
@@ -47,23 +44,18 @@
     /** The current ringtone. Only used by the ringtone thread. */
     private Ringtone mRingtone;
 
-    /**
-     * The context.
-     */
-    private final Context mContext;
-
-    AsyncRingtonePlayer(Context context) {
-        mContext = context;
-    }
-
     /** Plays the ringtone. */
-    void play(Uri ringtone) {
+    public void play(Ringtone ringtone) {
         Log.d(this, "Posting play.");
+        if (ringtone == null) {
+            Log.i(this, "null (silence -- not playing anything)");
+            return;
+        }
         postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
     }
 
     /** Stops playing the ringtone. */
-    void stop() {
+    public void stop() {
         Log.d(this, "Posting stop.");
         postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
     }
@@ -75,7 +67,7 @@
      * @param messageCode The message to post.
      * @param shouldCreateHandler True when a handler should be created to handle this message.
      */
-    private void postMessage(int messageCode, boolean shouldCreateHandler, Uri ringtone) {
+    private void postMessage(int messageCode, boolean shouldCreateHandler, Ringtone ringtone) {
         synchronized(this) {
             if (mHandler == null && shouldCreateHandler) {
                 mHandler = getNewHandler();
@@ -103,7 +95,7 @@
             public void handleMessage(Message msg) {
                 switch(msg.what) {
                     case EVENT_PLAY:
-                        handlePlay((Uri) msg.obj);
+                        handlePlay((Ringtone) msg.obj);
                         break;
                     case EVENT_REPEAT:
                         handleRepeat();
@@ -119,7 +111,7 @@
     /**
      * Starts the actual playback of the ringtone. Executes on ringtone-thread.
      */
-    private void handlePlay(Uri ringtoneUri) {
+    private void handlePlay(Ringtone ringtone) {
         // don't bother with any of this if there is an EVENT_STOP waiting.
         if (mHandler.hasMessages(EVENT_STOP)) {
             return;
@@ -129,13 +121,7 @@
         Log.i(this, "Play ringtone.");
 
         if (mRingtone == null) {
-            mRingtone = getRingtone(ringtoneUri);
-
-            // Cancel everything if there is no ringtone.
-            if (mRingtone == null) {
-                handleStop();
-                return;
-            }
+            mRingtone = ringtone;
         }
 
         handleRepeat();
@@ -189,16 +175,4 @@
             }
         }
     }
-
-    private Ringtone getRingtone(Uri ringtoneUri) {
-        if (ringtoneUri == null) {
-            ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
-        }
-
-        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri);
-        if (ringtone != null) {
-            ringtone.setStreamType(AudioManager.STREAM_RING);
-        }
-        return ringtone;
-    }
 }
diff --git a/src/com/android/server/telecom/BluetoothHeadsetProxy.java b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
new file mode 100644
index 0000000..48c60a1
--- /dev/null
+++ b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.bluetooth.BluetoothHeadset;
+
+/**
+ * A proxy class that facilitates testing of the BluetoothPhoneServiceImpl class.
+ *
+ * This is necessary due to the "final" attribute of the BluetoothHeadset class. In order to
+ * test the correct functioning of the BluetoothPhoneServiceImpl class, the final class must be put
+ * into a container that can be mocked correctly.
+ */
+public class BluetoothHeadsetProxy {
+
+    private BluetoothHeadset mBluetoothHeadset;
+
+    public BluetoothHeadsetProxy(BluetoothHeadset headset) {
+        mBluetoothHeadset = headset;
+    }
+
+    public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
+            String number, int type) {
+
+        mBluetoothHeadset.clccResponse(index, direction, status, mode, mpty, number, type);
+    }
+
+    public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
+            int type) {
+
+        mBluetoothHeadset.phoneStateChanged(numActive, numHeld, callState, number, type);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/BluetoothManager.java b/src/com/android/server/telecom/BluetoothManager.java
index aec0d3e..389d0f6 100644
--- a/src/com/android/server/telecom/BluetoothManager.java
+++ b/src/com/android/server/telecom/BluetoothManager.java
@@ -24,8 +24,11 @@
 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 com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.List;
@@ -35,6 +38,9 @@
  * overall audio state. Also provides method for connecting the bluetooth headset to the phone call.
  */
 public class BluetoothManager {
+    public interface BluetoothStateListener {
+        void onBluetoothStateChange(BluetoothManager bluetoothManager);
+    }
 
     private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
             new BluetoothProfile.ServiceListener() {
@@ -78,17 +84,31 @@
         }
     };
 
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
     private final BluetoothAdapter mBluetoothAdapter;
-    private final CallAudioManager mCallAudioManager;
+    private BluetoothStateListener mBluetoothStateListener;
 
     private BluetoothHeadset mBluetoothHeadset;
     private boolean mBluetoothConnectionPending = false;
     private long mBluetoothConnectionRequestTime;
+    private final Runnable mBluetoothConnectionTimeout = new Runnable() {
+        @Override
+        public void run() {
+            if (!isBluetoothAudioConnected()) {
+                Log.v(this, "Bluetooth audio inexplicably disconnected within 5 seconds of " +
+                        "connection. Updating UI.");
+            }
+            mBluetoothConnectionPending = false;
+            updateBluetoothState();
+        }
+    };
+    private final Context mContext;
 
 
-    public BluetoothManager(Context context, CallAudioManager callAudioManager) {
+    public BluetoothManager(Context context) {
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-        mCallAudioManager = callAudioManager;
+        mContext = context;
 
         if (mBluetoothAdapter != null) {
             mBluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
@@ -102,6 +122,10 @@
         context.registerReceiver(mReceiver, intentFilter);
     }
 
+    public void setBluetoothStateListener(BluetoothStateListener bluetoothStateListener) {
+        mBluetoothStateListener = bluetoothStateListener;
+    }
+
     //
     // Bluetooth helper methods.
     //
@@ -121,7 +145,8 @@
      *         available to the user (i.e. if the device is BT-capable
      *         and a headset is connected.)
      */
-    boolean isBluetoothAvailable() {
+    @VisibleForTesting
+    public boolean isBluetoothAvailable() {
         Log.v(this, "isBluetoothAvailable()...");
 
         // There's no need to ask the Bluetooth system service if BT is enabled:
@@ -191,7 +216,8 @@
      *              that the BT audio connection is currently being set
      *              up, and will be connected soon.)
      */
-    /* package */ boolean isBluetoothAudioConnectedOrPending() {
+    @VisibleForTesting
+    public boolean isBluetoothAudioConnectedOrPending() {
         if (isBluetoothAudioConnected()) {
             Log.v(this, "isBluetoothAudioConnectedOrPending: ==> TRUE (really connected)");
             return true;
@@ -203,16 +229,9 @@
         if (mBluetoothConnectionPending) {
             long timeSinceRequest =
                     SystemClock.elapsedRealtime() - mBluetoothConnectionRequestTime;
-            if (timeSinceRequest < 5000 /* 5 seconds */) {
-                Log.v(this, "isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
-                             + timeSinceRequest + " msec ago)");
-                return true;
-            } else {
-                Log.v(this, "isBluetoothAudioConnectedOrPending: ==> FALSE (request too old: "
-                             + timeSinceRequest + " msec ago)");
-                mBluetoothConnectionPending = false;
-                return false;
-            }
+            Log.v(this, "isBluetoothAudioConnectedOrPending: ==> TRUE (requested "
+                    + timeSinceRequest + " msec ago)");
+            return true;
         }
 
         Log.v(this, "isBluetoothAudioConnectedOrPending: ==> FALSE");
@@ -223,10 +242,11 @@
      * Notified audio manager of a change to the bluetooth state.
      */
     void updateBluetoothState() {
-        mCallAudioManager.onBluetoothStateChange(this);
+        mBluetoothStateListener.onBluetoothStateChange(this);
     }
 
-    void connectBluetoothAudio() {
+    @VisibleForTesting
+    public void connectBluetoothAudio() {
         Log.v(this, "connectBluetoothAudio()...");
         if (mBluetoothHeadset != null) {
             mBluetoothHeadset.connectAudio();
@@ -239,13 +259,18 @@
         // instantly. (See isBluetoothAudioConnectedOrPending() above.)
         mBluetoothConnectionPending = true;
         mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
+        mHandler.removeCallbacks(mBluetoothConnectionTimeout);
+        mHandler.postDelayed(mBluetoothConnectionTimeout,
+                Timeouts.getBluetoothPendingTimeoutMillis(mContext.getContentResolver()));
     }
 
-    void disconnectBluetoothAudio() {
+    @VisibleForTesting
+    public void disconnectBluetoothAudio() {
         Log.v(this, "disconnectBluetoothAudio()...");
         if (mBluetoothHeadset != null) {
             mBluetoothHeadset.disconnectAudio();
         }
+        mHandler.removeCallbacks(mBluetoothConnectionTimeout);
         mBluetoothConnectionPending = false;
     }
 
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index dab4545..5c7cae4 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -34,6 +34,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.CallsManager.CallsManagerListener;
 
 import java.util.Collection;
@@ -45,7 +46,13 @@
  * Bluetooth headset manager for Telecom. This class shares the call state with the bluetooth device
  * and accepts call-related commands to perform on behalf of the BT device.
  */
-public final class BluetoothPhoneServiceImpl {
+public class BluetoothPhoneServiceImpl {
+
+    public interface BluetoothPhoneServiceImplFactory {
+        BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
+                TelecomSystem.SyncRoot lock, CallsManager callsManager,
+                PhoneAccountRegistrar phoneAccountRegistrar);
+    }
 
     private static final String TAG = "BluetoothPhoneService";
 
@@ -79,7 +86,8 @@
      * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
      * bluetooth headset code uses to control call.
      */
-    private final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
+    @VisibleForTesting
+    public final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
         @Override
         public boolean answerCall() throws RemoteException {
             synchronized (mLock) {
@@ -271,7 +279,8 @@
      * Listens to call changes from the CallsManager and calls into methods to update the bluetooth
      * headset with the new states.
      */
-    private CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() {
+    @VisibleForTesting
+    public CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() {
         @Override
         public void onCallAdded(Call call) {
             updateHeadsetWithCallState(false /* force */);
@@ -350,27 +359,29 @@
      * Listens to connections and disconnections of bluetooth headsets.  We need to save the current
      * bluetooth headset so that we know where to send call updates.
      */
-    private BluetoothProfile.ServiceListener mProfileListener =
+    @VisibleForTesting
+    public BluetoothProfile.ServiceListener mProfileListener =
             new BluetoothProfile.ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            synchronized (mLock) {
-                mBluetoothHeadset = (BluetoothHeadset) proxy;
-            }
-        }
+                @Override
+                public void onServiceConnected(int profile, BluetoothProfile proxy) {
+                    synchronized (mLock) {
+                        setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy));
+                    }
+                }
 
-        @Override
-        public void onServiceDisconnected(int profile) {
-            synchronized (mLock) {
-                mBluetoothHeadset = null;
-            }
-        }
-    };
+                @Override
+                public void onServiceDisconnected(int profile) {
+                    synchronized (mLock) {
+                        mBluetoothHeadset = null;
+                    }
+                }
+            };
 
     /**
      * Receives events for global state changes of the bluetooth adapter.
      */
-    private final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() {
+    @VisibleForTesting
+    public final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
@@ -389,7 +400,7 @@
     };
 
     private BluetoothAdapter mBluetoothAdapter;
-    private BluetoothHeadset mBluetoothHeadset;
+    private BluetoothHeadsetProxy mBluetoothHeadset;
 
     // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
     private Map<Call, Integer> mClccIndexMap = new HashMap<>();
@@ -431,6 +442,11 @@
         updateHeadsetWithCallState(false /* force */);
     }
 
+    @VisibleForTesting
+    public void setBluetoothHeadset(BluetoothHeadsetProxy bluetoothHeadset) {
+        mBluetoothHeadset = bluetoothHeadset;
+    }
+
     private boolean processChld(int chld) {
         Call activeCall = mCallsManager.getActiveCall();
         Call ringingCall = mCallsManager.getRingingCall();
@@ -485,7 +501,7 @@
                     if (!conferenceable.isEmpty()) {
                         mCallsManager.conference(activeCall, conferenceable.get(0));
                         return true;
-                   }
+                    }
                 }
             }
         }
@@ -544,7 +560,7 @@
                 boolean shouldReevaluateState =
                         conferenceCall.can(Connection.CAPABILITY_MERGE_CONFERENCE) ||
                         (conferenceCall.can(Connection.CAPABILITY_SWAP_CONFERENCE) &&
-                         !conferenceCall.wasConferencePreviouslyMerged());
+                        !conferenceCall.wasConferencePreviouslyMerged());
 
                 if (shouldReevaluateState) {
                     isPartOfConference = false;
@@ -621,7 +637,6 @@
      *      changed.
      */
     private void updateHeadsetWithCallState(boolean force) {
-        CallsManager callsManager = mCallsManager;
         Call activeCall = mCallsManager.getActiveCall();
         Call ringingCall = mCallsManager.getRingingCall();
         Call heldCall = mCallsManager.getHeldCall();
@@ -676,11 +691,11 @@
                 (force ||
                         (!callsPendingSwitch &&
                                 (numActiveCalls != mNumActiveCalls ||
-                                 numHeldCalls != mNumHeldCalls ||
-                                 bluetoothCallState != mBluetoothCallState ||
-                                 !TextUtils.equals(ringingAddress, mRingingAddress) ||
-                                 ringingAddressType != mRingingAddressType ||
-                                 (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) {
+                                numHeldCalls != mNumHeldCalls ||
+                                bluetoothCallState != mBluetoothCallState ||
+                                !TextUtils.equals(ringingAddress, mRingingAddress) ||
+                                ringingAddressType != mRingingAddressType ||
+                                (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) {
 
             // If the call is transitioning into the alerting state, send DIALING first.
             // Some devices expect to see a DIALING state prior to seeing an ALERTING state
@@ -812,15 +827,15 @@
         PhoneAccount account = null;
         if (call != null) {
             // First try to get the network name of the foreground call.
-            account = mPhoneAccountRegistrar.getPhoneAccountCheckCallingUser(
+            account = mPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
                     call.getTargetPhoneAccount());
         }
 
         if (account == null) {
             // Second, Try to get the label for the default Phone Account.
-            account = mPhoneAccountRegistrar.getPhoneAccountCheckCallingUser(
-                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
-                        PhoneAccount.SCHEME_TEL));
+            account = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
+                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                            PhoneAccount.SCHEME_TEL));
         }
         return account;
     }
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index cee5332..8555c20 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -29,7 +29,6 @@
 import android.telecom.DisconnectCause;
 import android.telecom.Connection;
 import android.telecom.GatewayInfo;
-import android.telecom.InCallService.VideoCall;
 import android.telecom.ParcelableConnection;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
@@ -39,10 +38,12 @@
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
+import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.IVideoProvider;
 import com.android.internal.telephony.CallerInfo;
+import com.android.internal.telephony.CallerInfoAsyncQuery;
 import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener;
 import com.android.internal.telephony.SmsApplication;
 import com.android.server.telecom.ContactsAsyncHelper.OnImageLoadCompleteListener;
@@ -65,13 +66,21 @@
  */
 @VisibleForTesting
 public class Call implements CreateConnectionResponse {
+    public final static String CALL_ID_UNKNOWN = "-1";
+    public final static long DATA_USAGE_NOT_SET = -1;
+
+    public static final int CALL_DIRECTION_UNDEFINED = 0;
+    public static final int CALL_DIRECTION_OUTGOING = 1;
+    public static final int CALL_DIRECTION_INCOMING = 2;
+    public static final int CALL_DIRECTION_UNKNOWN = 3;
+
     /**
      * Listener for events on the call.
      */
     interface Listener {
         void onSuccessfulOutgoingCall(Call call, int callState);
         void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
-        void onSuccessfulIncomingCall(Call call);
+        void onSuccessfulIncomingCall(Call call, boolean shouldSendToVoicemail);
         void onFailedIncomingCall(Call call);
         void onSuccessfulUnknownCall(Call call, int callState);
         void onFailedUnknownCall(Call call);
@@ -103,7 +112,7 @@
         @Override
         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
         @Override
-        public void onSuccessfulIncomingCall(Call call) {}
+        public void onSuccessfulIncomingCall(Call call, boolean shouldSendToVoicemail) {}
         @Override
         public void onFailedIncomingCall(Call call) {}
         @Override
@@ -161,7 +170,10 @@
                 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) {
                     synchronized (mLock) {
                         if (cookie != null) {
-                            ((Call) cookie).setCallerInfo(callerInfo, token);
+                            CallSessionCookie callSession = (CallSessionCookie) cookie;
+                            Log.continueSession(callSession.mSession, "OQCL.oQC");
+                            callSession.mSessionCall.setCallerInfo(callerInfo, token);
+                            Log.endSession();
                         }
                     }
                 }
@@ -175,28 +187,56 @@
                         int token, Drawable photo, Bitmap photoIcon, Object cookie) {
                     synchronized (mLock) {
                         if (cookie != null) {
-                            ((Call) cookie).setPhoto(photo, photoIcon, token);
+                            CallSessionCookie callSession = (CallSessionCookie) cookie;
+                            Log.continueSession(callSession.mSession, "OCLCL.oILC");
+                            callSession.mSessionCall.setPhoto(photo, photoIcon, token);
+                            Log.endSession();
                         }
                     }
                 }
             };
 
-    private final Runnable mDirectToVoicemailRunnable = new Runnable() {
+    private class DirectToVoicemailRunnable implements Runnable {
+
+        Session mSession;
+
+        public DirectToVoicemailRunnable(Session session) {
+            mSession = session;
+        }
+
         @Override
         public void run() {
-            synchronized (mLock) {
-                processDirectToVoicemail();
+            try {
+                Log.continueSession(mSession, "DTVR.r");
+                synchronized (mLock) {
+                    processDirectToVoicemail();
+                }
+            } finally {
+                Log.endSession();
+                mSession = null;
             }
         }
-    };
+    }
 
-    /** True if this is an incoming call. */
-    private final boolean mIsIncoming;
+    private class CallSessionCookie {
+        Call mSessionCall;
+        Session mSession;
 
-    /** True if this is a currently unknown call that was not previously tracked by CallsManager,
-     *  and did not originate via the regular incoming/outgoing call code paths.
+        public CallSessionCookie(Call call, Session session) {
+            mSessionCall = call;
+            mSession = session;
+        }
+    }
+
+    /**
+     * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN
      */
-    private boolean mIsUnknown;
+    private final int mCallDirection;
+
+    /**
+     * The post-dial digits that were dialed after the network portion of the number
+     */
+    private final String mPostDialDigits;
 
     /**
      * The time this call was created. Beyond logging and such, may also be used for bookkeeping
@@ -220,6 +260,8 @@
 
     private PhoneAccountHandle mTargetPhoneAccountHandle;
 
+    private UserHandle mInitiatingUser;
+
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     private final List<Call> mConferenceableCalls = new ArrayList<>();
@@ -298,6 +340,8 @@
 
     private boolean mIsConference = false;
 
+    private final boolean mShouldAttachToExistingConnection;
+
     private Call mParentCall = null;
 
     private List<Call> mChildCalls = new LinkedList<>();
@@ -320,6 +364,8 @@
     private final CallsManager mCallsManager;
     private final TelecomSystem.SyncRoot mLock;
     private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
+    private final String mId;
+    private Analytics.CallInfo mAnalytics;
 
     private boolean mWasConferencePreviouslyMerged = false;
 
@@ -332,6 +378,11 @@
     private boolean mIsLocallyDisconnecting = false;
 
     /**
+     * Tracks the current call data usage as reported by the video provider.
+     */
+    private long mCallDataUsage = DATA_USAGE_NOT_SET;
+
+    /**
      * Persists the specified parameters and initializes the new instance.
      *
      * @param context The context.
@@ -343,9 +394,13 @@
      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
-     * @param isIncoming True if this is an incoming call.
+     * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
+     *         or CALL_DIRECTION_UNKNOWN.
+     * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
+     *         connection, regardless of whether it's incoming or outgoing.
      */
     public Call(
+            String callId,
             Context context,
             CallsManager callsManager,
             TelecomSystem.SyncRoot lock,
@@ -356,8 +411,10 @@
             GatewayInfo gatewayInfo,
             PhoneAccountHandle connectionManagerPhoneAccountHandle,
             PhoneAccountHandle targetPhoneAccountHandle,
-            boolean isIncoming,
+            int callDirection,
+            boolean shouldAttachToExistingConnection,
             boolean isConference) {
+        mId = callId;
         mState = isConference ? CallState.ACTIVE : CallState.NEW;
         mContext = context;
         mCallsManager = callsManager;
@@ -366,12 +423,18 @@
         mContactsAsyncHelper = contactsAsyncHelper;
         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
         setHandle(handle);
+        mPostDialDigits = handle != null
+                ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
         mGatewayInfo = gatewayInfo;
         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
         setTargetPhoneAccount(targetPhoneAccountHandle);
-        mIsIncoming = isIncoming;
+        mCallDirection = callDirection;
         mIsConference = isConference;
+        mShouldAttachToExistingConnection = shouldAttachToExistingConnection
+                || callDirection == CALL_DIRECTION_INCOMING;
+
         maybeLoadCannedSmsResponses();
+        mAnalytics = new Analytics.CallInfo();
 
         Log.event(this, Log.Events.CREATED);
     }
@@ -388,10 +451,14 @@
      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
-     * @param isIncoming True if this is an incoming call.
+     * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
+     *         or CALL_DIRECTION_UNKNOWN
+     * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
+     *         connection, regardless of whether it's incoming or outgoing.
      * @param connectTimeMillis The connection time of the call.
      */
     Call(
+            String callId,
             Context context,
             CallsManager callsManager,
             TelecomSystem.SyncRoot lock,
@@ -402,15 +469,17 @@
             GatewayInfo gatewayInfo,
             PhoneAccountHandle connectionManagerPhoneAccountHandle,
             PhoneAccountHandle targetPhoneAccountHandle,
-            boolean isIncoming,
+            int callDirection,
+            boolean shouldAttachToExistingConnection,
             boolean isConference,
             long connectTimeMillis) {
-        this(context, callsManager, lock, repository, contactsAsyncHelper,
+        this(callId, context, callsManager, lock, repository, contactsAsyncHelper,
                 callerInfoAsyncQueryFactory, handle, gatewayInfo,
-                connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, isIncoming,
-                isConference);
+                connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
+                shouldAttachToExistingConnection, isConference);
 
         mConnectTimeMillis = connectTimeMillis;
+        mAnalytics.setCallStartTime(connectTimeMillis);
     }
 
     public void addListener(Listener listener) {
@@ -423,6 +492,27 @@
         }
     }
 
+    public void initAnalytics() {
+        int analyticsDirection;
+        switch (mCallDirection) {
+            case CALL_DIRECTION_OUTGOING:
+                analyticsDirection = Analytics.OUTGOING_DIRECTION;
+                break;
+            case CALL_DIRECTION_INCOMING:
+                analyticsDirection = Analytics.INCOMING_DIRECTION;
+                break;
+            case CALL_DIRECTION_UNKNOWN:
+            case CALL_DIRECTION_UNDEFINED:
+            default:
+                analyticsDirection = Analytics.UNKNOWN_DIRECTION;
+        }
+        mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection);
+    }
+
+    public Analytics.CallInfo getAnalytics() {
+        return mAnalytics;
+    }
+
     public void destroy() {
         Log.event(this, Log.Events.DESTROYED);
     }
@@ -438,7 +528,7 @@
 
 
         return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), [%s]]",
-                System.identityHashCode(this),
+                mId,
                 CallState.toString(mState),
                 component,
                 Log.piiHandle(mHandle),
@@ -476,7 +566,8 @@
         return sb.toString();
     }
 
-    int getState() {
+    @VisibleForTesting
+    public int getState() {
         return mState;
     }
 
@@ -503,6 +594,14 @@
     }
 
     /**
+     * Returns the unique ID for this call as it exists in Telecom.
+     * @return The call ID.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
      * Sets the call state. Although there exists the notion of appropriate state transitions
      * (see {@link CallState}), in practice those expectations break down when cellular systems
      * misbehave and they do this very often. The result is that we do not enforce state transitions
@@ -527,6 +626,7 @@
                     // call from resetting active time when it goes in and out of
                     // ACTIVE/ON_HOLD
                     mConnectTimeMillis = System.currentTimeMillis();
+                    mAnalytics.setCallStartTime(mConnectTimeMillis);
                 }
 
                 // Video state changes are normally tracked against history when a call is active.
@@ -539,6 +639,7 @@
                 mDisconnectTimeMillis = 0;
             } else if (mState == CallState.DISCONNECTED) {
                 mDisconnectTimeMillis = System.currentTimeMillis();
+                mAnalytics.setCallEndTime(mDisconnectTimeMillis);
                 setLocallyDisconnecting(false);
                 fixParentAfterDisconnect();
             }
@@ -601,7 +702,8 @@
         return mRingbackRequested;
     }
 
-    boolean isConference() {
+    @VisibleForTesting
+    public boolean isConference() {
         return mIsConference;
     }
 
@@ -609,6 +711,10 @@
         return mHandle;
     }
 
+    public String getPostDialDigits() {
+        return mPostDialDigits;
+    }
+
     int getHandlePresentation() {
         return mHandlePresentation;
     }
@@ -689,6 +795,7 @@
     public void setDisconnectCause(DisconnectCause disconnectCause) {
         // TODO: Consider combining this method with a setDisconnected() method that is totally
         // separate from setState.
+        mAnalytics.setCallDisconnectCause(disconnectCause);
         mDisconnectCause = disconnectCause;
     }
 
@@ -696,7 +803,8 @@
         return mDisconnectCause;
     }
 
-    boolean isEmergencyCall() {
+    @VisibleForTesting
+    public boolean isEmergencyCall() {
         return mIsEmergencyCall;
     }
 
@@ -711,7 +819,8 @@
         return getHandle();
     }
 
-    GatewayInfo getGatewayInfo() {
+    @VisibleForTesting
+    public GatewayInfo getGatewayInfo() {
         return mGatewayInfo;
     }
 
@@ -719,11 +828,13 @@
         mGatewayInfo = gatewayInfo;
     }
 
-    PhoneAccountHandle getConnectionManagerPhoneAccount() {
+    @VisibleForTesting
+    public PhoneAccountHandle getConnectionManagerPhoneAccount() {
         return mConnectionManagerPhoneAccountHandle;
     }
 
-    void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
+    @VisibleForTesting
+    public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
             mConnectionManagerPhoneAccountHandle = accountHandle;
             for (Listener l : mListeners) {
@@ -733,11 +844,13 @@
 
     }
 
-    PhoneAccountHandle getTargetPhoneAccount() {
+    @VisibleForTesting
+    public PhoneAccountHandle getTargetPhoneAccount() {
         return mTargetPhoneAccountHandle;
     }
 
-    void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
+    @VisibleForTesting
+    public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
             mTargetPhoneAccountHandle = accountHandle;
             for (Listener l : mListeners) {
@@ -746,8 +859,13 @@
         }
     }
 
-    boolean isIncoming() {
-        return mIsIncoming;
+    @VisibleForTesting
+    public boolean isIncoming() {
+        return mCallDirection == CALL_DIRECTION_INCOMING;
+    }
+
+    boolean shouldAttachToExistingConnection() {
+        return mShouldAttachToExistingConnection;
     }
 
     /**
@@ -755,7 +873,8 @@
      *     period since this call was added to the set pending outgoing calls, see
      *     mCreationTimeMillis.
      */
-    long getAgeMillis() {
+    @VisibleForTesting
+    public long getAgeMillis() {
         if (mState == CallState.DISCONNECTED &&
                 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
@@ -808,23 +927,28 @@
         }
     }
 
-    Call getParentCall() {
+    @VisibleForTesting
+    public Call getParentCall() {
         return mParentCall;
     }
 
-    List<Call> getChildCalls() {
+    @VisibleForTesting
+    public List<Call> getChildCalls() {
         return mChildCalls;
     }
 
-    boolean wasConferencePreviouslyMerged() {
+    @VisibleForTesting
+    public boolean wasConferencePreviouslyMerged() {
         return mWasConferencePreviouslyMerged;
     }
 
-    Call getConferenceLevelActiveCall() {
+    @VisibleForTesting
+    public Call getConferenceLevelActiveCall() {
         return mConferenceLevelActiveCall;
     }
 
-    ConnectionServiceWrapper getConnectionService() {
+    @VisibleForTesting
+    public ConnectionServiceWrapper getConnectionService() {
         return mConnectionService;
     }
 
@@ -837,13 +961,15 @@
         return mContext;
     }
 
-    void setConnectionService(ConnectionServiceWrapper service) {
+    @VisibleForTesting
+    public void setConnectionService(ConnectionServiceWrapper service) {
         Preconditions.checkNotNull(service);
 
         clearConnectionService();
 
         service.incrementAssociatedCallCount();
         mConnectionService = service;
+        mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
         mConnectionService.addCall(this);
     }
 
@@ -868,20 +994,20 @@
 
     private void processDirectToVoicemail() {
         if (mDirectToVoicemailQueryPending) {
+            boolean shouldSendToVoicemail;
             if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
                 Log.i(this, "Directing call to voicemail: %s.", this);
                 // TODO: Once we move State handling from CallsManager to Call, we
                 // will not need to set STATE_RINGING state prior to calling reject.
-                setState(CallState.RINGING, "directing to voicemail");
-                reject(false, null);
+                shouldSendToVoicemail = true;
             } else {
-                // TODO: Make this class (not CallsManager) responsible for changing
-                // the call state to STATE_RINGING.
-
-                // TODO: Replace this with state transition to STATE_RINGING.
-                for (Listener l : mListeners) {
-                    l.onSuccessfulIncomingCall(this);
-                }
+                shouldSendToVoicemail = false;
+            }
+            // TODO: Make this class (not CallsManager) responsible for changing
+            // the call state to STATE_RINGING.
+            // TODO: Replace this with state transition to STATE_RINGING.
+            for (Listener l : mListeners) {
+                l.onSuccessfulIncomingCall(this, shouldSendToVoicemail);
             }
 
             mDirectToVoicemailQueryPending = false;
@@ -923,26 +1049,31 @@
             mConferenceableCalls.add(idMapper.getCall(id));
         }
 
-        if (mIsUnknown) {
-            for (Listener l : mListeners) {
-                l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
-            }
-        } else if (mIsIncoming) {
-            // We do not handle incoming calls immediately when they are verified by the connection
-            // service. We allow the caller-info-query code to execute first so that we can read the
-            // direct-to-voicemail property before deciding if we want to show the incoming call to
-            // the user or if we want to reject the call.
-            mDirectToVoicemailQueryPending = true;
+        switch (mCallDirection) {
+            case CALL_DIRECTION_INCOMING:
+                // We do not handle incoming calls immediately when they are verified by the
+                // connection service. We allow the caller-info-query code to execute first so
+                // that we can read the direct-to-voicemail property before deciding if we want
+                // to show the incoming call to the user or if we want to reject the call.
+                mDirectToVoicemailQueryPending = true;
 
-            // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
-            // showing the user the incoming call screen.
-            mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(
-                    mContext.getContentResolver()));
-        } else {
-            for (Listener l : mListeners) {
-                l.onSuccessfulOutgoingCall(this,
-                        getStateFromConnectionState(connection.getState()));
-            }
+                // Timeout the direct-to-voicemail lookup execution so that we dont wait too long
+                // before showing the user the incoming call screen.
+                mHandler.postDelayed(new DirectToVoicemailRunnable(Log.createSubsession()),
+                        Timeouts.getDirectToVoicemailMillis(mContext.getContentResolver()));
+                break;
+            case CALL_DIRECTION_OUTGOING:
+                for (Listener l : mListeners) {
+                    l.onSuccessfulOutgoingCall(this,
+                            getStateFromConnectionState(connection.getState()));
+                }
+                break;
+            case CALL_DIRECTION_UNKNOWN:
+                for (Listener l : mListeners) {
+                    l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
+                            .getState()));
+                }
+                break;
         }
     }
 
@@ -952,18 +1083,22 @@
         setDisconnectCause(disconnectCause);
         mCallsManager.markCallAsDisconnected(this, disconnectCause);
 
-        if (mIsUnknown) {
-            for (Listener listener : mListeners) {
-                listener.onFailedUnknownCall(this);
-            }
-        } else if (mIsIncoming) {
-            for (Listener listener : mListeners) {
-                listener.onFailedIncomingCall(this);
-            }
-        } else {
-            for (Listener listener : mListeners) {
-                listener.onFailedOutgoingCall(this, disconnectCause);
-            }
+        switch (mCallDirection) {
+            case CALL_DIRECTION_INCOMING:
+                for (Listener listener : mListeners) {
+                    listener.onFailedIncomingCall(this);
+                }
+                break;
+            case CALL_DIRECTION_OUTGOING:
+                for (Listener listener : mListeners) {
+                    listener.onFailedOutgoingCall(this, disconnectCause);
+                }
+                break;
+            case CALL_DIRECTION_UNKNOWN:
+                for (Listener listener : mListeners) {
+                    listener.onFailedUnknownCall(this);
+                }
+                break;
         }
     }
 
@@ -1134,7 +1269,8 @@
     }
 
     /** Checks if this is a live call or not. */
-    boolean isAlive() {
+    @VisibleForTesting
+    public boolean isAlive() {
         switch (mState) {
             case CallState.NEW:
             case CallState.RINGING:
@@ -1172,7 +1308,8 @@
     /**
      * @return the uri of the contact associated with this call.
      */
-    Uri getContactUri() {
+    @VisibleForTesting
+    public Uri getContactUri() {
         if (mCallerInfo == null || !mCallerInfo.contactExists) {
             return getHandle();
         }
@@ -1217,7 +1354,8 @@
         }
     }
 
-    void mergeConference() {
+    @VisibleForTesting
+    public void mergeConference() {
         if (mConnectionService == null) {
             Log.w(this, "merging conference calls without a connection service.");
         } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
@@ -1227,7 +1365,8 @@
         }
     }
 
-    void swapConference() {
+    @VisibleForTesting
+    public void swapConference() {
         if (mConnectionService == null) {
             Log.w(this, "swapping conference calls without a connection service.");
         } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
@@ -1285,11 +1424,13 @@
         }
     }
 
-    List<Call> getConferenceableCalls() {
+    @VisibleForTesting
+    public List<Call> getConferenceableCalls() {
         return mConferenceableCalls;
     }
 
-    boolean can(int capability) {
+    @VisibleForTesting
+    public boolean can(int capability) {
         return (mConnectionCapabilities & capability) == capability;
     }
 
@@ -1416,15 +1557,28 @@
         mCallerInfo = null;
         if (!TextUtils.isEmpty(number)) {
             Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
+            final Session subsession = Log.createSubsession();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallerInfoAsyncQueryFactory.startQuery(
-                            mQueryToken,
-                            mContext,
-                            number,
-                            mCallerInfoQueryListener,
-                            Call.this);
+                    Session subsubsession = null;
+                    try {
+                        Log.continueSession(subsession, "CIAQF.sQ");
+                        subsubsession = Log.createSubsession();
+                        CallerInfoAsyncQuery value = mCallerInfoAsyncQueryFactory.startQuery(
+                                mQueryToken, mContext, number, mCallerInfoQueryListener,
+                                new CallSessionCookie(Call.this, subsubsession));
+                        // If there is an exception in startQuery, then this assignment will never
+                        // occur.
+                        if(value != null) {
+                            subsubsession = null;
+                        }
+                    } finally {
+                        if (subsubsession != null) {
+                            Log.cancelSubsession(subsubsession);
+                        }
+                        Log.endSession();
+                    }
                 }
             });
         }
@@ -1446,15 +1600,25 @@
             Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
 
             if (mCallerInfo.contactDisplayPhotoUri != null) {
-                Log.d(this, "Searching person uri %s for call %s",
-                        mCallerInfo.contactDisplayPhotoUri, this);
-                mContactsAsyncHelper.startObtainPhotoAsync(
-                        token,
-                        mContext,
-                        mCallerInfo.contactDisplayPhotoUri,
-                        mPhotoLoadListener,
-                        this);
-                // Do not call onCallerInfoChanged yet in this case.  We call it in setPhoto().
+                Session subsession = null;
+                try {
+                    subsession = Log.createSubsession();
+                    Log.d(this, "Searching person uri %s for call %s",
+                            mCallerInfo.contactDisplayPhotoUri, this);
+                    mContactsAsyncHelper.startObtainPhotoAsync(
+                            token,
+                            mContext,
+                            mCallerInfo.contactDisplayPhotoUri,
+                            mPhotoLoadListener,
+                            new CallSessionCookie(this, subsession));
+                    // If there is an exception, then this assignment will never occur.
+                    subsession = null;
+                    // Do not call onCallerInfoChanged yet in this case.  We call it in setPhoto().
+                } finally {
+                    if(subsession != null) {
+                        Log.cancelSubsession(subsession);
+                    }
+                }
             } else {
                 for (Listener l : mListeners) {
                     l.onCallerInfoChanged(this);
@@ -1489,7 +1653,9 @@
     }
 
     private void maybeLoadCannedSmsResponses() {
-        if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) {
+        if (mCallDirection == CALL_DIRECTION_INCOMING
+                && isRespondViaSmsCapable()
+                && !mCannedSmsResponsesLoadingStarted) {
             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
             mCannedSmsResponsesLoadingStarted = true;
             mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
@@ -1641,11 +1807,7 @@
     }
 
     public boolean isUnknown() {
-        return mIsUnknown;
-    }
-
-    public void setIsUnknown(boolean isUnknown) {
-        mIsUnknown = isUnknown;
+        return mCallDirection == CALL_DIRECTION_UNKNOWN;
     }
 
     /**
@@ -1666,6 +1828,22 @@
         mIsLocallyDisconnecting = isLocallyDisconnecting;
     }
 
+    /**
+     * @return user handle of user initiating the outgoing call.
+     */
+    public UserHandle getInitiatingUser() {
+        return mInitiatingUser;
+    }
+
+    /**
+     * Set the user handle of user initiating the outgoing call.
+     * @param initiatingUser
+     */
+    public void setInitiatingUser(UserHandle initiatingUser) {
+        Preconditions.checkNotNull(initiatingUser);
+        mInitiatingUser = initiatingUser;
+    }
+
     static int getStateFromConnectionState(int state) {
         switch (state) {
             case Connection.STATE_INITIALIZING:
@@ -1694,4 +1872,22 @@
     public boolean isDisconnected() {
         return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
     }
+
+    /**
+     * Sets the call data usage for the call.
+     *
+     * @param callDataUsage The new call data usage (in bytes).
+     */
+    public void setCallDataUsage(long callDataUsage) {
+        mCallDataUsage = callDataUsage;
+    }
+
+    /**
+     * Returns the call data usage for the call.
+     *
+     * @return The call data usage (in bytes).
+     */
+    public long getCallDataUsage() {
+        return mCallDataUsage;
+    }
 }
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 23284e3..4fa08fa 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -16,31 +16,24 @@
 
 package com.android.server.telecom;
 
-import android.app.ActivityManagerNative;
 import android.content.Context;
-import android.content.pm.UserInfo;
 import android.media.AudioManager;
 import android.media.IAudioService;
-import android.os.Binder;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.telecom.CallAudioState;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
-import java.util.Objects;
-
 /**
  * This class manages audio modes, streams and other properties.
  */
-final class CallAudioManager extends CallsManagerListenerBase
-        implements WiredHeadsetManager.Listener, DockManager.Listener {
+@VisibleForTesting
+public class CallAudioManager extends CallsManagerListenerBase {
     private static final int STREAM_NONE = -1;
 
     private static final String STREAM_DESCRIPTION_NONE = "STEAM_NONE";
@@ -61,9 +54,7 @@
     private static final String MODE_DESCRIPTION_IN_COMMUNICATION = "MODE_IN_COMMUNICATION";
 
     private static final int MSG_AUDIO_MANAGER_INITIALIZE = 0;
-    private static final int MSG_AUDIO_MANAGER_TURN_ON_SPEAKER = 1;
     private static final int MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL = 2;
-    private static final int MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE = 3;
     private static final int MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL = 4;
     private static final int MSG_AUDIO_MANAGER_SET_MODE = 5;
 
@@ -73,124 +64,100 @@
 
         @Override
         public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_AUDIO_MANAGER_INITIALIZE: {
-                    mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-                    break;
+            SomeArgs args = (SomeArgs) msg.obj;
+            int arg2 = 0;
+            try {
+                if (args != null) {
+                    Session subsession = (Session) args.arg1;
+                    Log.continueSession(subsession, "CAM.hM_" + msg.what);
+                    arg2 = (int) args.arg2;
                 }
-                case MSG_AUDIO_MANAGER_TURN_ON_SPEAKER: {
-                    boolean on = (msg.arg1 != 0);
-                    // Wired headset and earpiece work the same way
-                    if (mAudioManager.isSpeakerphoneOn() != on) {
-                        Log.i(this, "turning speaker phone %s", on);
-                        mAudioManager.setSpeakerphoneOn(on);
+                switch (msg.what) {
+                    case MSG_AUDIO_MANAGER_INITIALIZE: {
+                        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+                        break;
                     }
-                    break;
-                }
-                case MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL: {
-                    mAudioManager.abandonAudioFocusForCall();
-                    break;
-                }
-                case MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE: {
-                    boolean mute = (msg.arg1 != 0);
-                    if (mute != mAudioManager.isMicrophoneMute()) {
-                        IAudioService audio = getAudioService();
-                        Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
-                                mute, audio == null);
-                        if (audio != null) {
-                            try {
-                                // We use the audio service directly here so that we can specify
-                                // the current user. Telecom runs in the system_server process which
-                                // may run as a separate user from the foreground user. If we
-                                // used AudioManager directly, we would change mute for the system's
-                                // user and not the current foreground, which we want to avoid.
-                                audio.setMicrophoneMute(
-                                        mute, mContext.getOpPackageName(), getCurrentUserId());
+                    case MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL: {
+                        mAudioManager.abandonAudioFocusForCall();
+                        break;
+                    }
+                    case MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL: {
+                        int stream = arg2;
+                        mAudioManager.requestAudioFocusForCall(
+                                stream,
+                                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+                        break;
+                    }
+                    case MSG_AUDIO_MANAGER_SET_MODE: {
+                        int newMode = arg2;
+                        int oldMode = mAudioManager.getMode();
+                        Log.v(this, "Request to change audio mode from %s to %s", modeToString(oldMode),
+                                modeToString(newMode));
 
-                            } catch (RemoteException e) {
-                                Log.e(this, e, "Remote exception while toggling mute.");
+                        if (oldMode != newMode) {
+                            if (oldMode == AudioManager.MODE_IN_CALL &&
+                                    newMode == AudioManager.MODE_RINGTONE) {
+                                Log.i(this, "Transition from IN_CALL -> RINGTONE."
+                                        + "  Resetting to NORMAL first.");
+                                mAudioManager.setMode(AudioManager.MODE_NORMAL);
                             }
-                            // TODO: Check microphone state after attempting to set to ensure that
-                            // our state corroborates AudioManager's state.
+                            mAudioManager.setMode(newMode);
+                            synchronized (mLock) {
+                                mMostRecentlyUsedMode = newMode;
+                            }
                         }
+                        break;
                     }
-
-                    break;
+                    default:
+                        break;
                 }
-                case MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL: {
-                    int stream = msg.arg1;
-                    mAudioManager.requestAudioFocusForCall(
-                            stream,
-                            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-                    break;
-                }
-                case MSG_AUDIO_MANAGER_SET_MODE: {
-                    int newMode = msg.arg1;
-                    int oldMode = mAudioManager.getMode();
-                    Log.v(this, "Request to change audio mode from %s to %s", modeToString(oldMode),
-                            modeToString(newMode));
-
-                    if (oldMode != newMode) {
-                        if (oldMode == AudioManager.MODE_IN_CALL &&
-                                newMode == AudioManager.MODE_RINGTONE) {
-                            Log.i(this, "Transition from IN_CALL -> RINGTONE."
-                                    + "  Resetting to NORMAL first.");
-                            mAudioManager.setMode(AudioManager.MODE_NORMAL);
-                        }
-                        mAudioManager.setMode(newMode);
-                        synchronized (mLock) {
-                            mMostRecentlyUsedMode = newMode;
-                        }
-                    }
-                    break;
-                }
-                default:
-                    break;
+            } finally {
+                Log.endSession();
+                args.recycle();
             }
         }
     };
 
+    public interface AudioServiceFactory {
+        IAudioService getAudioService();
+    }
+
     private final Context mContext;
     private final TelecomSystem.SyncRoot mLock;
-    private final StatusBarNotifier mStatusBarNotifier;
-    private final BluetoothManager mBluetoothManager;
-    private final WiredHeadsetManager mWiredHeadsetManager;
-    private final DockManager mDockManager;
     private final CallsManager mCallsManager;
+    private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
 
-    private CallAudioState mCallAudioState;
     private int mAudioFocusStreamType;
     private boolean mIsRinging;
     private boolean mIsTonePlaying;
-    private boolean mWasSpeakerOn;
     private int mMostRecentlyUsedMode = AudioManager.MODE_IN_CALL;
     private Call mCallToSpeedUpMTAudio = null;
 
-    CallAudioManager(
+    public CallAudioManager(
             Context context,
             TelecomSystem.SyncRoot lock,
-            StatusBarNotifier statusBarNotifier,
-            WiredHeadsetManager wiredHeadsetManager,
-            DockManager dockManager,
-            CallsManager callsManager) {
+            CallsManager callsManager,
+            CallAudioRouteStateMachine callAudioRouteStateMachine) {
         mContext = context;
         mLock = lock;
-        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_INITIALIZE, 0, 0).sendToTarget();
-        mStatusBarNotifier = statusBarNotifier;
-        mBluetoothManager = new BluetoothManager(context, this);
-        mWiredHeadsetManager = wiredHeadsetManager;
+        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_INITIALIZE,
+                setArgs(0)).sendToTarget();
         mCallsManager = callsManager;
-
-        mWiredHeadsetManager.addListener(this);
-        mDockManager = dockManager;
-        mDockManager.addListener(this);
-
-        saveAudioState(getInitialAudioState(null));
         mAudioFocusStreamType = STREAM_NONE;
+
+        mCallAudioRouteStateMachine = callAudioRouteStateMachine;
     }
 
-    CallAudioState getCallAudioState() {
-        return mCallAudioState;
+    private SomeArgs setArgs(int arg) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = Log.createSubsession();
+        args.arg2 = arg;
+        return args;
+    }
+
+    @VisibleForTesting
+    public CallAudioState getCallAudioState() {
+        return mCallAudioRouteStateMachine.getCurrentCallAudioState();
     }
 
     @Override
@@ -201,8 +168,8 @@
         if (hasFocus() && getForegroundCall() == call) {
             if (!call.isIncoming()) {
                 // Unmute new outgoing call.
-                setSystemAudioState(false, mCallAudioState.getRoute(),
-                        mCallAudioState.getSupportedRouteMask());
+                mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.MUTE_OFF);
             }
         }
     }
@@ -210,13 +177,14 @@
     @Override
     public void onCallRemoved(Call call) {
         Log.v(this, "onCallRemoved");
+        if (mCallsManager.getCalls().isEmpty()) {
+            Log.v(this, "all calls removed, resetting system audio to default state");
+            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                    CallAudioRouteStateMachine.REINITIALIZE);
+        }
+
         // If we didn't already have focus, there's nothing to do.
         if (hasFocus()) {
-            if (mCallsManager.getCalls().isEmpty()) {
-                Log.v(this, "all calls removed, resetting system audio to default state");
-                setInitialAudioState(null, false /* force */);
-                mWasSpeakerOn = false;
-            }
             updateAudioStreamAndMode(call);
         }
     }
@@ -230,19 +198,12 @@
     @Override
     public void onIncomingCallAnswered(Call call) {
         Log.v(this, "onIncomingCallAnswered");
-        int route = mCallAudioState.getRoute();
 
-        // We do two things:
-        // (1) If this is the first call, then we can to turn on bluetooth if available.
-        // (2) Unmute the audio for the new incoming call.
-        boolean isOnlyCall = mCallsManager.getCalls().size() == 1;
-        if (isOnlyCall && mBluetoothManager.isBluetoothAvailable()) {
-            mBluetoothManager.connectBluetoothAudio();
-            route = CallAudioState.ROUTE_BLUETOOTH;
+        if (mCallsManager.getCalls().size() == 1) {
+            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                    CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.HAS_FOCUS);
         }
 
-        setSystemAudioState(false /* isMute */, route, mCallAudioState.getSupportedRouteMask());
-
         if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
             Log.v(this, "Speed up audio setup for IMS MT call.");
             mCallToSpeedUpMTAudio = call;
@@ -262,73 +223,9 @@
         updateAudioStreamAndMode(call);
     }
 
-    /**
-      * 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.
-      */
-    @Override
-    public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
-        // This can happen even when there are no calls and we don't have focus.
-        if (!hasFocus()) {
-            return;
-        }
-
-        boolean isCurrentlyWiredHeadset = mCallAudioState.getRoute()
-                == CallAudioState.ROUTE_WIRED_HEADSET;
-
-        int newRoute = mCallAudioState.getRoute();  // start out with existing route
-        if (newIsPluggedIn) {
-            newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
-        } else if (isCurrentlyWiredHeadset) {
-            Call call = getForegroundCall();
-            boolean hasLiveCall = call != null && call.isAlive();
-
-            if (hasLiveCall) {
-                // In order of preference when a wireless headset is unplugged.
-                if (mWasSpeakerOn) {
-                    newRoute = CallAudioState.ROUTE_SPEAKER;
-                } else {
-                    newRoute = CallAudioState.ROUTE_EARPIECE;
-                }
-
-                // We don't automatically connect to bluetooth when user unplugs their wired headset
-                // and they were previously using the wired. Wired and earpiece are effectively the
-                // same choice in that they replace each other as an option when wired headsets
-                // are plugged in and out. This means that keeping it earpiece is a bit more
-                // consistent with the status quo.  Bluetooth also has more danger associated with
-                // choosing it in the wrong curcumstance because bluetooth devices can be
-                // semi-public (like in a very-occupied car) where earpiece doesn't carry that risk.
-            }
-        }
-
-        // We need to call this every time even if we do not change the route because the supported
-        // routes changed either to include or not include WIRED_HEADSET.
-        setSystemAudioState(mCallAudioState.isMuted(), newRoute, calculateSupportedRoutes());
-    }
-
-    @Override
-    public void onDockChanged(boolean isDocked) {
-        // This can happen even when there are no calls and we don't have focus.
-        if (!hasFocus()) {
-            return;
-        }
-
-        if (isDocked) {
-            // Device just docked, turn to speakerphone. Only do so if the route is currently
-            // earpiece so that we dont switch out of a BT headset or a wired headset.
-            if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE) {
-                setAudioRoute(CallAudioState.ROUTE_SPEAKER);
-            }
-        } else {
-            // Device just undocked, remove from speakerphone if possible.
-            if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
-                setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
-            }
-        }
-    }
-
     void toggleMute() {
-        mute(!mCallAudioState.isMuted());
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.TOGGLE_MUTE);
     }
 
     void mute(boolean shouldMute) {
@@ -344,15 +241,8 @@
             Log.v(this, "ignoring mute for emergency call");
         }
 
-        if (mCallAudioState.isMuted() != shouldMute) {
-            // We user CallsManager's foreground call so that we dont ignore ringing calls
-            // for logging purposes
-            Log.event(mCallsManager.getForegroundCall(), Log.Events.MUTE,
-                    shouldMute ? "on" : "off");
-
-            setSystemAudioState(shouldMute, mCallAudioState.getRoute(),
-                    mCallAudioState.getSupportedRouteMask());
-        }
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(shouldMute
+                ? CallAudioRouteStateMachine.MUTE_ON : CallAudioRouteStateMachine.MUTE_OFF);
     }
 
     /**
@@ -361,28 +251,30 @@
      * @param route The new audio route to use. See {@link CallAudioState}.
      */
     void setAudioRoute(int route) {
-        // This can happen even when there are no calls and we don't have focus.
-        if (!hasFocus()) {
-            return;
-        }
-
         Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
-
-        // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
-        int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
-
-        // If route is unsupported, do nothing.
-        if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
-            Log.wtf(this, "Asking to set to a route that is unsupported: %d", newRoute);
-            return;
-        }
-
-        if (mCallAudioState.getRoute() != newRoute) {
-            // Remember the new speaker state so it can be restored when the user plugs and unplugs
-            // a headset.
-            mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
-            setSystemAudioState(mCallAudioState.isMuted(), newRoute,
-                    mCallAudioState.getSupportedRouteMask());
+        switch (route) {
+            case CallAudioState.ROUTE_BLUETOOTH:
+                mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.SWITCH_BLUETOOTH);
+                return;
+            case CallAudioState.ROUTE_SPEAKER:
+                mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.SWITCH_SPEAKER);
+                return;
+            case CallAudioState.ROUTE_WIRED_HEADSET:
+                mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.SWITCH_HEADSET);
+                return;
+            case CallAudioState.ROUTE_EARPIECE:
+                mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.SWITCH_EARPIECE);
+                return;
+            case CallAudioState.ROUTE_WIRED_OR_EARPIECE:
+                mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                        CallAudioRouteStateMachine.SWITCH_WIRED_OR_EARPIECE);
+                return;
+            default:
+                Log.wtf(this, "Invalid route specified: %d", route);
         }
     }
 
@@ -392,7 +284,8 @@
      * @param call The call which changed ringing state.
      * @param isRinging {@code true} if the call is ringing, {@code false} otherwise.
      */
-    void setIsRinging(Call call, boolean isRinging) {
+    @VisibleForTesting
+    public void setIsRinging(Call call, boolean isRinging) {
         if (mIsRinging != isRinging) {
             Log.i(this, "setIsRinging %b -> %b (call = %s)", mIsRinging, isRinging, call);
             mIsRinging = isRinging;
@@ -415,44 +308,6 @@
         }
     }
 
-    /**
-     * Updates the audio routing according to the bluetooth state.
-     */
-    void onBluetoothStateChange(BluetoothManager bluetoothManager) {
-        // This can happen even when there are no calls and we don't have focus.
-        if (!hasFocus()) {
-            return;
-        }
-
-        int supportedRoutes = calculateSupportedRoutes();
-        int newRoute = mCallAudioState.getRoute();
-        if (bluetoothManager.isBluetoothAudioConnectedOrPending()) {
-            newRoute = CallAudioState.ROUTE_BLUETOOTH;
-        } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
-            newRoute = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
-                    supportedRoutes);
-            // Do not switch to speaker when bluetooth disconnects.
-            mWasSpeakerOn = false;
-        }
-
-        setSystemAudioState(mCallAudioState.isMuted(), newRoute, supportedRoutes);
-    }
-
-    boolean isBluetoothAudioOn() {
-        return mBluetoothManager.isBluetoothAudioConnected();
-    }
-
-    boolean isBluetoothDeviceAvailable() {
-        return mBluetoothManager.isBluetoothAvailable();
-    }
-
-    private void saveAudioState(CallAudioState callAudioState) {
-        mCallAudioState = callAudioState;
-        mStatusBarNotifier.notifyMute(mCallAudioState.isMuted());
-        mStatusBarNotifier.notifySpeakerphone(mCallAudioState.getRoute()
-                == CallAudioState.ROUTE_SPEAKER);
-    }
-
     private void onCallUpdated(Call call) {
         updateAudioStreamAndMode(call);
         if (call != null && call.getState() == CallState.ACTIVE &&
@@ -461,70 +316,6 @@
         }
     }
 
-    private void setSystemAudioState(boolean isMuted, int route, int supportedRouteMask) {
-        setSystemAudioState(false /* force */, isMuted, route, supportedRouteMask);
-    }
-
-    private void setSystemAudioState(
-            boolean force, boolean isMuted, int route, int supportedRouteMask) {
-        if (!hasFocus()) {
-            return;
-        }
-
-        CallAudioState oldAudioState = mCallAudioState;
-        saveAudioState(new CallAudioState(isMuted, route, supportedRouteMask));
-        if (!force && Objects.equals(oldAudioState, mCallAudioState)) {
-            return;
-        }
-
-        Log.i(this, "setSystemAudioState: changing from %s to %s", oldAudioState, mCallAudioState);
-        Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
-                CallAudioState.audioRouteToString(mCallAudioState.getRoute()));
-
-        mAudioManagerHandler.obtainMessage(
-                MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE,
-                mCallAudioState.isMuted() ? 1 : 0,
-                0)
-                .sendToTarget();
-
-        // Audio route.
-        if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
-            turnOnSpeaker(false);
-            turnOnBluetooth(true);
-        } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
-            turnOnBluetooth(false);
-            turnOnSpeaker(true);
-        } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
-                mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
-            turnOnBluetooth(false);
-            turnOnSpeaker(false);
-        }
-
-        if (!oldAudioState.equals(mCallAudioState)) {
-            mCallsManager.onCallAudioStateChanged(oldAudioState, mCallAudioState);
-            updateAudioForForegroundCall();
-        }
-    }
-
-    private void turnOnSpeaker(boolean on) {
-        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_TURN_ON_SPEAKER, on ? 1 : 0, 0)
-                .sendToTarget();
-    }
-
-    private void turnOnBluetooth(boolean on) {
-        if (mBluetoothManager.isBluetoothAvailable()) {
-            boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
-            if (on != isAlreadyOn) {
-                Log.i(this, "connecting bluetooth %s", on);
-                if (on) {
-                    mBluetoothManager.connectBluetoothAudio();
-                } else {
-                    mBluetoothManager.disconnectBluetoothAudio();
-                }
-            }
-        }
-    }
-
     private void updateAudioStreamAndMode() {
         updateAudioStreamAndMode(null /* call */);
     }
@@ -533,7 +324,6 @@
         Log.i(this, "updateAudioStreamAndMode :  mIsRinging: %b, mIsTonePlaying: %b, call: %s",
                 mIsRinging, mIsTonePlaying, callToUpdate);
 
-        boolean wasVoiceCall = mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL;
         if (mIsRinging) {
             Log.i(this, "updateAudioStreamAndMode : ringing");
             requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
@@ -576,14 +366,6 @@
                 // focus will be correctly abandoned by the if clause above.
             }
         }
-
-        boolean isVoiceCall = mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL;
-
-        // If we transition from not a voice call to a voice call, we need to set an initial audio
-        // state for the call.
-        if (!wasVoiceCall && isVoiceCall) {
-            setInitialAudioState(callToUpdate, true /* force */);
-        }
     }
 
     private void requestAudioFocusAndSetMode(int stream, int mode) {
@@ -597,13 +379,12 @@
         if (mAudioFocusStreamType != stream) {
             Log.i(this, "requestAudioFocusAndSetMode : requesting stream: %s -> %s",
                     streamTypeToString(mAudioFocusStreamType), streamTypeToString(stream));
-            mAudioManagerHandler.obtainMessage(
-                    MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL,
-                    stream,
-                    0)
-                    .sendToTarget();
+            mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL,
+                    setArgs(stream)).sendToTarget();
         }
         mAudioFocusStreamType = stream;
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.HAS_FOCUS);
 
         setMode(mode);
     }
@@ -612,11 +393,13 @@
         if (hasFocus()) {
             setMode(AudioManager.MODE_NORMAL);
             Log.v(this, "abandoning audio focus");
-            mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL, 0, 0)
-                    .sendToTarget();
+            mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL,
+                    setArgs(0)).sendToTarget();
             mAudioFocusStreamType = STREAM_NONE;
             mCallToSpeedUpMTAudio = null;
         }
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.SWITCH_FOCUS, CallAudioRouteStateMachine.NO_FOCUS);
     }
 
     /**
@@ -626,80 +409,15 @@
      */
     private void setMode(int newMode) {
         Preconditions.checkState(hasFocus());
-        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_SET_MODE, newMode, 0).sendToTarget();
-    }
-
-    private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
-        // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
-        // ROUTE_WIRED_OR_EARPIECE so that callers dont have to make a call to check which is
-        // supported before calling setAudioRoute.
-        if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
-            route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
-            if (route == 0) {
-                Log.wtf(this, "One of wired headset or earpiece should always be valid.");
-                // assume earpiece in this case.
-                route = CallAudioState.ROUTE_EARPIECE;
-            }
-        }
-        return route;
-    }
-
-    private int calculateSupportedRoutes() {
-        int routeMask = CallAudioState.ROUTE_SPEAKER;
-
-        if (mWiredHeadsetManager.isPluggedIn()) {
-            routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
-        } else {
-            routeMask |= CallAudioState.ROUTE_EARPIECE;
-        }
-
-        if (mBluetoothManager.isBluetoothAvailable()) {
-            routeMask |=  CallAudioState.ROUTE_BLUETOOTH;
-        }
-
-        return routeMask;
-    }
-
-    private CallAudioState getInitialAudioState(Call call) {
-        int supportedRouteMask = calculateSupportedRoutes();
-        int route = selectWiredOrEarpiece(
-                CallAudioState.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask);
-
-        // We want the UI to indicate that "bluetooth is in use" in two slightly different cases:
-        // (a) The obvious case: if a bluetooth headset is currently in use for an ongoing call.
-        // (b) The not-so-obvious case: if an incoming call is ringing, and we expect that audio
-        //     *will* be routed to a bluetooth headset once the call is answered. In this case, just
-        //     check if the headset is available. Note this only applies when we are dealing with
-        //     the first call.
-        if (call != null && mBluetoothManager.isBluetoothAvailable()) {
-            switch(call.getState()) {
-                case CallState.ACTIVE:
-                case CallState.ON_HOLD:
-                case CallState.DIALING:
-                case CallState.CONNECTING:
-                case CallState.RINGING:
-                    route = CallAudioState.ROUTE_BLUETOOTH;
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        return new CallAudioState(false, route, supportedRouteMask);
-    }
-
-    private void setInitialAudioState(Call call, boolean force) {
-        CallAudioState audioState = getInitialAudioState(call);
-        Log.i(this, "setInitialAudioState : audioState = %s, call = %s", audioState, call);
-        setSystemAudioState(
-                force, audioState.isMuted(), audioState.getRoute(),
-                audioState.getSupportedRouteMask());
+        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_SET_MODE,
+                setArgs(newMode)).sendToTarget();
     }
 
     private void updateAudioForForegroundCall() {
         Call call = mCallsManager.getForegroundCall();
         if (call != null && call.getConnectionService() != null) {
-            call.getConnectionService().onCallAudioStateChanged(call, mCallAudioState);
+            call.getConnectionService().onCallAudioStateChanged(call,
+                    mCallAudioRouteStateMachine.getCurrentCallAudioState());
         }
     }
 
@@ -727,23 +445,6 @@
         return mAudioFocusStreamType != STREAM_NONE;
     }
 
-    private IAudioService getAudioService() {
-        return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
-    }
-
-    private int getCurrentUserId() {
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
-            return currentUser.id;
-        } catch (RemoteException e) {
-            // Activity manager not running, nothing we can do assume user 0.
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return UserHandle.USER_OWNER;
-    }
-
     /**
      * Translates an {@link AudioManager} stream type to a human-readable string description.
      *
@@ -806,23 +507,10 @@
      * @param pw The {@code IndentingPrintWriter} to write the state to.
      */
     public void dump(IndentingPrintWriter pw) {
-        pw.println("mAudioState: " + mCallAudioState);
-        pw.println("mBluetoothManager:");
-        pw.increaseIndent();
-        mBluetoothManager.dump(pw);
-        pw.decreaseIndent();
-        if (mWiredHeadsetManager != null) {
-            pw.println("mWiredHeadsetManager:");
-            pw.increaseIndent();
-            mWiredHeadsetManager.dump(pw);
-            pw.decreaseIndent();
-        } else {
-            pw.println("mWiredHeadsetManager: null");
-        }
+        pw.println("mAudioState: " + mCallAudioRouteStateMachine.getCurrentCallAudioState());
         pw.println("mAudioFocusStreamType: " + streamTypeToString(mAudioFocusStreamType));
         pw.println("mIsRinging: " + mIsRinging);
         pw.println("mIsTonePlaying: " + mIsTonePlaying);
-        pw.println("mWasSpeakerOn: " + mWasSpeakerOn);
         pw.println("mMostRecentlyUsedMode: " + modeToString(mMostRecentlyUsedMode));
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
new file mode 100644
index 0000000..45e5afa
--- /dev/null
+++ b/src/com/android/server/telecom/CallAudioRoutePeripheralAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+/**
+ * A class that acts as a listener to things that could change call audio routing, namely
+ * bluetooth status, wired headset status, and dock status.
+ */
+public class CallAudioRoutePeripheralAdapter implements BluetoothManager.BluetoothStateListener,
+        WiredHeadsetManager.Listener, DockManager.Listener {
+    private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
+    private final BluetoothManager mBluetoothManager;
+
+    public CallAudioRoutePeripheralAdapter(
+            CallAudioRouteStateMachine callAudioRouteStateMachine,
+            BluetoothManager bluetoothManager,
+            WiredHeadsetManager wiredHeadsetManager,
+            DockManager dockManager) {
+        mCallAudioRouteStateMachine = callAudioRouteStateMachine;
+        mBluetoothManager = bluetoothManager;
+
+        mBluetoothManager.setBluetoothStateListener(this);
+        wiredHeadsetManager.addListener(this);
+        dockManager.addListener(this);
+    }
+
+    @Override
+    public void onBluetoothStateChange(BluetoothManager bluetoothManager) {
+        if (bluetoothManager.isBluetoothAvailable()) {
+            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                    CallAudioRouteStateMachine.CONNECT_BLUETOOTH);
+        } else {
+            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                    CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH);
+        }
+    }
+
+    public boolean isBluetoothAudioOn() {
+        return mBluetoothManager.isBluetoothAudioConnected();
+    }
+
+    /**
+      * 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.
+      */
+    @Override
+    public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
+        if (!oldIsPluggedIn && newIsPluggedIn) {
+            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                    CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET);
+        } else if (oldIsPluggedIn && !newIsPluggedIn){
+            mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                    CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET);
+        }
+    }
+
+    @Override
+    public void onDockChanged(boolean isDocked) {
+        mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
+                isDocked ? CallAudioRouteStateMachine.CONNECT_DOCK
+                        : CallAudioRouteStateMachine.DISCONNECT_DOCK
+        );
+    }
+}
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
new file mode 100644
index 0000000..a774130
--- /dev/null
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -0,0 +1,1157 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.media.AudioManager;
+import android.media.IAudioService;
+import android.os.Binder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.telecom.CallAudioState;
+
+import com.android.internal.util.IState;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.util.HashMap;
+
+/**
+ * This class describes the available routes of a call as a state machine.
+ * Transitions are caused solely by the commands sent as messages. Possible values for msg.what
+ * are defined as event constants in this file.
+ *
+ * The eight states are all instances of the abstract base class, {@link AudioState}. Each state
+ * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and
+ * speakerphone) and audio focus status (active or quiescent).
+ *
+ * Messages are processed first by the processMessage method in the base class, AudioState.
+ * Any messages not completely handled by AudioState are further processed by the same method in
+ * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute},
+ * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at
+ * this level are then processed by the classes corresponding to the state instances themselves.
+ *
+ * There are several variables carrying additional state. These include:
+ * mAvailableRoutes: A bitmask describing which audio routes are available
+ * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting
+ *     from a wired headset
+ * mIsMuted: a boolean indicating whether the audio is muted
+ */
+public class CallAudioRouteStateMachine extends StateMachine {
+    /** Direct the audio stream through the device's earpiece. */
+    public static final int ROUTE_EARPIECE      = CallAudioState.ROUTE_EARPIECE;
+
+    /** Direct the audio stream through Bluetooth. */
+    public static final int ROUTE_BLUETOOTH     = CallAudioState.ROUTE_BLUETOOTH;
+
+    /** Direct the audio stream through a wired headset. */
+    public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET;
+
+    /** Direct the audio stream through the device's speakerphone. */
+    public static final int ROUTE_SPEAKER       = CallAudioState.ROUTE_SPEAKER;
+
+    /** 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 SWITCH_EARPIECE = 1001;
+    public static final int SWITCH_BLUETOOTH = 1002;
+    public static final int SWITCH_HEADSET = 1003;
+    public static final int SWITCH_SPEAKER = 1004;
+    public static final int SWITCH_WIRED_OR_EARPIECE = 1005;
+
+    public static final int REINITIALIZE = 2001;
+
+    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 SWITCH_FOCUS = 4001;
+
+    /** Valid values for mAudioFocusType */
+    public static final int NO_FOCUS = 1;
+    public static final int HAS_FOCUS = 2;
+
+    private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute";
+    private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute";
+    private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute";
+    private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute";
+    private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute";
+    private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute";
+    private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute";
+    private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute";
+
+    public static final String NAME = CallAudioRouteStateMachine.class.getName();
+
+    @Override
+    protected void onPreHandleMessage(Message msg) {
+        if (msg.obj != null && msg.obj instanceof Session) {
+            Log.continueSession((Session) msg.obj, "CARSM.pM_" + msg.what);
+        }
+    }
+
+    @Override
+    protected void onPostHandleMessage(Message msg) {
+        Log.endSession();
+    }
+
+    abstract class AudioState extends State {
+        @Override
+        public void enter() {
+            super.enter();
+            Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                    "Entering state " + getName());
+        }
+
+        @Override
+        public void exit() {
+            Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                    "Leaving state " + getName());
+            super.exit();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            Log.d(this, "Message received: %s", msg);
+            switch (msg.what) {
+                case CONNECT_WIRED_HEADSET:
+                    Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                            "Wired headset connected");
+                    mAvailableRoutes &= ~ROUTE_EARPIECE;
+                    mAvailableRoutes |= ROUTE_WIRED_HEADSET;
+                    return NOT_HANDLED;
+                case CONNECT_BLUETOOTH:
+                    // This case is here because the bluetooth manager sends out a lot of spurious
+                    // state changes, and no layers above this one can tell which are actual changes
+                    // in connection/disconnection status. This filters it out.
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        return HANDLED; // Do nothing if we already have bluetooth as enabled.
+                    } else {
+                        Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                                "Bluetooth connected");
+                        mAvailableRoutes |= ROUTE_BLUETOOTH;
+                        return NOT_HANDLED;
+                    }
+                case DISCONNECT_WIRED_HEADSET:
+                    Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                            "Wired headset disconnected");
+                    mAvailableRoutes &= ~ROUTE_WIRED_HEADSET;
+                    mAvailableRoutes |= ROUTE_EARPIECE;
+                    return NOT_HANDLED;
+                case DISCONNECT_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) == 0) {
+                        return HANDLED;
+                    } else {
+                        Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                                "Bluetooth disconnected");
+                        mAvailableRoutes &= ~ROUTE_BLUETOOTH;
+                        return NOT_HANDLED;
+                    }
+                case SWITCH_WIRED_OR_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        sendInternalMessage(SWITCH_EARPIECE);
+                    } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        sendInternalMessage(SWITCH_HEADSET);
+                    } else {
+                        Log.e(this, new IllegalStateException(),
+                                "Neither headset nor earpiece are available. Defaulting to " +
+                                        "earpiece.");
+                        sendInternalMessage(SWITCH_EARPIECE);
+                    }
+                    return HANDLED;
+                case REINITIALIZE:
+                    CallAudioState initState = getInitialAudioState();
+                    mAvailableRoutes = initState.getSupportedRouteMask();
+                    mIsMuted = initState.isMuted();
+                    mWasOnSpeaker = initState.getRoute() == ROUTE_SPEAKER;
+                    transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute()));
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+
+        // Behavior will depend on whether the state is an active one or a quiescent one.
+        abstract public void updateSystemAudioState();
+        abstract public boolean isActive();
+    }
+
+    class ActiveEarpieceRoute extends EarpieceRoute {
+        @Override
+        public String getName() {
+            return ACTIVE_EARPIECE_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return true;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            setSpeakerphoneOn(false);
+            setBluetoothOn(false);
+            CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
+                    mAvailableRoutes);
+            setSystemAudioState(mCurrentCallAudioState, newState);
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            CallAudioState oldAudioState = mCurrentCallAudioState;
+            updateInternalCallAudioState();
+            setSystemAudioState(oldAudioState, mCurrentCallAudioState);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case SWITCH_EARPIECE:
+                    // Nothing to do here
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        transitionTo(mActiveBluetoothRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to bluetooth command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        transitionTo(mActiveHeadsetRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to headset command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    transitionTo(mActiveSpeakerRoute);
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == NO_FOCUS) {
+                        transitionTo(mQuiescentEarpieceRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class QuiescentEarpieceRoute extends EarpieceRoute {
+        @Override
+        public String getName() {
+            return QUIESCENT_EARPIECE_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return false;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case SWITCH_EARPIECE:
+                    // Nothing to do here
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        transitionTo(mQuiescentBluetoothRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to bluetooth command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        transitionTo(mQuiescentHeadsetRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to headset command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    transitionTo(mQuiescentSpeakerRoute);
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == HAS_FOCUS) {
+                        transitionTo(mActiveEarpieceRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    abstract class EarpieceRoute extends AudioState {
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case CONNECT_WIRED_HEADSET:
+                    sendInternalMessage(SWITCH_HEADSET);
+                    return HANDLED;
+                case CONNECT_BLUETOOTH:
+                    sendInternalMessage(SWITCH_BLUETOOTH);
+                    return HANDLED;
+                case DISCONNECT_BLUETOOTH:
+                    updateSystemAudioState();
+                    // 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 CONNECT_DOCK:
+                    sendInternalMessage(SWITCH_SPEAKER);
+                    return HANDLED;
+                case DISCONNECT_DOCK:
+                    // Nothing to do here
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class ActiveHeadsetRoute extends HeadsetRoute {
+        @Override
+        public String getName() {
+            return ACTIVE_HEADSET_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return true;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            setSpeakerphoneOn(false);
+            setBluetoothOn(false);
+            CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET,
+                    mAvailableRoutes);
+            setSystemAudioState(mCurrentCallAudioState, newState);
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            CallAudioState oldAudioState = mCurrentCallAudioState;
+            updateInternalCallAudioState();
+            setSystemAudioState(oldAudioState, mCurrentCallAudioState);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case SWITCH_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        transitionTo(mActiveEarpieceRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to earpiece command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        transitionTo(mActiveBluetoothRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to bluetooth command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    // Nothing to do
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    transitionTo(mActiveSpeakerRoute);
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == NO_FOCUS) {
+                        transitionTo(mQuiescentHeadsetRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class QuiescentHeadsetRoute extends HeadsetRoute {
+        @Override
+        public String getName() {
+            return QUIESCENT_HEADSET_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return false;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case SWITCH_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        transitionTo(mQuiescentEarpieceRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to earpiece command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        transitionTo(mQuiescentBluetoothRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to bluetooth command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    // Nothing to do
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    transitionTo(mQuiescentSpeakerRoute);
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == HAS_FOCUS) {
+                        transitionTo(mActiveHeadsetRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    abstract class HeadsetRoute extends AudioState {
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                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:
+                    sendInternalMessage(SWITCH_BLUETOOTH);
+                    return HANDLED;
+                case DISCONNECT_BLUETOOTH:
+                    updateSystemAudioState();
+                    // No change in audio route required
+                    return HANDLED;
+                case DISCONNECT_WIRED_HEADSET:
+                    if (mWasOnSpeaker) {
+                        sendInternalMessage(SWITCH_SPEAKER);
+                    } else {
+                        sendInternalMessage(SWITCH_EARPIECE);
+                    }
+                    return HANDLED;
+                case CONNECT_DOCK:
+                    // Nothing to do here
+                    return HANDLED;
+                case DISCONNECT_DOCK:
+                    // Nothing to do here
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class ActiveBluetoothRoute extends BluetoothRoute {
+        @Override
+        public String getName() {
+            return ACTIVE_BLUETOOTH_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return true;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            setSpeakerphoneOn(false);
+            setBluetoothOn(true);
+            CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
+                    mAvailableRoutes);
+            setSystemAudioState(mCurrentCallAudioState, newState);
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            CallAudioState oldAudioState = mCurrentCallAudioState;
+            updateInternalCallAudioState();
+            setSystemAudioState(oldAudioState, mCurrentCallAudioState);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case SWITCH_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        transitionTo(mActiveEarpieceRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to earpiece command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    // Nothing to do
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        transitionTo(mActiveHeadsetRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to headset command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    transitionTo(mActiveSpeakerRoute);
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == NO_FOCUS) {
+                        transitionTo(mQuiescentBluetoothRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class QuiescentBluetoothRoute extends BluetoothRoute {
+        @Override
+        public String getName() {
+            return QUIESCENT_BLUETOOTH_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return false;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case SWITCH_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        transitionTo(mQuiescentEarpieceRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to earpiece command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    // Nothing to do
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        transitionTo(mQuiescentHeadsetRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to headset command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    transitionTo(mQuiescentSpeakerRoute);
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == HAS_FOCUS) {
+                        transitionTo(mActiveBluetoothRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    abstract class BluetoothRoute extends AudioState {
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                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.
+                    return HANDLED;
+                case DISCONNECT_BLUETOOTH:
+                    sendInternalMessage(SWITCH_WIRED_OR_EARPIECE);
+                    mWasOnSpeaker = false;
+                    return HANDLED;
+                case DISCONNECT_WIRED_HEADSET:
+                    updateSystemAudioState();
+                    // No change in audio route required
+                    return HANDLED;
+                case CONNECT_DOCK:
+                    // Nothing to do here
+                    return HANDLED;
+                case DISCONNECT_DOCK:
+                    // Nothing to do here
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class ActiveSpeakerRoute extends SpeakerRoute {
+        @Override
+        public String getName() {
+            return ACTIVE_SPEAKER_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return true;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            mWasOnSpeaker = true;
+            setSpeakerphoneOn(true);
+            setBluetoothOn(false);
+            CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
+                    mAvailableRoutes);
+            setSystemAudioState(mCurrentCallAudioState, newState);
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            CallAudioState oldAudioState = mCurrentCallAudioState;
+            updateInternalCallAudioState();
+            setSystemAudioState(oldAudioState, mCurrentCallAudioState);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch(msg.what) {
+                case SWITCH_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        transitionTo(mActiveEarpieceRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to earpiece command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        transitionTo(mActiveBluetoothRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to bluetooth command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        transitionTo(mActiveHeadsetRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to headset command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    // Nothing to do
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == NO_FOCUS) {
+                        transitionTo(mQuiescentSpeakerRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class QuiescentSpeakerRoute extends SpeakerRoute {
+        @Override
+        public String getName() {
+            return QUIESCENT_SPEAKER_ROUTE_NAME;
+        }
+
+        @Override
+        public boolean isActive() {
+            return false;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            // Omit setting mWasOnSpeaker to true here, since this does not reflect a call
+            // actually being on speakerphone.
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public void updateSystemAudioState() {
+            updateInternalCallAudioState();
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch(msg.what) {
+                case SWITCH_EARPIECE:
+                    if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
+                        transitionTo(mQuiescentEarpieceRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to earpiece command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_BLUETOOTH:
+                    if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
+                        transitionTo(mQuiescentBluetoothRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to bluetooth command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_HEADSET:
+                    if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
+                        transitionTo(mQuiescentHeadsetRoute);
+                    } else {
+                        Log.w(this, "Ignoring switch to headset command. Not available.");
+                    }
+                    return HANDLED;
+                case SWITCH_SPEAKER:
+                    // Nothing to do
+                    return HANDLED;
+                case SWITCH_FOCUS:
+                    if (msg.arg1 == HAS_FOCUS) {
+                        transitionTo(mActiveSpeakerRoute);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    abstract class SpeakerRoute extends AudioState {
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            switch (msg.what) {
+                case CONNECT_WIRED_HEADSET:
+                    sendInternalMessage(SWITCH_HEADSET);
+                    return HANDLED;
+                case CONNECT_BLUETOOTH:
+                    sendInternalMessage(SWITCH_BLUETOOTH);
+                    return HANDLED;
+                case DISCONNECT_BLUETOOTH:
+                    updateSystemAudioState();
+                    // No change in audio route required
+                    return HANDLED;
+                case DISCONNECT_WIRED_HEADSET:
+                    updateSystemAudioState();
+                    // No change in audio route required
+                    return HANDLED;
+                case CONNECT_DOCK:
+                    // Nothing to do here
+                    return HANDLED;
+                case DISCONNECT_DOCK:
+                    sendInternalMessage(SWITCH_WIRED_OR_EARPIECE);
+                    return HANDLED;
+               default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute();
+    private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute();
+    private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute();
+    private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute();
+    private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute();
+    private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute();
+    private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute();
+    private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute();
+
+    /**
+     * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
+     * states
+     */
+    private int mAvailableRoutes;
+    private boolean mWasOnSpeaker;
+    private boolean mIsMuted;
+
+    private final Context mContext;
+    private final CallsManager mCallsManager;
+    private final AudioManager mAudioManager;
+    private final BluetoothManager mBluetoothManager;
+    private final WiredHeadsetManager mWiredHeadsetManager;
+    private final StatusBarNotifier mStatusBarNotifier;
+    private final CallAudioManager.AudioServiceFactory mAudioServiceFactory;
+
+    private HashMap<String, Integer> mStateNameToRouteCode;
+    private HashMap<Integer, AudioState> mRouteCodeToQuiescentState;
+
+    // CallAudioState is used as an interface to communicate with many other system components.
+    // No internal state transitions should depend on this variable.
+    private CallAudioState mCurrentCallAudioState;
+
+    public CallAudioRouteStateMachine(
+            Context context,
+            CallsManager callsManager,
+            BluetoothManager bluetoothManager,
+            WiredHeadsetManager wiredHeadsetManager,
+            StatusBarNotifier statusBarNotifier,
+            CallAudioManager.AudioServiceFactory audioServiceFactory) {
+        super(NAME);
+        addState(mActiveEarpieceRoute);
+        addState(mActiveHeadsetRoute);
+        addState(mActiveBluetoothRoute);
+        addState(mActiveSpeakerRoute);
+        addState(mQuiescentEarpieceRoute);
+        addState(mQuiescentHeadsetRoute);
+        addState(mQuiescentBluetoothRoute);
+        addState(mQuiescentSpeakerRoute);
+
+        mContext = context;
+        mCallsManager = callsManager;
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mBluetoothManager = bluetoothManager;
+        mWiredHeadsetManager = wiredHeadsetManager;
+        mStatusBarNotifier = statusBarNotifier;
+        mAudioServiceFactory = audioServiceFactory;
+
+        mStateNameToRouteCode = new HashMap<>(8);
+        mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE);
+        mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH);
+        mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
+        mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER);
+        mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE);
+        mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH);
+        mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
+        mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER);
+
+        mRouteCodeToQuiescentState = new HashMap<>(4);
+        mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute);
+        mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute);
+        mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute);
+        mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
+        initialize();
+    }
+
+    /**
+     * Initializes the state machine with info on initial audio route, supported audio routes,
+     * and mute status.
+     */
+    public void initialize() {
+        CallAudioState initState = getInitialAudioState();
+        initialize(initState);
+    }
+
+    public void initialize(CallAudioState initState) {
+        mCurrentCallAudioState = initState;
+        mAvailableRoutes = initState.getSupportedRouteMask();
+        mIsMuted = initState.isMuted();
+        mWasOnSpeaker = initState.getRoute() == ROUTE_SPEAKER;
+
+        mStatusBarNotifier.notifyMute(initState.isMuted());
+        mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
+        setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute()));
+        start();
+    }
+
+    /**
+     * Getter for the current CallAudioState object that the state machine is keeping track of.
+     * Used for compatibility purposes.
+     */
+    public CallAudioState getCurrentCallAudioState() {
+        return mCurrentCallAudioState;
+    }
+
+    public void sendMessageWithSessionInfo(int message, int arg) {
+        sendMessage(message, arg, 0, Log.createSubsession());
+    }
+
+    public void sendMessageWithSessionInfo(int message) {
+        sendMessage(message, 0, 0, Log.createSubsession());
+    }
+
+    /**
+     * This is for state-independent changes in audio route (i.e. muting)
+     * @param msg that couldn't be handled.
+     */
+    @Override
+    protected void unhandledMessage(Message msg) {
+        CallAudioState newCallAudioState;
+        switch (msg.what) {
+            case MUTE_ON:
+                setMuteOn(true);
+                newCallAudioState = new CallAudioState(mIsMuted,
+                        mCurrentCallAudioState.getRoute(),
+                        mAvailableRoutes);
+                setSystemAudioState(mCurrentCallAudioState, newCallAudioState);
+                updateInternalCallAudioState();
+                return;
+            case MUTE_OFF:
+                setMuteOn(false);
+                newCallAudioState = new CallAudioState(mIsMuted,
+                        mCurrentCallAudioState.getRoute(),
+                        mAvailableRoutes);
+                setSystemAudioState(mCurrentCallAudioState, newCallAudioState);
+                updateInternalCallAudioState();
+                return;
+            case TOGGLE_MUTE:
+                if (mIsMuted) {
+                    sendInternalMessage(MUTE_OFF);
+                } else {
+                    sendInternalMessage(MUTE_ON);
+                }
+                return;
+            default:
+                Log.e(this, new IllegalStateException(),
+                        "Unexpected message code");
+        }
+    }
+
+    private void setSpeakerphoneOn(boolean on) {
+        if (mAudioManager.isSpeakerphoneOn() != on) {
+            Log.i(this, "turning speaker phone %s", on);
+            mAudioManager.setSpeakerphoneOn(on);
+        }
+    }
+
+    private void setBluetoothOn(boolean on) {
+        if (mBluetoothManager.isBluetoothAvailable()) {
+            boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
+            if (on != isAlreadyOn) {
+                Log.i(this, "connecting bluetooth %s", on);
+                if (on) {
+                    mBluetoothManager.connectBluetoothAudio();
+                } else {
+                    mBluetoothManager.disconnectBluetoothAudio();
+                }
+            }
+        }
+    }
+
+    private void setMuteOn(boolean mute) {
+        mIsMuted = mute;
+        Log.event(mCallsManager.getForegroundCall(), Log.Events.MUTE,
+                mute ? "on" : "off");
+        if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) {
+            IAudioService audio = mAudioServiceFactory.getAudioService();
+            Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
+                    mute, audio == null);
+            if (audio != null) {
+                try {
+                    // We use the audio service directly here so that we can specify
+                    // the current user. Telecom runs in the system_server process which
+                    // may run as a separate user from the foreground user. If we
+                    // used AudioManager directly, we would change mute for the system's
+                    // user and not the current foreground, which we want to avoid.
+                    audio.setMicrophoneMute(
+                            mute, mContext.getOpPackageName(), getCurrentUserId());
+
+                } catch (RemoteException e) {
+                    Log.e(this, e, "Remote exception while toggling mute.");
+                }
+                // TODO: Check microphone state after attempting to set to ensure that
+                // our state corroborates AudioManager's state.
+            }
+        }
+    }
+
+    /**
+     * Updates the CallAudioState object from current internal state. The result is used for
+     * external communication only.
+     */
+    private void updateInternalCallAudioState() {
+        IState currentState = getCurrentState();
+        if (currentState == null) {
+            Log.e(this, new IllegalStateException(), "Current state should never be null" +
+                    " when updateInternalCallAudioState is called.");
+            mCurrentCallAudioState = new CallAudioState(
+                    mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes);
+            return;
+        }
+        int currentRoute = mStateNameToRouteCode.get(currentState.getName());
+        mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes);
+    }
+
+    private void setSystemAudioState(CallAudioState oldCallAudioState,
+            CallAudioState newCallAudioState) {
+        Log.i(this, "setSystemAudioState: changing from %s to %s", oldCallAudioState,
+                newCallAudioState);
+        Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
+                CallAudioState.audioRouteToString(newCallAudioState.getRoute()));
+
+        if (!oldCallAudioState.equals(newCallAudioState)) {
+            mCallsManager.onCallAudioStateChanged(oldCallAudioState, newCallAudioState);
+            updateAudioForForegroundCall(newCallAudioState);
+        }
+    }
+
+    private void updateAudioForForegroundCall(CallAudioState newCallAudioState) {
+        Call call = mCallsManager.getForegroundCall();
+        if (call != null && call.getConnectionService() != null) {
+            call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState);
+        }
+    }
+
+    private int calculateSupportedRoutes() {
+        int routeMask = CallAudioState.ROUTE_SPEAKER;
+
+        if (mWiredHeadsetManager.isPluggedIn()) {
+            routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
+        } else {
+            routeMask |= CallAudioState.ROUTE_EARPIECE;
+        }
+
+        if (mBluetoothManager.isBluetoothAvailable()) {
+            routeMask |=  CallAudioState.ROUTE_BLUETOOTH;
+        }
+
+        return routeMask;
+    }
+
+    private void sendInternalMessage(int messageCode) {
+        // Internal messages are messages which the state machine sends to itself in the
+        // course of processing externally-sourced messages. We want to send these messages at
+        // the front of the queue in order to make actions appear atomic to the user and to
+        // prevent scenarios such as these:
+        // 1. State machine handler thread is suspended for some reason.
+        // 2. Headset gets connected (sends CONNECT_HEADSET).
+        // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER).
+        // 4. State machine handler is un-suspended.
+        // 5. State machine handler processes the CONNECT_HEADSET message and sends
+        //    SWITCH_HEADSET at end of queue.
+        // 6. State machine handler processes SWITCH_SPEAKER.
+        // 7. State machine handler processes SWITCH_HEADSET.
+        Session subsession = Log.createSubsession();
+        if(subsession != null) {
+            sendMessageAtFrontOfQueue(messageCode, subsession);
+        } else {
+            sendMessageAtFrontOfQueue(messageCode);
+        }
+    }
+
+    private CallAudioState getInitialAudioState() {
+        int supportedRouteMask = calculateSupportedRoutes();
+        int route = (supportedRouteMask & ROUTE_WIRED_HEADSET) != 0
+                ? ROUTE_WIRED_HEADSET : ROUTE_EARPIECE;
+        if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0) {
+            route = ROUTE_BLUETOOTH;
+        }
+
+        return new CallAudioState(false, route, supportedRouteMask);
+    }
+
+    private int getCurrentUserId() {
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+            return currentUser.id;
+        } catch (RemoteException e) {
+            // Activity manager not running, nothing we can do assume user 0.
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return UserHandle.USER_OWNER;
+    }
+
+    private boolean isInActiveState() {
+        AudioState currentState = (AudioState) getCurrentState();
+        if (currentState == null) {
+            Log.w(this, "Current state is null, assuming inactive state");
+            return false;
+        }
+        return currentState.isActive();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/CallIdMapper.java b/src/com/android/server/telecom/CallIdMapper.java
index 8199dfa..b097bea 100644
--- a/src/com/android/server/telecom/CallIdMapper.java
+++ b/src/com/android/server/telecom/CallIdMapper.java
@@ -18,10 +18,13 @@
 
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Map;
 
 /** Utility to map {@link Call} objects to unique IDs. IDs are generated when a call is added. */
-class CallIdMapper {
+@VisibleForTesting
+public class CallIdMapper {
     /**
      * A very basic bidirectional map.
      */
@@ -75,12 +78,6 @@
     }
 
     private final BiMap<String, Call> mCalls = new BiMap<>();
-    private final String mCallIdPrefix;
-    private static int sIdCount;
-
-    CallIdMapper(String callIdPrefix) {
-        mCallIdPrefix = callIdPrefix + "@";
-    }
 
     void replaceCall(Call newCall, Call callToReplace) {
         // Use the old call's ID for the new call.
@@ -96,7 +93,7 @@
     }
 
     void addCall(Call call) {
-        addCall(call, getNewId());
+        addCall(call, call.getId());
     }
 
     void removeCall(Call call) {
@@ -111,10 +108,10 @@
     }
 
     String getCallId(Call call) {
-        if (call == null) {
+        if (call == null || mCalls.getKey(call) == null) {
             return null;
         }
-        return mCalls.getKey(call);
+        return call.getId();
     }
 
     Call getCall(Object objId) {
@@ -122,9 +119,6 @@
         if (objId instanceof String) {
             callId = (String) objId;
         }
-        if (!isValidCallId(callId) && !isValidConferenceId(callId)) {
-            return null;
-        }
 
         return mCalls.getValue(callId);
     }
@@ -132,18 +126,4 @@
     void clear() {
         mCalls.clear();
     }
-
-    boolean isValidCallId(String callId) {
-        // Note, no need for thread check, this method is thread safe.
-        return callId != null && callId.startsWith(mCallIdPrefix);
-    }
-
-    boolean isValidConferenceId(String callId) {
-        return callId != null;
-    }
-
-    String getNewId() {
-        sIdCount++;
-        return mCallIdPrefix + sIdCount;
-    }
 }
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index a6840b9..57db8a4 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -33,6 +33,12 @@
      */
     public static final String KEY_IS_PRIVILEGED_DIALER = "is_privileged_dialer";
 
+    /**
+     * The user initiating the outgoing call.
+     */
+    public static final String KEY_INITIATING_USER = "initiating_user";
+
+
     private final Context mContext;
     private final CallsManager mCallsManager;
 
@@ -96,8 +102,11 @@
 
         final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
 
+        UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
+
         // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
-        Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
+        Call call = callsManager
+                .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser);
 
         if (call != null) {
             // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 1fe491e..bf9aa39 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -20,13 +20,16 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.UserHandle;
 import android.provider.CallLog.Calls;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
 
 // TODO: Needed for move to system service: import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CallerInfo;
 
 /**
@@ -34,7 +37,8 @@
  * caller details to the call log. All logging activity will be performed asynchronously in a
  * background thread to avoid blocking on the main thread.
  */
-final class CallLogManager extends CallsManagerListenerBase {
+@VisibleForTesting
+public final class CallLogManager extends CallsManagerListenerBase {
     /**
      * Parameter object to hold the arguments to add a call in the call log DB.
      */
@@ -51,12 +55,14 @@
          * @param durationInMillis Duration of the call (milliseconds).
          * @param dataUsage Data usage in bytes, or null if not applicable.
          */
-        public AddCallArgs(Context context, CallerInfo callerInfo, String number,
+        public AddCallArgs(Context context, CallerInfo callerInfo, String number, String postDialDigits,
                 int presentation, int callType, int features, PhoneAccountHandle accountHandle,
-                long creationDate, long durationInMillis, Long dataUsage) {
+                long creationDate, long durationInMillis, Long dataUsage,
+                UserHandle initiatingUser) {
             this.context = context;
             this.callerInfo = callerInfo;
             this.number = number;
+            this.postDialDigits = postDialDigits;
             this.presentation = presentation;
             this.callType = callType;
             this.features = features;
@@ -64,12 +70,14 @@
             this.timestamp = creationDate;
             this.durationInSec = (int)(durationInMillis / 1000);
             this.dataUsage = dataUsage;
+            this.initiatingUser = initiatingUser;
         }
         // Since the members are accessed directly, we don't use the
         // mXxxx notation.
         public final Context context;
         public final CallerInfo callerInfo;
         public final String number;
+        public final String postDialDigits;
         public final int presentation;
         public final int callType;
         public final int features;
@@ -77,11 +85,13 @@
         public final long timestamp;
         public final int durationInSec;
         public final Long dataUsage;
+        public final UserHandle initiatingUser;
     }
 
     private static final String TAG = CallLogManager.class.getSimpleName();
 
     private final Context mContext;
+    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
     private static final String ACTION_CALLS_TABLE_ADD_ENTRY =
                 "com.android.server.telecom.intent.action.CALLS_ADD_ENTRY";
     private static final String PERMISSION_PROCESS_CALLLOG_INFO =
@@ -89,8 +99,9 @@
     private static final String CALL_TYPE = "callType";
     private static final String CALL_DURATION = "duration";
 
-    public CallLogManager(Context context) {
+    public CallLogManager(Context context, PhoneAccountRegistrar phoneAccountRegistrar) {
         mContext = context;
+        mPhoneAccountRegistrar = phoneAccountRegistrar;
     }
 
     @Override
@@ -145,11 +156,13 @@
             accountHandle = null;
         }
 
-        // TODO(vt): Once data usage is available, wire it up here.
+        Long callDataUsage = call.getCallDataUsage() == Call.DATA_USAGE_NOT_SET ? null :
+                call.getCallDataUsage();
+
         int callFeatures = getCallFeatures(call.getVideoStateHistory());
-        logCall(call.getCallerInfo(), logNumber, call.getHandlePresentation(),
-                callLogType, callFeatures, accountHandle, creationTime, age, null,
-                call.isEmergencyCall());
+        logCall(call.getCallerInfo(), logNumber, call.getPostDialDigits(),
+                call.getHandlePresentation(), callLogType, callFeatures, accountHandle,
+                creationTime, age, callDataUsage, call.isEmergencyCall(), call.getInitiatingUser());
     }
 
     /**
@@ -157,6 +170,8 @@
      *
      * @param callerInfo Caller details.
      * @param number The number the call was made to or from.
+     * @param postDialDigits The post-dial digits that were dialed after the number,
+     *                       if it was an outgoing call. Otherwise ''.
      * @param presentation
      * @param callType The type of call.
      * @param features The features of the call.
@@ -168,6 +183,7 @@
     private void logCall(
             CallerInfo callerInfo,
             String number,
+            String postDialDigits,
             int presentation,
             int callType,
             int features,
@@ -175,7 +191,8 @@
             long start,
             long duration,
             Long dataUsage,
-            boolean isEmergency) {
+            boolean isEmergency,
+            UserHandle initiatingUser) {
 
         // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
         // emergency calls to the Call Log.  (This behavior is set on a per-product basis, based
@@ -192,8 +209,9 @@
             Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
                     + Log.pii(number) + "," + presentation + ", " + callType
                     + ", " + start + ", " + duration);
-            AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, presentation,
-                    callType, features, accountHandle, start, duration, dataUsage);
+            AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, postDialDigits,
+                    presentation, callType, features, accountHandle, start, duration, dataUsage,
+                    initiatingUser);
             logCallAsync(args);
         } else {
           Log.d(TAG, "Not adding emergency call to call log.");
@@ -257,12 +275,9 @@
             Uri[] result = new Uri[count];
             for (int i = 0; i < count; i++) {
                 AddCallArgs c = callList[i];
-
                 try {
                     // May block.
-                    result[i] = Calls.addCall(c.callerInfo, c.context, c.number, c.presentation,
-                            c.callType, c.features, c.accountHandle, c.timestamp, c.durationInSec,
-                            c.dataUsage, true /* addForAllUsers */);
+                    result[i] = addCall(c);
                 } catch (Exception e) {
                     // This is very rare but may happen in legitimate cases.
                     // E.g. If the phone is encrypted and thus write request fails, it may cause
@@ -278,6 +293,34 @@
             return result;
         }
 
+        private Uri addCall(AddCallArgs c) {
+            PhoneAccount phoneAccount = mPhoneAccountRegistrar
+                    .getPhoneAccountUnchecked(c.accountHandle);
+            if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+                if (c.initiatingUser != null &&
+                        UserUtil.isManagedProfile(mContext, c.initiatingUser)) {
+                    return addCall(c, c.initiatingUser);
+                } else {
+                    return addCall(c, null);
+                }
+            } else {
+                return addCall(c, c.accountHandle.getUserHandle());
+            }
+        }
+
+        /**
+         * Insert the call to a specific user or all users except managed profile.
+         * @param c context
+         * @param userToBeInserted user handle of user that the call going be inserted to. null
+         *                         if insert to all users except managed profile.
+         */
+        private Uri addCall(AddCallArgs c, UserHandle userToBeInserted) {
+            return Calls.addCall(c.callerInfo, c.context, c.number, c.postDialDigits,
+                    c.presentation, c.callType, c.features, c.accountHandle, c.timestamp,
+                    c.durationInSec, c.dataUsage, userToBeInserted == null,
+                    userToBeInserted);
+        }
+
         /**
          * Performs a simple sanity check to make sure the call was written in the database.
          * Typically there is only one result per call so it is easy to identify which one failed.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 7204469..f388977 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -22,7 +22,9 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemProperties;
+import android.os.SystemVibrator;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.provider.CallLog.Calls;
 import android.telecom.CallAudioState;
 import android.telecom.Conference;
@@ -46,8 +48,10 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -63,7 +67,8 @@
 public class CallsManager extends Call.ListenerBase implements VideoProviderProxy.Listener {
 
     // TODO: Consider renaming this CallsManagerPlugin.
-    interface CallsManagerListener {
+    @VisibleForTesting
+    public interface CallsManagerListener {
         void onCallAdded(Call call);
         void onCallRemoved(Call call);
         void onCallStateChanged(Call call, int oldState, int newState);
@@ -97,6 +102,19 @@
 
     private static final int[] LIVE_CALL_STATES =
             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.ACTIVE};
+    public static final String TELECOM_CALL_ID_PREFIX = "TC@";
+
+    // Maps call technologies in PhoneConstants to those in Analytics.
+    private static final Map<Integer, Integer> sAnalyticsTechnologyMap;
+    static {
+        sAnalyticsTechnologyMap = new HashMap<>(5);
+        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE);
+        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE);
+        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE);
+        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE);
+        sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY,
+                Analytics.THIRD_PARTY_PHONE);
+    }
 
     /**
      * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
@@ -109,6 +127,13 @@
     private final Set<Call> mCalls = Collections.newSetFromMap(
             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
 
+    /**
+     * The current telecom call ID.  Used when creating new instances of {@link Call}.  Should
+     * only be accessed using the {@link #getNextCallId()} method which synchronizes on the
+     * {@link #mLock} sync root.
+     */
+    private int mCallId = 0;
+
     private final ConnectionServiceRepository mConnectionServiceRepository;
     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
     private final InCallController mInCallController;
@@ -122,6 +147,7 @@
             new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
     private final HeadsetMediaButton mHeadsetMediaButton;
     private final WiredHeadsetManager mWiredHeadsetManager;
+    private final BluetoothManager mBluetoothManager;
     private final DockManager mDockManager;
     private final TtyManager mTtyManager;
     private final ProximitySensorManager mProximitySensorManager;
@@ -160,7 +186,10 @@
             PhoneAccountRegistrar phoneAccountRegistrar,
             HeadsetMediaButtonFactory headsetMediaButtonFactory,
             ProximitySensorManagerFactory proximitySensorManagerFactory,
-            InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
+            InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
+            CallAudioManager.AudioServiceFactory audioServiceFactory,
+            BluetoothManager bluetoothManager,
+            WiredHeadsetManager wiredHeadsetManager) {
         mContext = context;
         mLock = lock;
         mContactsAsyncHelper = contactsAsyncHelper;
@@ -168,23 +197,48 @@
         mPhoneAccountRegistrar = phoneAccountRegistrar;
         mMissedCallNotifier = missedCallNotifier;
         StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
-        mWiredHeadsetManager = new WiredHeadsetManager(context);
+        mWiredHeadsetManager = wiredHeadsetManager;
+        mBluetoothManager = bluetoothManager;
         mDockManager = new DockManager(context);
-        mCallAudioManager = new CallAudioManager(
-                context, mLock, statusBarNotifier, mWiredHeadsetManager, mDockManager, this);
-        InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager, lock);
-        mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
+        CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
+                context,
+                this,
+                bluetoothManager,
+                wiredHeadsetManager,
+                statusBarNotifier,
+                audioServiceFactory
+        );
+        CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter =
+                new CallAudioRoutePeripheralAdapter(
+                        callAudioRouteStateMachine,
+                        bluetoothManager,
+                        wiredHeadsetManager,
+                        mDockManager);
+
+        mCallAudioManager = new CallAudioManager(context, mLock, this, callAudioRouteStateMachine);
+
+        InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager,
+                callAudioRoutePeripheralAdapter, lock);
+
+        RingtoneFactory ringtoneFactory = new RingtoneFactory(context);
+        SystemVibrator systemVibrator = new SystemVibrator(context);
+        AsyncRingtonePlayer asyncRingtonePlayer = new AsyncRingtonePlayer();
+        SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
+        mRinger = new Ringer(
+                mCallAudioManager, this, playerFactory, context, systemSettingsUtil,
+                asyncRingtonePlayer, ringtoneFactory, systemVibrator);
         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
         mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
-        mCallLogManager = new CallLogManager(context);
+        mCallLogManager = new CallLogManager(context, phoneAccountRegistrar);
         mInCallController = new InCallController(context, mLock, this);
         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
         mConnectionServiceRepository =
                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
 
+        mListeners.add(mInCallWakeLockController);
         mListeners.add(statusBarNotifier);
         mListeners.add(mCallLogManager);
         mListeners.add(mPhoneStateBroadcaster);
@@ -241,14 +295,25 @@
     }
 
     @Override
-    public void onSuccessfulIncomingCall(Call incomingCall) {
+    public void onSuccessfulIncomingCall(Call incomingCall, boolean shouldSendToVoicemail) {
         Log.d(this, "onSuccessfulIncomingCall");
-        setCallState(incomingCall, CallState.RINGING, "successful incoming call");
 
-        if (hasMaximumRingingCalls() || hasMaximumDialingCalls()) {
+        // Only set the incoming call as ringing if it isn't already disconnected.  It is possible
+        // that the connection service disconnected the call before it was even added to Telecom, in
+        // which case it makes no sense to set it back to a ringing state.
+        if (incomingCall.getState() != CallState.DISCONNECTED &&
+                incomingCall.getState() != CallState.DISCONNECTING) {
+            setCallState(incomingCall, CallState.RINGING,
+                    shouldSendToVoicemail ? "directing to voicemail" : "successful incoming call");
+        } else {
+            Log.i(this, "onSuccessfulIncomingCall: call already disconnected.");
+        }
+
+        if (hasMaximumRingingCalls() || hasMaximumDialingCalls() || shouldSendToVoicemail) {
             incomingCall.reject(false, null);
-            // since the call was not added to the list of calls, we have to call the missed
+            // Since the call was not added to the list of calls, we have to call the missed
             // call notifier and the call logger manually.
+            // Do we need missed call notification for direct to Voicemail calls?
             mMissedCallNotifier.showMissedCallNotification(incomingCall);
             mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
         } else {
@@ -304,7 +369,8 @@
                 @Override
                 public void run() {
                     synchronized (mLock) {
-                        // Set a timeout to stop the tone in case there isn't another tone to follow.
+                        // Set a timeout to stop the tone in case there isn't another tone to
+                        // follow.
                         mDtmfLocalTonePlayer.stopTone(call);
                     }
                 }
@@ -414,11 +480,13 @@
         }
     }
 
-    Collection<Call> getCalls() {
+    @VisibleForTesting
+    public Collection<Call> getCalls() {
         return Collections.unmodifiableCollection(mCalls);
     }
 
-    Call getForegroundCall() {
+    @VisibleForTesting
+    public Call getForegroundCall() {
         return mForegroundCall;
     }
 
@@ -469,7 +537,8 @@
         return mTtyManager.getCurrentTtyMode();
     }
 
-    void addListener(CallsManagerListener listener) {
+    @VisibleForTesting
+    public void addListener(CallsManagerListener listener) {
         mListeners.add(listener);
     }
 
@@ -492,6 +561,7 @@
             handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
         }
         Call call = new Call(
+                getNextCallId(),
                 mContext,
                 this,
                 mLock,
@@ -502,8 +572,16 @@
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
                 phoneAccountHandle,
-                true /* isIncoming */,
-                false /* isConference */);
+                Call.CALL_DIRECTION_INCOMING /* callDirection */,
+                false /* forceAttachToExistingConnection */,
+                false /* isConference */
+        );
+
+        call.initAnalytics();
+        if (mForegroundCall != null) {
+            mForegroundCall.getAnalytics().setCallIsInterrupted(true);
+            call.getAnalytics().setCallIsAdditional(true);
+        }
 
         call.setIntentExtras(extras);
         // TODO: Move this to be a part of addCall()
@@ -515,6 +593,7 @@
         Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
         Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
         Call call = new Call(
+                getNextCallId(),
                 mContext,
                 this,
                 mLock,
@@ -525,11 +604,14 @@
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
                 phoneAccountHandle,
+                Call.CALL_DIRECTION_UNKNOWN /* callDirection */,
                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
                 // to the existing connection instead of trying to create a new one.
-                true /* isIncoming */,
-                false /* isConference */);
-        call.setIsUnknown(true);
+                true /* forceAttachToExistingConnection */,
+                false /* isConference */
+        );
+        call.initAnalytics();
+
         call.setIntentExtras(extras);
         call.addListener(this);
         call.startCreateConnection(mPhoneAccountRegistrar);
@@ -549,8 +631,8 @@
         return TextUtils.equals(number1, number2);
     }
 
-    private Call getNewOutgoingCall(Uri handle) {
-        // First check to see if we can reuse any of the calls that are waiting to disconnect.
+    private Call reuseOutgoingCall(Uri handle) {
+        // Check to see if we can reuse any of the calls that are waiting to disconnect.
         // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
         Call reusedCall = null;
         for (Call pendingCall : mPendingCallsToDisconnect) {
@@ -563,25 +645,8 @@
                 pendingCall.disconnect();
             }
         }
-        if (reusedCall != null) {
-            return reusedCall;
-        }
 
-        // Create a call with original handle. The handle may be changed when the call is attached
-        // to a connection service, but in most cases will remain the same.
-        return new Call(
-                mContext,
-                this,
-                mLock,
-                mConnectionServiceRepository,
-                mContactsAsyncHelper,
-                mCallerInfoAsyncQueryFactory,
-                handle,
-                null /* gatewayInfo */,
-                null /* connectionManagerPhoneAccount */,
-                null /* phoneAccountHandle */,
-                false /* isIncoming */,
-                false /* isConference */);
+        return reusedCall;
     }
 
     /**
@@ -591,13 +656,40 @@
      * @param phoneAccountHandle The phone account which contains the component name of the
      *        connection service to use for this call.
      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
+     * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
      */
-    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
-        Call call = getNewOutgoingCall(handle);
+    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
+            UserHandle initiatingUser) {
+        boolean isReusedCall = true;
+        Call call = reuseOutgoingCall(handle);
+
+        // 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.
+        if (call == null) {
+            call = new Call(getNextCallId(), mContext,
+                    this,
+                    mLock,
+                    mConnectionServiceRepository,
+                    mContactsAsyncHelper,
+                    mCallerInfoAsyncQueryFactory,
+                    handle,
+                    null /* gatewayInfo */,
+                    null /* connectionManagerPhoneAccount */,
+                    null /* phoneAccountHandle */,
+                    Call.CALL_DIRECTION_OUTGOING /* callDirection */,
+                    false /* forceAttachToExistingConnection */,
+                    false /* isConference */
+            );
+            call.setInitiatingUser(initiatingUser);
+
+            call.initAnalytics();
+
+            isReusedCall = false;
+        }
 
         List<PhoneAccountHandle> accounts =
-                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false);
-
+                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false,
+                        initiatingUser);
         Log.v(this, "startOutgoingCall found accounts = " + accounts);
 
         if (mForegroundCall != null) {
@@ -628,7 +720,8 @@
             // No preset account, check if default exists that supports the URI scheme for the
             // handle.
             phoneAccountHandle =
-                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme());
+                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(),
+                            initiatingUser);
         }
 
         call.setTargetPhoneAccount(phoneAccountHandle);
@@ -636,13 +729,16 @@
         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
 
         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
-        // a call, or cancel this call altogether.
-        if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, call.isEmergencyCall())) {
+        // 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 (!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 #getNewOutgoingCall}.
+                // See {@link #reuseOutgoingCall}.
                 call.disconnect();
             }
             return null;
@@ -670,7 +766,7 @@
             call.addListener(this);
         } else if (!mCalls.contains(call)) {
             // We check if mCalls already contains the call because we could potentially be reusing
-            // a call which was previously added (See {@link #getNewOutgoingCall}).
+            // a call which was previously added (See {@link #reuseOutgoingCall}).
             addCall(call);
         }
 
@@ -705,6 +801,9 @@
 
         call.setHandle(uriHandle);
         call.setGatewayInfo(gatewayInfo);
+        // Auto-enable speakerphone if the originating intent specified to do so, or if the call
+        // is a video call.
+        call.setStartWithSpeakerphoneOn(speakerphoneOn || isSpeakerphoneAutoEnabled(videoState));
         call.setVideoState(videoState);
 
         if (speakerphoneOn) {
@@ -732,8 +831,8 @@
             // Otherwise the connection will be initiated when the account is set by the user.
             call.startCreateConnection(mPhoneAccountRegistrar);
         } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
-                requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false)
-                .isEmpty()) {
+                requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false,
+                call.getInitiatingUser()).isEmpty()) {
             // If there are no call capable accounts, disconnect the call.
             markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
                     "No registered PhoneAccounts"));
@@ -747,7 +846,8 @@
      * @param call The call to conference.
      * @param otherCall The other call to conference with.
      */
-    void conference(Call call, Call otherCall) {
+    @VisibleForTesting
+    public void conference(Call call, Call otherCall) {
         call.conferenceWith(otherCall);
     }
 
@@ -759,7 +859,8 @@
      * @param call The call to answer.
      * @param videoState The video state in which to answer the call.
      */
-    void answerCall(Call call, int videoState) {
+    @VisibleForTesting
+    public void answerCall(Call call, int videoState) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to answer a non-existent call %s", call);
         } else {
@@ -800,15 +901,32 @@
             // We do not update the UI until we get confirmation of the answer() through
             // {@link #markCallAsActive}.
             call.answer(videoState);
-            if (VideoProfile.isVideo(videoState) &&
-                !mWiredHeadsetManager.isPluggedIn() &&
-                !mCallAudioManager.isBluetoothDeviceAvailable() &&
-                isSpeakerEnabledForVideoCalls()) {
+            if (isSpeakerphoneAutoEnabled(videoState)) {
                 call.setStartWithSpeakerphoneOn(true);
             }
         }
     }
 
+    /**
+     * Determines if the speakerphone should be automatically enabled for the call.  Speakerphone
+     * should be enabled if the call is a video call and bluetooth or the wired headset are not in
+     * use.
+     *
+     * @param videoState The video state of the call.
+     * @return {@code true} if the speakerphone should be enabled.
+     */
+    private boolean isSpeakerphoneAutoEnabled(int videoState) {
+        return VideoProfile.isVideo(videoState) &&
+            !mWiredHeadsetManager.isPluggedIn() &&
+            !mBluetoothManager.isBluetoothAvailable() &&
+            isSpeakerEnabledForVideoCalls();
+    }
+
+    /**
+     * Determines if the speakerphone should be automatically enabled for video calls.
+     *
+     * @return {@code true} if the speakerphone should automatically be enabled.
+     */
     private static boolean isSpeakerEnabledForVideoCalls() {
         return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
                 PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
@@ -820,7 +938,8 @@
      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
      * the user opting to reject said call.
      */
-    void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
+    @VisibleForTesting
+    public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to reject a non-existent call %s", call);
         } else {
@@ -836,7 +955,8 @@
      *
      * @param digit The DTMF digit to play.
      */
-    void playDtmfTone(Call call, char digit) {
+    @VisibleForTesting
+    public void playDtmfTone(Call call, char digit) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
         } else {
@@ -848,7 +968,8 @@
     /**
      * Instructs Telecom to stop the currently playing DTMF tone, if any.
      */
-    void stopDtmfTone(Call call) {
+    @VisibleForTesting
+    public void stopDtmfTone(Call call) {
         if (!mCalls.contains(call)) {
             Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
         } else {
@@ -873,7 +994,8 @@
      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
      * the user hitting the end-call button.
      */
-    void disconnectCall(Call call) {
+    @VisibleForTesting
+    public void disconnectCall(Call call) {
         Log.v(this, "disconnectCall %s", call);
 
         if (!mCalls.contains(call)) {
@@ -901,7 +1023,8 @@
      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
      * the user hitting the hold button during an active call.
      */
-    void holdCall(Call call) {
+    @VisibleForTesting
+    public void holdCall(Call call) {
         if (!mCalls.contains(call)) {
             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
         } else {
@@ -915,7 +1038,8 @@
      * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
      * by the user hitting the hold button during a held call.
      */
-    void unholdCall(Call call) {
+    @VisibleForTesting
+    public void unholdCall(Call call) {
         if (!mCalls.contains(call)) {
             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
         } else {
@@ -930,6 +1054,20 @@
         }
     }
 
+    @Override
+    public void onExtrasChanged(Call call) {
+        if (call.getExtras() != null
+                && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) {
+
+            Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get(
+                    call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE));
+            if (analyticsCallTechnology == null) {
+                analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE;
+            }
+            call.getAnalytics().addCallTechnology(analyticsCallTechnology);
+        }
+    }
+
     /** Called by the in-call UI to change the mute state. */
     void mute(boolean shouldMute) {
         mCallAudioManager.mute(shouldMute);
@@ -978,13 +1116,16 @@
             }
 
             if (setDefault) {
-                mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account);
+                mPhoneAccountRegistrar
+                        .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
             }
         }
     }
 
     /** Called when the audio state changes. */
-    void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState) {
+    @VisibleForTesting
+    public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState
+            newAudioState) {
         Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
         for (CallsManagerListener listener : mListeners) {
             listener.onCallAudioStateChanged(oldAudioState, newAudioState);
@@ -1130,7 +1271,8 @@
         return getFirstCallWithState(CallState.RINGING);
     }
 
-    Call getActiveCall() {
+    @VisibleForTesting
+    public Call getActiveCall() {
         return getFirstCallWithState(CallState.ACTIVE);
     }
 
@@ -1138,11 +1280,13 @@
         return getFirstCallWithState(CallState.DIALING);
     }
 
-    Call getHeldCall() {
+    @VisibleForTesting
+    public Call getHeldCall() {
         return getFirstCallWithState(CallState.ON_HOLD);
     }
 
-    int getNumHeldCalls() {
+    @VisibleForTesting
+    public int getNumHeldCalls() {
         int count = 0;
         for (Call call : mCalls) {
             if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
@@ -1152,7 +1296,8 @@
         return count;
     }
 
-    Call getOutgoingCall() {
+    @VisibleForTesting
+    public Call getOutgoingCall() {
         return getFirstCallWithState(OUTGOING_CALL_STATES);
     }
 
@@ -1193,6 +1338,7 @@
     }
 
     Call createConferenceCall(
+            String callId,
             PhoneAccountHandle phoneAccount,
             ParcelableConference parcelableConference) {
 
@@ -1204,6 +1350,7 @@
                         parcelableConference.getConnectTimeMillis();
 
         Call call = new Call(
+                callId,
                 mContext,
                 this,
                 mLock,
@@ -1214,7 +1361,8 @@
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
                 phoneAccount,
-                false /* isIncoming */,
+                Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
+                false /* forceAttachToExistingConnection */,
                 true /* isConference */,
                 connectTime);
 
@@ -1267,7 +1415,7 @@
         call.addListener(this);
         mCalls.add(call);
 
-        // TODO: Update mForegroundCall prior to invoking
+        updateCallsManagerState();
         // onCallAdded for calls which immediately take the foreground (like the first call).
         for (CallsManagerListener listener : mListeners) {
             if (Log.SYSTRACE_DEBUG) {
@@ -1278,7 +1426,6 @@
                 Trace.endSection();
             }
         }
-        updateCallsManagerState();
         Trace.endSection();
     }
 
@@ -1300,6 +1447,7 @@
 
         // Only broadcast changes for calls that are being tracked.
         if (shouldNotify) {
+            updateCallsManagerState();
             for (CallsManagerListener listener : mListeners) {
                 if (Log.SYSTRACE_DEBUG) {
                     Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
@@ -1309,7 +1457,6 @@
                     Trace.endSection();
                 }
             }
-            updateCallsManagerState();
         }
         Trace.endSection();
     }
@@ -1340,6 +1487,7 @@
             Trace.beginSection("onCallStateChanged");
             // Only broadcast state change for calls that are being tracked.
             if (mCalls.contains(call)) {
+                updateCallsManagerState();
                 for (CallsManagerListener listener : mListeners) {
                     if (Log.SYSTRACE_DEBUG) {
                         Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
@@ -1349,7 +1497,6 @@
                         Trace.endSection();
                     }
                 }
-                updateCallsManagerState();
             }
             Trace.endSection();
         }
@@ -1433,7 +1580,7 @@
      * MMI codes which can be dialed when one or more calls are in progress.
      * <P>
      * Checks for numbers formatted similar to the MMI codes defined in:
-     * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)}
+     * {@link com.android.internal.telephony.GsmCdmaPhone#handleInCallMmiCommands(String)}
      * and
      * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)}
      *
@@ -1508,12 +1655,16 @@
                     // Disconnect the current outgoing call if it's not an emergency call. If the
                     // user tries to make two outgoing calls to different emergency call numbers,
                     // we will try to connect the first outgoing call.
+                    call.getAnalytics().setCallIsAdditional(true);
+                    outgoingCall.getAnalytics().setCallIsInterrupted(true);
                     outgoingCall.disconnect();
                     return true;
                 }
                 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
                     // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT}
                     // state, just disconnect it since the user has explicitly started a new call.
+                    call.getAnalytics().setCallIsAdditional(true);
+                    outgoingCall.getAnalytics().setCallIsInterrupted(true);
                     outgoingCall.disconnect();
                     return true;
                 }
@@ -1525,6 +1676,8 @@
                 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;
                 }
@@ -1554,6 +1707,8 @@
             // how to handle the new call relative to the current one.
             if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) {
                 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches.");
+                call.getAnalytics().setCallIsAdditional(true);
+                liveCall.getAnalytics().setCallIsInterrupted(true);
                 return true;
             } else if (call.getTargetPhoneAccount() == null) {
                 // Without a phone account, we can't say reliably that the call will fail.
@@ -1568,6 +1723,8 @@
             // Try to hold the live call before attempting the new outgoing call.
             if (liveCall.can(Connection.CAPABILITY_HOLD)) {
                 Log.i(this, "makeRoomForOutgoingCall: holding live call.");
+                call.getAnalytics().setCallIsAdditional(true);
+                liveCall.getAnalytics().setCallIsInterrupted(true);
                 liveCall.hold();
                 return true;
             }
@@ -1613,6 +1770,7 @@
      */
     Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
         Call call = new Call(
+                callId,
                 mContext,
                 this,
                 mLock,
@@ -1623,10 +1781,14 @@
                 null /* gatewayInfo */,
                 null /* connectionManagerPhoneAccount */,
                 connection.getPhoneAccount(), /* targetPhoneAccountHandle */
-                false /* isIncoming */,
+                Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
+                false /* forceAttachToExistingConnection */,
                 false /* isConference */,
                 connection.getConnectTimeMillis() /* connectTimeMillis */);
 
+        call.initAnalytics();
+        call.getAnalytics().setCreatedFromExistingConnection(true);
+
         setCallState(call, Call.getStateFromConnectionState(connection.getState()),
                 "existing connection");
         call.setConnectionCapabilities(connection.getConnectionCapabilities());
@@ -1640,6 +1802,15 @@
     }
 
     /**
+     * @return A new unique telecom call Id.
+     */
+    private String getNextCallId() {
+        synchronized(mLock) {
+            return TELECOM_CALL_ID_PREFIX + (++mCallId);
+        }
+    }
+
+    /**
      * Dumps the state of the {@link CallsManager}.
      *
      * @param pw The {@code IndentingPrintWriter} to write the state to.
diff --git a/src/com/android/server/telecom/ConnectionServiceRepository.java b/src/com/android/server/telecom/ConnectionServiceRepository.java
index a587b59..4685704 100644
--- a/src/com/android/server/telecom/ConnectionServiceRepository.java
+++ b/src/com/android/server/telecom/ConnectionServiceRepository.java
@@ -21,6 +21,7 @@
 import android.os.UserHandle;
 import android.util.Pair;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.HashMap;
@@ -28,7 +29,8 @@
 /**
  * Searches for and returns connection services.
  */
-final class ConnectionServiceRepository {
+@VisibleForTesting
+public class ConnectionServiceRepository {
     private final HashMap<Pair<ComponentName, UserHandle>, ConnectionServiceWrapper> mServiceCache =
             new HashMap<>();
     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
@@ -57,7 +59,8 @@
         mCallsManager = callsManager;
     }
 
-    ConnectionServiceWrapper getService(ComponentName componentName, UserHandle userHandle) {
+    @VisibleForTesting
+    public ConnectionServiceWrapper getService(ComponentName componentName, UserHandle userHandle) {
         Pair<ComponentName, UserHandle> cacheKey = Pair.create(componentName, userHandle);
         ConnectionServiceWrapper service = mServiceCache.get(cacheKey);
         if (service == null) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 8e58f22..56fbd5c 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -37,6 +37,7 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.IConnectionService;
 import com.android.internal.telecom.IConnectionServiceAdapter;
 import com.android.internal.telecom.IVideoProvider;
@@ -57,220 +58,214 @@
  * {@link IConnectionService} directly and instead should use this class to invoke methods of
  * {@link IConnectionService}.
  */
-final class ConnectionServiceWrapper extends ServiceBinder {
+@VisibleForTesting
+public class ConnectionServiceWrapper extends ServiceBinder {
 
     private final class Adapter extends IConnectionServiceAdapter.Stub {
 
         @Override
-        public void handleCreateConnectionComplete(
-                String callId,
-                ConnectionRequest request,
+        public void handleCreateConnectionComplete(String callId, ConnectionRequest request,
                 ParcelableConnection connection) {
+            Log.startSession("CSW.hCCC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("handleCreateConnectionComplete %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        ConnectionServiceWrapper.this
-                                .handleCreateConnectionComplete(callId, request, connection);
-                    }
+                    ConnectionServiceWrapper.this
+                            .handleCreateConnectionComplete(callId, request, connection);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setActive(String callId) {
+            Log.startSession("CSW.sA");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setActive %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper
-                            .isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            mCallsManager.markCallAsActive(call);
-                        } else {
-                            // Log.w(this, "setActive, unknown call id: %s", msg.obj);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.markCallAsActive(call);
+                    } else {
+                        // Log.w(this, "setActive, unknown call id: %s", msg.obj);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setRinging(String callId) {
+            Log.startSession("CSW.sR");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setRinging %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            mCallsManager.markCallAsRinging(call);
-                        } else {
-                            // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.markCallAsRinging(call);
+                    } else {
+                        // Log.w(this, "setRinging, unknown call id: %s", msg.obj);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setVideoProvider(String callId, IVideoProvider videoProvider) {
+            Log.startSession("CSW.sVP");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setVideoProvider %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId)
-                            || mCallIdMapper.isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setVideoProvider(videoProvider);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setVideoProvider(videoProvider);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setDialing(String callId) {
+            Log.startSession("CSW.sD");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setDialing %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            mCallsManager.markCallAsDialing(call);
-                        } else {
-                            // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.markCallAsDialing(call);
+                    } else {
+                        // Log.w(this, "setDialing, unknown call id: %s", msg.obj);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setDisconnected(String callId, DisconnectCause disconnectCause) {
+            Log.startSession("CSW.sD");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setDisconnected %s %s", callId, disconnectCause);
-                    if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper
-                            .isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        Log.d(this, "disconnect call %s %s", disconnectCause, call);
-                        if (call != null) {
-                            mCallsManager.markCallAsDisconnected(call, disconnectCause);
-                        } else {
-                            // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    Log.d(this, "disconnect call %s %s", disconnectCause, call);
+                    if (call != null) {
+                        mCallsManager.markCallAsDisconnected(call, disconnectCause);
+                    } else {
+                        // Log.w(this, "setDisconnected, unknown call id: %s", args.arg1);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setOnHold(String callId) {
+            Log.startSession("CSW.sOH");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setOnHold %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper
-                            .isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            mCallsManager.markCallAsOnHold(call);
-                        } else {
-                            // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        mCallsManager.markCallAsOnHold(call);
+                    } else {
+                        // Log.w(this, "setOnHold, unknown call id: %s", msg.obj);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setRingbackRequested(String callId, boolean ringback) {
+            Log.startSession("CSW.SRR");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setRingbackRequested %s %b", callId, ringback);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setRingbackRequested(ringback);
-                        } else {
-                            // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setRingbackRequested(ringback);
+                    } else {
+                        // Log.w(this, "setRingback, unknown call id: %s", args.arg1);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void removeCall(String callId) {
+            Log.startSession("CSW.rC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("removeCall %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper
-                            .isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            if (call.isAlive()) {
-                                mCallsManager.markCallAsDisconnected(
-                                        call, new DisconnectCause(DisconnectCause.REMOTE));
-                            } else {
-                                mCallsManager.markCallAsRemoved(call);
-                            }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        if (call.isAlive()) {
+                            mCallsManager.markCallAsDisconnected(
+                                    call, new DisconnectCause(DisconnectCause.REMOTE));
+                        } else {
+                            mCallsManager.markCallAsRemoved(call);
                         }
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setConnectionCapabilities(String callId, int connectionCapabilities) {
+            Log.startSession("CSW.sCC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setConnectionCapabilities %s %d", callId, connectionCapabilities);
-                    if (mCallIdMapper.isValidCallId(callId) || mCallIdMapper
-                            .isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setConnectionCapabilities(connectionCapabilities);
-                        } else {
-                            // Log.w(ConnectionServiceWrapper.this,
-                            // "setConnectionCapabilities, unknown call id: %s", msg.obj);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setConnectionCapabilities(connectionCapabilities);
+                    } else {
+                        // Log.w(ConnectionServiceWrapper.this,
+                        // "setConnectionCapabilities, unknown call id: %s", msg.obj);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setIsConferenced(String callId, String conferenceCallId) {
+            Log.startSession("CSW.sIC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -290,43 +285,44 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setConferenceMergeFailed(String callId) {
+            Log.startSession("CSW.sCMF");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setConferenceMergeFailed %s", callId);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        // TODO: we should move the UI for indication a merge failure here
-                        // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can
-                        // deliver the message anyway that they want. b/20530631.
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            // Just refresh the connection capabilities so that the UI
-                            // is forced to reenable the merge button as the capability
-                            // is still on the connection. Note when b/20530631 is fixed, we need
-                            // to revisit this fix to remove this hacky way of unhiding the merge
-                            // button (side effect of reprocessing the capabilities) and plumb
-                            // the failure event all the way to InCallUI instead of stopping
-                            // it here. That way we can also handle the UI of notifying that
-                            // the merged has failed.
-                            call.setConnectionCapabilities(call.getConnectionCapabilities(), true);
-                        } else {
-                            Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
-                        }
+                    // TODO: we should move the UI for indication a merge failure here
+                    // from CallNotifier.onSuppServiceFailed(). This way the InCallUI can
+                    // deliver the message anyway that they want. b/20530631.
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        // Just refresh the connection capabilities so that the UI
+                        // is forced to reenable the merge button as the capability
+                        // is still on the connection. Note when b/20530631 is fixed, we need
+                        // to revisit this fix to remove this hacky way of unhiding the merge
+                        // button (side effect of reprocessing the capabilities) and plumb
+                        // the failure event all the way to InCallUI instead of stopping
+                        // it here. That way we can also handle the UI of notifying that
+                        // the merged has failed.
+                        call.setConnectionCapabilities(call.getConnectionCapabilities(), true);
+                    } else {
+                        Log.w(this, "setConferenceMergeFailed, unknown call id: %s", callId);
                     }
-
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
+            Log.startSession("CSW.aCC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -358,7 +354,7 @@
                             parcelableConference.getPhoneAccount() != null) {
                         phAcc = parcelableConference.getPhoneAccount();
                     }
-                    Call conferenceCall = mCallsManager.createConferenceCall(
+                    Call conferenceCall = mCallsManager.createConferenceCall(callId,
                             phAcc, parcelableConference);
                     mCallIdMapper.addCall(conferenceCall, callId);
                     conferenceCall.setConnectionService(ConnectionServiceWrapper.this);
@@ -375,206 +371,208 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void onPostDialWait(String callId, String remaining) throws RemoteException {
+            Log.startSession("CSW.oPDW");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("onPostDialWait %s %s", callId, remaining);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.onPostDialWait(remaining);
-                        } else {
-                            // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.onPostDialWait(remaining);
+                    } else {
+                        // Log.w(this, "onPostDialWait, unknown call id: %s", args.arg1);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void onPostDialChar(String callId, char nextChar) throws RemoteException {
+            Log.startSession("CSW.oPDC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("onPostDialChar %s %s", callId, nextChar);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.onPostDialChar(nextChar);
-                        } else {
-                            // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.onPostDialChar(nextChar);
+                    } else {
+                        // Log.w(this, "onPostDialChar, unknown call id: %s", args.arg1);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+            final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+            Log.startSession("CSW.qRCS");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("queryRemoteConnectionServices %s", callback);
-                    ConnectionServiceWrapper.this.queryRemoteConnectionServices(callback);
+                    ConnectionServiceWrapper.this
+                            .queryRemoteConnectionServices(callingUserHandle, callback);
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setVideoState(String callId, int videoState) {
+            Log.startSession("CSW.sVS");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setVideoState %s %d", callId, videoState);
-                    if (mCallIdMapper.isValidCallId(callId)
-                            || mCallIdMapper.isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setVideoState(videoState);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setVideoState(videoState);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setIsVoipAudioMode(String callId, boolean isVoip) {
+            Log.startSession("CSW.sIVAM");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setIsVoipAudioMode %s %b", callId, isVoip);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setIsVoipAudioMode(isVoip);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setIsVoipAudioMode(isVoip);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setStatusHints(String callId, StatusHints statusHints) {
+            Log.startSession("CSW.sSH");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setStatusHints %s %s", callId, statusHints);
-                    if (mCallIdMapper.isValidCallId(callId)
-                            || mCallIdMapper.isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setStatusHints(statusHints);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setStatusHints(statusHints);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setExtras(String callId, Bundle extras) {
+            Log.startSession("CSW.sE");
             long token = Binder.clearCallingIdentity();
             try {
-                synchronized(mLock) {
+                synchronized (mLock) {
                     logIncoming("setExtras %s %s", callId, extras);
-                    if (mCallIdMapper.isValidCallId(callId)
-                            || mCallIdMapper.isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setExtras(extras);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setExtras(extras);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setAddress(String callId, Uri address, int presentation) {
+            Log.startSession("CSW.sA");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setAddress %s %s %d", callId, address, presentation);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setHandle(address, presentation);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setHandle(address, presentation);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setCallerDisplayName(
                 String callId, String callerDisplayName, int presentation) {
+            Log.startSession("CSW.sCDN");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setCallerDisplayName %s %s %d", callId, callerDisplayName,
                             presentation);
-                    if (mCallIdMapper.isValidCallId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            call.setCallerDisplayName(callerDisplayName, presentation);
-                        }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setCallerDisplayName(callerDisplayName, presentation);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void setConferenceableConnections(
                 String callId, List<String> conferenceableCallIds) {
+            Log.startSession("CSW.sCC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     logIncoming("setConferenceableConnections %s %s", callId,
                             conferenceableCallIds);
-                    if (mCallIdMapper.isValidCallId(callId) ||
-                            mCallIdMapper.isValidConferenceId(callId)) {
-                        Call call = mCallIdMapper.getCall(callId);
-                        if (call != null) {
-                            List<Call> conferenceableCalls =
-                                    new ArrayList<>(conferenceableCallIds.size());
-                            for (String otherId : conferenceableCallIds) {
-                                Call otherCall = mCallIdMapper.getCall(otherId);
-                                if (otherCall != null && otherCall != call) {
-                                    conferenceableCalls.add(otherCall);
-                                }
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        List<Call> conferenceableCalls =
+                                new ArrayList<>(conferenceableCallIds.size());
+                        for (String otherId : conferenceableCallIds) {
+                            Call otherCall = mCallIdMapper.getCall(otherId);
+                            if (otherCall != null && otherCall != call) {
+                                conferenceableCalls.add(otherCall);
                             }
-                            call.setConferenceableCalls(conferenceableCalls);
                         }
+                        call.setConferenceableCalls(conferenceableCalls);
                     }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
         @Override
         public void addExistingConnection(String callId, ParcelableConnection connection) {
+            Log.startSession("CSW.aEC");
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -586,12 +584,13 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
     }
 
     private final Adapter mAdapter = new Adapter();
-    private final CallIdMapper mCallIdMapper = new CallIdMapper("ConnectionService");
+    private final CallIdMapper mCallIdMapper = new CallIdMapper();
     private final Map<String, CreateConnectionResponse> mPendingResponses = new HashMap<>();
 
     private Binder2 mBinder = new Binder2();
@@ -642,7 +641,8 @@
     /**
      * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
      */
-    void createConnection(final Call call, final CreateConnectionResponse response) {
+    @VisibleForTesting
+    public void createConnection(final Call call, final CreateConnectionResponse response) {
         Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
         BindCallback callback = new BindCallback() {
             @Override
@@ -672,8 +672,9 @@
                                     call.getTargetPhoneAccount(),
                                     call.getHandle(),
                                     extras,
-                                    call.getVideoState()),
-                            call.isIncoming(),
+                                    call.getVideoState(),
+                                    callId),
+                            call.shouldAttachToExistingConnection(),
                             call.isUnknown());
                 } catch (RemoteException e) {
                     Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
@@ -745,8 +746,9 @@
         }
     }
 
-    /** @see IConnectionService#onCallAudioStateChanged(String,CallAudioState) */
-    void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
+    /** @see IConnectionService#onCallAudioStateChanged(String, CallAudioState) */
+    @VisibleForTesting
+    public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
         final String callId = mCallIdMapper.getCallId(activeCall);
         if (callId != null && isServiceValid("onCallAudioStateChanged")) {
             try {
@@ -803,7 +805,7 @@
         }
     }
 
-    /** @see IConnectionService#playDtmfTone(String,char) */
+    /** @see IConnectionService#playDtmfTone(String, char) */
     void playDtmfTone(Call call, char digit) {
         final String callId = mCallIdMapper.getCallId(call);
         if (callId != null && isServiceValid("playDtmfTone")) {
@@ -820,7 +822,7 @@
         final String callId = mCallIdMapper.getCallId(call);
         if (callId != null && isServiceValid("stopDtmfTone")) {
             try {
-                logOutgoing("stopDtmfTone %s",callId);
+                logOutgoing("stopDtmfTone %s", callId);
                 mServiceInterface.stopDtmfTone(callId);
             } catch (RemoteException e) {
             }
@@ -980,10 +982,11 @@
         Log.d(this, "Telecom -> ConnectionService: " + msg, params);
     }
 
-    private void queryRemoteConnectionServices(final RemoteServiceCallback callback) {
+    private void queryRemoteConnectionServices(final UserHandle userHandle,
+            final RemoteServiceCallback callback) {
         // Only give remote connection services to this connection service if it is listed as
         // the connection manager.
-        PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager();
+        PhoneAccountHandle simCallManager = mPhoneAccountRegistrar.getSimCallManager(userHandle);
         Log.d(this, "queryRemoteConnectionServices finds simCallManager = %s", simCallManager);
         if (simCallManager == null ||
                 !simCallManager.getComponentName().equals(getComponentName())) {
@@ -994,7 +997,7 @@
         // Make a list of ConnectionServices that are listed as being associated with SIM accounts
         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
-        for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts()) {
+        for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts(userHandle)) {
             ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
                     handle.getComponentName(), handle.getUserHandle());
             if (service != null) {
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index b846470..d82240e 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -17,23 +17,21 @@
 package com.android.server.telecom;
 
 import android.content.Context;
+import android.os.UserHandle;
 import android.telecom.DisconnectCause;
 import android.telecom.ParcelableConnection;
-import android.telecom.Phone;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.ServiceState;
 
 // TODO: Needed for move to system service: import com.android.internal.R;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 import java.util.Objects;
 
 /**
@@ -43,7 +41,8 @@
  *     to the user
  *   - a connection service cancels the process, in which case the call is aborted
  */
-final class CreateConnectionProcessor {
+@VisibleForTesting
+public class CreateConnectionProcessor implements CreateConnectionResponse {
 
     // Describes information required to attempt to make a phone call
     private static class CallAttemptRecord {
@@ -91,33 +90,35 @@
     private final ConnectionServiceRepository mRepository;
     private List<CallAttemptRecord> mAttemptRecords;
     private Iterator<CallAttemptRecord> mAttemptRecordIterator;
-    private CreateConnectionResponse mResponse;
+    private CreateConnectionResponse mCallResponse;
     private DisconnectCause mLastErrorDisconnectCause;
     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
     private final Context mContext;
-    private boolean mShouldUseConnectionManager = true;
     private CreateConnectionTimeout mTimeout;
+    private ConnectionServiceWrapper mService;
 
-    CreateConnectionProcessor(
+    @VisibleForTesting
+    public CreateConnectionProcessor(
             Call call, ConnectionServiceRepository repository, CreateConnectionResponse response,
             PhoneAccountRegistrar phoneAccountRegistrar, Context context) {
         Log.v(this, "CreateConnectionProcessor created for Call = %s", call);
         mCall = call;
         mRepository = repository;
-        mResponse = response;
+        mCallResponse = response;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
         mContext = context;
     }
 
     boolean isProcessingComplete() {
-        return mResponse == null;
+        return mCallResponse == null;
     }
 
     boolean isCallTimedOut() {
         return mTimeout != null && mTimeout.isCallTimedOut();
     }
 
-    void process() {
+    @VisibleForTesting
+    public void process() {
         Log.v(this, "process");
         clearTimeout();
         mAttemptRecords = new ArrayList<>();
@@ -138,7 +139,7 @@
     void continueProcessingIfPossible(CreateConnectionResponse response,
             DisconnectCause disconnectCause) {
         Log.v(this, "continueProcessingIfPossible");
-        mResponse = response;
+        mCallResponse = response;
         mLastErrorDisconnectCause = disconnectCause;
         attemptNextPhoneAccount();
     }
@@ -148,8 +149,8 @@
 
         // Clear the response first to prevent attemptNextConnectionService from attempting any
         // more services.
-        CreateConnectionResponse response = mResponse;
-        mResponse = null;
+        CreateConnectionResponse response = mCallResponse;
+        mCallResponse = null;
         clearTimeout();
 
         ConnectionServiceWrapper service = mCall.getConnectionService();
@@ -190,33 +191,27 @@
             }
         }
 
-        if (mResponse != null && attempt != null) {
+        if (mCallResponse != null && attempt != null) {
             Log.i(this, "Trying attempt %s", attempt);
             PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;
-            ConnectionServiceWrapper service =
-                    mRepository.getService(
-                            phoneAccount.getComponentName(),
-                            phoneAccount.getUserHandle());
-            if (service == null) {
+            mService = mRepository.getService(phoneAccount.getComponentName(),
+                    phoneAccount.getUserHandle());
+            if (mService == null) {
                 Log.i(this, "Found no connection service for attempt %s", attempt);
                 attemptNextPhoneAccount();
             } else {
                 mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
                 mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
-                mCall.setConnectionService(service);
-                setTimeoutIfNeeded(service, attempt);
+                mCall.setConnectionService(mService);
+                setTimeoutIfNeeded(mService, attempt);
 
-                service.createConnection(mCall, new Response(service));
+                mService.createConnection(mCall, this);
             }
         } else {
             Log.v(this, "attemptNextPhoneAccount, no more accounts, failing");
-            if (mResponse != null) {
-                clearTimeout();
-                mResponse.handleCreateConnectionFailure(mLastErrorDisconnectCause != null ?
-                        mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR));
-                mResponse = null;
-                mCall.clearConnectionService();
-            }
+            DisconnectCause disconnectCause = mLastErrorDisconnectCause != null ?
+                    mLastErrorDisconnectCause : new DisconnectCause(DisconnectCause.ERROR);
+            notifyCallConnectionFailure(disconnectCause);
         }
     }
 
@@ -240,10 +235,6 @@
     }
 
     private boolean shouldSetConnectionManager() {
-        if (!mShouldUseConnectionManager) {
-            return false;
-        }
-
         if (mAttemptRecords.size() == 0) {
             return false;
         }
@@ -254,7 +245,8 @@
             return false;
         }
 
-        PhoneAccountHandle connectionManager = mPhoneAccountRegistrar.getSimCallManager();
+        PhoneAccountHandle connectionManager =
+                mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall);
         if (connectionManager == null) {
             return false;
         }
@@ -266,8 +258,8 @@
 
         // Connection managers are only allowed to manage SIM subscriptions.
         // TODO: Should this really be checking the "calling user" test for phone account?
-        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccountCheckCallingUser(
-                targetPhoneAccountHandle);
+        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar
+                .getPhoneAccountUnchecked(targetPhoneAccountHandle);
         if (targetPhoneAccount == null) {
             Log.d(this, "shouldSetConnectionManager, phone account not found");
             return false;
@@ -285,10 +277,10 @@
     private void adjustAttemptsForConnectionManager() {
         if (shouldSetConnectionManager()) {
             CallAttemptRecord record = new CallAttemptRecord(
-                    mPhoneAccountRegistrar.getSimCallManager(),
+                    mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall),
                     mAttemptRecords.get(0).targetPhoneAccount);
             Log.v(this, "setConnectionManager, changing %s -> %s", mAttemptRecords.get(0), record);
-            mAttemptRecords.set(0, record);
+            mAttemptRecords.add(0, record);
         } else {
             Log.v(this, "setConnectionManager, not changing");
         }
@@ -296,11 +288,14 @@
 
     // If we are possibly attempting to call a local emergency number, ensure that the
     // plain PSTN connection services are listed, and nothing else.
-    private void adjustAttemptsForEmergency()  {
+    private void adjustAttemptsForEmergency() {
         if (mCall.isEmergencyCall()) {
             Log.i(this, "Emergency number detected");
             mAttemptRecords.clear();
-            List<PhoneAccount> allAccounts = mPhoneAccountRegistrar.getAllPhoneAccounts();
+            // Phone accounts in profile do not handle emergency call, use phone accounts in
+            // current user.
+            List<PhoneAccount> allAccounts = mPhoneAccountRegistrar
+                    .getAllPhoneAccountsOfCurrentUser();
 
             if (allAccounts.isEmpty()) {
                 // If the list of phone accounts is empty at this point, it means Telephony hasn't
@@ -312,7 +307,6 @@
                 allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount());
             }
 
-
             // First, add SIM phone accounts which can place emergency calls.
             for (PhoneAccount phoneAccount : allAccounts) {
                 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) &&
@@ -327,18 +321,17 @@
             }
 
             // Next, add the connection manager account as a backup if it can place emergency calls.
-            PhoneAccountHandle callManagerHandle = mPhoneAccountRegistrar.getSimCallManager();
-            if (mShouldUseConnectionManager && callManagerHandle != null) {
+            PhoneAccountHandle callManagerHandle =
+                    mPhoneAccountRegistrar.getSimCallManagerOfCurrentUser();
+            if (callManagerHandle != null) {
                 // TODO: Should this really be checking the "calling user" test for phone account?
                 PhoneAccount callManager = mPhoneAccountRegistrar
-                        .getPhoneAccountCheckCallingUser(callManagerHandle);
+                        .getPhoneAccountUnchecked(callManagerHandle);
                 if (callManager != null && callManager.hasCapabilities(
                         PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
                     CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
-                            mPhoneAccountRegistrar.
-                                    getOutgoingPhoneAccountForScheme(mCall.getHandle().getScheme())
-                    );
-
+                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                                    mCall.getHandle().getScheme()));
                     if (!mAttemptRecords.contains(callAttemptRecord)) {
                         Log.i(this, "Will try Connection Manager account %s for emergency",
                                 callManager);
@@ -359,69 +352,77 @@
         return result;
     }
 
-    private class Response implements CreateConnectionResponse {
-        private final ConnectionServiceWrapper mService;
 
-        Response(ConnectionServiceWrapper service) {
-            mService = service;
+    private void notifyCallConnectionFailure(DisconnectCause errorDisconnectCause) {
+        if (mCallResponse != null) {
+            clearTimeout();
+            mCallResponse.handleCreateConnectionFailure(errorDisconnectCause);
+            mCallResponse = null;
+            mCall.clearConnectionService();
         }
+    }
 
-        @Override
-        public void handleCreateConnectionSuccess(
-                CallIdMapper idMapper,
-                ParcelableConnection connection) {
-            if (mResponse == null) {
-                // Nobody is listening for this connection attempt any longer; ask the responsible
-                // ConnectionService to tear down any resources associated with the call
-                mService.abort(mCall);
-            } else {
-                // Success -- share the good news and remember that we are no longer interested
-                // in hearing about any more attempts
-                mResponse.handleCreateConnectionSuccess(idMapper, connection);
-                mResponse = null;
-                // If there's a timeout running then don't clear it. The timeout can be triggered
-                // after the call has successfully been created but before it has become active.
-            }
+    @Override
+    public void handleCreateConnectionSuccess(
+            CallIdMapper idMapper,
+            ParcelableConnection connection) {
+        if (mCallResponse == null) {
+            // Nobody is listening for this connection attempt any longer; ask the responsible
+            // ConnectionService to tear down any resources associated with the call
+            mService.abort(mCall);
+        } else {
+            // Success -- share the good news and remember that we are no longer interested
+            // in hearing about any more attempts
+            mCallResponse.handleCreateConnectionSuccess(idMapper, connection);
+            mCallResponse = null;
+            // If there's a timeout running then don't clear it. The timeout can be triggered
+            // after the call has successfully been created but before it has become active.
         }
+    }
 
-        private boolean shouldFallbackToNoConnectionManager(DisconnectCause cause) {
-            PhoneAccountHandle handle = mCall.getConnectionManagerPhoneAccount();
-            if (handle == null || !handle.equals(mPhoneAccountRegistrar.getSimCallManager())) {
-                return false;
-            }
-
-            ConnectionServiceWrapper connectionManager = mCall.getConnectionService();
-            if (connectionManager == null) {
-                return false;
-            }
-
-            if (cause.getCode() == DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED) {
-                Log.d(CreateConnectionProcessor.this, "Connection manager declined to handle the "
-                        + "call, falling back to not using a connection manager");
-                return true;
-            }
-
-            if (!connectionManager.isServiceValid("createConnection")) {
-                Log.d(CreateConnectionProcessor.this, "Connection manager unbound while trying "
-                        + "create a connection, falling back to not using a connection manager");
-                return true;
-            }
-
+    private boolean shouldFailCallIfConnectionManagerFails(DisconnectCause cause) {
+        // Connection Manager does not exist or does not match registered Connection Manager
+        // Since Connection manager is a proxy for SIM, fall back to SIM
+        PhoneAccountHandle handle = mCall.getConnectionManagerPhoneAccount();
+        if (handle == null || !handle.equals(mPhoneAccountRegistrar.getSimCallManagerFromCall(
+                mCall))) {
             return false;
         }
 
-        @Override
-        public void handleCreateConnectionFailure(DisconnectCause errorDisconnectCause) {
-            // Failure of some sort; record the reasons for failure and try again if possible
-            Log.d(CreateConnectionProcessor.this, "Connection failed: (%s)", errorDisconnectCause);
-            mLastErrorDisconnectCause = errorDisconnectCause;
-            if (shouldFallbackToNoConnectionManager(errorDisconnectCause)) {
-                mShouldUseConnectionManager = false;
-                // Restart from the beginning.
-                process();
-            } else {
-                attemptNextPhoneAccount();
-            }
+        // The Call's Connection Service does not exist
+        ConnectionServiceWrapper connectionManager = mCall.getConnectionService();
+        if (connectionManager == null) {
+            return true;
         }
+
+        // In this case, fall back to a sim because connection manager declined
+        if (cause.getCode() == DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED) {
+            Log.d(CreateConnectionProcessor.this, "Connection manager declined to handle the "
+                    + "call, falling back to not using a connection manager");
+            return false;
+        }
+
+        if (!connectionManager.isServiceValid("createConnection")) {
+            Log.d(CreateConnectionProcessor.this, "Connection manager unbound while trying "
+                    + "create a connection, falling back to not using a connection manager");
+            return false;
+        }
+
+        // Do not fall back from connection manager and simply fail call if the failure reason is
+        // other
+        Log.d(CreateConnectionProcessor.this, "Connection Manager denied call with the following " +
+                "error: " + cause.getReason() + ". Not falling back to SIM.");
+        return true;
+    }
+
+    @Override
+    public void handleCreateConnectionFailure(DisconnectCause errorDisconnectCause) {
+        // Failure of some sort; record the reasons for failure and try again if possible
+        Log.d(CreateConnectionProcessor.this, "Connection failed: (%s)", errorDisconnectCause);
+        if(shouldFailCallIfConnectionManagerFails(errorDisconnectCause)){
+            notifyCallConnectionFailure(errorDisconnectCause);
+            return;
+        }
+        attemptNextPhoneAccount();
     }
 }
diff --git a/src/com/android/server/telecom/CreateConnectionResponse.java b/src/com/android/server/telecom/CreateConnectionResponse.java
index 08c0cfc..8e3d0cf 100644
--- a/src/com/android/server/telecom/CreateConnectionResponse.java
+++ b/src/com/android/server/telecom/CreateConnectionResponse.java
@@ -19,10 +19,13 @@
 import android.telecom.DisconnectCause;
 import android.telecom.ParcelableConnection;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * A callback for providing the result of creating a connection.
  */
-interface CreateConnectionResponse {
+@VisibleForTesting
+public interface CreateConnectionResponse {
     void handleCreateConnectionSuccess(CallIdMapper idMapper, ParcelableConnection connection);
     void handleCreateConnectionFailure(DisconnectCause disconnectCaused);
 }
diff --git a/src/com/android/server/telecom/CreateConnectionTimeout.java b/src/com/android/server/telecom/CreateConnectionTimeout.java
index 06dc9ed..52a4643 100644
--- a/src/com/android/server/telecom/CreateConnectionTimeout.java
+++ b/src/com/android/server/telecom/CreateConnectionTimeout.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.UserHandle;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 
@@ -54,7 +55,8 @@
 
         // If there's no connection manager to fallback on then there's no point in having a
         // timeout.
-        PhoneAccountHandle connectionManager = mPhoneAccountRegistrar.getSimCallManager();
+        PhoneAccountHandle connectionManager =
+                mPhoneAccountRegistrar.getSimCallManagerFromCall(mCall);
         if (!accounts.contains(connectionManager)) {
             return false;
         }
diff --git a/src/com/android/server/telecom/DialerCodeReceiver.java b/src/com/android/server/telecom/DialerCodeReceiver.java
new file mode 100644
index 0000000..8732222
--- /dev/null
+++ b/src/com/android/server/telecom/DialerCodeReceiver.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telecom.TelecomManager;
+
+/**
+ * Receiver for "secret codes" broadcast by Dialer.
+ */
+public class DialerCodeReceiver extends BroadcastReceiver {
+    // Copied from TelephonyIntents.java.
+    public static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
+
+    // Enables extended logging for a period of time.
+    public static final String TELECOM_SECRET_CODE_DEBUG_ON = "823241";
+
+    // Disables extended logging.
+    public static final String TELECOM_SECRET_CODE_DEBUG_OFF = "823240";
+
+    // Writes a MARK to the Telecom log.
+    public static final String TELECOM_SECRET_CODE_MARK = "826275";
+
+    private final CallsManager mCallsManager;
+
+    DialerCodeReceiver(CallsManager callsManager) {
+        mCallsManager = callsManager;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (SECRET_CODE_ACTION.equals(intent.getAction()) && intent.getData() != null &&
+                intent.getData().getHost() != null) {
+            if (intent.getData().getHost().equals(TELECOM_SECRET_CODE_DEBUG_ON)) {
+                Log.i("DialerCodeReceiver", "Secret code used to enable extended logging mode");
+                Log.setIsExtendedLoggingEnabled(true);
+            } else if (intent.getData().getHost().equals(TELECOM_SECRET_CODE_DEBUG_OFF)) {
+                Log.i("DialerCodeReceiver", "Secret code used to disable extended logging mode");
+                Log.setIsExtendedLoggingEnabled(false);
+            } else if (intent.getData().getHost().equals(TELECOM_SECRET_CODE_MARK)) {
+                Log.i("DialerCodeReceiver", "Secret code used to mark logs.");
+
+                // If there is an active call, add the "log mark" for that call; otherwise we will
+                // add a non-call event.
+                Call currentCall = mCallsManager.getActiveCall();
+                Log.event(currentCall, Log.Events.USER_LOG_MARK);
+            }
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/DockManager.java b/src/com/android/server/telecom/DockManager.java
index e6ad446..1048e2b 100644
--- a/src/com/android/server/telecom/DockManager.java
+++ b/src/com/android/server/telecom/DockManager.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Collections;
@@ -28,8 +29,10 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 /** Listens for and caches car dock state. */
-class DockManager {
-    interface Listener {
+@VisibleForTesting
+public class DockManager {
+    @VisibleForTesting
+    public interface Listener {
         void onDockChanged(boolean isDocked);
     }
 
@@ -65,7 +68,8 @@
         context.registerReceiver(mReceiver, intentFilter);
     }
 
-    void addListener(Listener listener) {
+    @VisibleForTesting
+    public void addListener(Listener listener) {
         mListeners.add(listener);
     }
 
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index 9239288..5ec5889 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -41,11 +41,12 @@
 
     @Override
     public void answerCall(String callId, int videoState) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                Log.d(this, "answerCall(%s,%d)", callId, videoState);
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.aC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.d(this, "answerCall(%s,%d)", callId, videoState);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.answerCall(call, videoState);
@@ -53,19 +54,22 @@
                         Log.w(this, "answerCall, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage);
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.aC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.d(this, "rejectCall(%s,%b,%s)", callId, rejectWithMessage, textMessage);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.rejectCall(call, rejectWithMessage, textMessage);
@@ -73,19 +77,22 @@
                         Log.w(this, "setRingback, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void playDtmfTone(String callId, char digit) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                Log.d(this, "playDtmfTone(%s,%c)", callId, digit);
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.pDT");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.d(this, "playDtmfTone(%s,%c)", callId, digit);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.playDtmfTone(call, digit);
@@ -93,19 +100,22 @@
                         Log.w(this, "playDtmfTone, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void stopDtmfTone(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                Log.d(this, "stopDtmfTone(%s)", callId);
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.sDT");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.d(this, "stopDtmfTone(%s)", callId);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.stopDtmfTone(call);
@@ -113,19 +123,22 @@
                         Log.w(this, "stopDtmfTone, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void postDialContinue(String callId, boolean proceed) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                Log.d(this, "postDialContinue(%s)", callId);
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.pDC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.d(this, "postDialContinue(%s)", callId);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.postDialContinue(call, proceed);
@@ -133,19 +146,22 @@
                         Log.w(this, "postDialContinue, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void disconnectCall(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                Log.v(this, "disconnectCall: %s", callId);
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.dC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    Log.v(this, "disconnectCall: %s", callId);
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.disconnectCall(call);
@@ -153,18 +169,21 @@
                         Log.w(this, "disconnectCall, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void holdCall(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.hC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.holdCall(call);
@@ -172,18 +191,21 @@
                         Log.w(this, "holdCall, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void unholdCall(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.uC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.unholdCall(call);
@@ -191,19 +213,22 @@
                         Log.w(this, "unholdCall, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle,
             boolean setDefault) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.pAS");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         mCallsManager.phoneAccountSelected(call, accountHandle, setDefault);
@@ -211,43 +236,55 @@
                         Log.w(this, "phoneAccountSelected, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void mute(boolean shouldMute) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                mCallsManager.mute(shouldMute);
+            Log.startSession("ICA.m");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mCallsManager.mute(shouldMute);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void setAudioRoute(int route) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                mCallsManager.setAudioRoute(route);
+            Log.startSession("ICA.sAR");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mCallsManager.setAudioRoute(route);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void conference(String callId, String otherCallId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId) &&
-                        mCallIdMapper.isValidCallId(otherCallId)) {
+            Log.startSession("ICA.c");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     Call otherCall = mCallIdMapper.getCall(otherCallId);
                     if (call != null && otherCall != null) {
@@ -255,20 +292,22 @@
                     } else {
                         Log.w(this, "conference, unknown call id: %s or %s", callId, otherCallId);
                     }
-
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void splitFromConference(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.sFC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         call.splitFromConference();
@@ -276,18 +315,21 @@
                         Log.w(this, "splitFromConference, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void mergeConference(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.mC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         call.mergeConference();
@@ -295,18 +337,21 @@
                         Log.w(this, "mergeConference, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void swapConference(String callId) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                if (mCallIdMapper.isValidCallId(callId)) {
+            Log.startSession("ICA.sC");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
                     Call call = mCallIdMapper.getCall(callId);
                     if (call != null) {
                         call.swapConference();
@@ -314,33 +359,45 @@
                         Log.w(this, "swapConference, unknown call id: %s", callId);
                     }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void turnOnProximitySensor() {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                mCallsManager.turnOnProximitySensor();
+            Log.startSession("tOPS");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mCallsManager.turnOnProximitySensor();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Log.endSession();
         }
     }
 
     @Override
     public void turnOffProximitySensor(boolean screenOnImmediately) {
-        long token = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                mCallsManager.turnOffProximitySensor(screenOnImmediately);
+            Log.startSession("ICA.tOPS");
+            long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    mCallsManager.turnOffProximitySensor(screenOnImmediately);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+             Log.endSession();
         }
     }
 }
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 9562ea7..1ebf2eb 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -66,14 +66,18 @@
     private class InCallServiceConnection implements ServiceConnection {
         /** {@inheritDoc} */
         @Override public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.startSession("ICSC.oSC");
             Log.d(this, "onServiceConnected: %s", name);
             onConnected(name, service);
+            Log.endSession();
         }
 
         /** {@inheritDoc} */
         @Override public void onServiceDisconnected(ComponentName name) {
+            Log.startSession("ICSC.oSD");
             Log.d(this, "onDisconnected: %s", name);
             onDisconnected(name);
+            Log.endSession();
         }
     }
 
@@ -146,7 +150,7 @@
      */
     private ComponentName mInCallUIComponentName;
 
-    private final CallIdMapper mCallIdMapper = new CallIdMapper("InCall");
+    private final CallIdMapper mCallIdMapper = new CallIdMapper();
 
     /** The {@link ComponentName} of the default InCall UI. */
     private final ComponentName mSystemInCallComponentName;
@@ -198,15 +202,21 @@
             /** Let's add a 2 second delay before we send unbind to the services to hopefully
              *  give them enough time to process all the pending messages.
              */
+            final Session subsession = Log.createSubsession();
             Handler handler = new Handler(Looper.getMainLooper());
             final Runnable runnableUnbind = new Runnable() {
                 @Override
                 public void run() {
-                    synchronized (mLock) {
-                        // Check again to make sure there are no active calls.
-                        if (mCallsManager.getCalls().isEmpty()) {
-                            unbindFromServices();
+                    try {
+                        Log.continueSession(subsession, "ICC.oCR");
+                        synchronized (mLock) {
+                            // Check again to make sure there are no active calls.
+                            if (mCallsManager.getCalls().isEmpty()) {
+                                unbindFromServices();
+                            }
                         }
+                    } finally {
+                        Log.endSession();
                     }
                 }
             };
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 0d2e3c4..4fa4389 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -21,27 +21,34 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Play a call-related tone (ringback, busy signal, etc.) through ToneGenerator. To use, create an
  * instance using InCallTonePlayer.Factory (passing in the TONE_* constant for the tone you want)
  * and start() it. Implemented on top of {@link Thread} so that the tone plays in its own thread.
  */
-public final class InCallTonePlayer extends Thread {
+public class InCallTonePlayer extends Thread {
 
     /**
      * Factory used to create InCallTonePlayers. Exists to aid with testing mocks.
      */
     public static class Factory {
         private final CallAudioManager mCallAudioManager;
+        private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter;
         private final TelecomSystem.SyncRoot mLock;
 
-        Factory(CallAudioManager callAudioManager, TelecomSystem.SyncRoot lock) {
+        Factory(CallAudioManager callAudioManager,
+                CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter,
+                TelecomSystem.SyncRoot lock) {
             mCallAudioManager = callAudioManager;
+            mCallAudioRoutePeripheralAdapter = callAudioRoutePeripheralAdapter;
             mLock = lock;
         }
 
-        InCallTonePlayer createPlayer(int tone) {
-            return new InCallTonePlayer(tone, mCallAudioManager, mLock);
+        public InCallTonePlayer createPlayer(int tone) {
+            return new InCallTonePlayer(tone, mCallAudioManager,
+                    mCallAudioRoutePeripheralAdapter, mLock);
         }
     }
 
@@ -83,6 +90,7 @@
     private static int sTonesPlaying = 0;
 
     private final CallAudioManager mCallAudioManager;
+    private final CallAudioRoutePeripheralAdapter mCallAudioRoutePeripheralAdapter;
 
     private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
@@ -103,10 +111,12 @@
     private InCallTonePlayer(
             int toneId,
             CallAudioManager callAudioManager,
+            CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter,
             TelecomSystem.SyncRoot lock) {
         mState = STATE_OFF;
         mToneId = toneId;
         mCallAudioManager = callAudioManager;
+        mCallAudioRoutePeripheralAdapter = callAudioRoutePeripheralAdapter;
         mLock = lock;
     }
 
@@ -195,7 +205,7 @@
             }
 
             int stream = AudioManager.STREAM_VOICE_CALL;
-            if (mCallAudioManager.isBluetoothAudioOn()) {
+            if (mCallAudioRoutePeripheralAdapter.isBluetoothAudioOn()) {
                 stream = AudioManager.STREAM_BLUETOOTH_SCO;
             }
 
@@ -236,8 +246,9 @@
             cleanUpTonePlayer();
         }
     }
-
-    void startTone() {
+    
+    @VisibleForTesting
+    public void startTone() {
         sTonesPlaying++;
         if (sTonesPlaying == 1) {
             mCallAudioManager.setIsTonePlaying(true);
@@ -249,7 +260,8 @@
     /**
      * Stops the tone.
      */
-    void stopTone() {
+    @VisibleForTesting
+    public void stopTone() {
         synchronized (this) {
             if (mState == STATE_ON) {
                 Log.d(this, "Stopping the tone %d.", mToneId);
diff --git a/src/com/android/server/telecom/InCallWakeLockController.java b/src/com/android/server/telecom/InCallWakeLockController.java
index a6c63c3..8abacd9 100644
--- a/src/com/android/server/telecom/InCallWakeLockController.java
+++ b/src/com/android/server/telecom/InCallWakeLockController.java
@@ -41,8 +41,6 @@
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mFullWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
         mFullWakeLock.setReferenceCounted(false);
-
-        callsManager.addListener(this);
     }
 
     @Override
diff --git a/src/com/android/server/telecom/Log.java b/src/com/android/server/telecom/Log.java
index e6e85ba..075f4bb 100644
--- a/src/com/android/server/telecom/Log.java
+++ b/src/com/android/server/telecom/Log.java
@@ -16,25 +16,33 @@
 
 package com.android.server.telecom;
 
+import android.content.Context;
 import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
 import android.telecom.PhoneAccount;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
+import android.util.Base64;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.nio.ByteBuffer;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.IllegalFormatException;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.LinkedBlockingQueue;
 
 /**
@@ -82,6 +90,7 @@
         public static final String MUTE = "MUTE";
         public static final String AUDIO_ROUTE = "AUDIO_ROUTE";
         public static final String ERROR_LOG = "ERROR";
+        public static final String USER_LOG_MARK = "USER_LOG_MARK";
         public static final String SILENCE = "SILENCE";
 
         /**
@@ -104,11 +113,13 @@
 
     public static class CallEvent {
         public String eventId;
+        public String sessionId;
         public long time;
         public Object data;
 
-        public CallEvent(String eventId, long time, Object data) {
+        public CallEvent(String eventId, String sessionId, long time, Object data) {
             this.eventId = eventId;
+            this.sessionId = sessionId;
             this.time = time;
             this.data = data;
         }
@@ -119,27 +130,25 @@
         private static int sNextId = 1;
         private final List<CallEvent> mEvents = new LinkedList<>();
         private final Call mCall;
-        private final int mId;
 
         public CallEventRecord(Call call) {
             mCall = call;
-            mId = ++sNextId;
         }
 
         public Call getCall() {
             return mCall;
         }
 
-        public void addEvent(String event, Object data) {
-            mEvents.add(new CallEvent(event, System.currentTimeMillis(), data));
-            Log.i("Event", "Call %d: %s, %s", mId, event, data);
+        public void addEvent(String event, String sessionId, Object data) {
+            mEvents.add(new CallEvent(event, sessionId, System.currentTimeMillis(), data));
+            Log.i("Event", "Call %s: %s, %s", mCall.getId(), event, data);
         }
 
         public void dump(IndentingPrintWriter pw) {
             Map<String, CallEvent> pendingResponses = new HashMap<>();
 
             pw.print("Call ");
-            pw.print(mId);
+            pw.print(mCall.getId());
             pw.print(" [");
             pw.print(sDateFormat.format(new Date(mCall.getCreationTimeMillis())));
             pw.print("]");
@@ -165,6 +174,8 @@
 
                 pw.print(sDateFormat.format(new Date(event.time)));
                 pw.print(" - ");
+                pw.print(event.sessionId);
+                pw.print(":");
                 pw.print(event.eventId);
                 if (event.data != null) {
                     pw.print(" (");
@@ -175,7 +186,7 @@
                         // ID instead.
                         CallEventRecord record = mCallEventRecordMap.get(data);
                         if (record != null) {
-                            data = "Call " + record.mId;
+                            data = "Call " + record.mCall.getId();
                         }
                     }
 
@@ -200,10 +211,18 @@
     }
 
     public static final int MAX_CALLS_TO_CACHE = 5;  // Arbitrarily chosen.
+    public static final int MAX_CALLS_TO_CACHE_DEBUG = 20;  // Arbitrarily chosen.
+    private static final long EXTENDED_LOGGING_DURATION_MILLIS = 60000 * 30; // 30 minutes
+
+    private static final boolean LOG_DBG = false;
+
+    // Currently using 3 letters, So don't exceed 64^3
+    private static final long SESSION_ID_ROLLOVER_THRESHOLD = 262144;
 
     // Generic tag for all In Call logging
     @VisibleForTesting
     public static String TAG = "Telecom";
+    public static String LOGGING_TAG = "Logging";
 
     public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
     public static final boolean SYSTRACE_DEBUG = false; /* STOP SHIP if true */
@@ -214,43 +233,355 @@
     public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
 
     private static final Map<Call, CallEventRecord> mCallEventRecordMap = new HashMap<>();
-    private static final LinkedBlockingQueue<CallEventRecord> mCallEventRecords =
+    private static LinkedBlockingQueue<CallEventRecord> mCallEventRecords =
             new LinkedBlockingQueue<CallEventRecord>(MAX_CALLS_TO_CACHE);
 
-    private Log() {}
+    private static Context mContext = null;
+    // Synchronized in all method calls
+    private static int sCodeEntryCounter = 0;
+    @VisibleForTesting
+    public static ConcurrentHashMap<Integer, Session> sSessionMapper = new ConcurrentHashMap<>(100);
+    @VisibleForTesting
+    public static Handler sSessionCleanupHandler = new Handler(Looper.getMainLooper());
+    @VisibleForTesting
+    public static Runnable sCleanStaleSessions = new Runnable() {
+        @Override
+        public void run() {
+            cleanupStaleSessions(getSessionCleanupTimeoutMs());
+        }
+    };
+
+    // Set the logging container to be the system's. This will only change when being mocked
+    // during testing.
+    private static SystemLoggingContainer systemLogger = new SystemLoggingContainer();
+
+    /**
+     * Tracks whether user-activated extended logging is enabled.
+     */
+    private static boolean mIsUserExtendedLoggingEnabled = false;
+
+    /**
+     * The time when user-activated extended logging should be ended.  Used to determine when
+     * extended logging should automatically be disabled.
+     */
+    private static long mUserExtendedLoggingStopTime = 0;
+
+    private Log() {
+    }
+
+    public static void setContext(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Enable or disable extended telecom logging.
+     *
+     * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
+     *          {@code false} if it should be disabled.
+     */
+    public static void setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled) {
+        // If the state hasn't changed, bail early.
+        if (mIsUserExtendedLoggingEnabled == isExtendedLoggingEnabled) {
+            return;
+        }
+
+        // Resize the event queue.
+        int newSize = isExtendedLoggingEnabled ? MAX_CALLS_TO_CACHE_DEBUG : MAX_CALLS_TO_CACHE;
+        LinkedBlockingQueue<CallEventRecord> oldEventLog = mCallEventRecords;
+        mCallEventRecords = new LinkedBlockingQueue<CallEventRecord>(newSize);
+        mCallEventRecordMap.clear();
+
+        // Copy the existing queue into the new one.
+        for (CallEventRecord event : oldEventLog) {
+            addCallEventRecord(event);
+        }
+
+        mIsUserExtendedLoggingEnabled = isExtendedLoggingEnabled;
+        if (mIsUserExtendedLoggingEnabled) {
+            mUserExtendedLoggingStopTime = System.currentTimeMillis()
+                    + EXTENDED_LOGGING_DURATION_MILLIS;
+        } else {
+            mUserExtendedLoggingStopTime = 0;
+        }
+    }
 
     @VisibleForTesting
     public static void setTag(String tag) {
         TAG = tag;
     }
 
+    @VisibleForTesting
+    public static void setLoggingContainer(SystemLoggingContainer logger) {
+        systemLogger = logger;
+    }
+
+    // Overridden in LogTest to skip query to ContentProvider
+    public interface ISessionCleanupTimeoutMs {
+        long get();
+    }
+
+    @VisibleForTesting
+    public static ISessionCleanupTimeoutMs sSessionCleanupTimeoutMs =
+            new ISessionCleanupTimeoutMs() {
+                @Override
+                public long get() {
+                    return Timeouts.getStaleSessionCleanupTimeoutMillis(
+                            mContext.getContentResolver());
+                }
+            };
+
+    private static long getSessionCleanupTimeoutMs() {
+        return sSessionCleanupTimeoutMs.get();
+    }
+
+    private static synchronized void resetStaleSessionTimer() {
+        sSessionCleanupHandler.removeCallbacksAndMessages(null);
+        sSessionCleanupHandler.postDelayed(sCleanStaleSessions, getSessionCleanupTimeoutMs());
+    }
+
+    /**
+     * Call at an entry point to the Telecom code to track the session. This code must be
+     * accompanied by a Log.endSession().
+     */
+    public static synchronized void startSession(String shortMethodName) {
+        resetStaleSessionTimer();
+        int threadId = getCallingThreadId();
+        Session activeSession = sSessionMapper.get(threadId);
+        // We have called startSession within an active session that has not ended... Register this
+        // session as a subsession.
+        if (activeSession != null) {
+            Log.d(LOGGING_TAG, "Log.startSession was called with session active. Creating " +
+                    "a subsession...");
+            Session childSession = createSubsession();
+            continueSession(childSession, shortMethodName);
+            return;
+        }
+        Session newSession = new Session(getNextSessionID(), shortMethodName,
+                System.currentTimeMillis(), threadId);
+        sSessionMapper.put(threadId, newSession);
+
+        Log.i(LOGGING_TAG, Session.START_SESSION + " " + newSession.toString());
+    }
+
+
+    /**
+     * Notifies the logging system that a subsession will be run at a later point and
+     * allocates the resources. Returns a session object that must be used in
+     * Log.continueSession(...) to start the subsession.
+     */
+    public static synchronized Session createSubsession() {
+        int threadId = getCallingThreadId();
+        Session threadSession = sSessionMapper.get(threadId);
+        if (threadSession == null) {
+            Log.d(LOGGING_TAG, "Log.createSubsession was called with no session active.");
+            return null;
+        }
+        // Start execution time of the session will be overwritten in continueSession(...).
+        Session newSubsession = new Session(threadSession.getNextChildId(),
+                threadSession.getShortMethodName(), System.currentTimeMillis(), threadId);
+        threadSession.addChild(newSubsession);
+        newSubsession.setParentSession(threadSession);
+
+        Log.i(LOGGING_TAG, Session.CREATE_SUBSESSION + " " + newSubsession.toString());
+        return newSubsession;
+    }
+
+    /**
+     * Cancels a subsession that had Log.createSubsession() called on it, but will never have
+     * Log.continueSession(...) called on it due to an error. Allows the subsession to be cleaned
+     * gracefully instead of being removed by the sSessionCleanupHandler forcefully later.
+     */
+    public static synchronized void cancelSubsession(Session subsession) {
+        if (subsession == null) {
+            return;
+        }
+
+        subsession.markSessionCompleted(0);
+        endParentSessions(subsession);
+    }
+
+    /**
+     * Starts the subsession that was created in Log.CreateSubsession. The Log.endSession() method
+     * must be called at the end of this method. The full session will complete when all
+     * subsessions are completed.
+     */
+    public static synchronized void continueSession(Session subsession, String shortMethodName) {
+        if (subsession == null) {
+            return;
+        }
+        resetStaleSessionTimer();
+        String callingMethodName = subsession.getShortMethodName();
+        subsession.setShortMethodName(callingMethodName + "->" + shortMethodName);
+        subsession.setExecutionStartTimeMs(System.currentTimeMillis());
+        Session threadSession = subsession.getParentSession();
+        if (threadSession == null) {
+            Log.d(LOGGING_TAG, "Log.continueSession was called with no session active for " +
+                    "method %s.", shortMethodName);
+            return;
+        }
+
+        sSessionMapper.put(getCallingThreadId(), subsession);
+        Log.i(LOGGING_TAG, Session.CONTINUE_SUBSESSION + " " + subsession.toString());
+    }
+
+    public static void checkIsThreadLogged() {
+        int threadId = getCallingThreadId();
+        Session threadSession = sSessionMapper.get(threadId);
+        if (threadSession == null) {
+            android.util.Log.e(LOGGING_TAG, "Logging Thread Check Failed!", new Exception());
+        }
+    }
+
+    /**
+     * Ends the current session/subsession. Must be called after a Log.startSession(...) and
+     * Log.continueSession(...) call.
+     */
+    public static synchronized void endSession() {
+        int threadId = getCallingThreadId();
+        Session completedSession = sSessionMapper.get(threadId);
+        if (completedSession == null) {
+            Log.w(LOGGING_TAG, "Log.endSession was called with no session active.");
+            return;
+        }
+
+        completedSession.markSessionCompleted(System.currentTimeMillis());
+        Log.i(LOGGING_TAG, Session.END_SUBSESSION + " dur: " +
+                completedSession.getLocalExecutionTime() + " mS");
+        // Remove after completed so that reference still exists for logging the end events
+        Session parentSession = completedSession.getParentSession();
+        sSessionMapper.remove(threadId);
+        endParentSessions(completedSession);
+        // If this subsession was started from a parent session using Log.startSession, return the
+        // ThreadID back to the parent after completion.
+        if (parentSession != null && !parentSession.isSessionCompleted() &&
+                completedSession.getThreadId() == parentSession.getThreadId()) {
+            sSessionMapper.put(threadId, parentSession);
+        }
+    }
+
+    // Recursively deletes all complete parent sessions of the current subsession if it is a leaf.
+    private static void endParentSessions(Session subsession) {
+        // Session is not completed or not currently a leaf, so we can not remove because a child is
+        // still running
+        if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
+            return;
+        }
+
+        Session parentSession = subsession.getParentSession();
+        if (parentSession != null) {
+            subsession.setParentSession(null);
+            parentSession.removeChild(subsession);
+            endParentSessions(parentSession);
+        } else {
+            // All of the subsessions have been completed and it is time to report on the full
+            // running time of the session.
+            long fullSessionTimeMs =
+                    System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
+            Log.i(LOGGING_TAG, Session.END_SESSION + " " + subsession.toString() + " dur: " +
+                    fullSessionTimeMs + " ms");
+        }
+    }
+
+    private synchronized static String getNextSessionID() {
+        Integer nextId = sCodeEntryCounter++;
+        if (nextId >= SESSION_ID_ROLLOVER_THRESHOLD) {
+            restartSessionCounter();
+            nextId = sCodeEntryCounter++;
+        }
+        return getBase64Encoding(nextId);
+    }
+
+    @VisibleForTesting
+    public synchronized static void restartSessionCounter() {
+        sCodeEntryCounter = 0;
+    }
+
+    @VisibleForTesting
+    public static String getBase64Encoding(int number) {
+        byte[] idByteArray = ByteBuffer.allocate(4).putInt(number).array();
+        idByteArray = Arrays.copyOfRange(idByteArray, 2, 4);
+        return Base64.encodeToString(idByteArray, Base64.NO_WRAP | Base64.NO_PADDING);
+    }
+
+    public static int getCallingThreadId() {
+        return android.os.Process.myTid();
+    }
+
     public static void event(Call call, String event) {
         event(call, event, null);
     }
 
     public static void event(Call call, String event, Object data) {
+        Session currentSession = sSessionMapper.get(getCallingThreadId());
+        String currentSessionID = currentSession != null ? currentSession.toString() : "";
+
         if (call == null) {
             Log.i(TAG, "Non-call EVENT: %s, %s", event, data);
             return;
         }
         synchronized (mCallEventRecords) {
             if (!mCallEventRecordMap.containsKey(call)) {
-                // First remove the oldest entry if no new ones exist.
-                if (mCallEventRecords.remainingCapacity() == 0) {
-                    CallEventRecord record = mCallEventRecords.poll();
-                    if (record != null) {
-                        mCallEventRecordMap.remove(record.getCall());
-                    }
-                }
-
-                // Now add a new entry
                 CallEventRecord newRecord = new CallEventRecord(call);
-                mCallEventRecords.add(newRecord);
-                mCallEventRecordMap.put(call, newRecord);
+                addCallEventRecord(newRecord);
             }
 
             CallEventRecord record = mCallEventRecordMap.get(call);
-            record.addEvent(event, data);
+            record.addEvent(event, currentSessionID, data);
+        }
+    }
+
+    @VisibleForTesting
+    public static void cleanupStaleSessions(long timeoutMs) {
+        String logMessage = "Stale Sessions Cleaned:\n";
+        boolean isSessionsStale = false;
+        long currentTimeMs = System.currentTimeMillis();
+        // Remove references that are in the Session Mapper (causing GC to occur) on
+        // sessions that are lasting longer than LOGGING_SESSION_TIMEOUT_MS.
+        // If this occurs, then there is most likely a Session active that never had
+        // Log.endSession called on it.
+        for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it =
+             sSessionMapper.entrySet().iterator(); it.hasNext(); ) {
+            ConcurrentHashMap.Entry<Integer, Session> entry = it.next();
+            Session session = entry.getValue();
+            if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) {
+                it.remove();
+                logMessage += session.printFullSessionTree() + "\n";
+                isSessionsStale = true;
+            }
+        }
+        if (isSessionsStale) {
+            Log.w(LOGGING_TAG, logMessage);
+        }
+    }
+
+    private static void addCallEventRecord(CallEventRecord newRecord) {
+        Call call = newRecord.getCall();
+
+        // First remove the oldest entry if no new ones exist.
+        if (mCallEventRecords.remainingCapacity() == 0) {
+            CallEventRecord record = mCallEventRecords.poll();
+            if (record != null) {
+                mCallEventRecordMap.remove(record.getCall());
+            }
+        }
+
+        // Now add a new entry
+        mCallEventRecords.add(newRecord);
+        mCallEventRecordMap.put(call, newRecord);
+    }
+
+    /**
+     * If user enabled extended logging is enabled and the time limit has passed, disables the
+     * extended logging.
+     */
+    private static void maybeDisableLogging() {
+        if (!mIsUserExtendedLoggingEnabled) {
+            return;
+        }
+
+        if (mUserExtendedLoggingStopTime < System.currentTimeMillis()) {
+            mUserExtendedLoggingStopTime = 0;
+            mIsUserExtendedLoggingEnabled = false;
         }
     }
 
@@ -259,83 +590,95 @@
     }
 
     public static void d(String prefix, String format, Object... args) {
-        if (DEBUG) {
-            android.util.Slog.d(TAG, buildMessage(prefix, format, args));
+        if (mIsUserExtendedLoggingEnabled) {
+            maybeDisableLogging();
+            systemLogger.i(TAG, buildMessage(prefix, format, args));
+        } else if (DEBUG) {
+            systemLogger.d(TAG, buildMessage(prefix, format, args));
         }
     }
 
     public static void d(Object objectPrefix, String format, Object... args) {
-        if (DEBUG) {
-            android.util.Slog.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+        if (mIsUserExtendedLoggingEnabled) {
+            maybeDisableLogging();
+            systemLogger.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+        } else if (DEBUG) {
+            systemLogger.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
         }
     }
 
     public static void i(String prefix, String format, Object... args) {
         if (INFO) {
-            android.util.Slog.i(TAG, buildMessage(prefix, format, args));
+            systemLogger.i(TAG, buildMessage(prefix, format, args));
         }
     }
 
     public static void i(Object objectPrefix, String format, Object... args) {
         if (INFO) {
-            android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+            systemLogger.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
         }
     }
 
     public static void v(String prefix, String format, Object... args) {
-        if (VERBOSE) {
-            android.util.Slog.v(TAG, buildMessage(prefix, format, args));
+        if (mIsUserExtendedLoggingEnabled) {
+            maybeDisableLogging();
+            systemLogger.i(TAG, buildMessage(prefix, format, args));
+        } else if (VERBOSE) {
+            systemLogger.v(TAG, buildMessage(prefix, format, args));
         }
     }
 
     public static void v(Object objectPrefix, String format, Object... args) {
-        if (VERBOSE) {
-            android.util.Slog.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+        if (mIsUserExtendedLoggingEnabled) {
+            maybeDisableLogging();
+            systemLogger.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+        } else if (VERBOSE) {
+            systemLogger.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
         }
     }
 
     public static void w(String prefix, String format, Object... args) {
         if (WARN) {
-            android.util.Slog.w(TAG, buildMessage(prefix, format, args));
+            systemLogger.w(TAG, buildMessage(prefix, format, args));
         }
     }
 
     public static void w(Object objectPrefix, String format, Object... args) {
         if (WARN) {
-            android.util.Slog.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
+            systemLogger.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
         }
     }
 
     public static void e(String prefix, Throwable tr, String format, Object... args) {
         if (ERROR) {
-            android.util.Slog.e(TAG, buildMessage(prefix, format, args), tr);
+            systemLogger.e(TAG, buildMessage(prefix, format, args), tr);
         }
     }
 
     public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
         if (ERROR) {
-            android.util.Slog.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
+            systemLogger.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
                     tr);
         }
     }
 
     public static void wtf(String prefix, Throwable tr, String format, Object... args) {
-        android.util.Slog.wtf(TAG, buildMessage(prefix, format, args), tr);
+        systemLogger.wtf(TAG, buildMessage(prefix, format, args), tr);
     }
 
     public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
-        android.util.Slog.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
+        systemLogger.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
                 tr);
     }
 
     public static void wtf(String prefix, String format, Object... args) {
         String msg = buildMessage(prefix, format, args);
-        android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
+        systemLogger.wtf(TAG, msg, new IllegalStateException(msg));
     }
 
     public static void wtf(Object objectPrefix, String format, Object... args) {
         String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
-        android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
+        systemLogger.wtf(TAG, msg, new IllegalStateException(msg));
     }
 
     public static String piiHandle(Object pii) {
@@ -426,6 +769,15 @@
     }
 
     private static String buildMessage(String prefix, String format, Object... args) {
+        if (LOG_DBG) {
+            checkIsThreadLogged();
+        }
+        // Incorporate thread ID and calling method into prefix
+        Session currentSession = sSessionMapper.get(getCallingThreadId());
+        if (currentSession != null) {
+            prefix = prefix + " " + currentSession.toString();
+        }
+
         String msg;
         try {
             msg = (args == null || args.length == 0) ? format
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index 17ccdb1..d9c6c33 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -96,53 +96,60 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            Trace.beginSection("onReceiveNewOutgoingCallBroadcast");
-            Log.v(this, "onReceive: %s", intent);
+            try {
+                Log.startSession("NOCBIR.oR");
+                Trace.beginSection("onReceiveNewOutgoingCallBroadcast");
+                Log.v(this, "onReceive: %s", intent);
 
-            // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData is used as the
-            // actual number to call. (If null, no call will be placed.)
-            String resultNumber = getResultData();
-            Log.i(this, "Received new-outgoing-call-broadcast for %s with data %s", mCall,
-                    Log.pii(resultNumber));
+                // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData is used as the
+                // actual number to call. (If null, no call will be placed.)
+                String resultNumber = getResultData();
+                Log.i(this, "Received new-outgoing-call-broadcast for %s with data %s", mCall,
+                        Log.pii(resultNumber));
 
-            boolean endEarly = false;
-            if (resultNumber == null) {
-                Log.v(this, "Call cancelled (null number), returning...");
-                endEarly = true;
-            } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(mContext, resultNumber)) {
-                Log.w(this, "Cannot modify outgoing call to emergency number %s.", resultNumber);
-                endEarly = true;
-            }
-
-            if (endEarly) {
-                if (mCall != null) {
-                    mCall.disconnect(true /* wasViaNewOutgoingCall */);
+                boolean endEarly = false;
+                if (resultNumber == null) {
+                    Log.v(this, "Call cancelled (null number), returning...");
+                    endEarly = true;
+                } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(
+                        mContext, resultNumber)) {
+                    Log.w(this, "Cannot modify outgoing call to emergency number %s.",
+                            resultNumber);
+                    endEarly = true;
                 }
+
+                if (endEarly) {
+                    if (mCall != null) {
+                        mCall.disconnect(true /* wasViaNewOutgoingCall */);
+                    }
+                    return;
+                }
+
+                Uri resultHandleUri = Uri.fromParts(PhoneNumberUtils.isUriNumber(resultNumber) ?
+                        PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, resultNumber, null);
+
+                Uri originalUri = mIntent.getData();
+
+                if (originalUri.getSchemeSpecificPart().equals(resultNumber)) {
+                    Log.v(this, "Call number unmodified after new outgoing call intent broadcast.");
+                } else {
+                    Log.v(this, "Retrieved modified handle after outgoing call intent broadcast: "
+                                    + "Original: %s, Modified: %s",
+                            Log.pii(originalUri),
+                            Log.pii(resultHandleUri));
+                }
+
+                GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
+                mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
+                        mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
+                                false),
+                        mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+                                VideoProfile.STATE_AUDIO_ONLY));
+
+            } finally {
                 Trace.endSection();
-                return;
+                Log.endSession();
             }
-
-            Uri resultHandleUri = Uri.fromParts(PhoneNumberUtils.isUriNumber(resultNumber) ?
-                    PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, resultNumber, null);
-
-            Uri originalUri = mIntent.getData();
-
-            if (originalUri.getSchemeSpecificPart().equals(resultNumber)) {
-                Log.v(this, "Call number unmodified after new outgoing call intent broadcast.");
-            } else {
-                Log.v(this, "Retrieved modified handle after outgoing call intent broadcast: "
-                        + "Original: %s, Modified: %s",
-                        Log.pii(originalUri),
-                        Log.pii(resultHandleUri));
-            }
-
-            GatewayInfo gatewayInfo = getGateWayInfoFromIntent(intent, resultHandleUri);
-            mCallsManager.placeOutgoingCall(mCall, resultHandleUri, gatewayInfo,
-                    mIntent.getBooleanExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE,
-                            false),
-                    mIntent.getIntExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
-                            VideoProfile.STATE_AUDIO_ONLY));
-            Trace.endSection();
         }
     }
 
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index aab193e..709c044 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -16,7 +16,6 @@
 
 package com.android.server.telecom;
 
-import android.app.ActivityManager;
 import android.Manifest;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,7 +28,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.os.Binder;
+import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
@@ -74,7 +73,9 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -95,20 +96,16 @@
  * 3) The user running the app that is requesting the phone account information.
  *
  * For example, I have a device with 2 users, primary (A) and secondary (B), and the secondary user
- * has a work profile running as another user (B2). Lets say that user B opens the phone settings
- * (not currently supported, but theoretically speaking), and phone settings queries for a phone
- * account list. Lets also say that an app running in the work profile has registered a phone
- * account. This means that:
- *
- * Since phone settings always runs as the primary user, We have the following situation:
- * User A (settings) is requesting a list of phone accounts while the active user is User B, and
- * that list contains a phone account for profile User B2.
+ * has a work profile running as another user (B2). Each user/profile only have the visibility of
+ * phone accounts owned by them. Lets say, user B (settings) is requesting a list of phone accounts,
+ * and the list only contains phone accounts owned by user B and accounts with
+ * {@link PhoneAccount#CAPABILITY_MULTI_USER}.
  *
  * In practice, (2) is stored with the phone account handle and is part of the handle's ID. (1) is
  * saved in {@link #mCurrentUserHandle} and (3) we get from Binder.getCallingUser(). We check these
  * users for visibility before returning any phone accounts.
  */
-public final class PhoneAccountRegistrar {
+public class PhoneAccountRegistrar {
 
     public static final PhoneAccountHandle NO_ACCOUNT_SELECTED =
             new PhoneAccountHandle(new ComponentName("null", "null"), "NO_ACCOUNT_SELECTED");
@@ -121,7 +118,7 @@
 
     private static final String FILE_NAME = "phone-account-registrar-state.xml";
     @VisibleForTesting
-    public static final int EXPECTED_STATE_VERSION = 8;
+    public static final int EXPECTED_STATE_VERSION = 9;
 
     /** Keep in sync with the same in SipSettings.java */
     private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
@@ -168,7 +165,7 @@
      * @return The value of the subscription id or -1 if it does not exist or is not valid.
      */
     public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
-        PhoneAccount account = getPhoneAccountCheckCallingUser(accountHandle);
+        PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
 
         if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
             TelephonyManager tm =
@@ -186,19 +183,21 @@
      * @param uriScheme The URI scheme for the outgoing call.
      * @return The {@link PhoneAccountHandle} to use.
      */
-    public PhoneAccountHandle getOutgoingPhoneAccountForScheme(String uriScheme) {
-        final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount();
+    public PhoneAccountHandle getOutgoingPhoneAccountForScheme(String uriScheme,
+            UserHandle userHandle) {
+        final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount(userHandle);
 
         if (userSelected != null) {
             // If there is a default PhoneAccount, ensure it supports calls to handles with the
             // specified uriScheme.
-            final PhoneAccount userSelectedAccount = getPhoneAccountCheckCallingUser(userSelected);
+            final PhoneAccount userSelectedAccount = getPhoneAccountUnchecked(userSelected);
             if (userSelectedAccount.supportsUriScheme(uriScheme)) {
                 return userSelected;
             }
         }
 
-        List<PhoneAccountHandle> outgoing = getCallCapablePhoneAccounts(uriScheme, false);
+        List<PhoneAccountHandle> outgoing = getCallCapablePhoneAccounts(uriScheme, false,
+                userHandle);
         switch (outgoing.size()) {
             case 0:
                 // There are no accounts, so there can be no default
@@ -212,14 +211,28 @@
         }
     }
 
+    public PhoneAccountHandle getOutgoingPhoneAccountForSchemeOfCurrentUser(String uriScheme) {
+        return getOutgoingPhoneAccountForScheme(uriScheme, mCurrentUserHandle);
+    }
+
     /**
      * @return The user-selected outgoing {@link PhoneAccount}, or null if it hasn't been set (or
      *      if it was set by another user).
      */
-    PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
-        PhoneAccount account = getPhoneAccountCheckCallingUser(mState.defaultOutgoing);
+    PhoneAccountHandle getUserSelectedOutgoingPhoneAccount(UserHandle userHandle) {
+        if (userHandle == null) {
+            return null;
+        }
+        DefaultPhoneAccountHandle defaultPhoneAccountHandle = mState.defaultOutgoingAccountHandles
+                .get(userHandle);
+        if (defaultPhoneAccountHandle == null) {
+            return null;
+        }
+        // Make sure the account is still registered and owned by the user.
+        PhoneAccount account = getPhoneAccount(defaultPhoneAccountHandle.phoneAccountHandle,
+                userHandle);
         if (account != null) {
-            return mState.defaultOutgoing;
+            return defaultPhoneAccountHandle.phoneAccountHandle;
         }
         return null;
     }
@@ -228,13 +241,16 @@
      * Sets the phone account with which to place all calls by default. Set by the user
      * within phone settings.
      */
-    public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+    public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle,
+            UserHandle userHandle) {
+        if (userHandle == null) {
+            return;
+        }
         if (accountHandle == null) {
             // Asking to clear the default outgoing is a valid request
-            mState.defaultOutgoing = null;
+            mState.defaultOutgoingAccountHandles.remove(userHandle);
         } else {
-            // TODO: Do we really want to return for *any* user?
-            PhoneAccount account = getPhoneAccount(accountHandle);
+            PhoneAccount account = getPhoneAccount(accountHandle, userHandle);
             if (account == null) {
                 Log.w(this, "Trying to set nonexistent default outgoing %s",
                         accountHandle);
@@ -254,7 +270,8 @@
                 mSubscriptionManager.setDefaultVoiceSubId(subId);
             }
 
-            mState.defaultOutgoing = accountHandle;
+            mState.defaultOutgoingAccountHandles
+                    .put(userHandle, new DefaultPhoneAccountHandle(userHandle, accountHandle));
         }
 
         write();
@@ -262,28 +279,8 @@
     }
 
     boolean isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle) {
-        return getSubscriptionIdForPhoneAccount(accountHandle) ==
-                SubscriptionManager.getDefaultSmsSubId();
-    }
-
-    /**
-     * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call
-     * Manager. SIM Call Manager returned corresponds to the following priority order:
-     * 1. If a SIM Call Manager {@link PhoneAccount} is registered for the same package as the
-     * default dialer, then that one is returned.
-     * 2. If there is a SIM Call Manager {@link PhoneAccount} registered which matches the
-     * carrier configuration's default, then that one is returned.
-     * 3. Otherwise, we return null.
-     */
-    public PhoneAccountHandle getSimCallManager() {
-        long token = Binder.clearCallingIdentity();
-        int user;
-        try {
-            user = ActivityManager.getCurrentUser();
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return getSimCallManager(user);
+        return getSubscriptionIdForPhoneAccount(accountHandle) == SubscriptionManager
+                .getDefaultSmsSubId();
     }
 
     public ComponentName getSystemSimCallManagerComponent() {
@@ -299,6 +296,10 @@
             ?  null : ComponentName.unflattenFromString(defaultSimCallManager);
     }
 
+    public PhoneAccountHandle getSimCallManagerOfCurrentUser() {
+        return getSimCallManager(mCurrentUserHandle);
+    }
+
     /**
      * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call
      * Manager. SIM Call Manager returned corresponds to the following priority order:
@@ -308,9 +309,10 @@
      * carrier configuration's default, then that one is returned.
      * 3. Otherwise, we return null.
      */
-    public PhoneAccountHandle getSimCallManager(int user) {
+    public PhoneAccountHandle getSimCallManager(UserHandle userHandle) {
         // Get the default dialer in case it has a connection manager associated with it.
-        String dialerPackage = DefaultDialerManager.getDefaultDialerApplication(mContext, user);
+        String dialerPackage = DefaultDialerManager
+                .getDefaultDialerApplication(mContext, userHandle.getIdentifier());
 
         // Check carrier config.
         ComponentName systemSimCallManagerComponent = getSystemSimCallManagerComponent();
@@ -322,7 +324,7 @@
             // loop through and look for any connection manager in the same package.
             List<PhoneAccountHandle> allSimCallManagers = getPhoneAccountHandles(
                     PhoneAccount.CAPABILITY_CONNECTION_MANAGER, null, null,
-                    true /* includeDisabledAccounts */);
+                    true /* includeDisabledAccounts */, userHandle);
             for (PhoneAccountHandle accountHandle : allSimCallManagers) {
                 ComponentName component = accountHandle.getComponentName();
 
@@ -350,6 +352,23 @@
     }
 
     /**
+     * If it is a outgoing call, sim call manager of call-initiating user is returned.
+     * Otherwise, we return the sim call manager of the user associated with the
+     * target phone account.
+     * @return phone account handle of sim call manager based on the ongoing call.
+     */
+    public PhoneAccountHandle getSimCallManagerFromCall(Call call) {
+        if (call == null) {
+            return null;
+        }
+        UserHandle userHandle = call.getInitiatingUser();
+        if (userHandle == null) {
+            userHandle = call.getTargetPhoneAccount().getUserHandle();
+        }
+        return getSimCallManager(userHandle);
+    }
+
+    /**
      * Update the current UserHandle to track when users are switched. This will allow the
      * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything
      * across users.
@@ -373,7 +392,7 @@
      *         otherwise.
      */
     public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) {
-        PhoneAccount account = getPhoneAccount(accountHandle);
+        PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
         if (account == null) {
             Log.w(this, "Could not find account to enable: " + accountHandle);
             return false;
@@ -391,11 +410,17 @@
         return true;
     }
 
-    private boolean isVisibleForUser(PhoneAccount account) {
+    private boolean isVisibleForUser(PhoneAccount account, UserHandle userHandle,
+            boolean acrossProfiles) {
         if (account == null) {
             return false;
         }
 
+        if (userHandle == null) {
+            Log.w(this, "userHandle is null in isVisibleForUser");
+            return false;
+        }
+
         // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and
         // all profiles. Only Telephony and SIP accounts should have this capability.
         if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
@@ -408,34 +433,22 @@
         }
 
         if (mCurrentUserHandle == null) {
+            // In case we need to have emergency phone calls from the lock screen.
             Log.d(this, "Current user is null; assuming true");
             return true;
         }
 
-        if (phoneAccountUserHandle.equals(Binder.getCallingUserHandle())) {
-            return true;
+        if (acrossProfiles) {
+            return UserManager.get(mContext).isSameProfileGroup(userHandle.getIdentifier(),
+                    phoneAccountUserHandle.getIdentifier());
+        } else {
+            return phoneAccountUserHandle.equals(userHandle);
         }
-
-        // Special check for work profiles.
-        // Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure
-        // that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is
-        // fine.
-        if (UserHandle.getCallingUserId() == UserHandle.USER_OWNER) {
-            List<UserInfo> profileUsers =
-                    mUserManager.getProfiles(mCurrentUserHandle.getIdentifier());
-            for (UserInfo profileInfo : profileUsers) {
-                if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
     }
 
     private List<ResolveInfo> resolveComponent(PhoneAccountHandle phoneAccountHandle) {
         return resolveComponent(phoneAccountHandle.getComponentName(),
-                    phoneAccountHandle.getUserHandle());
+                phoneAccountHandle.getUserHandle());
     }
 
     private List<ResolveInfo> resolveComponent(ComponentName componentName,
@@ -461,12 +474,16 @@
      *
      * @return The list of {@link PhoneAccountHandle}s.
      */
-    public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
-        return getPhoneAccountHandles(0, null, null, false);
+    public List<PhoneAccountHandle> getAllPhoneAccountHandles(UserHandle userHandle) {
+        return getPhoneAccountHandles(0, null, null, false, userHandle);
     }
 
-    public List<PhoneAccount> getAllPhoneAccounts() {
-        return getPhoneAccounts(0, null, null, false);
+    public List<PhoneAccount> getAllPhoneAccounts(UserHandle userHandle) {
+        return getPhoneAccounts(0, null, null, false, userHandle);
+    }
+
+    public List<PhoneAccount> getAllPhoneAccountsOfCurrentUser() {
+        return getAllPhoneAccounts(mCurrentUserHandle);
     }
 
     /**
@@ -477,30 +494,40 @@
      * @return The phone account handles.
      */
     public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
-            String uriScheme, boolean includeDisabledAccounts) {
+            String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle) {
         return getPhoneAccountHandles(
                 PhoneAccount.CAPABILITY_CALL_PROVIDER,
                 PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY /*excludedCapabilities*/,
-                uriScheme, null, includeDisabledAccounts);
+                uriScheme, null, includeDisabledAccounts, userHandle);
+    }
+
+    public List<PhoneAccountHandle> getCallCapablePhoneAccountsOfCurrentUser(
+            String uriScheme, boolean includeDisabledAccounts) {
+        return getCallCapablePhoneAccounts(uriScheme, includeDisabledAccounts, mCurrentUserHandle);
     }
 
     /**
      * Retrieves a list of all the SIM-based phone accounts.
      */
-    public List<PhoneAccountHandle> getSimPhoneAccounts() {
+    public List<PhoneAccountHandle> getSimPhoneAccounts(UserHandle userHandle) {
         return getPhoneAccountHandles(
                 PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION,
-                null, null, false);
+                null, null, false, userHandle);
     }
 
-    /**
-     * Retrieves a list of all phone accounts registered by a specified package.
-     *
-     * @param packageName The name of the package that registered the phone accounts.
-     * @return The phone account handles.
-     */
-    public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
-        return getPhoneAccountHandles(0, null, packageName, false);
+    public List<PhoneAccountHandle> getSimPhoneAccountsOfCurrentUser() {
+        return getSimPhoneAccounts(mCurrentUserHandle);
+    }
+
+        /**
+         * Retrieves a list of all phone accounts registered by a specified package.
+         *
+         * @param packageName The name of the package that registered the phone accounts.
+         * @return The phone account handles.
+         */
+    public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName,
+            UserHandle userHandle) {
+        return getPhoneAccountHandles(0, null, packageName, false, userHandle);
     }
 
     // TODO: Should we implement an artificial limit for # of accounts associated with a single
@@ -533,7 +560,7 @@
         // source app provides or else an third party app could enable itself.
         boolean isEnabled = false;
 
-        PhoneAccount oldAccount = getPhoneAccount(account.getAccountHandle());
+        PhoneAccount oldAccount = getPhoneAccountUnchecked(account.getAccountHandle());
         if (oldAccount != null) {
             mState.accounts.remove(oldAccount);
             isEnabled = oldAccount.isEnabled();
@@ -553,7 +580,7 @@
     }
 
     public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
-        PhoneAccount account = getPhoneAccount(accountHandle);
+        PhoneAccount account = getPhoneAccountUnchecked(accountHandle);
         if (account != null) {
             if (mState.accounts.remove(account)) {
                 write();
@@ -696,7 +723,7 @@
      * @param handle
      * @return The corresponding phone account if one exists.
      */
-    PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
+    public PhoneAccount getPhoneAccountUnchecked(PhoneAccountHandle handle) {
         for (PhoneAccount m : mState.accounts) {
             if (Objects.equals(handle, m.getAccountHandle())) {
                 return m;
@@ -710,21 +737,31 @@
      * account before returning it. The current user is the active user on the actual android
      * device.
      */
-    public PhoneAccount getPhoneAccountCheckCallingUser(PhoneAccountHandle handle) {
-        PhoneAccount account = getPhoneAccount(handle);
-        if (account != null && isVisibleForUser(account)) {
+    public PhoneAccount getPhoneAccount(PhoneAccountHandle handle, UserHandle userHandle) {
+        return getPhoneAccount(handle, userHandle, /* acrossProfiles */ false);
+    }
+
+    public PhoneAccount getPhoneAccount(PhoneAccountHandle handle,
+            UserHandle userHandle, boolean acrossProfiles) {
+        PhoneAccount account = getPhoneAccountUnchecked(handle);
+        if (account != null && (isVisibleForUser(account, userHandle, acrossProfiles))) {
             return account;
         }
         return null;
     }
 
+    public PhoneAccount getPhoneAccountOfCurrentUser(PhoneAccountHandle handle) {
+        return getPhoneAccount(handle, mCurrentUserHandle);
+    }
+
     private List<PhoneAccountHandle> getPhoneAccountHandles(
             int capabilities,
             String uriScheme,
             String packageName,
-            boolean includeDisabledAccounts) {
+            boolean includeDisabledAccounts,
+            UserHandle userHandle) {
         return getPhoneAccountHandles(capabilities, 0 /*excludedCapabilities*/, uriScheme,
-                packageName, includeDisabledAccounts);
+                packageName, includeDisabledAccounts, userHandle);
     }
 
     /**
@@ -736,12 +773,13 @@
             int excludedCapabilities,
             String uriScheme,
             String packageName,
-            boolean includeDisabledAccounts) {
+            boolean includeDisabledAccounts,
+            UserHandle userHandle) {
         List<PhoneAccountHandle> handles = new ArrayList<>();
 
         for (PhoneAccount account : getPhoneAccounts(
                 capabilities, excludedCapabilities, uriScheme, packageName,
-                includeDisabledAccounts)) {
+                includeDisabledAccounts, userHandle)) {
             handles.add(account.getAccountHandle());
         }
         return handles;
@@ -751,9 +789,10 @@
             int capabilities,
             String uriScheme,
             String packageName,
-            boolean includeDisabledAccounts) {
+            boolean includeDisabledAccounts,
+            UserHandle userHandle) {
         return getPhoneAccounts(capabilities, 0 /*excludedCapabilities*/, uriScheme, packageName,
-                includeDisabledAccounts);
+                includeDisabledAccounts, userHandle);
     }
 
     /**
@@ -772,7 +811,8 @@
             int excludedCapabilities,
             String uriScheme,
             String packageName,
-            boolean includeDisabledAccounts) {
+            boolean includeDisabledAccounts,
+            UserHandle userHandle) {
         List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
         for (PhoneAccount m : mState.accounts) {
             if (!(m.isEnabled() || includeDisabledAccounts)) {
@@ -804,7 +844,7 @@
                 // Not the right package name; skip this one.
                 continue;
             }
-            if (!isVisibleForUser(m)) {
+            if (!isVisibleForUser(m, userHandle, false)) {
                 // Account is not visible for the current user; skip this one.
                 continue;
             }
@@ -823,10 +863,11 @@
     @VisibleForTesting
     public static class State {
         /**
-         * The account selected by the user to be employed by default for making outgoing calls.
-         * If the user has not made such a selection, then this is null.
+         * Store the default phone account handle of users. If no record of a user can be found in
+         * the map, it means that no default phone account handle is set in that user.
          */
-        public PhoneAccountHandle defaultOutgoing = null;
+        public final Map<UserHandle, DefaultPhoneAccountHandle> defaultOutgoingAccountHandles
+                = new ConcurrentHashMap<>();
 
         /**
          * The complete list of {@code PhoneAccount}s known to the Telecom subsystem.
@@ -840,6 +881,22 @@
     }
 
     /**
+     * The default {@link PhoneAccountHandle} of a user.
+     */
+    public static class DefaultPhoneAccountHandle {
+
+        public final UserHandle userHandle;
+
+        public final PhoneAccountHandle phoneAccountHandle;
+
+        public DefaultPhoneAccountHandle(UserHandle userHandle,
+                PhoneAccountHandle phoneAccountHandle) {
+            this.userHandle = userHandle;
+            this.phoneAccountHandle = phoneAccountHandle;
+        }
+    }
+
+    /**
      * Dumps the state of the {@link CallsManager}.
      *
      * @param pw The {@code IndentingPrintWriter} to write the state to.
@@ -847,9 +904,11 @@
     public void dump(IndentingPrintWriter pw) {
         if (mState != null) {
             pw.println("xmlVersion: " + mState.versionNumber);
-            pw.println("defaultOutgoing: " + (mState.defaultOutgoing == null ? "none" :
-                    mState.defaultOutgoing));
-            pw.println("simCallManager: " + getSimCallManager());
+            DefaultPhoneAccountHandle defaultPhoneAccountHandle
+                    = mState.defaultOutgoingAccountHandles.get(Process.myUserHandle());
+            pw.println("defaultOutgoing: " + (defaultPhoneAccountHandle == null ? "none" :
+                    defaultPhoneAccountHandle.phoneAccountHandle));
+            pw.println("simCallManager: " + getSimCallManager(mCurrentUserHandle));
             pw.println("phoneAccounts:");
             pw.increaseIndent();
             for (PhoneAccount phoneAccount : mState.accounts) {
@@ -954,8 +1013,13 @@
 
     @VisibleForTesting
     public abstract static class XmlSerialization<T> {
-        private static final String LENGTH_ATTRIBUTE = "length";
-        private static final String VALUE_TAG = "value";
+        private static final String TAG_VALUE = "value";
+        private static final String ATTRIBUTE_LENGTH = "length";
+        private static final String ATTRIBUTE_KEY = "key";
+        private static final String ATTRIBUTE_VALUE_TYPE = "type";
+        private static final String VALUE_TYPE_STRING = "string";
+        private static final String VALUE_TYPE_INTEGER = "integer";
+        private static final String VALUE_TYPE_BOOLEAN = "boolean";
 
         /**
          * Write the supplied object to XML
@@ -996,16 +1060,51 @@
 
             serializer.startTag(null, tagName);
             if (values != null) {
-                serializer.attribute(null, LENGTH_ATTRIBUTE, Objects.toString(values.size()));
+                serializer.attribute(null, ATTRIBUTE_LENGTH, Objects.toString(values.size()));
                 for (String toSerialize : values) {
-                    serializer.startTag(null, VALUE_TAG);
+                    serializer.startTag(null, TAG_VALUE);
                     if (toSerialize != null ){
                         serializer.text(toSerialize);
                     }
-                    serializer.endTag(null, VALUE_TAG);
+                    serializer.endTag(null, TAG_VALUE);
                 }
             } else {
-                serializer.attribute(null, LENGTH_ATTRIBUTE, "0");
+                serializer.attribute(null, ATTRIBUTE_LENGTH, "0");
+            }
+            serializer.endTag(null, tagName);
+        }
+
+        protected void writeBundle(String tagName, Bundle values, XmlSerializer serializer)
+            throws IOException {
+
+            serializer.startTag(null, tagName);
+            if (values != null) {
+                for (String key : values.keySet()) {
+                    Object value = values.get(key);
+
+                    if (value == null) {
+                        continue;
+                    }
+
+                    String valueType;
+                    if (value instanceof String) {
+                        valueType = VALUE_TYPE_STRING;
+                    } else if (value instanceof Integer) {
+                        valueType = VALUE_TYPE_INTEGER;
+                    } else if (value instanceof Boolean) {
+                        valueType = VALUE_TYPE_BOOLEAN;
+                    } else {
+                        Log.w(this,
+                                "PhoneAccounts support only string, integer and boolean extras TY.");
+                        continue;
+                    }
+
+                    serializer.startTag(null, TAG_VALUE);
+                    serializer.attribute(null, ATTRIBUTE_KEY, key);
+                    serializer.attribute(null, ATTRIBUTE_VALUE_TYPE, valueType);
+                    serializer.text(Objects.toString(value));
+                    serializer.endTag(null, TAG_VALUE);
+                }
             }
             serializer.endTag(null, tagName);
         }
@@ -1042,7 +1141,7 @@
         protected List<String> readStringList(XmlPullParser parser)
                 throws IOException, XmlPullParserException {
 
-            int length = Integer.parseInt(parser.getAttributeValue(null, LENGTH_ATTRIBUTE));
+            int length = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_LENGTH));
             List<String> arrayEntries = new ArrayList<String>(length);
             String value = null;
 
@@ -1052,7 +1151,7 @@
 
             int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
-                if (parser.getName().equals(VALUE_TAG)) {
+                if (parser.getName().equals(TAG_VALUE)) {
                     parser.next();
                     value = parser.getText();
                     arrayEntries.add(value);
@@ -1062,6 +1161,55 @@
             return arrayEntries;
         }
 
+        /**
+         * Reads a bundle from the XML parser.
+         *
+         * @param parser The XML parser.
+         * @return Bundle containing the parsed values.
+         * @throws IOException Exception related to IO.
+         * @throws XmlPullParserException Exception related to parsing.
+         */
+        protected Bundle readBundle(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+
+            Bundle bundle = null;
+            int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                if (parser.getName().equals(TAG_VALUE)) {
+                    String valueType = parser.getAttributeValue(null, ATTRIBUTE_VALUE_TYPE);
+                    String key = parser.getAttributeValue(null, ATTRIBUTE_KEY);
+                    parser.next();
+                    String value = parser.getText();
+
+                    if (bundle == null) {
+                        bundle = new Bundle();
+                    }
+
+                    // Do not write null values to the bundle.
+                    if (value == null) {
+                        continue;
+                    }
+
+                    if (VALUE_TYPE_STRING.equals(valueType)) {
+                        bundle.putString(key, value);
+                    } else if (VALUE_TYPE_INTEGER.equals(valueType)) {
+                        try {
+                            int intValue = Integer.parseInt(value);
+                            bundle.putInt(key, intValue);
+                        } catch (NumberFormatException nfe) {
+                            Log.w(this, "Invalid integer PhoneAccount extra.");
+                        }
+                    } else if (VALUE_TYPE_BOOLEAN.equals(valueType)) {
+                        boolean boolValue = Boolean.parseBoolean(value);
+                        bundle.putBoolean(key, boolValue);
+                    } else {
+                        Log.w(this, "Invalid type " + valueType + " for PhoneAccount bundle.");
+                    }
+                }
+            }
+            return bundle;
+        }
+
         protected Bitmap readBitmap(XmlPullParser parser) {
             byte[] imageByteArray = Base64.decode(parser.getText(), 0);
             return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length);
@@ -1089,11 +1237,13 @@
                 serializer.startTag(null, CLASS_STATE);
                 serializer.attribute(null, VERSION, Objects.toString(EXPECTED_STATE_VERSION));
 
-                if (o.defaultOutgoing != null) {
-                    serializer.startTag(null, DEFAULT_OUTGOING);
-                    sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer, context);
-                    serializer.endTag(null, DEFAULT_OUTGOING);
+                serializer.startTag(null, DEFAULT_OUTGOING);
+                for (DefaultPhoneAccountHandle defaultPhoneAccountHandle : o
+                        .defaultOutgoingAccountHandles.values()) {
+                    sDefaultPhoneAcountHandleXml
+                            .writeToXml(defaultPhoneAccountHandle, serializer, context);
                 }
+                serializer.endTag(null, DEFAULT_OUTGOING);
 
                 serializer.startTag(null, ACCOUNTS);
                 for (PhoneAccount m : o.accounts) {
@@ -1112,15 +1262,39 @@
                 State s = new State();
 
                 String rawVersion = parser.getAttributeValue(null, VERSION);
-                s.versionNumber = TextUtils.isEmpty(rawVersion) ? 1 :
-                        Integer.parseInt(rawVersion);
+                s.versionNumber = TextUtils.isEmpty(rawVersion) ? 1 : Integer.parseInt(rawVersion);
 
                 int outerDepth = parser.getDepth();
                 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                     if (parser.getName().equals(DEFAULT_OUTGOING)) {
-                        parser.nextTag();
-                        s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser,
-                                s.versionNumber, context);
+                        if (s.versionNumber < 9) {
+                            // Migration old default phone account handle here by assuming the
+                            // default phone account handle is belong to primary user.
+                            parser.nextTag();
+                            PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleXml
+                                    .readFromXml(parser, s.versionNumber, context);
+                            UserManager userManager = UserManager.get(context);
+                            UserInfo primaryUser = userManager.getPrimaryUser();
+                            if (primaryUser != null) {
+                                UserHandle userHandle = primaryUser.getUserHandle();
+                                DefaultPhoneAccountHandle defaultPhoneAccountHandle
+                                        = new DefaultPhoneAccountHandle(userHandle,
+                                        phoneAccountHandle);
+                                s.defaultOutgoingAccountHandles
+                                        .put(userHandle, defaultPhoneAccountHandle);
+                            }
+                        } else {
+                            int defaultAccountHandlesDepth = parser.getDepth();
+                            while (XmlUtils.nextElementWithin(parser, defaultAccountHandlesDepth)) {
+                                DefaultPhoneAccountHandle accountHandle
+                                        = sDefaultPhoneAcountHandleXml
+                                        .readFromXml(parser, s.versionNumber, context);
+                                if (accountHandle != null && s.accounts != null) {
+                                    s.defaultOutgoingAccountHandles
+                                            .put(accountHandle.userHandle, accountHandle);
+                                }
+                            }
+                        }
                     } else if (parser.getName().equals(ACCOUNTS)) {
                         int accountsDepth = parser.getDepth();
                         while (XmlUtils.nextElementWithin(parser, accountsDepth)) {
@@ -1140,6 +1314,70 @@
     };
 
     @VisibleForTesting
+    public static final XmlSerialization<DefaultPhoneAccountHandle> sDefaultPhoneAcountHandleXml  =
+            new XmlSerialization<DefaultPhoneAccountHandle>() {
+                private static final String CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE
+                        = "default_outgoing_phone_account_handle";
+                private static final String USER_SERIAL_NUMBER = "user_serial_number";
+                private static final String ACCOUNT_HANDLE = "account_handle";
+
+                @Override
+                public void writeToXml(DefaultPhoneAccountHandle o, XmlSerializer serializer,
+                        Context context) throws IOException {
+                    if (o != null) {
+                        final UserManager userManager = UserManager.get(context);
+                        final long serialNumber = userManager.getSerialNumberForUser(o.userHandle);
+                        if (serialNumber != -1) {
+                            serializer.startTag(null, CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE);
+                            writeLong(USER_SERIAL_NUMBER, serialNumber, serializer);
+                            serializer.startTag(null, ACCOUNT_HANDLE);
+                            sPhoneAccountHandleXml.writeToXml(o.phoneAccountHandle, serializer,
+                                    context);
+                            serializer.endTag(null, ACCOUNT_HANDLE);
+                            serializer.endTag(null, CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE);
+                        }
+                    }
+                }
+
+                @Override
+                public DefaultPhoneAccountHandle readFromXml(XmlPullParser parser, int version,
+                        Context context)
+                        throws IOException, XmlPullParserException {
+                    if (parser.getName().equals(CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE)) {
+                        int outerDepth = parser.getDepth();
+                        PhoneAccountHandle accountHandle = null;
+                        String userSerialNumberString = null;
+                        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                            if (parser.getName().equals(ACCOUNT_HANDLE)) {
+                                parser.nextTag();
+                                accountHandle = sPhoneAccountHandleXml.readFromXml(parser, version,
+                                        context);
+                            } else if (parser.getName().equals(USER_SERIAL_NUMBER)) {
+                                parser.next();
+                                userSerialNumberString = parser.getText();
+                            }
+                        }
+                        UserHandle userHandle = null;
+                        if (userSerialNumberString != null) {
+                            try {
+                                long serialNumber = Long.parseLong(userSerialNumberString);
+                                userHandle = UserManager.get(context)
+                                        .getUserForSerialNumber(serialNumber);
+                            } catch (NumberFormatException e) {
+                                Log.e(this, e,
+                                        "Could not parse UserHandle " + userSerialNumberString);
+                            }
+                        }
+                        if (accountHandle != null && userHandle != null) {
+                            return new DefaultPhoneAccountHandle(userHandle, accountHandle);
+                        }
+                    }
+                    return null;
+                }
+            };
+
+
+    @VisibleForTesting
     public static final XmlSerialization<PhoneAccount> sPhoneAccountXml =
             new XmlSerialization<PhoneAccount>() {
         private static final String CLASS_PHONE_ACCOUNT = "phone_account";
@@ -1156,6 +1394,7 @@
         private static final String SHORT_DESCRIPTION = "short_description";
         private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes";
         private static final String ICON = "icon";
+        private static final String EXTRAS = "extras";
         private static final String ENABLED = "enabled";
 
         @Override
@@ -1179,6 +1418,7 @@
                 writeTextIfNonNull(LABEL, o.getLabel(), serializer);
                 writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer);
                 writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer);
+                writeBundle(EXTRAS, o.getExtras(), serializer);
                 writeTextIfNonNull(ENABLED, o.isEnabled() ? "true" : "false" , serializer);
 
                 serializer.endTag(null, CLASS_PHONE_ACCOUNT);
@@ -1203,6 +1443,7 @@
                 List<String> supportedUriSchemes = null;
                 Icon icon = null;
                 boolean enabled = false;
+                Bundle extras = null;
 
                 while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                     if (parser.getName().equals(ACCOUNT_HANDLE)) {
@@ -1248,6 +1489,9 @@
                     } else if (parser.getName().equals(ENABLED)) {
                         parser.next();
                         enabled = "true".equalsIgnoreCase(parser.getText());
+                    } else if (parser.getName().equals(EXTRAS)) {
+                        parser.next();
+                        extras = readBundle(parser);
                     }
                 }
 
@@ -1313,6 +1557,7 @@
                         .setShortDescription(shortDescription)
                         .setSupportedUriSchemes(supportedUriSchemes)
                         .setHighlightColor(highlightColor)
+                        .setExtras(extras)
                         .setIsEnabled(enabled);
 
                 if (icon != null) {
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index b57090d..da22e98 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -23,9 +23,9 @@
 import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.SystemVibrator;
 import android.os.Vibrator;
-import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.LinkedList;
 import java.util.List;
@@ -34,7 +34,8 @@
  * Controls the ringtone player.
  * TODO: Turn this into a proper state machine: Ringing, CallWaiting, Stopped.
  */
-final class Ringer extends CallsManagerListenerBase {
+@VisibleForTesting
+public final class Ringer extends CallsManagerListenerBase {
     private static final long[] VIBRATION_PATTERN = new long[] {
         0, // No delay before starting
         1000, // How long to vibrate
@@ -53,22 +54,23 @@
     /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
     private static final int VIBRATION_PATTERN_REPEAT = 1;
 
-    private final AsyncRingtonePlayer mRingtonePlayer;
-
     /**
      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
      */
     private final List<Call> mRingingCalls = new LinkedList<>();
 
+    private final SystemSettingsUtil mSystemSettingsUtil;
     private final CallAudioManager mCallAudioManager;
     private final CallsManager mCallsManager;
     private final InCallTonePlayer.Factory mPlayerFactory;
+    private final AsyncRingtonePlayer mRingtonePlayer;
     private final Context mContext;
     private final Vibrator mVibrator;
 
     private int mState = STATE_STOPPED;
     private InCallTonePlayer mCallWaitingPlayer;
+    private RingtoneFactory mRingtoneFactory;
 
     /**
      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
@@ -76,20 +78,27 @@
     private boolean mIsVibrating = false;
 
     /** Initializes the Ringer. */
-    Ringer(
+    @VisibleForTesting
+    public Ringer(
             CallAudioManager callAudioManager,
             CallsManager callsManager,
             InCallTonePlayer.Factory playerFactory,
-            Context context) {
+            Context context,
+            SystemSettingsUtil systemSettingsUtil,
+            AsyncRingtonePlayer asyncRingtonePlayer,
+            RingtoneFactory ringtoneFactory,
+            Vibrator vibrator) {
 
+        mSystemSettingsUtil = systemSettingsUtil;
         mCallAudioManager = callAudioManager;
         mCallsManager = callsManager;
         mPlayerFactory = playerFactory;
         mContext = context;
         // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
         // vibrator object will be isolated from others.
-        mVibrator = new SystemVibrator(context);
-        mRingtonePlayer = new AsyncRingtonePlayer(context);
+        mVibrator = vibrator;
+        mRingtonePlayer = asyncRingtonePlayer;
+        mRingtoneFactory = ringtoneFactory;
     }
 
     @Override
@@ -185,8 +194,7 @@
         Call foregroundCall = mCallsManager.getForegroundCall();
         Log.v(this, "startRingingOrCallWaiting, foregroundCall: %s.", foregroundCall);
 
-        if (Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON,
-                0) == 1) {
+        if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
             return;
         }
 
@@ -211,7 +219,8 @@
                 // call (for the purposes of direct-to-voicemail), the information about custom
                 // 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(foregroundCall.getRingtone());
+                mRingtonePlayer.play(
+                        mRingtoneFactory.getRingtone(foregroundCall.getRingtone()));
             } else {
                 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
             }
@@ -300,7 +309,6 @@
         if (!mVibrator.hasVibrator()) {
             return false;
         }
-        return Settings.System.getInt(context.getContentResolver(),
-                Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
+        return mSystemSettingsUtil.canVibrateWhenRinging(context);
     }
 }
diff --git a/src/com/android/server/telecom/RingtoneFactory.java b/src/com/android/server/telecom/RingtoneFactory.java
new file mode 100644
index 0000000..5044a90
--- /dev/null
+++ b/src/com/android/server/telecom/RingtoneFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.RingtoneManager;
+import android.media.Ringtone;
+import android.net.Uri;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Uses a Uri to obtain a {@link Ringtone} from the {@link RingtoneManager} that can be played
+ * by the system during an incoming call.
+ */
+@VisibleForTesting
+public class RingtoneFactory {
+
+    private final Context mContext;
+
+    public RingtoneFactory(Context context) {
+        mContext = context;
+    }
+
+    public Ringtone getRingtone(Uri ringtoneUri) {
+        if (ringtoneUri == null) {
+            ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
+        }
+
+        Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri);
+        if (ringtone != null) {
+            ringtone.setStreamType(AudioManager.STREAM_RING);
+        }
+        return ringtone;
+    }
+}
diff --git a/src/com/android/server/telecom/ServiceBinder.java b/src/com/android/server/telecom/ServiceBinder.java
index 2e63512..18d6581 100644
--- a/src/com/android/server/telecom/ServiceBinder.java
+++ b/src/com/android/server/telecom/ServiceBinder.java
@@ -25,6 +25,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.util.Collections;
@@ -114,36 +115,46 @@
 
         @Override
         public void onServiceConnected(ComponentName componentName, IBinder binder) {
-            synchronized (mLock) {
-                Log.i(this, "Service bound %s", componentName);
+            try {
+                Log.startSession("SBC.oSC");
+                synchronized (mLock) {
+                    Log.i(this, "Service bound %s", componentName);
 
-                Log.event(mCall, Log.Events.CS_BOUND, componentName);
-                mCall = null;
+                    Log.event(mCall, Log.Events.CS_BOUND, componentName);
+                    mCall = null;
 
-                // Unbind request was queued so unbind immediately.
-                if (mIsBindingAborted) {
-                    clearAbort();
-                    logServiceDisconnected("onServiceConnected");
-                    mContext.unbindService(this);
-                    handleFailedConnection();
-                    return;
+                    // Unbind request was queued so unbind immediately.
+                    if (mIsBindingAborted) {
+                        clearAbort();
+                        logServiceDisconnected("onServiceConnected");
+                        mContext.unbindService(this);
+                        handleFailedConnection();
+                        return;
+                    }
+
+                    mServiceConnection = this;
+                    setBinder(binder);
+                    handleSuccessfulConnection();
                 }
-
-                mServiceConnection = this;
-                setBinder(binder);
-                handleSuccessfulConnection();
+            } finally {
+                Log.endSession();
             }
         }
 
         @Override
         public void onServiceDisconnected(ComponentName componentName) {
-            synchronized (mLock) {
-                logServiceDisconnected("onServiceDisconnected");
+            try {
+                Log.startSession("SBC.oSD");
+                synchronized (mLock) {
+                    logServiceDisconnected("onServiceDisconnected");
 
-                mServiceConnection = null;
-                clearAbort();
+                    mServiceConnection = null;
+                    clearAbort();
 
-                handleServiceDisconnected();
+                    handleServiceDisconnected();
+                }
+            } finally {
+                Log.endSession();
             }
         }
     }
@@ -253,7 +264,8 @@
         return mComponentName;
     }
 
-    final boolean isServiceValid(String actionName) {
+    @VisibleForTesting
+    public boolean isServiceValid(String actionName) {
         if (mBinder == null) {
             Log.w(this, "%s invoked while service is unbound", actionName);
             return false;
diff --git a/src/com/android/server/telecom/Session.java b/src/com/android/server/telecom/Session.java
new file mode 100644
index 0000000..435c505
--- /dev/null
+++ b/src/com/android/server/telecom/Session.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+
+/**
+ * The session that stores information about a thread's point of entry into the Telecom code that
+ * persists until the thread exits Telecom.
+ */
+public class Session {
+
+    public static final String START_SESSION = "START_SESSION";
+    public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
+    public static final String CONTINUE_SUBSESSION = "CONTINUE_SUBSESSION";
+    public static final String END_SUBSESSION = "END_SUBSESSION";
+    public static final String END_SESSION = "END_SESSION";
+
+    public static final int UNDEFINED = -1;
+
+    private String mSessionId;
+    private String mShortMethodName;
+    private long mExecutionStartTimeMs;
+    private long mExecutionEndTimeMs = UNDEFINED;
+    private Session mParentSession;
+    private ArrayList<Session> mChildSessions;
+    private boolean mIsCompleted = false;
+    private int mChildCounter = 0;
+    private long mThreadId = 0;
+
+    public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID) {
+        setSessionId(sessionId);
+        setShortMethodName(shortMethodName);
+        mExecutionStartTimeMs = startTimeMs;
+        mParentSession = null;
+        mChildSessions = new ArrayList<>(5);
+        mThreadId = threadID;
+    }
+
+    public void setSessionId(@NonNull String sessionId) {
+       if(sessionId == null) {
+           mSessionId = "?";
+       }
+       mSessionId = sessionId;
+    }
+
+    public String getShortMethodName() {
+        return mShortMethodName;
+    }
+
+    public void setShortMethodName(String shortMethodName) {
+        if(shortMethodName == null) {
+            shortMethodName = "";
+        }
+        mShortMethodName = shortMethodName;
+    }
+
+    public void setParentSession(Session parentSession) {
+        mParentSession = parentSession;
+    }
+
+    public void addChild(Session childSession) {
+        if(childSession != null) {
+            mChildSessions.add(childSession);
+        }
+    }
+
+    public void removeChild(Session child) {
+        if(child != null) {
+            mChildSessions.remove(child);
+        }
+    }
+
+    public long getExecutionStartTimeMilliseconds() {
+        return mExecutionStartTimeMs;
+    }
+
+    public void setExecutionStartTimeMs(long startTimeMs) {
+        mExecutionStartTimeMs = startTimeMs;
+    }
+
+    public Session getParentSession() {
+        return mParentSession;
+    }
+
+    public ArrayList<Session> getChildSessions() {
+        return mChildSessions;
+    }
+
+    public boolean isSessionCompleted() {
+        return mIsCompleted;
+    }
+
+    // Mark this session complete. This will be deleted by Log when all subsessions are complete
+    // as well.
+    public void markSessionCompleted(long executionEndTimeMs) {
+        mExecutionEndTimeMs = executionEndTimeMs;
+        mIsCompleted = true;
+    }
+
+    public long getLocalExecutionTime() {
+        if(mExecutionEndTimeMs == UNDEFINED) {
+            return UNDEFINED;
+        }
+        return mExecutionEndTimeMs - mExecutionStartTimeMs;
+    }
+
+    public synchronized String getNextChildId() {
+        return String.valueOf(mChildCounter++);
+    }
+
+    public long getThreadId () {
+        return mThreadId;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof Session)) {
+            return false;
+        }
+        if (obj == this) {
+            return true;
+        }
+        Session otherSession = (Session) obj;
+        return (mSessionId.equals(otherSession.mSessionId)) &&
+                (mShortMethodName.equals(otherSession.mShortMethodName)) &&
+                mExecutionStartTimeMs == otherSession.mExecutionStartTimeMs &&
+                mParentSession == otherSession.mParentSession &&
+                mChildSessions.equals(otherSession.mChildSessions) &&
+                mIsCompleted == otherSession.mIsCompleted &&
+                mExecutionEndTimeMs == otherSession.mExecutionEndTimeMs &&
+                mChildCounter == otherSession.mChildCounter &&
+                mThreadId == otherSession.mThreadId;
+    }
+
+    // Builds full session id recursively
+    private String getFullSessionId() {
+        if(mParentSession == null) {
+            return mSessionId;
+        } else {
+            return mParentSession.getFullSessionId() + "_" + mSessionId;
+        }
+    }
+
+    // Print out the full Session tree from any subsession node
+    public String printFullSessionTree() {
+        // Get to the top of the tree
+        Session topNode = this;
+        while(topNode.getParentSession() != null) {
+            topNode = topNode.getParentSession();
+        }
+        return topNode.printSessionTree();
+    }
+
+    // Recursively move down session tree using DFS, but print out each node when it is reached.
+    public String printSessionTree() {
+        StringBuilder sb = new StringBuilder();
+        printSessionTree(0, sb);
+        return sb.toString();
+    }
+
+    private void printSessionTree(int tabI, StringBuilder sb) {
+        sb.append(toString());
+        for (Session child : mChildSessions) {
+            sb.append("\n");
+            for(int i = 0; i <= tabI; i++) {
+                sb.append("\t");
+            }
+            child.printSessionTree(tabI + 1, sb);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return mShortMethodName + "@" + getFullSessionId();
+    }
+}
diff --git a/src/com/android/server/telecom/StatusBarNotifier.java b/src/com/android/server/telecom/StatusBarNotifier.java
index c8c3c18..d8ede59 100644
--- a/src/com/android/server/telecom/StatusBarNotifier.java
+++ b/src/com/android/server/telecom/StatusBarNotifier.java
@@ -19,12 +19,15 @@
 import android.app.StatusBarManager;
 import android.content.Context;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 // TODO: Needed for move to system service: import com.android.internal.R;
 
 /**
  * Manages the special status bar notifications used by the phone app.
  */
-final class StatusBarNotifier extends CallsManagerListenerBase {
+@VisibleForTesting
+public class StatusBarNotifier extends CallsManagerListenerBase {
     private static final String SLOT_MUTE = "mute";
     private static final String SLOT_SPEAKERPHONE = "speakerphone";
 
@@ -50,7 +53,8 @@
         }
     }
 
-    void notifyMute(boolean isMuted) {
+    @VisibleForTesting
+    public void notifyMute(boolean isMuted) {
         // Never display anything if there are no calls.
         if (!mCallsManager.hasAnyCalls()) {
             isMuted = false;
@@ -74,7 +78,8 @@
         mIsShowingMute = isMuted;
     }
 
-    void notifySpeakerphone(boolean isSpeakerphone) {
+    @VisibleForTesting
+    public void notifySpeakerphone(boolean isSpeakerphone) {
         // Never display anything if there are no calls.
         if (!mCallsManager.hasAnyCalls()) {
             isSpeakerphone = false;
diff --git a/src/com/android/server/telecom/SystemLoggingContainer.java b/src/com/android/server/telecom/SystemLoggingContainer.java
new file mode 100644
index 0000000..0b65b09
--- /dev/null
+++ b/src/com/android/server/telecom/SystemLoggingContainer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+/**
+ * Create a container for the Android Logging class so that it can be mocked in testing.
+ */
+public class SystemLoggingContainer {
+
+    public void v(String TAG, String msg) {
+        android.util.Slog.v(TAG, msg);
+    }
+
+    public void d(String TAG, String msg) {
+        android.util.Slog.d(TAG, msg);
+    }
+
+    public void i(String TAG, String msg) {
+        android.util.Slog.i(TAG, msg);
+    }
+
+    public void w(String TAG, String msg) {
+        android.util.Slog.w(TAG, msg);
+    }
+
+    public void e(String TAG, String msg, Throwable tr) {
+        android.util.Slog.e(TAG, msg, tr);
+    }
+
+    public void wtf(String TAG, String msg, Throwable tr) {
+        android.util.Slog.wtf(TAG, msg, tr);
+    }
+}
diff --git a/src/com/android/server/telecom/SystemSettingsUtil.java b/src/com/android/server/telecom/SystemSettingsUtil.java
new file mode 100644
index 0000000..3c75e4d
--- /dev/null
+++ b/src/com/android/server/telecom/SystemSettingsUtil.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Accesses the Global System settings for more control during testing.
+ */
+@VisibleForTesting
+public class SystemSettingsUtil {
+
+    public boolean isTheaterModeOn(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(), Settings.Global.THEATER_MODE_ON,
+                0) == 1;
+    }
+
+    public boolean canVibrateWhenRinging(Context context) {
+        return Settings.System.getInt(context.getContentResolver(),
+                Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
+    }
+}
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 1650d58..30fcb65 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -34,16 +34,16 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.IBinder;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telecom.DefaultDialerManager;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telecom.VideoProfile;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 
 // TODO: Needed for move to system service: import com.android.internal.R;
 import com.android.internal.telecom.ITelecomService;
@@ -62,35 +62,33 @@
 public class TelecomServiceImpl {
     private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION =
             "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION";
+    private static final int DEFAULT_VIDEO_STATE = -1;
 
     private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
         @Override
         public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme,
                 String callingPackage) {
-            synchronized (mLock) {
-                if (!canReadPhoneState(callingPackage, "getDefaultOutgoingPhoneAccount")) {
-                    return null;
-                }
-
-                long token = Binder.clearCallingIdentity();
-                try {
-                    PhoneAccountHandle defaultOutgoingPhoneAccount =
-                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(uriScheme);
-                    // Make sure that the calling user can see this phone account.
-                    // TODO: Does this isVisible check actually work considering we are clearing
-                    // the calling identity?
-                    if (defaultOutgoingPhoneAccount != null
-                            && !isVisibleToCaller(defaultOutgoingPhoneAccount)) {
-                        Log.w(this, "No account found for the calling user");
+            try {
+                Log.startSession("TSI.gDOPA");
+                synchronized (mLock) {
+                    if (!canReadPhoneState(callingPackage, "getDefaultOutgoingPhoneAccount")) {
                         return null;
                     }
-                    return defaultOutgoingPhoneAccount;
-                } catch (Exception e) {
-                    Log.e(this, e, "getDefaultOutgoingPhoneAccount");
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        return mPhoneAccountRegistrar
+                                .getOutgoingPhoneAccountForScheme(uriScheme, callingUserHandle);
+                    } catch (Exception e) {
+                        Log.e(this, e, "getDefaultOutgoingPhoneAccount");
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -98,94 +96,109 @@
         public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
             synchronized (mLock) {
                 try {
-                    PhoneAccountHandle userSelectedOutgoingPhoneAccount =
-                            mPhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount();
-                    // Make sure that the calling user can see this phone account.
-                    if (!isVisibleToCaller(userSelectedOutgoingPhoneAccount)) {
-                        Log.w(this, "No account found for the calling user");
-                        return null;
-                    }
-                    return userSelectedOutgoingPhoneAccount;
+                    Log.startSession("TSI.gUSOPA");
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    return mPhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount(
+                            callingUserHandle);
                 } catch (Exception e) {
                     Log.e(this, e, "getUserSelectedOutgoingPhoneAccount");
                     throw e;
+                } finally {
+                    Log.endSession();
                 }
             }
         }
 
         @Override
         public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
-            synchronized (mLock) {
-                enforceModifyPermission();
-
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(accountHandle);
-                } catch (Exception e) {
-                    Log.e(this, e, "setUserSelectedOutgoingPhoneAccount");
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            try {
+                Log.startSession("TSI.sUSOPA");
+                synchronized (mLock) {
+                    enforceModifyPermission();
+                    UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(
+                                accountHandle, callingUserHandle);
+                    } catch (Exception e) {
+                        Log.e(this, e, "setUserSelectedOutgoingPhoneAccount");
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
         @Override
         public List<PhoneAccountHandle> getCallCapablePhoneAccounts(
                 boolean includeDisabledAccounts, String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "getDefaultOutgoingPhoneAccount")) {
-                return Collections.emptyList();
-            }
-
-            synchronized (mLock) {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    // TODO: Does this isVisible check actually work considering we are clearing
-                    // the calling identity?
-                    return filterForAccountsVisibleToCaller(
-                            mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
-                                    null, includeDisabledAccounts));
-                } catch (Exception e) {
-                    Log.e(this, e, "getCallCapablePhoneAccounts");
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            try {
+                Log.startSession("TSI.gCCPA");
+                if (!canReadPhoneState(callingPackage, "getDefaultOutgoingPhoneAccount")) {
+                    return Collections.emptyList();
                 }
+                synchronized (mLock) {
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
+                                includeDisabledAccounts, callingUserHandle);
+                    } catch (Exception e) {
+                        Log.e(this, e, "getCallCapablePhoneAccounts");
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
         @Override
         public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme,
                 String callingPackage) {
-            synchronized (mLock) {
-                if (!canReadPhoneState(callingPackage, "getPhoneAccountsSupportingScheme")) {
-                    return Collections.emptyList();
+            try {
+                Log.startSession("TSI.gPASS");
+                synchronized (mLock) {
+                    if (!canReadPhoneState(callingPackage, "getPhoneAccountsSupportingScheme")) {
+                        return Collections.emptyList();
+                    }
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        return mPhoneAccountRegistrar.getCallCapablePhoneAccounts(uriScheme, false,
+                                callingUserHandle);
+                    } catch (Exception e) {
+                        Log.e(this, e, "getPhoneAccountsSupportingScheme %s", uriScheme);
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
-
-                long token = Binder.clearCallingIdentity();
-                try {
-                    // TODO: Does this isVisible check actually work considering we are clearing
-                    // the calling identity?
-                    return filterForAccountsVisibleToCaller(
-                            mPhoneAccountRegistrar.getCallCapablePhoneAccounts(uriScheme, false));
-                } catch (Exception e) {
-                    Log.e(this, e, "getPhoneAccountsSupportingScheme %s", uriScheme);
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+            } finally {
+                Log.endSession();
             }
         }
 
         @Override
         public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
             synchronized (mLock) {
+                final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                long token = Binder.clearCallingIdentity();
                 try {
-                    return filterForAccountsVisibleToCaller(
-                            mPhoneAccountRegistrar.getPhoneAccountsForPackage(packageName));
+                    Log.startSession("TSI.gPAFP");
+                    return mPhoneAccountRegistrar.getPhoneAccountsForPackage(packageName,
+                            callingUserHandle);
                 } catch (Exception e) {
                     Log.e(this, e, "getPhoneAccountsForPackage %s", packageName);
                     throw e;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    Log.endSession();
                 }
             }
         }
@@ -193,16 +206,23 @@
         @Override
         public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle) {
             synchronized (mLock) {
+                final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                long token = Binder.clearCallingIdentity();
                 try {
-                    if (!isVisibleToCaller(accountHandle)) {
-                        Log.d(this, "%s is not visible for the calling user [gPA]", accountHandle);
-                        return null;
-                    }
-                    // TODO: Do we really want to return for *any* user?
-                    return mPhoneAccountRegistrar.getPhoneAccount(accountHandle);
+                    Log.startSession("TSI.gPA");
+                    // In ideal case, we should not resolve the handle across profiles. But given
+                    // the fact that profile's call is handled by its parent user's in-call UI,
+                    // parent user's in call UI need to be able to get phone account from the
+                    // profile's phone account handle.
+                    return mPhoneAccountRegistrar
+                            .getPhoneAccount(accountHandle, callingUserHandle,
+                            /* acrossProfiles */ true);
                 } catch (Exception e) {
                     Log.e(this, e, "getPhoneAccount %s", accountHandle);
                     throw e;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    Log.endSession();
                 }
             }
         }
@@ -211,11 +231,14 @@
         public int getAllPhoneAccountsCount() {
             synchronized (mLock) {
                 try {
+                    Log.startSession("TSI.gAPAC");
                     // This list is pre-filtered for the calling user.
                     return getAllPhoneAccounts().size();
                 } catch (Exception e) {
                     Log.e(this, e, "getAllPhoneAccountsCount");
                     throw e;
+                } finally {
+                    Log.endSession();
                 }
             }
         }
@@ -223,20 +246,17 @@
         @Override
         public List<PhoneAccount> getAllPhoneAccounts() {
             synchronized (mLock) {
+                final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                long token = Binder.clearCallingIdentity();
                 try {
-                    List<PhoneAccount> allPhoneAccounts = mPhoneAccountRegistrar
-                            .getAllPhoneAccounts();
-                    List<PhoneAccount> profilePhoneAccounts = new ArrayList<>(
-                            allPhoneAccounts.size());
-                    for (PhoneAccount phoneAccount : allPhoneAccounts) {
-                        if (isVisibleToCaller(phoneAccount)) {
-                            profilePhoneAccounts.add(phoneAccount);
-                        }
-                    }
-                    return profilePhoneAccounts;
+                    Log.startSession("TSI.gAPA");
+                    return mPhoneAccountRegistrar.getAllPhoneAccounts(callingUserHandle);
                 } catch (Exception e) {
                     Log.e(this, e, "getAllPhoneAccounts");
                     throw e;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    Log.endSession();
                 }
             }
         }
@@ -244,92 +264,104 @@
         @Override
         public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
             synchronized (mLock) {
+                final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                long token = Binder.clearCallingIdentity();
                 try {
-                    return filterForAccountsVisibleToCaller(
-                            mPhoneAccountRegistrar.getAllPhoneAccountHandles());
+                    Log.startSession("TSI.gAPAH");
+                    return mPhoneAccountRegistrar.getAllPhoneAccountHandles(callingUserHandle);
                 } catch (Exception e) {
                     Log.e(this, e, "getAllPhoneAccounts");
                     throw e;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                    Log.endSession();
                 }
             }
         }
 
         @Override
         public PhoneAccountHandle getSimCallManager() {
-            long token  = Binder.clearCallingIdentity();
-            int user;
             try {
-                user = ActivityManager.getCurrentUser();
+                Log.startSession("TSI.gSCM");
+                long token = Binder.clearCallingIdentity();
+                int user;
+                try {
+                    user = ActivityManager.getCurrentUser();
+                    return getSimCallManagerForUser(user);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             } finally {
-                Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
-            return getSimCallManagerForUser(user);
         }
 
         @Override
         public PhoneAccountHandle getSimCallManagerForUser(int user) {
             synchronized (mLock) {
                 try {
-                    PhoneAccountHandle accountHandle = null;
-
+                    Log.startSession("TSI.gSCMFU");
+                    final int callingUid = Binder.getCallingUid();
                     long token = Binder.clearCallingIdentity();
                     try {
-                        accountHandle = mPhoneAccountRegistrar.getSimCallManager(user);
+                        if (user != ActivityManager.getCurrentUser()) {
+                            enforceCrossUserPermission(callingUid);
+                        }
+                        return mPhoneAccountRegistrar.getSimCallManager(UserHandle.of(user));
                     } finally {
-                        // We restore early so that isVisibleToCaller invocation below uses the
-                        // right user context.
                         Binder.restoreCallingIdentity(token);
                     }
-
-                    if (!isVisibleToCaller(accountHandle)) {
-                        Log.d(this, "%s is not visible for the calling user [gsCM]", accountHandle);
-                        return null;
-                    }
-                    return accountHandle;
                 } catch (Exception e) {
                     Log.e(this, e, "getSimCallManager");
                     throw e;
+                } finally {
+                    Log.endSession();
                 }
             }
         }
 
         @Override
         public void registerPhoneAccount(PhoneAccount account) {
-            synchronized (mLock) {
-                if (!mContext.getApplicationContext().getResources().getBoolean(
-                        com.android.internal.R.bool.config_voice_capable)) {
-                    Log.w(this, "registerPhoneAccount not allowed on non-voice capable device.");
-                    return;
-                }
-                try {
-                    enforcePhoneAccountModificationForPackage(
-                            account.getAccountHandle().getComponentName().getPackageName());
-                    if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-                        enforceRegisterSimSubscriptionPermission();
+            try {
+                Log.startSession("TSI.rPA");
+                synchronized (mLock) {
+                    if (!mContext.getApplicationContext().getResources().getBoolean(
+                            com.android.internal.R.bool.config_voice_capable)) {
+                        Log.w(this,
+                                "registerPhoneAccount not allowed on non-voice capable device.");
+                        return;
                     }
-                    if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
-                        enforceRegisterMultiUser();
-                    }
-                    enforceUserHandleMatchesCaller(account.getAccountHandle());
-
-                    mPhoneAccountRegistrar.registerPhoneAccount(account);
-
-                    // Broadcast an intent indicating the phone account which was registered.
-                    long token = Binder.clearCallingIdentity();
                     try {
-                        Intent intent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED);
-                        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
-                                account.getAccountHandle());
-                        Log.i(this, "Sending phone-account registered intent as user");
-                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                                PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
+                        enforcePhoneAccountModificationForPackage(
+                                account.getAccountHandle().getComponentName().getPackageName());
+                        if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+                            enforceRegisterSimSubscriptionPermission();
+                        }
+                        if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
+                            enforceRegisterMultiUser();
+                        }
+                        enforceUserHandleMatchesCaller(account.getAccountHandle());
+                        mPhoneAccountRegistrar.registerPhoneAccount(account);
+                        // Broadcast an intent indicating the phone account which was registered.
+                        long token = Binder.clearCallingIdentity();
+                        try {
+                            Intent intent = new Intent(
+                                    TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED);
+                            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                                    account.getAccountHandle());
+                            Log.i(this, "Sending phone-account registered intent as user");
+                            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                                    PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    } catch (Exception e) {
+                        Log.e(this, e, "registerPhoneAccount %s", account);
+                        throw e;
                     }
-                } catch (Exception e) {
-                    Log.e(this, e, "registerPhoneAccount %s", account);
-                    throw e;
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -337,6 +369,7 @@
         public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
             synchronized (mLock) {
                 try {
+                    Log.startSession("TSI.uPA");
                     enforcePhoneAccountModificationForPackage(
                             accountHandle.getComponentName().getPackageName());
                     enforceUserHandleMatchesCaller(accountHandle);
@@ -347,7 +380,8 @@
                     try {
                         Intent intent =
                                 new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED);
-                        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
+                        intent.putExtra(
+                                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
                         Log.i(this, "Sending phone-account unregistered intent as user");
                         mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                                 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION);
@@ -357,6 +391,8 @@
                 } catch (Exception e) {
                     Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
                     throw e;
+                } finally {
+                    Log.endSession();
                 }
             }
         }
@@ -365,12 +401,15 @@
         public void clearAccounts(String packageName) {
             synchronized (mLock) {
                 try {
+                    Log.startSession("TSI.cA");
                     enforcePhoneAccountModificationForPackage(packageName);
                     mPhoneAccountRegistrar
                             .clearAccounts(packageName, Binder.getCallingUserHandle());
                 } catch (Exception e) {
                     Log.e(this, e, "clearAccounts %s", packageName);
                     throw e;
+                } finally {
+                    Log.endSession();
                 }
             }
         }
@@ -381,25 +420,30 @@
         @Override
         public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number,
                 String callingPackage) {
-            synchronized (mLock) {
-                if (!canReadPhoneState(callingPackage, "isVoiceMailNumber")) {
-                    return false;
+            try {
+                Log.startSession("TSI.iVMN");
+                synchronized (mLock) {
+                    if (!canReadPhoneState(callingPackage, "isVoiceMailNumber")) {
+                        return false;
+                    }
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                            callingUserHandle)) {
+                        Log.d(this, "%s is not visible for the calling user [iVMN]", accountHandle);
+                        return false;
+                    }
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
+                    } catch (Exception e) {
+                        Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
-
-                if (!isVisibleToCaller(accountHandle)) {
-                    Log.d(this, "%s is not visible for the calling user [iVMN]", accountHandle);
-                    return false;
-                }
-
-                long token = Binder.clearCallingIdentity();
-                try {
-                    return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
-                } catch (Exception e) {
-                    Log.e(this, e, "getSubscriptionIdForPhoneAccount");
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -408,27 +452,33 @@
          */
         @Override
         public String getVoiceMailNumber(PhoneAccountHandle accountHandle, String callingPackage) {
-            synchronized (mLock) {
-                if (!canReadPhoneState(callingPackage, "getVoiceMailNumber")) {
-                    return null;
-                }
-
-                try {
-                    if (!isVisibleToCaller(accountHandle)) {
-                        Log.d(this, "%s is not visible for the calling user [gVMN]", accountHandle);
+            try {
+                Log.startSession("TSI.gVMN");
+                synchronized (mLock) {
+                    if (!canReadPhoneState(callingPackage, "getVoiceMailNumber")) {
                         return null;
                     }
-
-                    int subId = SubscriptionManager.getDefaultVoiceSubId();
-                    if (accountHandle != null) {
-                        subId = mPhoneAccountRegistrar
-                                .getSubscriptionIdForPhoneAccount(accountHandle);
+                    try {
+                        final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                        if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                                callingUserHandle)) {
+                            Log.d(this, "%s is not visible for the calling user [gVMN]",
+                                    accountHandle);
+                            return null;
+                        }
+                        int subId = SubscriptionManager.getDefaultVoiceSubId();
+                        if (accountHandle != null) {
+                            subId = mPhoneAccountRegistrar
+                                    .getSubscriptionIdForPhoneAccount(accountHandle);
+                        }
+                        return getTelephonyManager().getVoiceMailNumber(subId);
+                    } catch (Exception e) {
+                        Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+                        throw e;
                     }
-                    return getTelephonyManager().getVoiceMailNumber(subId);
-                } catch (Exception e) {
-                    Log.e(this, e, "getSubscriptionIdForPhoneAccount");
-                    throw e;
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -437,27 +487,34 @@
          */
         @Override
         public String getLine1Number(PhoneAccountHandle accountHandle, String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "getLine1Number")) {
-                return null;
-            }
-
-            synchronized (mLock) {
-                if (!isVisibleToCaller(accountHandle)) {
-                    Log.d(this, "%s is not visible for the calling user [gL1N]", accountHandle);
+            try {
+                Log.startSession("getL1N");
+                if (!canReadPhoneState(callingPackage, "getLine1Number")) {
                     return null;
                 }
 
-                long token = Binder.clearCallingIdentity();
-                try {
-                    int subId =
-                            mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle);
-                    return getTelephonyManager().getLine1NumberForSubscriber(subId);
-                } catch (Exception e) {
-                    Log.e(this, e, "getSubscriptionIdForPhoneAccount");
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+                synchronized (mLock) {
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                            callingUserHandle)) {
+                        Log.d(this, "%s is not visible for the calling user [gL1N]", accountHandle);
+                        return null;
+                    }
+
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
+                                accountHandle);
+                        return getTelephonyManager().getLine1NumberForSubscriber(subId);
+                    } catch (Exception e) {
+                        Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -466,16 +523,21 @@
          */
         @Override
         public void silenceRinger(String callingPackage) {
-            synchronized (mLock) {
-                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+            try {
+                Log.startSession("TSI.sR");
+                synchronized (mLock) {
+                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
 
-                long token = Binder.clearCallingIdentity();
-                try {
-                    Log.i(this, "Silence Ringer requested by %s", callingPackage);
-                    mCallsManager.getRinger().silence();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        Log.i(this, "Silence Ringer requested by %s", callingPackage);
+                        mCallsManager.getRinger().silence();
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -486,11 +548,16 @@
          */
         @Override
         public ComponentName getDefaultPhoneApp() {
-            // No need to synchronize
-            Resources resources = mContext.getResources();
-            return new ComponentName(
-                    resources.getString(R.string.ui_default_package),
-                    resources.getString(R.string.dialer_default_class));
+            try {
+                Log.startSession("TSI.gDPA");
+                // No need to synchronize
+                Resources resources = mContext.getResources();
+                return new ComponentName(
+                        resources.getString(R.string.ui_default_package),
+                        resources.getString(R.string.dialer_default_class));
+            } finally {
+                Log.endSession();
+            }
         }
 
         /**
@@ -501,11 +568,16 @@
          */
         @Override
         public String getDefaultDialerPackage() {
-            final long token = Binder.clearCallingIdentity();
             try {
-                return DefaultDialerManager.getDefaultDialerApplication(mContext);
+                Log.startSession("TSI.gDDP");
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    return DefaultDialerManager.getDefaultDialerApplication(mContext);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             } finally {
-                Binder.restoreCallingIdentity(token);
+                Log.endSession();
             }
         }
 
@@ -514,7 +586,12 @@
          */
         @Override
         public String getSystemDialerPackage() {
-            return mContext.getResources().getString(R.string.ui_default_package);
+            try {
+                Log.startSession("TSI.gSDP");
+                return mContext.getResources().getString(R.string.ui_default_package);
+            } finally {
+                Log.endSession();
+            }
         }
 
         /**
@@ -522,14 +599,19 @@
          */
         @Override
         public boolean isInCall(String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "isInCall")) {
-                return false;
-            }
+            try {
+                Log.startSession("TSI.iIC");
+                if (!canReadPhoneState(callingPackage, "isInCall")) {
+                    return false;
+                }
 
-            synchronized (mLock) {
-                final int callState = mCallsManager.getCallState();
-                return callState == TelephonyManager.CALL_STATE_OFFHOOK
-                        || callState == TelephonyManager.CALL_STATE_RINGING;
+                synchronized (mLock) {
+                    final int callState = mCallsManager.getCallState();
+                    return callState == TelephonyManager.CALL_STATE_OFFHOOK
+                            || callState == TelephonyManager.CALL_STATE_RINGING;
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -538,12 +620,22 @@
          */
         @Override
         public boolean isRinging(String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "isRinging")) {
-                return false;
-            }
+            try {
+                Log.startSession("TSI.iR");
+                if (!canReadPhoneState(callingPackage, "isRinging")) {
+                    return false;
+                }
 
-            synchronized (mLock) {
-                return mCallsManager.getCallState() == TelephonyManager.CALL_STATE_RINGING;
+                synchronized (mLock) {
+                    // Note: We are explicitly checking the calls telecom is tracking rather than
+                    // relying on mCallsManager#getCallState(). Since getCallState() relies on the
+                    // current state as tracked by PhoneStateBroadcaster, any failure to properly
+                    // track the current call state there could result in the wrong ringing state
+                    // being reported by this API.
+                    return mCallsManager.hasRingingCall();
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -552,8 +644,13 @@
          */
         @Override
         public int getCallState() {
-            synchronized (mLock) {
-                return mCallsManager.getCallState();
+            try {
+                Log.startSession("TSI.getCallState");
+                synchronized (mLock) {
+                    return mCallsManager.getCallState();
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -562,15 +659,20 @@
          */
         @Override
         public boolean endCall() {
-            synchronized (mLock) {
-                enforceModifyPermission();
+            try {
+                Log.startSession("TSI.eC");
+                synchronized (mLock) {
+                    enforceModifyPermission();
 
-                long token = Binder.clearCallingIdentity();
-                try {
-                    return endCallInternal();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        return endCallInternal();
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -579,15 +681,43 @@
          */
         @Override
         public void acceptRingingCall() {
-            synchronized (mLock) {
-                enforceModifyPermission();
+            try {
+                Log.startSession("TSI.aRC");
+                synchronized (mLock) {
+                    enforceModifyPermission();
 
-                long token = Binder.clearCallingIdentity();
-                try {
-                    acceptRingingCallInternal();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        acceptRingingCallInternal(DEFAULT_VIDEO_STATE);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        /**
+         * @see android.telecom.TelecomManager#acceptRingingCall(int)
+         *
+         */
+        @Override
+        public void acceptRingingCallWithVideoState(int videoState) {
+            try {
+                Log.startSession("TSI.aRCWVS");
+                synchronized (mLock) {
+                    enforceModifyPermission();
+
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        acceptRingingCallInternal(videoState);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -596,18 +726,23 @@
          */
         @Override
         public void showInCallScreen(boolean showDialpad, String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "showInCallScreen")) {
-                return;
-            }
-
-            synchronized (mLock) {
-
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mCallsManager.getInCallController().bringToForeground(showDialpad);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            try {
+                Log.startSession("TSI.sICS");
+                if (!canReadPhoneState(callingPackage, "showInCallScreen")) {
+                    return;
                 }
+
+                synchronized (mLock) {
+
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mCallsManager.getInCallController().bringToForeground(showDialpad);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -616,35 +751,45 @@
          */
         @Override
         public void cancelMissedCallsNotification(String callingPackage) {
-            synchronized (mLock) {
-                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mCallsManager.getMissedCallNotifier().clearMissedCalls();
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            try {
+                Log.startSession("TSI.cMCN");
+                synchronized (mLock) {
+                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mCallsManager.getMissedCallNotifier().clearMissedCalls();
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
-
         /**
          * @see android.telecom.TelecomManager#handleMmi
          */
         @Override
         public boolean handlePinMmi(String dialString, String callingPackage) {
-            synchronized (mLock) {
-                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+            try {
+                Log.startSession("TSI.hPM");
+                synchronized (mLock) {
+                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
 
-                // Switch identity so that TelephonyManager checks Telecom's permissions instead.
-                long token = Binder.clearCallingIdentity();
-                boolean retval = false;
-                try {
-                    retval = getTelephonyManager().handlePinMmi(dialString);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+                    // Switch identity so that TelephonyManager checks Telecom's permissions
+                    // instead.
+                    long token = Binder.clearCallingIdentity();
+                    boolean retval = false;
+                    try {
+                        retval = getTelephonyManager().handlePinMmi(dialString);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+
+                    return retval;
                 }
-
-                return retval;
+            }finally {
+                Log.endSession();
             }
         }
 
@@ -652,30 +797,35 @@
          * @see android.telecom.TelecomManager#handleMmi
          */
         @Override
-        public boolean handlePinMmiForPhoneAccount(
-                PhoneAccountHandle accountHandle,
-                String dialString,
-                String callingPackage) {
-            synchronized (mLock) {
-                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+        public boolean handlePinMmiForPhoneAccount(PhoneAccountHandle accountHandle,
+                String dialString, String callingPackage) {
+            try {
+                Log.startSession("TSI.hPMFPA");
+                synchronized (mLock) {
+                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
 
-                if (!isVisibleToCaller(accountHandle)) {
-                    Log.d(this, "%s is not visible for the calling user [hMMI]", accountHandle);
-                    return false;
+                    UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                            callingUserHandle)) {
+                        Log.d(this, "%s is not visible for the calling user [hMMI]", accountHandle);
+                        return false;
+                    }
+
+                    // Switch identity so that TelephonyManager checks Telecom's permissions
+                    // instead.
+                    long token = Binder.clearCallingIdentity();
+                    boolean retval = false;
+                    try {
+                        int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
+                                accountHandle);
+                        retval = getTelephonyManager().handlePinMmiForSubscriber(subId, dialString);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                    return retval;
                 }
-
-                // Switch identity so that TelephonyManager checks Telecom's permissions instead.
-                long token = Binder.clearCallingIdentity();
-                boolean retval = false;
-                try {
-                    int subId = mPhoneAccountRegistrar
-                            .getSubscriptionIdForPhoneAccount(accountHandle);
-                    retval = getTelephonyManager().handlePinMmiForSubscriber(subId, dialString);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-
-                return retval;
+            }finally {
+                Log.endSession();
             }
         }
 
@@ -685,26 +835,32 @@
         @Override
         public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle,
                 String callingPackage) {
-            synchronized (mLock) {
-                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+            try {
+                Log.startSession("TSI.aAUFPA");
+                synchronized (mLock) {
+                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+                    if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                            Binder.getCallingUserHandle())) {
+                        Log.d(this, "%s is not visible for the calling user [gA4PA]",
+                                accountHandle);
+                        return null;
+                    }
+                    // Switch identity so that TelephonyManager checks Telecom's permissions
+                    // instead.
+                    long token = Binder.clearCallingIdentity();
+                    String retval = "content://icc/adn/";
+                    try {
+                        long subId = mPhoneAccountRegistrar
+                                .getSubscriptionIdForPhoneAccount(accountHandle);
+                        retval = retval + "subId/" + subId;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
 
-                if (!isVisibleToCaller(accountHandle)) {
-                    Log.d(this, "%s is not visible for the calling user [gA4PA]", accountHandle);
-                    return null;
+                    return Uri.parse(retval);
                 }
-
-                // Switch identity so that TelephonyManager checks Telecom's permissions instead.
-                long token = Binder.clearCallingIdentity();
-                String retval = "content://icc/adn/";
-                try {
-                    long subId = mPhoneAccountRegistrar
-                            .getSubscriptionIdForPhoneAccount(accountHandle);
-                    retval = retval + "subId/" + subId;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-
-                return Uri.parse(retval);
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -713,12 +869,17 @@
          */
         @Override
         public boolean isTtySupported(String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "hasVoiceMailNumber")) {
-                return false;
-            }
+            try {
+                Log.startSession("TSI.iTS");
+                if (!canReadPhoneState(callingPackage, "hasVoiceMailNumber")) {
+                    return false;
+                }
 
-            synchronized (mLock) {
-                return mCallsManager.isTtySupported();
+                synchronized (mLock) {
+                    return mCallsManager.isTtySupported();
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -727,12 +888,17 @@
          */
         @Override
         public int getCurrentTtyMode(String callingPackage) {
-            if (!canReadPhoneState(callingPackage, "getCurrentTtyMode")) {
-                return TelecomManager.TTY_MODE_OFF;
-            }
+            try {
+                Log.startSession("TSI.gCTM");
+                if (!canReadPhoneState(callingPackage, "getCurrentTtyMode")) {
+                    return TelecomManager.TTY_MODE_OFF;
+                }
 
-            synchronized (mLock) {
-                return mCallsManager.getCurrentTtyMode();
+                synchronized (mLock) {
+                    return mCallsManager.getCurrentTtyMode();
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -741,39 +907,47 @@
          */
         @Override
         public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
-            synchronized (mLock) {
-                Log.i(this, "Adding new incoming call with phoneAccountHandle %s",
-                        phoneAccountHandle);
-                if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
-                    // TODO(sail): Add unit tests for adding incoming calls from a SIM call manager.
-                    if (isCallerSimCallManager() && TelephonyUtil.isPstnComponentName(
-                            phoneAccountHandle.getComponentName())) {
-                        Log.v(this, "Allowing call manager to add incoming call with PSTN handle");
-                    } else {
-                        mAppOpsManager.checkPackage(
-                                Binder.getCallingUid(),
-                                phoneAccountHandle.getComponentName().getPackageName());
-                        // Make sure it doesn't cross the UserHandle boundary
-                        enforceUserHandleMatchesCaller(phoneAccountHandle);
-                    }
-
-                    long token = Binder.clearCallingIdentity();
-                    try {
-                        Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
-                        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+            try {
+                Log.startSession("TSI.aNIC");
+                synchronized (mLock) {
+                    Log.i(this, "Adding new incoming call with phoneAccountHandle %s",
                             phoneAccountHandle);
-                        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true);
-                        if (extras != null) {
-                            intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
+                    if (phoneAccountHandle != null &&
+                            phoneAccountHandle.getComponentName() != null) {
+                        // TODO(sail): Add unit tests for adding incoming calls from a SIM call
+                        // manager.
+                        if (isCallerSimCallManager() && TelephonyUtil.isPstnComponentName(
+                                phoneAccountHandle.getComponentName())) {
+                            Log.v(this, "Allowing call manager to add incoming call with PSTN" +
+                                    " handle");
+                        } else {
+                            mAppOpsManager.checkPackage(
+                                    Binder.getCallingUid(),
+                                    phoneAccountHandle.getComponentName().getPackageName());
+                            // Make sure it doesn't cross the UserHandle boundary
+                            enforceUserHandleMatchesCaller(phoneAccountHandle);
                         }
-                        CallIntentProcessor.processIncomingCallIntent(mCallsManager, intent);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
+
+                        long token = Binder.clearCallingIdentity();
+                        try {
+                            Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
+                            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                                    phoneAccountHandle);
+                            intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true);
+                            if (extras != null) {
+                                intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
+                            }
+                            CallIntentProcessor.processIncomingCallIntent(mCallsManager, intent);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    } else {
+                        Log.w(this, "Null phoneAccountHandle. Ignoring request to add new" +
+                                " incoming call");
                     }
-                } else {
-                    Log.w(this,
-                            "Null phoneAccountHandle. Ignoring request to add new incoming call");
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -782,31 +956,37 @@
          */
         @Override
         public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
-            synchronized (mLock) {
-                if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
-                    mAppOpsManager.checkPackage(
-                            Binder.getCallingUid(),
-                            phoneAccountHandle.getComponentName().getPackageName());
+            try {
+                Log.startSession("TSI.aNUC");
+                synchronized (mLock) {
+                    if (phoneAccountHandle != null &&
+                            phoneAccountHandle.getComponentName() != null) {
+                        mAppOpsManager.checkPackage(
+                                Binder.getCallingUid(),
+                                phoneAccountHandle.getComponentName().getPackageName());
 
-                    // Make sure it doesn't cross the UserHandle boundary
-                    enforceUserHandleMatchesCaller(phoneAccountHandle);
-                    long token = Binder.clearCallingIdentity();
+                        // Make sure it doesn't cross the UserHandle boundary
+                        enforceUserHandleMatchesCaller(phoneAccountHandle);
+                        long token = Binder.clearCallingIdentity();
 
-                    try {
-                        Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
-                        intent.putExtras(extras);
-                        intent.putExtra(CallIntentProcessor.KEY_IS_UNKNOWN_CALL, true);
-                        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
-                            phoneAccountHandle);
-                        CallIntentProcessor.processUnknownCallIntent(mCallsManager, intent);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
+                        try {
+                            Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
+                            intent.putExtras(extras);
+                            intent.putExtra(CallIntentProcessor.KEY_IS_UNKNOWN_CALL, true);
+                            intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                                    phoneAccountHandle);
+                            CallIntentProcessor.processUnknownCallIntent(mCallsManager, intent);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    } else {
+                        Log.i(this,
+                                "Null phoneAccountHandle or not initiated by Telephony. " +
+                                        "Ignoring request to add new unknown call.");
                     }
-                } else {
-                    Log.i(this,
-                            "Null phoneAccountHandle or not initiated by Telephony. " +
-                            "Ignoring request to add new unknown call.");
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -815,36 +995,41 @@
          */
         @Override
         public void placeCall(Uri handle, Bundle extras, String callingPackage) {
-            enforceCallingPackage(callingPackage);
-            if (!canCallPhone(callingPackage, "placeCall")) {
-                throw new SecurityException("Package " + callingPackage
-                        + " is not allowed to place phone calls");
-            }
-
-            // Note: we can still get here for the default/system dialer, even if the Phone
-            // permission is turned off. This is because the default/system dialer is always
-            // allowed to attempt to place a call (regardless of permission state), in case
-            // it turns out to be an emergency call. If the permission is denied and the
-            // call is being made to a non-emergency number, the call will be denied later on
-            // by {@link UserCallIntentProcessor}.
-
-            final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
-                    Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
-
-            final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
-                    PackageManager.PERMISSION_GRANTED;
-
-            synchronized (mLock) {
-                final UserHandle userHandle = Binder.getCallingUserHandle();
-                long token = Binder.clearCallingIdentity();
-                try {
-                    final Intent intent = new Intent(Intent.ACTION_CALL, handle);
-                    intent.putExtras(extras);
-                    new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
-                            callingPackage, hasCallAppOp && hasCallPermission);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            try {
+                Log.startSession("TSI.pC");
+                enforceCallingPackage(callingPackage);
+                if (!canCallPhone(callingPackage, "placeCall")) {
+                    throw new SecurityException("Package " + callingPackage
+                            + " is not allowed to place phone calls");
                 }
+
+                // Note: we can still get here for the default/system dialer, even if the Phone
+                // permission is turned off. This is because the default/system dialer is always
+                // allowed to attempt to place a call (regardless of permission state), in case
+                // it turns out to be an emergency call. If the permission is denied and the
+                // call is being made to a non-emergency number, the call will be denied later on
+                // by {@link UserCallIntentProcessor}.
+
+                final boolean hasCallAppOp = mAppOpsManager.noteOp(AppOpsManager.OP_CALL_PHONE,
+                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
+
+                final boolean hasCallPermission = mContext.checkCallingPermission(CALL_PHONE) ==
+                        PackageManager.PERMISSION_GRANTED;
+
+                synchronized (mLock) {
+                    final UserHandle userHandle = Binder.getCallingUserHandle();
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        final Intent intent = new Intent(Intent.ACTION_CALL, handle);
+                        intent.putExtras(extras);
+                        new UserCallIntentProcessor(mContext, userHandle).processIntent(intent,
+                                callingPackage, hasCallAppOp && hasCallPermission);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -853,39 +1038,50 @@
          */
         @Override
         public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) {
-            enforceModifyPermission();
-            synchronized (mLock) {
-                long token  = Binder.clearCallingIdentity();
-                try {
-                    // enable/disable phone account
-                    return mPhoneAccountRegistrar.enablePhoneAccount(accountHandle, isEnabled);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
+            try {
+                Log.startSession("TSI.ePA");
+                enforceModifyPermission();
+                synchronized (mLock) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        // enable/disable phone account
+                        return mPhoneAccountRegistrar.enablePhoneAccount(accountHandle, isEnabled);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
         @Override
         public boolean setDefaultDialer(String packageName) {
-            enforcePermission(MODIFY_PHONE_STATE);
-            enforcePermission(WRITE_SECURE_SETTINGS);
-            synchronized (mLock) {
-                long token  = Binder.clearCallingIdentity();
-                try {
-                    final boolean result =
-                            DefaultDialerManager.setDefaultDialerApplication(mContext, packageName);
-                    if (result) {
-                        final Intent intent =
-                                new Intent(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
-                        intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
-                                packageName);
-                        mContext.sendBroadcastAsUser(intent,
-                                new UserHandle(ActivityManager.getCurrentUser()));
+            try {
+                Log.startSession("TSI.sDD");
+                enforcePermission(MODIFY_PHONE_STATE);
+                enforcePermission(WRITE_SECURE_SETTINGS);
+                synchronized (mLock) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        final boolean result =
+                                DefaultDialerManager.setDefaultDialerApplication(mContext,
+                                        packageName);
+                        if (result) {
+                            final Intent intent =
+                                    new Intent(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
+                            intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
+                                    packageName);
+                            mContext.sendBroadcastAsUser(intent,
+                                    new UserHandle(ActivityManager.getCurrentUser()));
+                        }
+                        return result;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
                     }
-                    return result;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
                 }
+            } finally {
+                Log.endSession();
             }
         }
 
@@ -918,6 +1114,11 @@
                 pw.increaseIndent();
                 mPhoneAccountRegistrar.dump(pw);
                 pw.decreaseIndent();
+
+                pw.println("Analytics:");
+                pw.increaseIndent();
+                Analytics.dump(pw);
+                pw.decreaseIndent();
             }
 
             Log.dumpCallEvents(pw);
@@ -956,63 +1157,9 @@
     // Supporting methods for the ITelecomService interface implementation.
     //
 
-    private boolean isVisibleToCaller(PhoneAccountHandle accountHandle) {
-        if (accountHandle == null) {
-            return false;
-        }
-        return isVisibleToCaller(mPhoneAccountRegistrar.getPhoneAccount(accountHandle));
-    }
-
-    private boolean isVisibleToCaller(PhoneAccount account) {
-        if (account == null) {
-            return false;
-        }
-
-        // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and
-        // all profiles. Only Telephony and SIP accounts should have this capability.
-        if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
-            return true;
-        }
-
-        UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle();
-        if (phoneAccountUserHandle == null) {
-            return false;
-        }
-
-        if (phoneAccountUserHandle.equals(Binder.getCallingUserHandle())) {
-            return true;
-        }
-
-        List<UserHandle> profileUserHandles;
-        if (UserHandle.getCallingUserId() == UserHandle.USER_OWNER) {
-            profileUserHandles = mUserManager.getUserProfiles();
-        } else {
-            // Otherwise, it has to be owned by the current caller's profile.
-            profileUserHandles = new ArrayList<>(1);
-            profileUserHandles.add(Binder.getCallingUserHandle());
-        }
-
-        return profileUserHandles.contains(phoneAccountUserHandle);
-    }
-
-    /**
-     * Given a list of {@link PhoneAccountHandle}s, filter them to the ones that the calling
-     * user can see.
-     *
-     * @param phoneAccountHandles Unfiltered list of account handles.
-     *
-     * @return {@link PhoneAccountHandle}s visible to the calling user and its profiles.
-     */
-    private List<PhoneAccountHandle> filterForAccountsVisibleToCaller(
-            List<PhoneAccountHandle> phoneAccountHandles) {
-        List<PhoneAccountHandle> profilePhoneAccountHandles =
-                new ArrayList<>(phoneAccountHandles.size());
-        for (PhoneAccountHandle phoneAccountHandle : phoneAccountHandles) {
-            if (isVisibleToCaller(phoneAccountHandle)) {
-                profilePhoneAccountHandles.add(phoneAccountHandle);
-            }
-        }
-        return profilePhoneAccountHandles;
+    private boolean isPhoneAccountHandleVisibleToCallingUser(
+            PhoneAccountHandle phoneAccountUserHandle, UserHandle callingUser) {
+        return mPhoneAccountRegistrar.getPhoneAccount(phoneAccountUserHandle, callingUser) != null;
     }
 
     private boolean isCallerSystemApp() {
@@ -1038,10 +1185,13 @@
         return false;
     }
 
-    private void acceptRingingCallInternal() {
+    private void acceptRingingCallInternal(int videoState) {
         Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
         if (call != null) {
-            call.answer(call.getVideoState());
+            if (videoState == DEFAULT_VIDEO_STATE || !isValidAcceptVideoState(videoState)) {
+                videoState = call.getVideoState();
+            }
+            call.answer(videoState);
         }
     }
 
@@ -1131,6 +1281,14 @@
         }
     }
 
+    private void enforceCrossUserPermission(int callingUid) {
+        if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
+                            + " INTERACT_ACROSS_USERS_FULL permission");
+        }
+    }
+
     private void enforceFeature(String feature) {
         PackageManager pm = mContext.getPackageManager();
         if (!pm.hasSystemFeature(feature)) {
@@ -1177,10 +1335,10 @@
     }
 
     private boolean isCallerSimCallManager() {
-        PhoneAccountHandle accountHandle = null;
         long token = Binder.clearCallingIdentity();
+        PhoneAccountHandle accountHandle = null;
         try {
-            accountHandle = mPhoneAccountRegistrar.getSimCallManager();
+             accountHandle = mPhoneAccountRegistrar.getSimCallManagerOfCurrentUser();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1204,4 +1362,23 @@
     private TelephonyManager getTelephonyManager() {
         return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
     }
+
+    /**
+     * Determines if a video state is valid for accepting an incoming call.
+     * For the purpose of accepting a call, states {@link VideoProfile#STATE_AUDIO_ONLY}, and
+     * any combination of {@link VideoProfile#STATE_RX_ENABLED} and
+     * {@link VideoProfile#STATE_TX_ENABLED} are considered valid.
+     *
+     * @param videoState The video state.
+     * @return {@code true} if the video state is valid, {@code false} otherwise.
+     */
+    private boolean isValidAcceptVideoState(int videoState) {
+        // Given a video state input, turn off TX and RX so that we can determine if those were the
+        // only bits set.
+        int remainingState = videoState & ~VideoProfile.STATE_TX_ENABLED;
+        remainingState = remainingState & ~VideoProfile.STATE_RX_ENABLED;
+
+        // If only TX or RX were set (or neither), the video state is valid.
+        return remainingState == 0;
+    }
 }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index c3ab0dc..e0c3602 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import android.Manifest;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -54,6 +55,25 @@
     private static final IntentFilter USER_SWITCHED_FILTER =
             new IntentFilter(Intent.ACTION_USER_SWITCHED);
 
+    /** Intent filter for dialer secret codes. */
+    private static final IntentFilter DIALER_SECRET_CODE_FILTER;
+
+    /**
+     * Initializes the dialer secret code intent filter.  Setup to handle the various secret codes
+     * which can be dialed (e.g. in format *#*#code#*#*) to trigger various behavior in Telecom.
+     */
+    static {
+        DIALER_SECRET_CODE_FILTER = new IntentFilter(
+                "android.provider.Telephony.SECRET_CODE");
+        DIALER_SECRET_CODE_FILTER.addDataScheme("android_secret_code");
+        DIALER_SECRET_CODE_FILTER
+                .addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_DEBUG_ON, null);
+        DIALER_SECRET_CODE_FILTER
+                .addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_DEBUG_OFF, null);
+        DIALER_SECRET_CODE_FILTER
+                .addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_MARK, null);
+    }
+
     private static TelecomSystem INSTANCE = null;
 
     private final SyncRoot mLock = new SyncRoot() { };
@@ -67,6 +87,7 @@
     private final TelecomBroadcastIntentProcessor mTelecomBroadcastIntentProcessor;
     private final TelecomServiceImpl mTelecomServiceImpl;
     private final ContactsAsyncHelper mContactsAsyncHelper;
+    private final DialerCodeReceiver mDialerCodeReceiver;
 
     private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
         @Override
@@ -95,12 +116,18 @@
             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
             HeadsetMediaButtonFactory headsetMediaButtonFactory,
             ProximitySensorManagerFactory proximitySensorManagerFactory,
-            InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
+            InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
+            CallAudioManager.AudioServiceFactory audioServiceFactory,
+            BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory
+                    bluetoothPhoneServiceImplFactory) {
         mContext = context.getApplicationContext();
+        Log.setContext(mContext);
 
         mMissedCallNotifier = missedCallNotifier;
         mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext);
         mContactsAsyncHelper = new ContactsAsyncHelper(mLock);
+        BluetoothManager bluetoothManager = new BluetoothManager(mContext);
+        WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
 
         mCallsManager = new CallsManager(
                 mContext,
@@ -111,17 +138,26 @@
                 mPhoneAccountRegistrar,
                 headsetMediaButtonFactory,
                 proximitySensorManagerFactory,
-                inCallWakeLockControllerFactory);
+                inCallWakeLockControllerFactory,
+                audioServiceFactory,
+                bluetoothManager,
+                wiredHeadsetManager);
 
         mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
         mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
 
         mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
-        mBluetoothPhoneServiceImpl = new BluetoothPhoneServiceImpl(
+        mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
                 mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
         mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
         mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
                 mContext, mCallsManager);
+
+        // Register the receiver for the dialer secret codes, used to enable extended logging.
+        mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
+        mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
+                Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
+
         mTelecomServiceImpl = new TelecomServiceImpl(
                 mContext, mCallsManager, mPhoneAccountRegistrar, mLock);
     }
diff --git a/src/com/android/server/telecom/TelephonyUtil.java b/src/com/android/server/telecom/TelephonyUtil.java
index 5f43ee9..69adaf9 100644
--- a/src/com/android/server/telecom/TelephonyUtil.java
+++ b/src/com/android/server/telecom/TelephonyUtil.java
@@ -23,6 +23,8 @@
 import android.telecom.PhoneAccountHandle;
 import android.telephony.PhoneNumberUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Utilities to deal with the system telephony services. The system telephony services are treated
  * differently from 3rd party services in some situations (emergency calls, audio focus, etc...).
@@ -45,7 +47,8 @@
      * account are not expected to be displayed in the UI, so the description, etc are not
      * populated.
      */
-    static PhoneAccount getDefaultEmergencyPhoneAccount() {
+    @VisibleForTesting
+    public static PhoneAccount getDefaultEmergencyPhoneAccount() {
         return PhoneAccount.builder(DEFAULT_EMERGENCY_PHONE_ACCOUNT_HANDLE, "E")
                 .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
                         PhoneAccount.CAPABILITY_CALL_PROVIDER |
diff --git a/src/com/android/server/telecom/Timeouts.java b/src/com/android/server/telecom/Timeouts.java
index f047971..ba16990 100644
--- a/src/com/android/server/telecom/Timeouts.java
+++ b/src/com/android/server/telecom/Timeouts.java
@@ -98,4 +98,21 @@
         return get(contentResolver, "call_remove_unbind_in_call_services_delay",
                 2000L /* 2 seconds */);
     }
+
+    /**
+     * Returns the amount of time for which bluetooth is considered connected after requesting
+     * connection. This compensates for the amount of time it takes for the audio route to
+     * actually change to bluetooth.
+     */
+    public static long getBluetoothPendingTimeoutMillis(ContentResolver contentResolver) {
+        return get(contentResolver, "bluetooth_pending_timeout_millis", 5000L);
+    }
+
+    /**
+     * Returns the amount of time after a Logging session has been started that Telecom is set to
+     * perform a sweep to check and make sure that the session is still not incomplete (stale).
+     */
+    public static long getStaleSessionCleanupTimeoutMillis(ContentResolver contentResolver) {
+        return get(contentResolver, "stale_session_cleanup_timeout_millis", 30000L);
+    }
 }
diff --git a/src/com/android/server/telecom/UserUtil.java b/src/com/android/server/telecom/UserUtil.java
new file mode 100644
index 0000000..a35a61e
--- /dev/null
+++ b/src/com/android/server/telecom/UserUtil.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+public final class UserUtil {
+
+    private UserUtil() {
+    }
+
+    private static UserInfo getUserInfoFromUserHandle(Context context, UserHandle userHandle) {
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        return userManager.getUserInfo(userHandle.getIdentifier());
+    }
+
+    public static boolean isManagedProfile(Context context, UserHandle userHandle) {
+        UserInfo userInfo = getUserInfoFromUserHandle(context, userHandle);
+        return userInfo != null && userInfo.isManagedProfile();
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/VideoProviderProxy.java b/src/com/android/server/telecom/VideoProviderProxy.java
index 7dcfdfb..b531242 100644
--- a/src/com/android/server/telecom/VideoProviderProxy.java
+++ b/src/com/android/server/telecom/VideoProviderProxy.java
@@ -206,6 +206,9 @@
          * Proxies a request from the {@link #mConectionServiceVideoProvider} to the
          * {@link InCallService} when the call data usage changes.
          *
+         * Also tracks the current call data usage on the {@link Call} for use when writing to the
+         * call log.
+         *
          * @param dataUsage The data usage.
          */
         @Override
@@ -213,6 +216,7 @@
             synchronized (mLock) {
                 logFromVideoProvider("changeCallDataUsage: " + dataUsage);
                 VideoProviderProxy.this.setCallDataUsage(dataUsage);
+                mCall.setCallDataUsage(dataUsage);
             }
         }
 
diff --git a/src/com/android/server/telecom/WiredHeadsetManager.java b/src/com/android/server/telecom/WiredHeadsetManager.java
index ef5f38c..f25e928 100644
--- a/src/com/android/server/telecom/WiredHeadsetManager.java
+++ b/src/com/android/server/telecom/WiredHeadsetManager.java
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Collections;
@@ -29,8 +30,10 @@
 import java.util.concurrent.ConcurrentHashMap;
 
 /** Listens for and caches headset state. */
-class WiredHeadsetManager {
-    interface Listener {
+@VisibleForTesting
+public class WiredHeadsetManager {
+    @VisibleForTesting
+    public interface Listener {
         void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn);
     }
 
@@ -58,7 +61,7 @@
     private final Set<Listener> mListeners = Collections.newSetFromMap(
             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
 
-    WiredHeadsetManager(Context context) {
+    public WiredHeadsetManager(Context context) {
         mReceiver = new WiredHeadsetBroadcastReceiver();
 
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -69,7 +72,8 @@
         context.registerReceiver(mReceiver, intentFilter);
     }
 
-    void addListener(Listener listener) {
+    @VisibleForTesting
+    public void addListener(Listener listener) {
         mListeners.add(listener);
     }
 
@@ -79,7 +83,8 @@
         }
     }
 
-    boolean isPluggedIn() {
+    @VisibleForTesting
+    public boolean isPluggedIn() {
         return mIsPluggedIn;
     }
 
diff --git a/src/com/android/server/telecom/components/PrimaryCallReceiver.java b/src/com/android/server/telecom/components/PrimaryCallReceiver.java
index 5198862..a05f04e 100644
--- a/src/com/android/server/telecom/components/PrimaryCallReceiver.java
+++ b/src/com/android/server/telecom/components/PrimaryCallReceiver.java
@@ -1,5 +1,6 @@
 package com.android.server.telecom.components;
 
+import com.android.server.telecom.Log;
 import com.android.server.telecom.TelecomSystem;
 
 import android.content.BroadcastReceiver;
@@ -16,9 +17,11 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
+        Log.startSession("PCR.oR");
         synchronized (getTelecomSystem().getLock()) {
             getTelecomSystem().getCallIntentProcessor().processIntent(intent);
         }
+        Log.endSession();
     }
 
     @Override
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 7856094..3d24aa2 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -20,14 +20,19 @@
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
 import android.content.Intent;
+import android.media.IAudioService;
 import android.os.IBinder;
+import android.os.ServiceManager;
 
 import com.android.internal.telephony.CallerInfoAsyncQuery;
+import com.android.server.telecom.BluetoothPhoneServiceImpl;
+import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
 import com.android.server.telecom.InCallWakeLockControllerFactory;
+import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.Log;
@@ -101,7 +106,25 @@
                                         CallsManager callsManager) {
                                     return new InCallWakeLockController(context, callsManager);
                                 }
-                            }));
+                            },
+                            new CallAudioManager.AudioServiceFactory() {
+                                @Override
+                                public IAudioService getAudioService() {
+                                    return IAudioService.Stub.asInterface(
+                                            ServiceManager.getService(Context.AUDIO_SERVICE));
+                                }
+                            },
+                            new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
+                                @Override
+                                public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(
+                                        Context context, TelecomSystem.SyncRoot lock,
+                                        CallsManager callsManager,
+                                        PhoneAccountRegistrar phoneAccountRegistrar) {
+                                    return new BluetoothPhoneServiceImpl(context, lock,
+                                            callsManager, phoneAccountRegistrar);
+                                }
+                            }
+                    ));
         }
         if (BluetoothAdapter.getDefaultAdapter() != null) {
             context.startService(new Intent(context, BluetoothPhoneService.class));
diff --git a/src/com/android/server/telecom/components/UserCallActivity.java b/src/com/android/server/telecom/components/UserCallActivity.java
index d88e09e..39cb4f9 100644
--- a/src/com/android/server/telecom/components/UserCallActivity.java
+++ b/src/com/android/server/telecom/components/UserCallActivity.java
@@ -52,18 +52,23 @@
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
-        // TODO: Figure out if there is something to restore from bundle.
-        // See OutgoingCallBroadcaster in services/Telephony for more.
-        Intent intent = getIntent();
-        verifyCallAction(intent);
-        final UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
-        final UserHandle userHandle = new UserHandle(userManager.getUserHandle());
-        // Once control flow has passed to this activity, it is no longer guaranteed that we can
-        // accurately determine whether the calling package has the CALL_PHONE runtime permission.
-        // At this point in time we trust that the ActivityManager has already performed this
-        // validation before starting this activity.
-        new UserCallIntentProcessor(this, userHandle).processIntent(getIntent(),
-                getCallingPackage(), true /* hasCallAppOp*/);
+        Log.startSession("UCA.oC");
+        try {
+            // TODO: Figure out if there is something to restore from bundle.
+            // See OutgoingCallBroadcaster in services/Telephony for more.
+            Intent intent = getIntent();
+            verifyCallAction(intent);
+            final UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+            final UserHandle userHandle = new UserHandle(userManager.getUserHandle());
+            // Once control flow has passed to this activity, it is no longer guaranteed that we can
+            // accurately determine whether the calling package has the CALL_PHONE runtime permission.
+            // At this point in time we trust that the ActivityManager has already performed this
+            // validation before starting this activity.
+            new UserCallIntentProcessor(this, userHandle).processIntent(getIntent(),
+                    getCallingPackage(), true /* hasCallAppOp*/);
+        } finally {
+            Log.endSession();
+        }
         finish();
     }
 
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index 8f451b5..a73a33e 100644
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -123,15 +123,9 @@
                 VideoProfile.STATE_AUDIO_ONLY);
         Log.d(this, "processOutgoingCallIntent videoState = " + videoState);
 
-        if (VideoProfile.isVideo(videoState)
-                && TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
-            Log.d(this, "Emergency call...Converting video call to voice...");
-            videoState = VideoProfile.STATE_AUDIO_ONLY;
-            intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
-                    videoState);
-        }
+        if (VideoProfile.isVideo(videoState) && isTtyModeEnabled() &&
+                !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
 
-        if (VideoProfile.isVideo(videoState) && isTtyModeEnabled()) {
             Toast.makeText(mContext, mContext.getResources().getString(R.string.
                     video_call_not_allowed_if_tty_enabled), Toast.LENGTH_SHORT).show();
             Log.d(this, "Rejecting video calls as tty is enabled");
@@ -140,6 +134,10 @@
 
         intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
                 isDefaultOrSystemDialer(callingPackageName));
+
+        // Save the user handle of current user before forwarding the intent to primary user.
+        intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
+
         sendBroadcastToReceiver(intent);
     }
 
@@ -184,7 +182,7 @@
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setClass(mContext, PrimaryCallReceiver.class);
         Log.d(this, "Sending broadcast as user to CallReceiver");
-        mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+        mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
         return true;
     }
 
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index a864a83..b650400 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.telecom.TelecomManager;
+
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
@@ -75,6 +76,19 @@
  */
 public class MissedCallNotifierImpl extends CallsManagerListenerBase implements MissedCallNotifier {
 
+    public interface NotificationBuilderFactory {
+        Notification.Builder getBuilder(Context context);
+    }
+
+    private static class DefaultNotificationBuilderFactory implements NotificationBuilderFactory {
+        public DefaultNotificationBuilderFactory() {}
+
+        @Override
+        public Notification.Builder getBuilder(Context context) {
+            return new Notification.Builder(context);
+        }
+    }
+
     private static final String[] CALL_LOG_PROJECTION = new String[] {
         Calls._ID,
         Calls.NUMBER,
@@ -95,6 +109,7 @@
 
     private final Context mContext;
     private final NotificationManager mNotificationManager;
+    private final NotificationBuilderFactory mNotificationBuilderFactory;
 
     private final ComponentName mNotificationComponent;
 
@@ -102,11 +117,17 @@
     private int mMissedCallCount = 0;
 
     public MissedCallNotifierImpl(Context context) {
+        this(context, new DefaultNotificationBuilderFactory());
+    }
+
+    public MissedCallNotifierImpl(Context context,
+            NotificationBuilderFactory notificationBuilderFactory) {
         mContext = context;
         mNotificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         final String notificationComponent = context.getString(R.string.notification_component);
 
+        mNotificationBuilderFactory = notificationBuilderFactory;
         mNotificationComponent = notificationComponent != null
                 ? ComponentName.unflattenFromString(notificationComponent) : null;
     }
@@ -149,8 +170,6 @@
 
     /**
      * Broadcasts missed call notification to custom component if set.
-     * @param number The phone number associated with the notification. null if
-     *               no call.
      * @param count The number of calls associated with the notification.
      * @return {@code true} if the broadcast was sent. {@code false} otherwise.
      */
@@ -215,7 +234,7 @@
 
         // Create a public viewable version of the notification, suitable for display when sensitive
         // notification content is hidden.
-        Notification.Builder publicBuilder = new Notification.Builder(mContext);
+        Notification.Builder publicBuilder = mNotificationBuilderFactory.getBuilder(mContext);
         publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                 .setColor(mContext.getResources().getColor(R.color.theme_color))
                 .setWhen(call.getCreationTimeMillis())
@@ -229,7 +248,7 @@
                 .setDeleteIntent(createClearMissedCallsPendingIntent());
 
         // Create the notification suitable for display when sensitive information is showing.
-        Notification.Builder builder = new Notification.Builder(mContext);
+        Notification.Builder builder = mNotificationBuilderFactory.getBuilder(mContext);
         builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                 .setColor(mContext.getResources().getColor(R.color.theme_color))
                 .setWhen(call.getCreationTimeMillis())
@@ -470,9 +489,10 @@
                             synchronized (lock) {
 
                                 // Convert the data to a call object
-                                Call call = new Call(mContext, callsManager, lock,
-                                        null, contactsAsyncHelper, callerInfoAsyncQueryFactory,
-                                        null, null, null, null, true, false);
+                                Call call = new Call(Call.CALL_ID_UNKNOWN, mContext, callsManager,
+                                        lock, null, contactsAsyncHelper,
+                                        callerInfoAsyncQueryFactory, null, null, null, null,
+                                        Call.CALL_DIRECTION_INCOMING, false, false);
                                 call.setDisconnectCause(
                                         new DisconnectCause(DisconnectCause.MISSED));
                                 call.setState(CallState.DISCONNECTED, "throw away call");
diff --git a/testapps/AndroidManifest.xml b/testapps/AndroidManifest.xml
index df333a5..0672b6d 100644
--- a/testapps/AndroidManifest.xml
+++ b/testapps/AndroidManifest.xml
@@ -18,6 +18,10 @@
           coreApp="true"
           package="com.android.server.telecom.testapps">
 
+    <uses-sdk
+        android:minSdkVersion="23"
+        android:targetSdkVersion="23" />
+
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE" />
diff --git a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
index c1ced80..28c43c8 100644
--- a/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
+++ b/testapps/src/com/android/server/telecom/testapps/CallServiceNotifier.java
@@ -29,6 +29,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Bundle;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -103,6 +104,14 @@
 
         telecomManager.clearAccounts();
 
+        Bundle testBundle = new Bundle();
+        testBundle.putInt("EXTRA_INT_1", 1);
+        testBundle.putInt("EXTRA_INT_100", 100);
+        testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
+        testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
+        testBundle.putString("EXTRA_STR1", "Hello");
+        testBundle.putString("EXTRA_STR2", "There");
+
         telecomManager.registerPhoneAccount(PhoneAccount.builder(
                 new PhoneAccountHandle(
                         new ComponentName(context, TestConnectionService.class),
@@ -115,10 +124,11 @@
                         PhoneAccount.CAPABILITY_CALL_SUBJECT)
                 .setIcon(Icon.createWithResource(
                         context.getResources(), R.drawable.stat_sys_phone_call))
-                // TODO: Add icon tint (Color.RED)
                 .setHighlightColor(Color.RED)
+                // TODO: Add icon tint (Color.RED)
                 .setShortDescription("a short description for the call provider")
                 .setSupportedUriSchemes(Arrays.asList("tel"))
+                .setExtras(testBundle)
                 .build());
 
         telecomManager.registerPhoneAccount(PhoneAccount.builder(
@@ -134,8 +144,8 @@
                         PhoneAccount.CAPABILITY_CALL_SUBJECT)
                 .setIcon(Icon.createWithResource(
                         context.getResources(), R.drawable.stat_sys_phone_call))
-                // TODO: Add icon tint (Color.GREEN)
                 .setHighlightColor(Color.GREEN)
+                // TODO: Add icon tint (Color.GREEN)
                 .setShortDescription("a short description for the sim subscription")
                 .build());
 
diff --git a/tests/Android.mk b/tests/Android.mk
index a802768..317bfe3 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -39,6 +39,9 @@
     --auto-add-overlay \
     --extra-packages com.android.server.telecom
 
+LOCAL_JACK_ENABLED := disabled
+LOCAL_PROGUARD_ENABLED := disabled
+
 LOCAL_PACKAGE_NAME := TelecomUnitTests
 LOCAL_CERTIFICATE := platform
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 6a08c63..4576690 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -19,6 +19,10 @@
           package="com.android.server.telecom.tests"
           android:debuggable="true">
 
+    <uses-sdk
+        android:minSdkVersion="23"
+        android:targetSdkVersion="23" />
+
     <!-- TODO: Needed because we call BluetoothAdapter.getDefaultAdapter() statically, and
          BluetoothAdapter is a final class. -->
     <uses-permission android:name="android.permission.BLUETOOTH" />
@@ -27,6 +31,9 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
 
+    <!-- Used to access TelephonyManager APIs -->
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+
     <application android:label="@string/app_name"
                  android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
new file mode 100644
index 0000000..74a2d64
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/BluetoothPhoneServiceTest.java
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.ComponentName;
+import android.content.Intent;
+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 com.android.server.telecom.BluetoothHeadsetProxy;
+import com.android.server.telecom.BluetoothPhoneServiceImpl;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.TelecomSystem;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyChar;
+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.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+public class BluetoothPhoneServiceTest extends TelecomTestCase {
+
+    private static final int TEST_DTMF_TONE = 0;
+    private static final String TEST_ACCOUNT_ADDRESS = "//foo.com/";
+    private static final int TEST_ACCOUNT_INDEX = 0;
+
+    // match up with BluetoothPhoneServiceImpl
+    private static final int CALL_STATE_ACTIVE = 0;
+    private static final int CALL_STATE_HELD = 1;
+    private static final int CALL_STATE_DIALING = 2;
+    private static final int CALL_STATE_ALERTING = 3;
+    private static final int CALL_STATE_INCOMING = 4;
+    private static final int CALL_STATE_WAITING = 5;
+    private static final int CALL_STATE_IDLE = 6;
+    // 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
+    private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
+    // Hold all active calls and accepts a waiting/held call
+    private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
+    // Add all held calls to a conference
+    private static final int CHLD_TYPE_ADDHELDTOCONF = 3;
+
+    private BluetoothPhoneServiceImpl mBluetoothPhoneService;
+    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {
+    };
+
+    @Mock CallsManager mMockCallsManager;
+    @Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar;
+    @Mock BluetoothHeadsetProxy mMockBluetoothHeadset;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+
+        // Ensure initialization does not actually try to access any of the CallsManager fields.
+        // This also works to return null if it is not overwritten later in the test.
+        doNothing().when(mMockCallsManager).addListener(any(
+                CallsManager.CallsManagerListener.class));
+        doReturn(null).when(mMockCallsManager).getActiveCall();
+        doReturn(null).when(mMockCallsManager).getRingingCall();
+        doReturn(null).when(mMockCallsManager).getHeldCall();
+        doReturn(null).when(mMockCallsManager).getOutgoingCall();
+        doReturn(0).when(mMockCallsManager).getNumHeldCalls();
+        mBluetoothPhoneService = new BluetoothPhoneServiceImpl(mContext, mLock, mMockCallsManager,
+                mMockPhoneAccountRegistrar);
+
+        // Bring in test Bluetooth Headset
+        mBluetoothPhoneService.setBluetoothHeadset(mMockBluetoothHeadset);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+
+        mBluetoothPhoneService = null;
+        super.tearDown();
+    }
+
+    public void testHeadsetAnswerCall() throws Exception {
+        Call mockCall = createRingingCall();
+
+        boolean callAnswered = mBluetoothPhoneService.mBinder.answerCall();
+
+        verify(mMockCallsManager).answerCall(eq(mockCall), any(int.class));
+        assertEquals(callAnswered, true);
+    }
+
+    public void testHeadsetAnswerCallNull() throws Exception {
+        when(mMockCallsManager.getRingingCall()).thenReturn(null);
+
+        boolean callAnswered = mBluetoothPhoneService.mBinder.answerCall();
+
+        verify(mMockCallsManager,never()).answerCall(any(Call.class), any(int.class));
+        assertEquals(callAnswered, false);
+    }
+
+    public void testHeadsetHangupCall() throws Exception {
+        Call mockCall = createForegroundCall();
+
+        boolean callHungup = mBluetoothPhoneService.mBinder.hangupCall();
+
+        verify(mMockCallsManager).disconnectCall(eq(mockCall));
+        assertEquals(callHungup, true);
+    }
+
+    public void testHeadsetHangupCallNull() throws Exception {
+        when(mMockCallsManager.getForegroundCall()).thenReturn(null);
+
+        boolean callHungup = mBluetoothPhoneService.mBinder.hangupCall();
+
+        verify(mMockCallsManager,never()).disconnectCall(any(Call.class));
+        assertEquals(callHungup, false);
+    }
+
+    public void testHeadsetSendDTMF() throws Exception {
+        Call mockCall = createForegroundCall();
+
+        boolean sentDtmf = mBluetoothPhoneService.mBinder.sendDtmf(TEST_DTMF_TONE);
+
+        verify(mMockCallsManager).playDtmfTone(eq(mockCall), eq((char) TEST_DTMF_TONE));
+        verify(mMockCallsManager).stopDtmfTone(eq(mockCall));
+        assertEquals(sentDtmf, true);
+    }
+
+    public void testHeadsetSendDTMFNull() throws Exception {
+        when(mMockCallsManager.getForegroundCall()).thenReturn(null);
+
+        boolean sentDtmf = mBluetoothPhoneService.mBinder.sendDtmf(TEST_DTMF_TONE);
+
+        verify(mMockCallsManager,never()).playDtmfTone(any(Call.class), anyChar());
+        verify(mMockCallsManager,never()).stopDtmfTone(any(Call.class));
+        assertEquals(sentDtmf, false);
+    }
+
+    public void testGetNetworkOperator() throws Exception {
+        Call mockCall = createForegroundCall();
+        PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
+        when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
+                any(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
+
+        String networkOperator = mBluetoothPhoneService.mBinder.getNetworkOperator();
+
+        assertEquals(networkOperator, "label0");
+    }
+
+    public void testGetNetworkOperatorNoPhoneAccount() throws Exception {
+        when(mMockCallsManager.getForegroundCall()).thenReturn(null);
+
+        String networkOperator = mBluetoothPhoneService.mBinder.getNetworkOperator();
+
+        assertEquals(networkOperator, "label1");
+    }
+
+    public void testGetSubscriberNumber() throws Exception {
+        Call mockCall = createForegroundCall();
+        PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
+        when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
+                any(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
+
+        String subscriberNumber = mBluetoothPhoneService.mBinder.getSubscriberNumber();
+
+        assertEquals(subscriberNumber, TEST_ACCOUNT_ADDRESS + TEST_ACCOUNT_INDEX);
+    }
+
+    public void testGetSubscriberNumberFallbackToTelephony() throws Exception {
+        Call mockCall = createForegroundCall();
+        String fakeNumber = "8675309";
+        when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
+                any(PhoneAccountHandle.class))).thenReturn(null);
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(
+                any(PhoneAccountHandle.class))).thenReturn(null);
+        when(TelephonyManager.from(mContext).getLine1Number()).thenReturn(fakeNumber);
+
+        String subscriberNumber = mBluetoothPhoneService.mBinder.getSubscriberNumber();
+
+        assertEquals(subscriberNumber, fakeNumber);
+    }
+
+    public void testListCurrentCallsOneCall() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        Call activeCall = createActiveCall();
+        when(activeCall.getState()).thenReturn(CallState.ACTIVE);
+        calls.add(activeCall);
+        when(activeCall.isConference()).thenReturn(false);
+        when(activeCall.getHandle()).thenReturn(Uri.parse("tel:555-000"));
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+
+        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(0), eq(0), eq(false),
+                eq("555-000"), eq(PhoneNumberUtils.TOA_Unknown));
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+    }
+
+    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
+        // transitioning into a conference.
+        // Call has been put into a CDMA "conference" with one call on hold.
+        ArrayList<Call> calls = new ArrayList<>();
+        Call parentCall = createActiveCall();
+        final Call confCall1 = mock(Call.class);
+        final Call confCall2 = createHeldCall();
+        calls.add(parentCall);
+        calls.add(confCall1);
+        calls.add(confCall2);
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        when(confCall1.getState()).thenReturn(CallState.ACTIVE);
+        when(confCall2.getState()).thenReturn(CallState.ACTIVE);
+        when(confCall1.isIncoming()).thenReturn(false);
+        when(confCall2.isIncoming()).thenReturn(true);
+        when(confCall1.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+        when(confCall2.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0001")));
+        addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
+        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
+        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentCall.getConferenceLevelActiveCall()).thenReturn(confCall1);
+        when(parentCall.isConference()).thenReturn(true);
+        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
+            add(confCall1);
+            add(confCall2);
+        }});
+        //Add links from child calls to parent
+        when(confCall1.getParentCall()).thenReturn(parentCall);
+        when(confCall2.getParentCall()).thenReturn(parentCall);
+
+        mBluetoothPhoneService.mBinder.queryPhoneState();
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(true);
+        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+        when(mMockCallsManager.getHeldCall()).thenReturn(null);
+        // Spurious call to onIsConferencedChanged.
+        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
+        // Make sure the call has only occurred collectively 2 times (not on the third)
+        verify(mMockBluetoothHeadset, times(2)).phoneStateChanged(any(int.class),
+                any(int.class), any(int.class), any(String.class), any(int.class));
+    }
+
+    public void testListCurrentCallsCdmaHold() throws Exception {
+        // Call has been put into a CDMA "conference" with one call on hold.
+        ArrayList<Call> calls = new ArrayList<>();
+        Call parentCall = createActiveCall();
+        final Call foregroundCall = mock(Call.class);
+        final Call heldCall = createHeldCall();
+        calls.add(parentCall);
+        calls.add(foregroundCall);
+        calls.add(heldCall);
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        when(foregroundCall.getState()).thenReturn(CallState.ACTIVE);
+        when(heldCall.getState()).thenReturn(CallState.ACTIVE);
+        when(foregroundCall.isIncoming()).thenReturn(false);
+        when(heldCall.isIncoming()).thenReturn(true);
+        when(foregroundCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+        when(heldCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0001")));
+        addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
+        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
+        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentCall.getConferenceLevelActiveCall()).thenReturn(foregroundCall);
+        when(parentCall.isConference()).thenReturn(true);
+        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
+            add(foregroundCall);
+            add(heldCall);
+        }});
+        //Add links from child calls to parent
+        when(foregroundCall.getParentCall()).thenReturn(parentCall);
+        when(heldCall.getParentCall()).thenReturn(parentCall);
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+
+        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_ACTIVE), eq(0),
+                eq(false), eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
+        verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(1), eq(CALL_STATE_HELD), eq(0),
+                eq(false), eq("555-0001"), eq(PhoneNumberUtils.TOA_Unknown));
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+    }
+
+    public void testListCurrentCallsCdmaConference() throws Exception {
+        // Call is in a true CDMA conference
+        ArrayList<Call> calls = new ArrayList<>();
+        Call parentCall = createActiveCall();
+        final Call confCall1 = mock(Call.class);
+        final Call confCall2 = createHeldCall();
+        calls.add(parentCall);
+        calls.add(confCall1);
+        calls.add(confCall2);
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        when(confCall1.getState()).thenReturn(CallState.ACTIVE);
+        when(confCall2.getState()).thenReturn(CallState.ACTIVE);
+        when(confCall1.isIncoming()).thenReturn(false);
+        when(confCall2.isIncoming()).thenReturn(true);
+        when(confCall1.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+        when(confCall2.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0001")));
+        removeCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
+        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(true);
+        when(parentCall.getConferenceLevelActiveCall()).thenReturn(confCall1);
+        when(parentCall.isConference()).thenReturn(true);
+        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
+            add(confCall1);
+            add(confCall2);
+        }});
+        //Add links from child calls to parent
+        when(confCall1.getParentCall()).thenReturn(parentCall);
+        when(confCall2.getParentCall()).thenReturn(parentCall);
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+
+        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_ACTIVE), eq(0),
+                eq(true), eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
+        verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(1), eq(CALL_STATE_ACTIVE), eq(0),
+                eq(true), eq("555-0001"), eq(PhoneNumberUtils.TOA_Unknown));
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+    }
+
+    public void testWaitingCallClccResponse() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        // This test does not define a value for getForegroundCall(), so this ringing call will
+        // be treated as if it is a waiting call when listCurrentCalls() is invoked.
+        Call waitingCall = createRingingCall();
+        calls.add(waitingCall);
+        when(waitingCall.isIncoming()).thenReturn(true);
+        when(waitingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+        when(waitingCall.getState()).thenReturn(CallState.RINGING);
+        when(waitingCall.isConference()).thenReturn(false);
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_WAITING, 0, false,
+                "555-0000", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+        verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
+                anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+    }
+
+    public void testNewCallClccResponse() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        Call newCall = createForegroundCall();
+        calls.add(newCall);
+        when(newCall.getState()).thenReturn(CallState.NEW);
+        when(newCall.isConference()).thenReturn(false);
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+        verify(mMockBluetoothHeadset, times(1)).clccResponse(anyInt(),
+                anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+    }
+
+    public void testRingingCallClccResponse() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        Call ringingCall = createForegroundCall();
+        calls.add(ringingCall);
+        when(ringingCall.getState()).thenReturn(CallState.RINGING);
+        when(ringingCall.isIncoming()).thenReturn(true);
+        when(ringingCall.isConference()).thenReturn(false);
+        when(ringingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_INCOMING, 0, false,
+                "555-0000", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+        verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
+                anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+    }
+
+    public void testCallClccCache() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        Call ringingCall = createForegroundCall();
+        calls.add(ringingCall);
+        when(ringingCall.getState()).thenReturn(CallState.RINGING);
+        when(ringingCall.isIncoming()).thenReturn(true);
+        when(ringingCall.isConference()).thenReturn(false);
+        when(ringingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_INCOMING, 0, false,
+                "555-0000", PhoneNumberUtils.TOA_Unknown);
+
+        // Test Caching of old call indicies in clcc
+        when(ringingCall.getState()).thenReturn(CallState.ACTIVE);
+        Call newHoldingCall = createHeldCall();
+        calls.add(0, newHoldingCall);
+        when(newHoldingCall.getState()).thenReturn(CallState.ON_HOLD);
+        when(newHoldingCall.isIncoming()).thenReturn(true);
+        when(newHoldingCall.isConference()).thenReturn(false);
+        when(newHoldingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0001")));
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_ACTIVE, 0, false,
+                "555-0000", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset).clccResponse(2, 1, CALL_STATE_HELD, 0, false,
+                "555-0001", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset, times(2)).clccResponse(0, 0, 0, 0, false, null, 0);
+    }
+
+    public void testAlertingCallClccResponse() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        Call dialingCall = createForegroundCall();
+        calls.add(dialingCall);
+        when(dialingCall.getState()).thenReturn(CallState.DIALING);
+        when(dialingCall.isIncoming()).thenReturn(false);
+        when(dialingCall.isConference()).thenReturn(false);
+        when(dialingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(1, 0, CALL_STATE_ALERTING, 0, false,
+                "555-0000", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+        verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
+                anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+    }
+
+    public void testHoldingCallClccResponse() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+        Call dialingCall = createForegroundCall();
+        calls.add(dialingCall);
+        when(dialingCall.getState()).thenReturn(CallState.DIALING);
+        when(dialingCall.isIncoming()).thenReturn(false);
+        when(dialingCall.isConference()).thenReturn(false);
+        when(dialingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0000")));
+        Call holdingCall = createHeldCall();
+        calls.add(holdingCall);
+        when(holdingCall.getState()).thenReturn(CallState.ON_HOLD);
+        when(holdingCall.isIncoming()).thenReturn(true);
+        when(holdingCall.isConference()).thenReturn(false);
+        when(holdingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
+                Uri.parse("tel:555-0001")));
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+        verify(mMockBluetoothHeadset).clccResponse(1, 0, CALL_STATE_ALERTING, 0, false,
+                "555-0000", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset).clccResponse(2, 1, CALL_STATE_HELD, 0, false,
+                "555-0001", PhoneNumberUtils.TOA_Unknown);
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+        verify(mMockBluetoothHeadset, times(3)).clccResponse(anyInt(),
+                anyInt(), anyInt(), anyInt(), anyBoolean(), anyString(), anyInt());
+    }
+
+    public void testListCurrentCallsImsConference() throws Exception {
+        ArrayList<Call> calls = new ArrayList<>();
+        Call parentCall = createActiveCall();
+        calls.add(parentCall);
+        addCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentCall.isConference()).thenReturn(true);
+        when(parentCall.getState()).thenReturn(CallState.ACTIVE);
+        when(parentCall.isIncoming()).thenReturn(true);
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+
+        mBluetoothPhoneService.mBinder.listCurrentCalls();
+
+        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(1), eq(CALL_STATE_ACTIVE), eq(0),
+                eq(true), (String) isNull(), eq(-1));
+        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
+    }
+
+    public void testQueryPhoneState() throws Exception {
+        Call ringingCall = createRingingCall();
+        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
+
+        mBluetoothPhoneService.mBinder.queryPhoneState();
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
+                eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
+    }
+
+    public void testCDMAConferenceQueryState() throws Exception {
+        Call parentConfCall = createActiveCall();
+        final Call confCall1 = mock(Call.class);
+        final Call confCall2 = mock(Call.class);
+        when(parentConfCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
+        addCallCapability(parentConfCall, Connection.CAPABILITY_SWAP_CONFERENCE);
+        removeCallCapability(parentConfCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentConfCall.wasConferencePreviouslyMerged()).thenReturn(true);
+        when(parentConfCall.isConference()).thenReturn(true);
+        when(parentConfCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
+            add(confCall1);
+            add(confCall2);
+        }});
+
+        mBluetoothPhoneService.mBinder.queryPhoneState();
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+    }
+
+    public void testProcessChldTypeReleaseHeldRinging() throws Exception {
+        Call ringingCall = createRingingCall();
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_RELEASEHELD);
+
+        verify(mMockCallsManager).rejectCall(eq(ringingCall), eq(false), any(String.class));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldTypeReleaseHeldHold() throws Exception {
+        Call onHoldCall = createHeldCall();
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_RELEASEHELD);
+
+        verify(mMockCallsManager).disconnectCall(eq(onHoldCall));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldReleaseActiveRinging() throws Exception {
+        Call activeCall = createActiveCall();
+        Call ringingCall = createRingingCall();
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
+                CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD);
+
+        verify(mMockCallsManager).disconnectCall(eq(activeCall));
+        verify(mMockCallsManager).answerCall(eq(ringingCall), any(int.class));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldReleaseActiveHold() throws Exception {
+        Call activeCall = createActiveCall();
+        Call heldCall = createHeldCall();
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
+                CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD);
+
+        verify(mMockCallsManager).disconnectCall(eq(activeCall));
+        verify(mMockCallsManager).unholdCall(eq(heldCall));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldHoldActiveRinging() throws Exception {
+        Call ringingCall = createRingingCall();
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
+                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
+
+        verify(mMockCallsManager).answerCall(eq(ringingCall), any(int.class));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldHoldActiveUnhold() throws Exception {
+        Call heldCall = createHeldCall();
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
+                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
+
+        verify(mMockCallsManager).unholdCall(eq(heldCall));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldHoldActiveHold() throws Exception {
+        Call activeCall = createActiveCall();
+        addCallCapability(activeCall, Connection.CAPABILITY_HOLD);
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
+                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
+
+        verify(mMockCallsManager).holdCall(eq(activeCall));
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldAddHeldToConfHolding() throws Exception {
+        Call activeCall = createActiveCall();
+        addCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_ADDHELDTOCONF);
+
+        verify(activeCall).mergeConference();
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldAddHeldToConf() throws Exception {
+        Call activeCall = createActiveCall();
+        removeCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
+        Call conferenceableCall = mock(Call.class);
+        ArrayList<Call> conferenceableCalls = new ArrayList<>();
+        conferenceableCalls.add(conferenceableCall);
+        when(activeCall.getConferenceableCalls()).thenReturn(conferenceableCalls);
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_ADDHELDTOCONF);
+
+        verify(mMockCallsManager).conference(activeCall, conferenceableCall);
+        assertEquals(didProcess, true);
+    }
+
+    public void testProcessChldHoldActiveSwapConference() throws Exception {
+        // Create an active CDMA Call with a call on hold and simulate a swapConference().
+        Call parentCall = createActiveCall();
+        final Call foregroundCall = mock(Call.class);
+        final Call heldCall = createHeldCall();
+        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
+        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentCall.isConference()).thenReturn(true);
+        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(false);
+        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
+            add(foregroundCall);
+            add(heldCall);
+        }});
+
+        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
+                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
+
+        verify(parentCall).swapConference();
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE), eq(""),
+                eq(128));
+        assertEquals(didProcess, true);
+    }
+
+    // Testing the CallsManager Listener Functionality on Bluetooth
+    public void testOnCallAddedRinging() throws Exception {
+        Call ringingCall = createRingingCall();
+        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-000"));
+
+        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(ringingCall);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
+                eq("555-000"), eq(PhoneNumberUtils.TOA_Unknown));
+
+    }
+
+    public void testOnCallAddedCdmaActiveHold() throws Exception {
+        // Call has been put into a CDMA "conference" with one call on hold.
+        Call parentCall = createActiveCall();
+        final Call foregroundCall = mock(Call.class);
+        final Call heldCall = createHeldCall();
+        addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
+        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        when(parentCall.isConference()).thenReturn(true);
+        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
+            add(foregroundCall);
+            add(heldCall);
+        }});
+
+        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(parentCall);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+
+    }
+
+    public void testOnCallRemoved() throws Exception {
+        Call activeCall = createActiveCall();
+        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(activeCall);
+        doReturn(null).when(mMockCallsManager).getActiveCall();
+        mBluetoothPhoneService.mCallsManagerListener.onCallRemoved(activeCall);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+    }
+
+    public void testOnCallStateChangedConnectingCall() throws Exception {
+        Call activeCall = mock(Call.class);
+        Call connectingCall = mock(Call.class);
+        when(connectingCall.getState()).thenReturn(CallState.CONNECTING);
+        ArrayList<Call> calls = new ArrayList<>();
+        calls.add(connectingCall);
+        calls.add(activeCall);
+        when(mMockCallsManager.getCalls()).thenReturn(calls);
+
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(activeCall,
+                CallState.ACTIVE, CallState.ON_HOLD);
+
+        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
+                anyString(), anyInt());
+    }
+
+    public void testOnCallStateChangedDialing() throws Exception {
+        Call activeCall = createActiveCall();
+
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(activeCall,
+                CallState.CONNECTING, CallState.DIALING);
+
+        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
+                anyString(), anyInt());
+    }
+
+    public void testOnCallStateChangedAlerting() throws Exception {
+        Call outgoingCall = createOutgoingCall();
+
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(outgoingCall,
+                CallState.NEW, CallState.DIALING);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(0, 0, CALL_STATE_DIALING, "", 128);
+        verify(mMockBluetoothHeadset).phoneStateChanged(0, 0, CALL_STATE_ALERTING, "", 128);
+    }
+
+    public void testOnCallStateChanged() throws Exception {
+        Call ringingCall = createRingingCall();
+        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
+        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(ringingCall);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
+                eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
+
+        //Switch to active
+        doReturn(null).when(mMockCallsManager).getRingingCall();
+        when(mMockCallsManager.getActiveCall()).thenReturn(ringingCall);
+
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(ringingCall,
+                CallState.RINGING, CallState.ACTIVE);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+    }
+
+    public void testOnCallStateChangedGSMSwap() throws Exception {
+        Call heldCall = createHeldCall();
+        when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
+        doReturn(2).when(mMockCallsManager).getNumHeldCalls();
+        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(heldCall,
+                CallState.ACTIVE, CallState.ON_HOLD);
+
+        verify(mMockBluetoothHeadset, never()).phoneStateChanged(eq(0), eq(2), eq(CALL_STATE_HELD),
+                eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
+    }
+
+    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
+        // that the bluetooth phone state is updated properly.
+        Call parentCall = createActiveCall();
+        Call activeCall = mock(Call.class);
+        Call heldCall = createHeldCall();
+        when(activeCall.getParentCall()).thenReturn(parentCall);
+        when(heldCall.getParentCall()).thenReturn(parentCall);
+        ArrayList<Call> calls = new ArrayList<>();
+        calls.add(activeCall);
+        when(parentCall.getChildCalls()).thenReturn(calls);
+        when(parentCall.isConference()).thenReturn(true);
+        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
+        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
+        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(false);
+
+        // Be sure that onIsConferencedChanged rejects spurious changes during set up of
+        // CDMA "conference"
+        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(activeCall);
+        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
+                anyString(), anyInt());
+        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(heldCall);
+        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
+                anyString(), anyInt());
+        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
+        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
+                anyString(), anyInt());
+
+        calls.add(heldCall);
+        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
+                eq(""), eq(128));
+    }
+
+    public void testBluetoothAdapterReceiver() throws Exception {
+        Call ringingCall = createRingingCall();
+        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
+
+        Intent intent = new Intent();
+        intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
+        mBluetoothPhoneService.mBluetoothAdapterReceiver.onReceive(mContext, intent);
+
+        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
+                eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
+    }
+
+    private void addCallCapability(Call call, int capability) {
+        when(call.can(capability)).thenReturn(true);
+    }
+
+    private void removeCallCapability(Call call, int capability) {
+        when(call.can(capability)).thenReturn(false);
+    }
+
+    private Call createActiveCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getActiveCall()).thenReturn(call);
+        return call;
+    }
+
+    private Call createRingingCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getRingingCall()).thenReturn(call);
+        return call;
+    }
+
+    private Call createHeldCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getHeldCall()).thenReturn(call);
+        return call;
+    }
+
+    private Call createOutgoingCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getOutgoingCall()).thenReturn(call);
+        return call;
+    }
+
+    private Call createForegroundCall() {
+        Call call = mock(Call.class);
+        when(mMockCallsManager.getForegroundCall()).thenReturn(call);
+        return call;
+    }
+
+    private static ComponentName makeQuickConnectionServiceComponentName() {
+        return new ComponentName("com.android.server.telecom.tests",
+                "com.android.server.telecom.tests.MockConnectionService");
+    }
+
+    private static PhoneAccountHandle makeQuickAccountHandle(String id) {
+        return new PhoneAccountHandle(makeQuickConnectionServiceComponentName(), id,
+                Binder.getCallingUserHandle());
+    }
+
+    private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
+        return new PhoneAccount.Builder(makeQuickAccountHandle(id), "label" + idx);
+    }
+
+    private PhoneAccount makeQuickAccount(String id, int idx) {
+        return makeQuickAccountBuilder(id, idx)
+                .setAddress(Uri.parse(TEST_ACCOUNT_ADDRESS + idx))
+                .setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
+                .setCapabilities(idx)
+                .setIcon(Icon.createWithResource(
+                        "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
+                .setShortDescription("desc" + idx)
+                .setIsEnabled(true)
+                .build();
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
new file mode 100644
index 0000000..b12ef8d
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.IAudioService;
+import android.telecom.CallAudioState;
+
+import com.android.server.telecom.BluetoothManager;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallAudioManager;
+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.WiredHeadsetManager;
+
+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.List;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+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;
+
+
+public class CallAudioRouteStateMachineTest extends TelecomTestCase {
+    private static final int NONE = 0;
+    private static final int ON = 1;
+    private static final int OFF = 2;
+
+    private class 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 TestParameters(String name, int initialRoute, int availableRoutes, int
+                speakerInteraction, int bluetoothInteraction, int action, int expectedRoute, int
+                expectedAvailableRoutes) {
+            this.name = name;
+            this.initialRoute = initialRoute;
+            this.availableRoutes = availableRoutes;
+            this.speakerInteraction = speakerInteraction;
+            this.bluetoothInteraction = bluetoothInteraction;
+            this.action = action;
+            this.expectedRoute = expectedRoute;
+            this.expectedAvailableRoutes = expectedAvailableRoutes;
+        }
+
+        @Override
+        public String toString() {
+            return "TestParameters{" +
+                    "name='" + name + '\'' +
+                    ", initialRoute=" + initialRoute +
+                    ", availableRoutes=" + availableRoutes +
+                    ", speakerInteraction=" + speakerInteraction +
+                    ", bluetoothInteraction=" + bluetoothInteraction +
+                    ", action=" + action +
+                    ", expectedRoute=" + expectedRoute +
+                    ", expectedAvailableRoutes=" + expectedAvailableRoutes +
+                    '}';
+        }
+    }
+
+    @Mock CallsManager mockCallsManager;
+    @Mock BluetoothManager mockBluetoothManager;
+    @Mock IAudioService mockAudioService;
+    @Mock ConnectionServiceWrapper mockConnectionServiceWrapper;
+    @Mock WiredHeadsetManager mockWiredHeadsetManager;
+    @Mock StatusBarNotifier mockStatusBarNotifier;
+    @Mock Call fakeCall;
+
+    private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
+    private static final int TEST_TIMEOUT = 500;
+    private AudioManager mockAudioManager;
+    @Override
+    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(fakeCall.getConnectionService()).thenReturn(mockConnectionServiceWrapper);
+        when(fakeCall.isAlive()).thenReturn(true);
+        doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
+                any(CallAudioState.class));
+    }
+
+    public void testStateMachineTransitionsWithFocus() throws Throwable {
+        List<TestParameters> paramList = generateTransitionTests();
+        for (TestParameters params : paramList) {
+            try {
+                runParametrizedTestCaseWithFocus(params);
+            } catch (Throwable e) {
+                String newMessage = "Failed at parameters: \n" + params.toString() + '\n'
+                        + e.getMessage();
+                throw(new Throwable(newMessage, e));
+            }
+        }
+    }
+
+    public void testStateMachineTransitionsWithoutFocus() throws Throwable {
+        List<TestParameters> paramList = generateTransitionTests();
+        for (TestParameters params : paramList) {
+            try {
+                runParametrizedTestCaseWithoutFocus(params);
+            } catch (Throwable e) {
+                String newMessage = "Failed at parameters: \n" + params.toString() + '\n'
+                        + e.getMessage();
+                throw(new Throwable(newMessage, e));
+            }
+        }
+    }
+
+    public void testSpeakerPersistence() {
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+
+        when(mockBluetoothManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(true);
+        doAnswer(new Answer() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                when(mockAudioManager.isSpeakerphoneOn()).thenReturn((Boolean) args[0]);
+                return null;
+            }
+        }).when(mockAudioManager).setSpeakerphoneOn(any(Boolean.class));
+        CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);
+        stateMachine.initialize(initState);
+
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.HAS_FOCUS);
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET);
+        CallAudioState expectedMiddleState = new CallAudioState(false,
+                CallAudioState.ROUTE_WIRED_HEADSET,
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
+        verifyNewSystemCallAudioState(initState, expectedMiddleState);
+        resetMocks();
+
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET);
+        verifyNewSystemCallAudioState(expectedMiddleState, initState);
+    }
+
+    public void testInitializationWithNoHeadsetNoBluetooth() {
+        when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(false);
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(false);
+
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+        stateMachine.initialize();
+        CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);
+        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+    }
+
+    public void testInitializationWithHeadsetNoBluetooth() {
+        when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(true);
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(false);
+
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+        stateMachine.initialize();
+        CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_WIRED_HEADSET,
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER);
+        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+    }
+
+    public void testInitializationWithHeadsetAndBluetooth() {
+        when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(true);
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(true);
+
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+        stateMachine.initialize();
+        CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER
+                | CallAudioState.ROUTE_BLUETOOTH);
+        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+    }
+
+    public void testInitializationWithBluetoothNoHeadset() {
+        when(mockWiredHeadsetManager.isPluggedIn()).thenReturn(false);
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(true);
+
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+        stateMachine.initialize();
+        CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER
+                        | CallAudioState.ROUTE_BLUETOOTH);
+        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+    }
+
+    private List<TestParameters> generateTransitionTests() {
+        List<TestParameters> params = new ArrayList<>();
+        params.add(new TestParameters(
+                "Connect headset during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "Connect headset during bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                NONE, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH // expectedAvail
+        ));
+
+        params.add(new TestParameters(
+                "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
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect headset during headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect headset during headset with bluetooth available", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect headset during bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect headset during speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect headset during speakerphone with bluetooth available", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_WIRED_HEADSET, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Connect bluetooth during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE, // availableRoutes
+                NONE, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_EARPIECE // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Connect bluetooth during wired headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET, // availableRoutes
+                NONE, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.CONNECT_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET // expectedAvail
+        ));
+
+        params.add(new TestParameters(
+                "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 // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect bluetooth during bluetooth without headset in", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                NONE, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect bluetooth during bluetooth with headset in", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                NONE, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
+                CallAudioState.ROUTE_WIRED_HEADSET, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect bluetooth during speakerphone", // name
+                CallAudioState.ROUTE_SPEAKER, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
+                CallAudioState.ROUTE_SPEAKER, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "Disconnect bluetooth during earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE| CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                NONE, // speakerInteraction
+                NONE, // bluetoothInteraction
+                CallAudioRouteStateMachine.DISCONNECT_BLUETOOTH, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE // expectedAvailableRoutes
+        ));
+
+        params.add(new TestParameters(
+                "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
+        ));
+
+        params.add(new TestParameters(
+                "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
+        ));
+
+        params.add(new TestParameters(
+                "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 // expectedAvail
+        ));
+
+        params.add(new TestParameters(
+                "Switch to earpiece from bluetooth", // name
+                CallAudioState.ROUTE_BLUETOOTH, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                NONE, // speakerInteraction
+                OFF, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_EARPIECE, // action
+                CallAudioState.ROUTE_EARPIECE, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "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
+        ));
+
+        params.add(new TestParameters(
+                "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 // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Switch to bluetooth from earpiece", // name
+                CallAudioState.ROUTE_EARPIECE, // initialRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
+                NONE, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH // expectedAvailableR
+        ));
+
+        params.add(new TestParameters(
+                "Switch to bluetooth from wired headset", // name
+                CallAudioState.ROUTE_WIRED_HEADSET, // initialRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH, // availableRou
+                NONE, // speakerInteraction
+                ON, // bluetoothInteraction
+                CallAudioRouteStateMachine.SWITCH_BLUETOOTH, // action
+                CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
+                CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_BLUETOOTH // expectedAvail
+        ));
+
+        return params;
+    }
+
+    private void runParametrizedTestCaseWithFocus(TestParameters params) {
+        resetMocks();
+
+        // Construct a fresh state machine on every case
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+
+        // Set up bluetooth and speakerphone state
+        when(mockBluetoothManager.isBluetoothAudioConnectedOrPending()).thenReturn(
+                params.initialRoute == CallAudioState.ROUTE_BLUETOOTH);
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(
+                (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
+                        || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
+                params.initialRoute == CallAudioState.ROUTE_SPEAKER);
+
+        // Set the initial CallAudioState object
+        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.HAS_FOCUS);
+        stateMachine.sendMessageWithSessionInfo(params.action);
+
+        // Verify interactions with the speakerphone and bluetooth systems
+        switch(params.bluetoothInteraction) {
+            case NONE:
+                verify(mockBluetoothManager, never()).disconnectBluetoothAudio();
+                verify(mockBluetoothManager, never()).connectBluetoothAudio();
+                break;
+            case ON:
+                verify(mockBluetoothManager, timeout(TEST_TIMEOUT)).connectBluetoothAudio();
+                verify(mockBluetoothManager, never()).disconnectBluetoothAudio();
+                break;
+            case OFF:
+                verify(mockBluetoothManager, never()).connectBluetoothAudio();
+                verify(mockBluetoothManager, timeout(TEST_TIMEOUT)).disconnectBluetoothAudio();
+        }
+
+        switch (params.speakerInteraction) {
+            case NONE:
+                verify(mockAudioManager, never()).setSpeakerphoneOn(any(Boolean.class));
+                break;
+            case ON: // fall through
+            case OFF:
+                verify(mockAudioManager, timeout(TEST_TIMEOUT)).setSpeakerphoneOn(
+                        params.speakerInteraction == ON);
+        }
+
+        // Verify the end state
+        CallAudioState expectedState = new CallAudioState(false, params.expectedRoute,
+                params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER);
+        verifyNewSystemCallAudioState(initState, expectedState);
+    }
+
+    private void runParametrizedTestCaseWithoutFocus(TestParameters params) {
+        resetMocks();
+
+        // Construct a fresh state machine on every case
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory);
+
+        // Set up bluetooth and speakerphone state
+        when(mockBluetoothManager.isBluetoothAvailable()).thenReturn(
+                (params.availableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0
+                || (params.expectedAvailableRoutes & CallAudioState.ROUTE_BLUETOOTH) != 0);
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(
+                params.initialRoute == CallAudioState.ROUTE_SPEAKER);
+
+        // 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);
+        try {
+            Thread.sleep(100L);
+        } catch (InterruptedException e) {
+            // Just a pause to make sure the state machine handler thread has a chance to update
+            // its state. Do nothing.
+        }
+
+        // Verify that no substantive interactions have taken place with the rest of the system
+        verify(mockBluetoothManager, never()).disconnectBluetoothAudio();
+        verify(mockBluetoothManager, never()).connectBluetoothAudio();
+        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));
+
+        // Verify the end state
+        CallAudioState expectedState = new CallAudioState(false, params.expectedRoute,
+                params.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER);
+        assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
+    }
+
+    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());
+
+        assertTrue(oldStateCaptor.getValue().equals(expectedOldState));
+        assertTrue(newStateCaptor1.getValue().equals(expectedNewState));
+        assertTrue(newStateCaptor2.getValue().equals(expectedNewState));
+    }
+
+    private void resetMocks() {
+        reset(mockAudioManager, mockBluetoothManager, mockCallsManager,
+                mockConnectionServiceWrapper);
+        when(mockCallsManager.getForegroundCall()).thenReturn(fakeCall);
+        doNothing().when(mockConnectionServiceWrapper).onCallAudioStateChanged(any(Call.class),
+                any(CallAudioState.class));
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
new file mode 100644
index 0000000..f5ead6c
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.IContentProvider;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.VideoProfile;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallLogManager;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.TelephonyUtil;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+
+public class CallLogManagerTest extends TelecomTestCase {
+
+    private CallLogManager mCallLogManager;
+    private IContentProvider mContentProvider;
+    private PhoneAccountHandle mDefaultAccountHandle;
+    private PhoneAccountHandle mOtherUserAccountHandle;
+    private PhoneAccountHandle mManagedProfileAccountHandle;
+
+    private static final Uri TEL_PHONEHANDLE = Uri.parse("tel:5555551234");
+
+    private static final PhoneAccountHandle EMERGENCY_ACCT_HANDLE = TelephonyUtil
+            .getDefaultEmergencyPhoneAccount()
+            .getAccountHandle();
+
+    private static final int NO_VIDEO_STATE = VideoProfile.STATE_AUDIO_ONLY;
+    private static final int BIDIRECTIONAL_VIDEO_STATE = VideoProfile.STATE_BIDIRECTIONAL;
+    private static final String POST_DIAL_STRING = ";12345";
+    private static final String TEST_PHONE_ACCOUNT_ID= "testPhoneAccountId";
+
+    private static final int TEST_TIMEOUT_MILLIS = 100;
+    private static final int CURRENT_USER_ID = 0;
+    private static final int OTHER_USER_ID = 10;
+    private static final int MANAGED_USER_ID = 11;
+
+    @Mock
+    PhoneAccountRegistrar mMockPhoneAccountRegistrar;
+
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+        mCallLogManager = new CallLogManager(mContext, mMockPhoneAccountRegistrar);
+        mContentProvider = mContext.getContentResolver().acquireProvider("test");
+        mDefaultAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
+                TEST_PHONE_ACCOUNT_ID,
+                UserHandle.of(CURRENT_USER_ID)
+        );
+
+        mOtherUserAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
+                TEST_PHONE_ACCOUNT_ID,
+                UserHandle.of(OTHER_USER_ID)
+        );
+
+        mManagedProfileAccountHandle = new PhoneAccountHandle(
+                new ComponentName("com.android.server.telecom.tests", "CallLogManagerTest"),
+                TEST_PHONE_ACCOUNT_ID,
+                UserHandle.of(MANAGED_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);
+        UserInfo managedProfileUserInfo = new UserInfo(OTHER_USER_ID, "test3",
+                UserInfo.FLAG_MANAGED_PROFILE);
+
+        when(userManager.isUserRunning(any(UserHandle.class))).thenReturn(true);
+        when(userManager.hasUserRestriction(any(String.class), any(UserHandle.class)))
+                .thenReturn(false);
+        when(userManager.getUsers(any(Boolean.class)))
+                .thenReturn(Arrays.asList(userInfo, otherUserInfo, managedProfileUserInfo));
+        when(userManager.getUserInfo(eq(CURRENT_USER_ID))).thenReturn(userInfo);
+        when(userManager.getUserInfo(eq(OTHER_USER_ID))).thenReturn(otherUserInfo);
+        when(userManager.getUserInfo(eq(MANAGED_USER_ID))).thenReturn(managedProfileUserInfo);
+    }
+
+    public void testDontLogCancelledCall() {
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.CANCELED,
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits,
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.DIALING, CallState.DISCONNECTED);
+        verifyNoInsertion();
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.DIALING, CallState.ABORTED);
+        verifyNoInsertion();
+    }
+
+    public void testDontLogChoosingAccountCall() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.SELECT_PHONE_ACCOUNT,
+                CallState.DISCONNECTED);
+        verifyNoInsertion();
+    }
+
+    public void testDontLogCallsFromEmergencyAccount() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(EMERGENCY_ACCT_HANDLE, 0));
+        mComponentContextFixture.putBooleanResource(R.bool.allow_emergency_numbers_in_call_log,
+                false);
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                EMERGENCY_ACCT_HANDLE, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits,
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        verifyNoInsertion();
+    }
+
+    public void testLogCallDirectionOutgoing() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+    }
+
+    public void testLogCallDirectionIncoming() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeIncomingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                null
+        );
+        mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
+    }
+
+    public void testLogCallDirectionMissed() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeMissedCall = makeFakeCall(
+                DisconnectCause.MISSED, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                null
+        );
+
+        mCallLogManager.onCallStateChanged(fakeMissedCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.MISSED_TYPE));
+    }
+
+    public void testCreationTimeAndAge() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        long currentTime = System.currentTimeMillis();
+        long duration = 1000L;
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                currentTime, // creationTimeMillis
+                duration, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsLong(CallLog.Calls.DATE),
+                Long.valueOf(currentTime));
+        assertEquals(insertedValues.getAsLong(CallLog.Calls.DURATION),
+                Long.valueOf(duration / 1000));
+    }
+
+    public void testLogPhoneAccountId() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsString(CallLog.Calls.PHONE_ACCOUNT_ID),
+                TEST_PHONE_ACCOUNT_ID);
+    }
+
+    public void testLogCorrectPhoneNumber() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsString(CallLog.Calls.NUMBER),
+                TEL_PHONEHANDLE.getSchemeSpecificPart());
+        assertEquals(insertedValues.getAsString(CallLog.Calls.POST_DIAL_DIGITS), POST_DIAL_STRING);
+    }
+
+    public void testLogCallVideoFeatures() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeVideoCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertTrue((insertedValues.getAsInteger(CallLog.Calls.FEATURES)
+                & CallLog.Calls.FEATURES_VIDEO) == CallLog.Calls.FEATURES_VIDEO);
+    }
+
+    public void testLogCallDirectionOutgoingWithMultiUserCapability() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
+                        PhoneAccount.CAPABILITY_MULTI_USER));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Outgoing call placed through a phone account with multi user capability is inserted to
+        // all users except managed profile.
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+        verifyNoInsertionInUser(MANAGED_USER_ID);
+    }
+
+    public void testLogCallDirectionIncomingWithMultiUserCapability() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mOtherUserAccountHandle,
+                        PhoneAccount.CAPABILITY_MULTI_USER));
+        Call fakeIncomingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                null
+        );
+        mCallLogManager.onCallStateChanged(fakeIncomingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Incoming call using a phone account with multi user capability is inserted to all users
+        // except managed profile.
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
+        insertedValues = verifyInsertionWithCapture(OTHER_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.INCOMING_TYPE));
+        verifyNoInsertionInUser(MANAGED_USER_ID);
+    }
+
+
+    public void testLogCallDirectionOutgoingWithMultiUserCapabilityFromManagedProfile() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle,
+                        PhoneAccount.CAPABILITY_MULTI_USER));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mManagedProfileAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(MANAGED_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Outgoing call placed through work dialer should be inserted to managed profile only.
+        verifyNoInsertionInUser(CURRENT_USER_ID);
+        verifyNoInsertionInUser(OTHER_USER_ID);
+        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+    }
+
+    public void testLogCallDirectionOutgoingFromManagedProfile() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                false, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mManagedProfileAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(MANAGED_USER_ID)
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Outgoing call using phone account in managed profile should be inserted to managed
+        // profile only.
+        verifyNoInsertionInUser(CURRENT_USER_ID);
+        verifyNoInsertionInUser(OTHER_USER_ID);
+        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(CallLog.Calls.OUTGOING_TYPE));
+    }
+
+    public void testLogCallDirectionIngoingFromManagedProfile() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mManagedProfileAccountHandle, 0));
+        Call fakeOutgoingCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mManagedProfileAccountHandle, // phoneAccountHandle
+                NO_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                null
+        );
+        mCallLogManager.onCallStateChanged(fakeOutgoingCall, CallState.ACTIVE,
+                CallState.DISCONNECTED);
+
+        // Incoming call using phone account in managed profile should be inserted to managed
+        // profile only.
+        verifyNoInsertionInUser(CURRENT_USER_ID);
+        verifyNoInsertionInUser(OTHER_USER_ID);
+        ContentValues insertedValues = verifyInsertionWithCapture(MANAGED_USER_ID);
+        assertEquals(insertedValues.getAsInteger(CallLog.Calls.TYPE),
+                Integer.valueOf(Calls.INCOMING_TYPE));
+    }
+
+    /**
+     * Ensure call data usage is persisted to the call log when present in the call.
+     */
+    public void testLogCallDataUsageSet() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeVideoCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID), // initiatingUser
+                1000 // callDataUsage
+        );
+        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertEquals(Long.valueOf(1000), insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
+    }
+
+    /**
+     * Ensures call data usage is null in the call log when not set on the call.
+     */
+    public void testLogCallDataUsageNotSet() {
+        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(any(PhoneAccountHandle.class)))
+                .thenReturn(makeFakePhoneAccount(mDefaultAccountHandle, CURRENT_USER_ID));
+        Call fakeVideoCall = makeFakeCall(
+                DisconnectCause.OTHER, // disconnectCauseCode
+                false, // isConference
+                true, // isIncoming
+                1L, // creationTimeMillis
+                1000L, // ageMillis
+                TEL_PHONEHANDLE, // callHandle
+                mDefaultAccountHandle, // phoneAccountHandle
+                BIDIRECTIONAL_VIDEO_STATE, // callVideoState
+                POST_DIAL_STRING, // postDialDigits
+                UserHandle.of(CURRENT_USER_ID), // initiatingUser
+                Call.DATA_USAGE_NOT_SET // callDataUsage
+        );
+        mCallLogManager.onCallStateChanged(fakeVideoCall, CallState.ACTIVE, CallState.DISCONNECTED);
+        ContentValues insertedValues = verifyInsertionWithCapture(CURRENT_USER_ID);
+        assertNull(insertedValues.getAsLong(CallLog.Calls.DATA_USAGE));
+    }
+
+    private void verifyNoInsertion() {
+        try {
+            verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).never()).insert(any(String.class),
+                    any(Uri.class), any(ContentValues.class));
+        } catch (android.os.RemoteException e) {
+            fail("Remote exception occurred during test execution");
+        }
+    }
+
+
+    private void verifyNoInsertionInUser(int userId) {
+        try {
+            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
+            verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS).never()).insert(any(String.class),
+                    eq(uri), any(ContentValues.class));
+        } catch (android.os.RemoteException e) {
+            fail("Remote exception occurred during test execution");
+        }
+    }
+
+    private ContentValues verifyInsertionWithCapture(int userId) {
+        ArgumentCaptor<ContentValues> captor = ArgumentCaptor.forClass(ContentValues.class);
+        try {
+            Uri uri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, userId);
+            verify(mContentProvider, timeout(TEST_TIMEOUT_MILLIS)).insert(any(String.class),
+                    eq(uri), captor.capture());
+        } catch (android.os.RemoteException e) {
+            fail("Remote exception occurred during test execution");
+        }
+
+        return captor.getValue();
+    }
+
+    private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
+            long creationTimeMillis, long ageMillis, Uri callHandle,
+            PhoneAccountHandle phoneAccountHandle, int callVideoState,
+            String postDialDigits, UserHandle initiatingUser) {
+        return makeFakeCall(disconnectCauseCode, isConference, isIncoming, creationTimeMillis,
+                ageMillis,
+                callHandle, phoneAccountHandle, callVideoState, postDialDigits, initiatingUser,
+                Call.DATA_USAGE_NOT_SET);
+    }
+
+    private Call makeFakeCall(int disconnectCauseCode, boolean isConference, boolean isIncoming,
+            long creationTimeMillis, long ageMillis, Uri callHandle,
+            PhoneAccountHandle phoneAccountHandle, int callVideoState,
+            String postDialDigits, UserHandle initiatingUser, long callDataUsage) {
+        Call fakeCall = mock(Call.class);
+        when(fakeCall.getDisconnectCause()).thenReturn(
+                new DisconnectCause(disconnectCauseCode));
+        when(fakeCall.isConference()).thenReturn(isConference);
+        when(fakeCall.isIncoming()).thenReturn(isIncoming);
+        when(fakeCall.getCreationTimeMillis()).thenReturn(creationTimeMillis);
+        when(fakeCall.getAgeMillis()).thenReturn(ageMillis);
+        when(fakeCall.getOriginalHandle()).thenReturn(callHandle);
+        when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
+        when(fakeCall.getVideoStateHistory()).thenReturn(callVideoState);
+        when(fakeCall.getPostDialDigits()).thenReturn(postDialDigits);
+        when(fakeCall.getInitiatingUser()).thenReturn(initiatingUser);
+        when(fakeCall.getCallDataUsage()).thenReturn(callDataUsage);
+        return fakeCall;
+    }
+
+    private PhoneAccount makeFakePhoneAccount(PhoneAccountHandle phoneAccountHandle,
+            int capabilities) {
+        return PhoneAccount.builder(phoneAccountHandle, "testing")
+                .setCapabilities(capabilities).build();
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java b/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java
index 1ebf170..ea18560 100644
--- a/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java
+++ b/tests/src/com/android/server/telecom/tests/CallerInfoAsyncQueryFactoryFixture.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -55,7 +56,7 @@
         }
     };
 
-    final List<Request> mRequests = new ArrayList<>();
+    final List<Request> mRequests = Collections.synchronizedList(new ArrayList<Request>());
 
     public CallerInfoAsyncQueryFactoryFixture() throws Exception {
         Log.i(this, "Creating ...");
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 9f66f00..52ab512 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -53,6 +53,8 @@
 import android.telecom.ConnectionService;
 import android.telecom.InCallService;
 import android.telecom.PhoneAccount;
+import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContext;
@@ -60,6 +62,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
@@ -69,6 +72,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -90,6 +94,21 @@
         }
 
         @Override
+        public String getPackageName() {
+            return "com.android.server.telecom.tests";
+        }
+
+        @Override
+        public String getPackageResourcePath() {
+            return "/tmp/i/dont/know";
+        }
+
+        @Override
+        public Context getApplicationContext() {
+            return mApplicationContextSpy;
+        }
+
+        @Override
         public File getFilesDir() {
             try {
                 return File.createTempFile("temp", "temp").getParentFile();
@@ -153,6 +172,10 @@
                     return mUserManager;
                 case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
                     return mSubscriptionManager;
+                case Context.TELECOM_SERVICE:
+                    return mTelecomManager;
+                case Context.CARRIER_CONFIG_SERVICE:
+                    return mCarrierConfigManager;
                 default:
                     return null;
             }
@@ -170,7 +193,7 @@
 
         @Override
         public String getOpPackageName() {
-            return "test";
+            return "com.android.server.telecom.tests";
         }
 
         @Override
@@ -179,7 +202,7 @@
                 @Override
                 protected IContentProvider acquireProvider(Context c, String name) {
                     Log.i(this, "acquireProvider %s", name);
-                    return mock(IContentProvider.class);
+                    return mContentProvider;
                 }
 
                 @Override
@@ -190,7 +213,7 @@
                 @Override
                 protected IContentProvider acquireUnstableProvider(Context c, String name) {
                     Log.i(this, "acquireUnstableProvider %s", name);
-                    return mock(IContentProvider.class);
+                    return mContentProvider;
                 }
 
                 @Override
@@ -211,6 +234,12 @@
         }
 
         @Override
+        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+                String broadcastPermission, Handler scheduler) {
+            return null;
+        }
+
+        @Override
         public void sendBroadcast(Intent intent) {
             // TODO -- need to ensure this is captured
         }
@@ -221,6 +250,10 @@
         }
 
         @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle userHandle) {
+        }
+
+        @Override
         public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
                 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
                 int initialCode, String initialData, Bundle initialExtras) {
@@ -238,13 +271,20 @@
                 throws PackageManager.NameNotFoundException {
             return this;
         }
+
+        @Override
+        public void enforceCallingOrSelfPermission(String permission, String message) {
+            // Don't bother enforcing anything in mock.
+        }
     };
 
     public class FakeAudioManager extends AudioManager {
 
         private boolean mMute = false;
         private boolean mSpeakerphoneOn = false;
+        private int mAudioStreamValue = 1;
         private int mMode = AudioManager.MODE_NORMAL;
+        private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
 
         public FakeAudioManager(Context context) {
             super(context);
@@ -279,6 +319,26 @@
         public int getMode() {
             return mMode;
         }
+
+        @Override
+        public void setRingerModeInternal(int ringerMode) {
+            mRingerMode = ringerMode;
+        }
+
+        @Override
+        public int getRingerModeInternal() {
+            return mRingerMode;
+        }
+
+        @Override
+        public void setStreamVolume(int streamTypeUnused, int index, int flagsUnused){
+            mAudioStreamValue = index;
+        }
+
+        @Override
+        public int getStreamVolume(int streamValueUnused) {
+            return mAudioStreamValue;
+        }
     }
 
     private final Multimap<String, ComponentName> mComponentNamesByAction =
@@ -317,8 +377,12 @@
     private final UserManager mUserManager = mock(UserManager.class);
     private final StatusBarManager mStatusBarManager = mock(StatusBarManager.class);
     private final SubscriptionManager mSubscriptionManager = mock(SubscriptionManager.class);
+    private final CarrierConfigManager mCarrierConfigManager = mock(CarrierConfigManager.class);
+    private final IContentProvider mContentProvider = mock(IContentProvider.class);
     private final Configuration mResourceConfiguration = new Configuration();
 
+    private TelecomManager mTelecomManager = null;
+
     public ComponentContextFixture() {
         MockitoAnnotations.initMocks(this);
         when(mResources.getConfiguration()).thenReturn(mResourceConfiguration);
@@ -347,6 +411,8 @@
 
         when(mTelephonyManager.getSubIdForPhoneAccount((PhoneAccount) any())).thenReturn(1);
 
+        when(mTelephonyManager.getNetworkOperatorName()).thenReturn("label1");
+
         doAnswer(new Answer<Void>(){
             @Override
             public Void answer(InvocationOnMock invocation) throws Throwable {
@@ -357,6 +423,9 @@
         when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
 
         when(mUserManager.getSerialNumberForUser(any(UserHandle.class))).thenReturn(-1L);
+
+        doReturn(null).when(mApplicationContextSpy).registerReceiver(any(BroadcastReceiver.class),
+                any(IntentFilter.class));
     }
 
     @Override
@@ -388,8 +457,24 @@
         mServiceInfoByComponentName.put(componentName, serviceInfo);
     }
 
-    public void putResource(int id, String value) {
+    public void putResource(int id, final String value) {
+        when(mResources.getText(eq(id))).thenReturn(value);
         when(mResources.getString(eq(id))).thenReturn(value);
+        when(mResources.getString(eq(id), any())).thenAnswer(new Answer<String>() {
+            @Override
+            public String answer(InvocationOnMock invocation) {
+                Object[] args = invocation.getArguments();
+                return String.format(value, Arrays.copyOfRange(args, 1, args.length));
+            }
+        });
+    }
+
+    public void putBooleanResource(int id, boolean value) {
+        when(mResources.getBoolean(eq(id))).thenReturn(value);
+    }
+
+    public void setTelecomManager(TelecomManager telecomManager) {
+        mTelecomManager = telecomManager;
     }
 
     private void addService(String action, ComponentName name, IInterface service) {
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index bdcfb5b..0e16986 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -32,8 +32,10 @@
 import android.os.IInterface;
 import android.os.RemoteException;
 import android.telecom.CallAudioState;
+import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
+import android.telecom.ConnectionService;
 import android.telecom.DisconnectCause;
 import android.telecom.ParcelableConference;
 import android.telecom.ParcelableConnection;
@@ -48,14 +50,76 @@
 import java.util.Map;
 import java.util.Set;
 
-import static org.mockito.Matchers.any;
-
 /**
  * Controls a test {@link IConnectionService} as would be provided by a source of connectivity
  * to the Telecom framework.
  */
 public class ConnectionServiceFixture implements TestFixture<IConnectionService> {
 
+    /**
+     * Implementation of ConnectionService that performs no-ops for tasks normally meant for
+     * Telephony and reports success back to Telecom
+     */
+    public class FakeConnectionServiceDelegate extends ConnectionService {
+        @Override
+        public Connection onCreateUnknownConnection(
+                PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
+            return new FakeConnection();
+        }
+
+        @Override
+        public Connection onCreateIncomingConnection(
+                PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
+            return new FakeConnection();
+        }
+
+        @Override
+        public Connection onCreateOutgoingConnection(
+                PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
+            return new FakeConnection();
+        }
+
+        @Override
+        public void onConference(Connection cxn1, Connection cxn2) {
+            // Usually, this is implemented by something in Telephony, which does a bunch of radio
+            // work to conference the two connections together. Here we just short-cut that and
+            // declare them conferenced.
+            Conference fakeConference = new FakeConference();
+            fakeConference.addConnection(cxn1);
+            fakeConference.addConnection(cxn2);
+            addConference(fakeConference);
+        }
+    }
+
+    public class FakeConnection extends Connection {
+        public FakeConnection() {
+            super();
+            int capabilities = getConnectionCapabilities();
+            capabilities |= CAPABILITY_MUTE;
+            capabilities |= CAPABILITY_SUPPORT_HOLD;
+            capabilities |= CAPABILITY_HOLD;
+            setConnectionCapabilities(capabilities);
+            setActive();
+        }
+    }
+
+    public class FakeConference extends Conference {
+        public FakeConference() {
+            super(null);
+            setConnectionCapabilities(
+                    Connection.CAPABILITY_SUPPORT_HOLD
+                            | Connection.CAPABILITY_HOLD
+                            | Connection.CAPABILITY_MUTE
+                            | Connection.CAPABILITY_MANAGE_CONFERENCE);
+        }
+
+        @Override
+        public void onMerge(Connection connection) {
+            // Do nothing besides inform the connection that it was merged into this conference.
+            connection.setConference(this);
+        }
+    }
+
     public class FakeConnectionService extends IConnectionService.Stub {
 
         @Override
@@ -64,6 +128,7 @@
             if (!mConnectionServiceAdapters.add(adapter)) {
                 throw new RuntimeException("Adapter already added: " + adapter);
             }
+            mConnectionServiceDelegateAdapter.addConnectionServiceAdapter(adapter);
         }
 
         @Override
@@ -72,6 +137,7 @@
             if (!mConnectionServiceAdapters.remove(adapter)) {
                 throw new RuntimeException("Adapter never added: " + adapter);
             }
+            mConnectionServiceDelegateAdapter.removeConnectionServiceAdapter(adapter);
         }
 
         @Override
@@ -93,6 +159,8 @@
             c.isUnknown = isUnknown;
             c.capabilities |= Connection.CAPABILITY_HOLD | Connection.CAPABILITY_SUPPORT_HOLD;
             mConnectionById.put(id, c);
+            mConnectionServiceDelegateAdapter.createConnection(connectionManagerPhoneAccount,
+                    id, request, isIncoming, isUnknown);
         }
 
         @Override
@@ -133,7 +201,9 @@
         public void stopDtmfTone(String callId) throws RemoteException { }
 
         @Override
-        public void conference(String conferenceCallId, String callId) throws RemoteException { }
+        public void conference(String conferenceCallId, String callId) throws RemoteException {
+            mConnectionServiceDelegateAdapter.conference(conferenceCallId, callId);
+        }
 
         @Override
         public void splitFromConference(String callId) throws RemoteException { }
@@ -156,7 +226,12 @@
         public IInterface queryLocalInterface(String descriptor) {
             return this;
         }
-    };
+    }
+
+    private FakeConnectionServiceDelegate mConnectionServiceDelegate =
+            new FakeConnectionServiceDelegate();
+    private IConnectionService mConnectionServiceDelegateAdapter =
+            IConnectionService.Stub.asInterface(mConnectionServiceDelegate.onBind(null));
 
     private IConnectionService.Stub mConnectionService = new FakeConnectionService();
     private IConnectionService.Stub mConnectionServiceSpy = Mockito.spy(mConnectionService);
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
new file mode 100644
index 0000000..e8bd05c
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+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;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallIdMapper;
+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.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit testing for CreateConnectionProcessor as well as CreateConnectionTimeout classes.
+ */
+public class CreateConnectionProcessorTest extends TelecomTestCase {
+
+    private static final String TEST_PACKAGE = "com.android.server.telecom.tests";
+    private static final String TEST_CLASS =
+            "com.android.server.telecom.tests.MockConnectionService";
+
+    @Mock
+    ConnectionServiceRepository mMockConnectionServiceRepository;
+    @Mock
+    PhoneAccountRegistrar mMockAccountRegistrar;
+    @Mock
+    CreateConnectionResponse mMockCreateConnectionResponse;
+    @Mock
+    Call mMockCall;
+
+    CreateConnectionProcessor mTestCreateConnectionProcessor;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+
+        mTestCreateConnectionProcessor = new CreateConnectionProcessor(mMockCall,
+                mMockConnectionServiceRepository, mMockCreateConnectionResponse,
+                mMockAccountRegistrar, mContext);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mTestCreateConnectionProcessor = null;
+        super.tearDown();
+    }
+
+    public void testSimPhoneAccountSuccess() throws Exception {
+        PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
+        when(mMockCall.isEmergencyCall()).thenReturn(false);
+        // No Connection Manager in this case
+        when(mMockAccountRegistrar.getSimCallManagerFromCall(any(Call.class))).thenReturn(null);
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+
+        mTestCreateConnectionProcessor.process();
+
+        verify(mMockCall).setConnectionManagerPhoneAccount(eq(pAHandle));
+        verify(mMockCall).setTargetPhoneAccount(eq(pAHandle));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        // Notify successful connection to call
+        CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
+        mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
+        verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
+    }
+
+    public void testbadPhoneAccount() throws Exception {
+        PhoneAccountHandle pAHandle = null;
+        when(mMockCall.isEmergencyCall()).thenReturn(false);
+        when(mMockCall.getTargetPhoneAccount()).thenReturn(pAHandle);
+        givePhoneAccountBindPermission(pAHandle);
+        // No Connection Manager in this case
+        when(mMockAccountRegistrar.getSimCallManagerFromCall(any(Call.class))).thenReturn(null);
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+
+        mTestCreateConnectionProcessor.process();
+
+        verify(service, never()).createConnection(eq(mMockCall),
+                any(CreateConnectionResponse.class));
+        verify(mMockCreateConnectionResponse).handleCreateConnectionFailure(
+                eq(new DisconnectCause(DisconnectCause.ERROR)));
+    }
+
+    public void testConnectionManagerSuccess() throws Exception {
+        PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
+        when(mMockCall.isEmergencyCall()).thenReturn(false);
+        // Include a Connection Manager
+        PhoneAccountHandle callManagerPAHandle = getNewConnectionMangerHandle("cm_acct");
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        // Make sure the target phone account has the correct permissions
+        PhoneAccount mFakeTargetPhoneAccount = makeQuickAccount("cm_acct",
+                PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
+        when(mMockAccountRegistrar.getPhoneAccountUnchecked(pAHandle)).thenReturn(
+                mFakeTargetPhoneAccount);
+
+        mTestCreateConnectionProcessor.process();
+
+        verify(mMockCall).setConnectionManagerPhoneAccount(eq(callManagerPAHandle));
+        verify(mMockCall).setTargetPhoneAccount(eq(pAHandle));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        // Notify successful connection to call
+        CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
+        mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
+        verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
+    }
+
+    public void testConnectionManagerFailedFallToSim() throws Exception {
+        PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
+        when(mMockCall.isEmergencyCall()).thenReturn(false);
+        // Include a Connection Manager
+        PhoneAccountHandle callManagerPAHandle = getNewConnectionMangerHandle("cm_acct");
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        when(mMockCall.getConnectionManagerPhoneAccount()).thenReturn(callManagerPAHandle);
+        PhoneAccount mFakeTargetPhoneAccount = makeQuickAccount("cm_acct",
+                PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
+        when(mMockAccountRegistrar.getPhoneAccountUnchecked(pAHandle)).thenReturn(
+                mFakeTargetPhoneAccount);
+        when(mMockCall.getConnectionService()).thenReturn(service);
+        // Put CreateConnectionProcessor in correct state to fail with ConnectionManager
+        mTestCreateConnectionProcessor.process();
+        reset(mMockCall);
+        reset(service);
+
+        // Notify that the ConnectionManager has denied the call.
+        when(mMockCall.getConnectionManagerPhoneAccount()).thenReturn(callManagerPAHandle);
+        when(mMockCall.getConnectionService()).thenReturn(service);
+        mTestCreateConnectionProcessor.handleCreateConnectionFailure(
+                new DisconnectCause(DisconnectCause.CONNECTION_MANAGER_NOT_SUPPORTED));
+
+        // Verify that the Sim Phone Account is used correctly
+        verify(mMockCall).setConnectionManagerPhoneAccount(eq(pAHandle));
+        verify(mMockCall).setTargetPhoneAccount(eq(pAHandle));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        // Notify successful connection to call
+        CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
+        mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
+        verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
+    }
+
+    public void testConnectionManagerFailedDoNotFallToSim() throws Exception {
+        PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
+        when(mMockCall.isEmergencyCall()).thenReturn(false);
+        // Include a Connection Manager
+        PhoneAccountHandle callManagerPAHandle = getNewConnectionMangerHandle("cm_acct");
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        when(mMockCall.getConnectionManagerPhoneAccount()).thenReturn(callManagerPAHandle);
+        PhoneAccount mFakeTargetPhoneAccount = makeQuickAccount("cm_acct",
+                PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
+        when(mMockAccountRegistrar.getPhoneAccountUnchecked(pAHandle)).thenReturn(
+                mFakeTargetPhoneAccount);
+        when(mMockCall.getConnectionService()).thenReturn(service);
+        // Put CreateConnectionProcessor in correct state to fail with ConnectionManager
+        mTestCreateConnectionProcessor.process();
+        reset(mMockCall);
+        reset(service);
+
+        // Notify that the ConnectionManager has rejected the call.
+        when(mMockCall.getConnectionManagerPhoneAccount()).thenReturn(callManagerPAHandle);
+        when(mMockCall.getConnectionService()).thenReturn(service);
+        when(service.isServiceValid("createConnection")).thenReturn(true);
+        mTestCreateConnectionProcessor.handleCreateConnectionFailure(
+                new DisconnectCause(DisconnectCause.OTHER));
+
+        // Verify call connection rejected
+        verify(mMockCreateConnectionResponse).handleCreateConnectionFailure(
+                new DisconnectCause(DisconnectCause.OTHER));
+    }
+
+    public void testEmergencyCallToSim() throws Exception {
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        // Put in a regular phone account to be sure it doesn't call that
+        PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
+        // Include a Connection Manager to be sure it doesn't call that
+        PhoneAccount callManagerPA = getNewConnectionManagerPhoneAccount("cm_acct", 0);
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        PhoneAccount emergencyPhoneAccount = makeEmergencyPhoneAccount("tel_emer");
+        PhoneAccountHandle emergencyPhoneAccountHandle = emergencyPhoneAccount.getAccountHandle();
+
+        mTestCreateConnectionProcessor.process();
+
+        verify(mMockCall).setConnectionManagerPhoneAccount(eq(emergencyPhoneAccountHandle));
+        verify(mMockCall).setTargetPhoneAccount(eq(emergencyPhoneAccountHandle));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        // Notify successful connection to call
+        CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
+        mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
+        verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
+    }
+
+    public void testEmergencyCallSimFailToConnectionManager() throws Exception {
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.getHandle()).thenReturn(Uri.parse(""));
+        // Put in a regular phone account to be sure it doesn't call that
+        PhoneAccountHandle pAHandle = getNewTargetPhoneAccountHandle("tel_acct");
+        when(mMockAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                any(String.class))).thenReturn(pAHandle);
+        // Include a normal Connection Manager to be sure it doesn't call that
+        PhoneAccount callManagerPA = getNewConnectionManagerPhoneAccount("cm_acct", 0);
+        // Include a connection Manager for the user with the capability to make calls
+        PhoneAccount emerCallManagerPA = getNewEmergencyConnectionManagerPhoneAccount("cm_acct",
+                PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS);
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        PhoneAccount emergencyPhoneAccount = makeEmergencyPhoneAccount("tel_emer");
+        mTestCreateConnectionProcessor.process();
+        reset(mMockCall);
+        reset(service);
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+
+        // When Notify SIM connection fails, fall back to connection manager
+        mTestCreateConnectionProcessor.handleCreateConnectionFailure(new DisconnectCause(
+                DisconnectCause.REJECTED));
+
+        verify(mMockCall).setConnectionManagerPhoneAccount(
+                eq(emerCallManagerPA.getAccountHandle()));
+        verify(mMockCall).setTargetPhoneAccount(eq(pAHandle));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+    }
+
+    private PhoneAccount makeEmergencyPhoneAccount(String id) {
+        final PhoneAccount emergencyPhoneAccount = makeQuickAccount(id,
+                PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS |
+                        PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
+        PhoneAccountHandle emergencyPhoneAccountHandle = emergencyPhoneAccount.getAccountHandle();
+        givePhoneAccountBindPermission(emergencyPhoneAccountHandle);
+        ArrayList<PhoneAccount> phoneAccounts = new ArrayList<PhoneAccount>() {{
+            add(emergencyPhoneAccount);
+        }};
+        when(mMockAccountRegistrar.getAllPhoneAccountsOfCurrentUser()).thenReturn(phoneAccounts);
+        return emergencyPhoneAccount;
+    }
+
+    private void givePhoneAccountBindPermission(PhoneAccountHandle handle) {
+        when(mMockAccountRegistrar.phoneAccountRequiresBindPermission(eq(handle))).thenReturn(true);
+    }
+
+    private PhoneAccountHandle getNewConnectionMangerHandle(String id) {
+        PhoneAccountHandle callManagerPAHandle = makeQuickAccountHandle(id);
+        when(mMockAccountRegistrar.getSimCallManagerFromCall(any(Call.class))).thenReturn(
+                callManagerPAHandle);
+        givePhoneAccountBindPermission(callManagerPAHandle);
+        return callManagerPAHandle;
+    }
+
+    private PhoneAccountHandle getNewTargetPhoneAccountHandle(String id) {
+        PhoneAccountHandle pAHandle = makeQuickAccountHandle(id);
+        when(mMockCall.getTargetPhoneAccount()).thenReturn(pAHandle);
+        givePhoneAccountBindPermission(pAHandle);
+        return pAHandle;
+    }
+
+    private PhoneAccount getNewConnectionManagerPhoneAccount(String id, int capability) {
+        PhoneAccount callManagerPA = makeQuickAccount(id, capability);
+        when(mMockAccountRegistrar.getSimCallManagerFromCall(any(Call.class))).thenReturn(
+                callManagerPA.getAccountHandle());
+        givePhoneAccountBindPermission(callManagerPA.getAccountHandle());
+        when(mMockAccountRegistrar.getPhoneAccountUnchecked(
+                callManagerPA.getAccountHandle())).thenReturn(callManagerPA);
+        return callManagerPA;
+    }
+
+    private PhoneAccount getNewEmergencyConnectionManagerPhoneAccount(String id, int capability) {
+        PhoneAccount callManagerPA = makeQuickAccount(id, capability);
+        when(mMockAccountRegistrar.getSimCallManagerOfCurrentUser()).thenReturn(
+                callManagerPA.getAccountHandle());
+        givePhoneAccountBindPermission(callManagerPA.getAccountHandle());
+        when(mMockAccountRegistrar.getPhoneAccountUnchecked(
+                callManagerPA.getAccountHandle())).thenReturn(callManagerPA);
+        return callManagerPA;
+    }
+
+    private static ComponentName makeQuickConnectionServiceComponentName() {
+        return new ComponentName(TEST_PACKAGE, TEST_CLASS);
+    }
+
+    private ConnectionServiceWrapper makeConnectionServiceWrapper() {
+        ConnectionServiceWrapper wrapper = mock(ConnectionServiceWrapper.class);
+        when(mMockConnectionServiceRepository.getService(
+                eq(makeQuickConnectionServiceComponentName()),
+                eq(Binder.getCallingUserHandle()))).thenReturn(wrapper);
+        return wrapper;
+    }
+
+    private static PhoneAccountHandle makeQuickAccountHandle(String id) {
+        return new PhoneAccountHandle(makeQuickConnectionServiceComponentName(), id,
+                Binder.getCallingUserHandle());
+    }
+
+    private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
+        return new PhoneAccount.Builder(makeQuickAccountHandle(id), "label" + idx);
+    }
+
+    private PhoneAccount makeQuickAccount(String id, int idx) {
+        return makeQuickAccountBuilder(id, idx)
+                .setAddress(Uri.parse("http://foo.com/" + idx))
+                .setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
+                .setCapabilities(idx)
+                .setIcon(Icon.createWithResource(
+                        "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
+                .setShortDescription("desc" + idx)
+                .setIsEnabled(true)
+                .build();
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
index 2dd4b97..3790c8b 100644
--- a/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/InCallServiceFixture.java
@@ -128,4 +128,8 @@
     public ParcelableCall getCall(String id) {
         return mCallById.get(id);
     }
+
+    public IInCallAdapter getInCallAdapter() {
+        return mInCallAdapter;
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/LogTest.java b/tests/src/com/android/server/telecom/tests/LogTest.java
new file mode 100644
index 0000000..673582a
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/LogTest.java
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+
+import com.android.internal.os.SomeArgs;
+import com.android.server.telecom.Session;
+import com.android.server.telecom.SystemLoggingContainer;
+import com.android.server.telecom.Log;
+
+import org.junit.Assert;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Unit tests for Telecom's Logging system.
+ */
+public class LogTest extends TelecomTestCase{
+
+    /**
+     * This helper class captures the logs that are sent to Log and stores them in an array to be
+     * verified by LogTest.
+     */
+    private class TestLoggingContainer extends SystemLoggingContainer {
+        public ArrayList<String> receivedStrings;
+
+        public TestLoggingContainer() {
+            receivedStrings = new ArrayList<>(100);
+        }
+
+        @Override
+        public synchronized void v(String msgTag, String msg) {
+            if (msgTag.equals(LogTest.TESTING_TAG)) {
+                synchronized (this) {
+                    receivedStrings.add(msg);
+                }
+            }
+        }
+
+        @Override
+        public synchronized void i(String msgTag, String msg) {
+            if (msgTag.equals(LogTest.TESTING_TAG)) {
+                synchronized (this) {
+                    receivedStrings.add(msg);
+                }
+            }
+        }
+
+        public boolean didReceiveMessage(int timeoutMs, String msg) {
+            String matchedString = null;
+            // Wait for timeout to expire before checking received messages
+            if (timeoutMs > 0) {
+                try {
+                    Thread.sleep(timeoutMs);
+                } catch (InterruptedException e) {
+                    Log.w(LogTest.TESTING_TAG, "TestLoggingContainer: Thread Interrupted!");
+                }
+            }
+            synchronized (this) {
+                for (String receivedString : receivedStrings) {
+                    if (receivedString.contains(msg)) {
+                        matchedString = receivedString;
+                        break;
+                    }
+                }
+                if (matchedString != null) {
+                    receivedStrings.remove(matchedString);
+                    return true;
+                }
+            }
+            android.util.Log.i(TESTING_TAG, "Did not receive message: " + msg);
+            return false;
+        }
+
+        public boolean isMessagesEmpty() {
+            boolean isEmpty = receivedStrings.isEmpty();
+            if (!isEmpty) {
+                printMessagesThatAreLeft();
+            }
+            return isEmpty;
+        }
+
+        public synchronized void printMessagesThatAreLeft() {
+            android.util.Log.i(TESTING_TAG, "Remaining Messages in Log Queue:");
+            for (String receivedString : receivedStrings) {
+                android.util.Log.i(TESTING_TAG, "\t- " + receivedString);
+            }
+        }
+    }
+
+    public static final int TEST_THREAD_COUNT = 150;
+    public static final int TEST_SLEEP_TIME_MS = 50;
+    // Should be larger than TEST_SLEEP_TIME_MS!
+    public static final int TEST_VERIFY_TIMEOUT_MS = 100;
+    public static final String TEST_ENTER_METHOD1 = "TEM1";
+    public static final String TEST_ENTER_METHOD2 = "TEM2";
+    public static final String TEST_ENTER_METHOD3 = "TEM3";
+    public static final String TEST_ENTER_METHOD4 = "TEM4";
+    public static final String TEST_CLASS_NAME = "LogTest";
+
+    private static final int EVENT_START_TEST_SLEEPY_METHOD = 0;
+    private static final int EVENT_START_TEST_SLEEPY_MULTIPLE_METHOD = 1;
+    private static final int EVENT_LAST_MESSAGE = 2;
+
+    private static final long RANDOM_NUMBER_SEED = 6191991;
+
+    Random rng = new Random(RANDOM_NUMBER_SEED);
+
+    private Handler mSleepyHandler = new Handler(
+            new HandlerThread("sleepyThread"){{start();}}.getLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            SomeArgs args = (SomeArgs) msg.obj;
+            Session subsession = (Session) args.arg1;
+            Log.continueSession(subsession, "lTSH.hM");
+            switch (msg.what) {
+                case EVENT_START_TEST_SLEEPY_METHOD:
+                    sleepyMethod(TEST_SLEEP_TIME_MS);
+                    break;
+            }
+            Log.endSession();
+        }
+    };
+
+    private boolean isHandlerCompleteWithEvents;
+    private Handler mSleepyMultipleHandler = new Handler(
+            new HandlerThread("sleepyMultipleThread"){{start();}}.getLooper()){
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_START_TEST_SLEEPY_MULTIPLE_METHOD:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Session subsession = (Session) args.arg1;
+                    Log.continueSession(subsession, "lTSCH.hM");
+                    sleepyMultipleMethod();
+                    Log.endSession();
+                    break;
+                case EVENT_LAST_MESSAGE:
+                    isHandlerCompleteWithEvents = true;
+                    break;
+            }
+        }
+    };
+
+    private AtomicInteger mCompleteCount;
+    class LogTestRunnable implements Runnable {
+        private String mshortMethodName;
+        public LogTestRunnable(String shortMethodName) {
+            mshortMethodName = shortMethodName;
+        }
+
+        public void run() {
+            Log.startSession(mshortMethodName);
+            sleepyCallerMethod(TEST_SLEEP_TIME_MS);
+            Log.endSession();
+            mCompleteCount.incrementAndGet();
+        }
+    }
+
+    TestLoggingContainer mTestSystemLogger;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mTestSystemLogger = new TestLoggingContainer();
+        Log.setLoggingContainer(mTestSystemLogger);
+        Log.restartSessionCounter();
+        Log.sCleanStaleSessions = null;
+        Log.sSessionMapper.clear();
+        Log.setContext(mComponentContextFixture.getTestDouble().getApplicationContext());
+        Log.sSessionCleanupTimeoutMs = new Log.ISessionCleanupTimeoutMs() {
+            @Override
+            // Set to the default value of Timeouts.getStaleSessionCleanupTimeoutMillis without
+            // needing to query.
+            public long get() {
+                return 30000;
+            }
+        };
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mTestSystemLogger = null;
+        Log.setLoggingContainer(new SystemLoggingContainer());
+        super.tearDown();
+    }
+
+    public void testSingleThreadSession() throws Exception {
+        String sessionName = "LT.sTS";
+        Log.startSession(sessionName);
+        sleepyMethod(TEST_SLEEP_TIME_MS);
+        Log.endSession();
+
+        verifyEventResult(Session.START_SESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyMethodCall("", sessionName, 0, "", TEST_ENTER_METHOD1, TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.END_SUBSESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEndEventResult(sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testSingleHandlerThreadSession() throws Exception {
+        String sessionName = "LT.tSHTS";
+        Log.startSession(sessionName);
+        Session subsession = Log.createSubsession();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = subsession;
+        mSleepyHandler.obtainMessage(EVENT_START_TEST_SLEEPY_METHOD, args).sendToTarget();
+        Log.endSession();
+
+        verifyEventResult(Session.START_SESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.CREATE_SUBSESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyContinueEventResult(sessionName, "lTSH.hM", "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.END_SUBSESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyMethodCall(sessionName, "lTSH.hM", 0, "_0", TEST_ENTER_METHOD1,
+                TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.END_SUBSESSION, "lTSH.hM", "_0", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEndEventResult(sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testSpawnMultipleThreadSessions() throws Exception {
+        final String sessionName = "LT.lTR";
+        mCompleteCount = new AtomicInteger(0);
+        for (int i = 0; i < TEST_THREAD_COUNT; i++) {
+            Thread.sleep(10);
+            new Thread(new LogTestRunnable(sessionName)).start();
+        }
+
+        // Poll until all of the threads have completed
+        while (mCompleteCount.get() < TEST_THREAD_COUNT) {
+            Thread.sleep(1000);
+        }
+
+        // Loop through verification separately to spawn threads fast so there is possible overlap
+        // (verifyEventResult(...) delays)
+        for (int i = 0; i < TEST_THREAD_COUNT; i++) {
+            verifyEventResult(Session.START_SESSION, sessionName, "", i, 0);
+            verifyMethodCall("", sessionName, i, "", TEST_ENTER_METHOD2, 0);
+            verifyMethodCall("", sessionName, i, "", TEST_ENTER_METHOD1, 0);
+            verifyEventResult(Session.END_SUBSESSION, sessionName, "", i, 0);
+            verifyEndEventResult(sessionName, "", i, 0);
+        }
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testSpawnMultipleThreadMultipleHandlerSession() throws Exception {
+        String sessionName = "LT.tSMTMHS";
+        Log.startSession(sessionName);
+        Session subsession = Log.createSubsession();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = subsession;
+        mSleepyMultipleHandler.obtainMessage(EVENT_START_TEST_SLEEPY_MULTIPLE_METHOD,
+                args).sendToTarget();
+        Log.endSession();
+
+        verifyEventResult(Session.START_SESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.END_SUBSESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.CREATE_SUBSESSION, sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyContinueEventResult(sessionName, "lTSCH.hM", "_0", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyMethodCall(sessionName, "lTSCH.hM", 0, "_0", TEST_ENTER_METHOD3,
+                TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.END_SUBSESSION, "lTSCH.hM", "_0", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.CREATE_SUBSESSION, "lTSCH.hM", "_0", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyContinueEventResult(sessionName + "->" + "lTSCH.hM", "lTSH.hM", "_0_0", 0,
+                TEST_VERIFY_TIMEOUT_MS);
+        verifyMethodCall(sessionName + "->lTSCH.hM", "lTSH.hM", 0, "_0_0", TEST_ENTER_METHOD1,
+                TEST_VERIFY_TIMEOUT_MS);
+        verifyEventResult(Session.END_SUBSESSION, "lTSH.hM", "_0_0", 0, TEST_VERIFY_TIMEOUT_MS);
+        verifyEndEventResult(sessionName, "", 0, TEST_VERIFY_TIMEOUT_MS);
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testSpawnMultipleThreadMultipleHandlerSessions() throws Exception {
+        String sessionName = "LT.tSMTMHSs";
+        isHandlerCompleteWithEvents = false;
+        for (int i = 0; i < TEST_THREAD_COUNT; i++) {
+            Log.startSession(sessionName);
+            Session subsession = Log.createSubsession();
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = subsession;
+            mSleepyMultipleHandler.obtainMessage(EVENT_START_TEST_SLEEPY_MULTIPLE_METHOD,
+                    args).sendToTarget();
+            Log.endSession();
+        }
+        // Send a message that denotes the last message that is sent. We poll until this message
+        // is processed in order to verify the results without waiting an arbitrary amount of time
+        // (that can change per device).
+        mSleepyMultipleHandler.obtainMessage(EVENT_LAST_MESSAGE).sendToTarget();
+
+        while (!isHandlerCompleteWithEvents) {
+            Thread.sleep(1000);
+        }
+
+        for (int i = 0; i < TEST_THREAD_COUNT; i++) {
+            verifyEventResult(Session.START_SESSION, sessionName, "", i, 0);
+            verifyEventResult(Session.END_SUBSESSION, sessionName, "", i, 0);
+            verifyEventResult(Session.CREATE_SUBSESSION, sessionName, "", i, 0);
+            verifyContinueEventResult(sessionName, "lTSCH.hM", "_0", i, 0);
+            verifyMethodCall(sessionName, "lTSCH.hM", i, "_0", TEST_ENTER_METHOD3, 0);
+            verifyEventResult(Session.END_SUBSESSION, "lTSCH.hM", "_0", i, 0);
+            verifyEventResult(Session.CREATE_SUBSESSION, "lTSCH.hM", "_0", i, 0);
+            verifyContinueEventResult(sessionName + "->" + "lTSCH.hM", "lTSH.hM", "_0_0", i, 0);
+            verifyMethodCall(sessionName + "->lTSCH.hM", "lTSH.hM", i, "_0_0", TEST_ENTER_METHOD1,
+                    0);
+            verifyEventResult(Session.END_SUBSESSION, "lTSH.hM", "_0_0", i, 0);
+            verifyEndEventResult(sessionName, "", i, 0);
+        }
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testCancelSubsession() throws Exception {
+        String sessionName = "LT.tCS";
+        Log.startSession(sessionName);
+        Session subsession = Log.createSubsession();
+        Log.cancelSubsession(subsession);
+        Log.endSession();
+
+        verifyEventResult(Session.START_SESSION, sessionName, "", 0, 0);
+        verifyEventResult(Session.CREATE_SUBSESSION, sessionName, "", 0, 0);
+        verifyEventResult(Session.END_SUBSESSION, sessionName, "", 0, 0);
+        verifyEndEventResult(sessionName, "", 0, 0);
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testInternalExternalCallToMethod() throws Exception {
+        String sessionName = "LT.tIECTM";
+        Log.startSession(sessionName);
+        internalExternalMethod();
+        Log.endSession();
+
+        verifyEventResult(Session.START_SESSION, sessionName, "", 0, 0);
+        verifyEventResult(Session.CREATE_SUBSESSION, sessionName, "", 0, 0);
+        verifyContinueEventResult(sessionName, "LT.iEM", "", 0, 0);
+        verifyEventResult(Session.END_SUBSESSION, sessionName, "", 0, 0);
+        verifyMethodCall(sessionName, "LT.iEM", 0, "_0", TEST_ENTER_METHOD4, 0);
+        verifyEventResult(Session.END_SUBSESSION, "LT.iEM", "_0", 0, 0);
+        verifyEndEventResult(sessionName, "", 0, 0);
+
+        assertEquals(Log.sSessionMapper.size(), 0);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+    }
+
+    public void testGarbageCollectionWithTimeout() throws Exception {
+        String sessionName = "LT.tGCWT";
+
+        // Don't end session (Oops!)
+        Log.startSession(sessionName);
+        internalDanglingMethod();
+        Log.sSessionCleanupHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                android.util.Log.i(TESTING_TAG, "Running Test SessionCleanupHandler method.");
+                Log.cleanupStaleSessions(1000);
+            }
+        }, 1000);
+
+        verifyEventResult(Session.START_SESSION, sessionName, "", 0, 0);
+        verifyEventResult(Session.CREATE_SUBSESSION, sessionName, "", 0, 0);
+        verifyContinueEventResult(sessionName, "LT.iEM", "", 0, 0);
+        verifyMethodCall(sessionName, "LT.iEM", 0, "_0", TEST_ENTER_METHOD4, 0);
+
+        // Verify the session is still active in sSessionMapper
+        assertEquals(Log.sSessionMapper.size(), 1);
+        assertEquals(true, mTestSystemLogger.isMessagesEmpty());
+
+        // Keep a weak reference to the object to check if it eventually gets garbage collected.
+        int threadId = Log.getCallingThreadId();
+        WeakReference<Session> sessionRef = new WeakReference<>(
+                Log.sSessionMapper.get(threadId));
+
+        Thread.sleep(1100);
+        assertEquals(0, Log.sSessionMapper.size());
+        // "Suggest" that the GC collects the now isolated Session and subsession and wait for it
+        // to occur.
+        System.gc();
+        Thread.sleep(1000);
+        assertEquals(null, sessionRef.get());
+    }
+
+    private void verifyMethodCall(String parentSessionName, String methodName, int sessionId,
+            String subsession, String shortMethodName, int timeoutMs) {
+        if (!parentSessionName.isEmpty()){
+            parentSessionName += "->";
+        }
+        boolean isMessageReceived = mTestSystemLogger.didReceiveMessage(timeoutMs,
+                buildExpectedResult(parentSessionName + methodName, sessionId, subsession,
+                        shortMethodName));
+
+        assertEquals(true, isMessageReceived);
+    }
+
+    private String buildExpectedSession(String shortMethodName, int sessionId) {
+        return shortMethodName + "@" + Log.getBase64Encoding(sessionId);
+    }
+
+    private String buildExpectedResult(String shortMethodName, int sessionId,
+            String subsessionId, String logText) {
+        return TEST_CLASS_NAME + " " + buildExpectedSession(shortMethodName, sessionId) +
+                subsessionId + ": " + logText;
+    }
+
+    private void verifyContinueEventResult(String shortOldMethodName, String shortNewMethodName,
+                String subsession, int sessionId, int timeoutMs) {
+        String expectedSession = buildExpectedSession(shortNewMethodName, sessionId);
+        boolean isMessageReceived = mTestSystemLogger.didReceiveMessage(timeoutMs,
+                Session.CONTINUE_SUBSESSION + " " + shortOldMethodName + "->" + expectedSession +
+                        subsession);
+        assertEquals(true, isMessageReceived);
+    }
+
+    private void verifyEventResult(String event, String shortMethodName,  String subsession,
+            int sessionId, int timeoutMs) {
+        String expectedSession = buildExpectedSession(shortMethodName, sessionId);
+        boolean isMessageReceived = mTestSystemLogger.didReceiveMessage(timeoutMs, expectedSession +
+                subsession + ": " + event);
+        assertEquals(true, isMessageReceived);
+    }
+
+    private void verifyEndEventResult(String shortMethodName, String subsession, int sessionId,
+            int timeoutMs) {
+        String expectedSession = buildExpectedSession(shortMethodName, sessionId);
+        boolean isMessageReceived = mTestSystemLogger.didReceiveMessage(timeoutMs,
+                Session.END_SESSION + " " + expectedSession + subsession);
+        assertEquals(true, isMessageReceived);
+    }
+
+    private void internalExternalMethod() {
+        Log.startSession("LT.iEM");
+        Log.i(TEST_CLASS_NAME, TEST_ENTER_METHOD4);
+        Log.endSession();
+    }
+
+    private void internalDanglingMethod() {
+        Log.startSession("LT.iEM");
+        Log.i(TEST_CLASS_NAME, TEST_ENTER_METHOD4);
+    }
+
+    private void sleepyCallerMethod(int timeToSleepMs) {
+        Log.i(TEST_CLASS_NAME, TEST_ENTER_METHOD2);
+        try {
+            Thread.sleep(timeToSleepMs);
+            sleepyMethod(rng.nextInt(TEST_SLEEP_TIME_MS));
+        } catch (InterruptedException e) {
+            // This should not happen
+            Assert.fail("Thread sleep interrupted: " + e.getMessage());
+        }
+
+    }
+
+    private void sleepyMultipleMethod() {
+        Log.i(TEST_CLASS_NAME, TEST_ENTER_METHOD3);
+        Session subsession = Log.createSubsession();
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = subsession;
+        mSleepyHandler.obtainMessage(EVENT_START_TEST_SLEEPY_METHOD, args).sendToTarget();
+        try {
+            Thread.sleep(TEST_SLEEP_TIME_MS);
+        } catch (InterruptedException e) {
+            // This should not happen
+            Assert.fail("Thread sleep interrupted: " + e.getMessage());
+        }
+    }
+
+    private void sleepyMethod(int timeToSleepMs) {
+        Log.i(TEST_CLASS_NAME, TEST_ENTER_METHOD1);
+        try {
+            Thread.sleep(timeToSleepMs);
+        } catch (InterruptedException e) {
+            // This should not happen
+            Assert.fail("Thread sleep interrupted: " + e.getMessage());
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
new file mode 100644
index 0000000..ba32a5d
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.Constants;
+import com.android.server.telecom.MissedCallNotifier;
+import com.android.server.telecom.TelecomBroadcastIntentProcessor;
+import com.android.server.telecom.components.TelecomBroadcastReceiver;
+import com.android.server.telecom.ui.MissedCallNotifierImpl;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class MissedCallNotifierImplTest extends TelecomTestCase {
+
+    private static final Uri TEL_CALL_HANDLE = Uri.parse("tel:+11915552620");
+    private static final Uri SIP_CALL_HANDLE = Uri.parse("sip:testaddress@testdomain.com");
+    private static final String CALLER_NAME = "Fake Name";
+    private static final String MISSED_CALL_TITLE = "Missed Call";
+    private static final String MISSED_CALLS_TITLE = "Missed Calls";
+    private static final String MISSED_CALLS_MSG = "%s missed calls";
+    private static final String USER_CALL_ACTIVITY_LABEL = "Phone";
+
+    private static final int REQUEST_ID = 0;
+    private static final long CALL_TIMESTAMP;
+    static {
+         CALL_TIMESTAMP = System.currentTimeMillis() - 60 * 1000 * 5;
+    }
+
+    @Mock
+    private NotificationManager mNotificationManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+
+        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+        mNotificationManager = (NotificationManager) mContext.getSystemService(
+                Context.NOTIFICATION_SERVICE);
+        TelephonyManager fakeTelephonyManager = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        when(fakeTelephonyManager.getNetworkCountryIso()).thenReturn("US");
+        doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
+        doReturn("com.android.server.telecom.tests").when(mContext).getPackageName();
+
+        mComponentContextFixture.putResource(R.string.notification_missedCallTitle,
+                MISSED_CALL_TITLE);
+        mComponentContextFixture.putResource(R.string.notification_missedCallsTitle,
+                MISSED_CALLS_TITLE);
+        mComponentContextFixture.putResource(R.string.notification_missedCallsMsg,
+                MISSED_CALLS_MSG);
+        mComponentContextFixture.putResource(R.string.userCallActivityLabel,
+                USER_CALL_ACTIVITY_LABEL);
+    }
+
+    public void testCancelNotification() {
+        Notification.Builder builder1 = makeNotificationBuilder("builder1");
+        Notification.Builder builder2 = makeNotificationBuilder("builder2");
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builder1, builder1, builder2, builder2);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                fakeBuilderFactory);
+
+        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP);
+
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+        missedCallNotifier.clearMissedCalls();
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+
+        ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(
+                Integer.class);
+        verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class),
+                requestIdCaptor.capture(), any(Notification.class), eq(UserHandle.CURRENT));
+        verify(mNotificationManager).cancelAsUser(any(String.class), eq(requestIdCaptor.getValue()),
+                eq(UserHandle.CURRENT));
+
+        // Verify that the second call to showMissedCallNotification behaves like it were the first.
+        verify(builder2).setContentText(CALLER_NAME);
+    }
+
+    public void testNotifyMultipleMissedCalls() {
+        Notification.Builder[] builders = new Notification.Builder[4];
+
+        for (int i = 0; i < 4; i++) {
+            builders[i] = makeNotificationBuilder("builder" + Integer.toString(i));
+        }
+
+        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP);
+
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builders);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                fakeBuilderFactory);
+
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+
+        // The following captor is to capture the two notifications that got passed into
+        // notifyAsUser. This distinguishes between the builders used for the full notification
+        // (i.e. the one potentially containing sensitive information, such as phone numbers),
+        // and the builders used for the notifications shown on the lockscreen, which have been
+        // scrubbed of such sensitive info. The notifications which are used as arguments
+        // to notifyAsUser are the versions which contain sensitive information.
+        ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
+                Notification.class);
+        verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1),
+                notificationArgumentCaptor.capture(), eq(UserHandle.CURRENT));
+        HashSet<String> privateNotifications = new HashSet<>();
+        for (Notification n : notificationArgumentCaptor.getAllValues()) {
+            privateNotifications.add(n.toString());
+        }
+
+        for (int i = 0; i < 4; i++) {
+            Notification.Builder builder = builders[i];
+            verify(builder).setWhen(CALL_TIMESTAMP);
+            if (i >= 2) {
+                // The builders after the first two are for multiple missed calls. The notification
+                // for subsequent missed calls is expected to be different in terms of the text
+                // contents of the notification, and that is verified here.
+                if (privateNotifications.contains(builder.toString())) {
+                    verify(builder).setContentText(String.format(MISSED_CALLS_MSG, 2));
+                    verify(builder).setContentTitle(MISSED_CALLS_TITLE);
+                } else {
+                    verify(builder).setContentText(MISSED_CALLS_TITLE);
+                    verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
+                }
+                verify(builder, never()).addAction(any(Notification.Action.class));
+            } else {
+                if (privateNotifications.contains(builder.toString())) {
+                    verify(builder).setContentText(CALLER_NAME);
+                    verify(builder).setContentTitle(MISSED_CALL_TITLE);
+                } else {
+                    verify(builder).setContentText(MISSED_CALL_TITLE);
+                    verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
+                }
+            }
+        }
+    }
+
+    public void testNotifySingleCall() {
+        Notification.Builder builder1 = makeNotificationBuilder("builder1");
+        Notification.Builder builder2 = makeNotificationBuilder("builder2");
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builder1, builder2);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                fakeBuilderFactory);
+
+        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP);
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+
+        ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
+                Notification.class);
+        verify(mNotificationManager).notifyAsUser(isNull(String.class), eq(1),
+                notificationArgumentCaptor.capture(), eq(UserHandle.CURRENT));
+
+        Notification.Builder builder;
+        Notification.Builder publicBuilder;
+
+        if (notificationArgumentCaptor.getValue().toString().equals("builder1")) {
+            builder = builder1;
+            publicBuilder = builder2;
+        } else {
+            builder = builder2;
+            publicBuilder = builder1;
+        }
+
+        verify(builder).setWhen(CALL_TIMESTAMP);
+        verify(publicBuilder).setWhen(CALL_TIMESTAMP);
+
+        verify(builder).setContentText(CALLER_NAME);
+        verify(publicBuilder).setContentText(MISSED_CALL_TITLE);
+
+        verify(builder).setContentTitle(MISSED_CALL_TITLE);
+        verify(publicBuilder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
+
+        // Create two intents that correspond to call-back and respond back with SMS, and assert
+        // that these pending intents have in fact been registered.
+        Intent callBackIntent = new Intent(
+                TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION,
+                TEL_CALL_HANDLE,
+                mContext,
+                TelecomBroadcastReceiver.class);
+        Intent smsIntent = new Intent(
+                TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION,
+                Uri.fromParts(Constants.SCHEME_SMSTO, TEL_CALL_HANDLE.getSchemeSpecificPart(), null),
+                mContext,
+                TelecomBroadcastReceiver.class);
+
+        assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
+                callBackIntent, PendingIntent.FLAG_NO_CREATE));
+        assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
+                smsIntent, PendingIntent.FLAG_NO_CREATE));
+    }
+
+    public void testNoSmsBackAfterMissedSipCall() {
+        Notification.Builder builder1 = makeNotificationBuilder("builder1");
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builder1);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                fakeBuilderFactory);
+
+        Call fakeCall = makeFakeCall(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP);
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+
+        // Create two intents that correspond to call-back and respond back with SMS, and assert
+        // that in the case of a SIP call, no SMS intent is generated.
+        Intent callBackIntent = new Intent(
+                TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION,
+                SIP_CALL_HANDLE,
+                mContext,
+                TelecomBroadcastReceiver.class);
+        Intent smsIntent = new Intent(
+                TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION,
+                Uri.fromParts(Constants.SCHEME_SMSTO, SIP_CALL_HANDLE.getSchemeSpecificPart(),
+                        null),
+                mContext,
+                TelecomBroadcastReceiver.class);
+
+        assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
+                callBackIntent, PendingIntent.FLAG_NO_CREATE));
+        assertNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
+                smsIntent, PendingIntent.FLAG_NO_CREATE));
+    }
+
+    private Notification.Builder makeNotificationBuilder(String label) {
+        Notification.Builder builder = spy(new Notification.Builder(mContext));
+        Notification notification = mock(Notification.class);
+        when(notification.toString()).thenReturn(label);
+        when(builder.toString()).thenReturn(label);
+        doReturn(notification).when(builder).build();
+        return builder;
+    }
+
+    private Call makeFakeCall(Uri handle, String name, long timestamp) {
+        Call fakeCall = mock(Call.class);
+        when(fakeCall.getHandle()).thenReturn(handle);
+        when(fakeCall.getName()).thenReturn(name);
+        when(fakeCall.getCreationTimeMillis()).thenReturn(timestamp);
+        return fakeCall;
+    }
+
+    private MissedCallNotifierImpl.NotificationBuilderFactory makeNotificationBuilderFactory(
+            Notification.Builder... builders) {
+        MissedCallNotifierImpl.NotificationBuilderFactory builderFactory =
+                mock(MissedCallNotifierImpl.NotificationBuilderFactory.class);
+        when(builderFactory.getBuilder(mContext)).thenReturn(builders[0],
+                Arrays.copyOfRange(builders, 1, builders.length));
+        return builderFactory;
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/MockitoHelper.java b/tests/src/com/android/server/telecom/tests/MockitoHelper.java
index 5193bba..3425b0e 100644
--- a/tests/src/com/android/server/telecom/tests/MockitoHelper.java
+++ b/tests/src/com/android/server/telecom/tests/MockitoHelper.java
@@ -18,9 +18,7 @@
 
 import com.android.server.telecom.Log;
 
-import android.annotation.TargetApi;
 import android.content.Context;
-import android.os.Looper;
 
 /**
  * Helper for Mockito-based test cases.
@@ -28,10 +26,6 @@
 public final class MockitoHelper {
     private static final String DEXCACHE = "dexmaker.dexcache";
 
-    private Thread mRequestThread;
-    private ClassLoader mRequestThreadOriginalClassLoader;
-    private ClassLoader mMainThreadOriginalClassLoader;
-
     /**
      * Creates a new helper, which in turn will set the context classloader so
      * it can load Mockito resources.
@@ -39,24 +33,6 @@
      * @param packageClass test case class
      */
     public void setUp(Context context, Class<?> packageClass) throws Exception {
-        // makes a copy of the context classloader
-        mRequestThread = Thread.currentThread();
-        mRequestThreadOriginalClassLoader = mRequestThread.getContextClassLoader();
-        mMainThreadOriginalClassLoader = Looper.getMainLooper().getThread().getContextClassLoader();
-
-        ClassLoader newClassLoader = packageClass.getClassLoader();
-
-        Log.v(this, "Changing context classloader for thread %s from %s to %s",
-                mRequestThread.getName(),
-                mRequestThreadOriginalClassLoader,
-                newClassLoader);
-        mRequestThread.setContextClassLoader(newClassLoader);
-
-        Log.v(this, "Changing context classloader for MAIN thread from %s to %s",
-                mMainThreadOriginalClassLoader,
-                newClassLoader);
-        Looper.getMainLooper().getThread().setContextClassLoader(newClassLoader);
-
         String dexCache = context.getCacheDir().toString();
         Log.v(this, "Setting property %s to %s", DEXCACHE, dexCache);
         System.setProperty(DEXCACHE, dexCache);
@@ -67,7 +43,6 @@
      */
     public void tearDown() throws Exception {
         Log.v(this, "Restoring context classloaders");
-        mRequestThread.setContextClassLoader(mRequestThreadOriginalClassLoader);
         Log.v(this, "Clearing property %s", DEXCACHE);
         System.clearProperty(DEXCACHE);
     }
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index f815fed..03b1946 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -16,44 +16,55 @@
 
 package com.android.server.telecom.tests;
 
-import android.os.Binder;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.UserHandle;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.util.Xml;
 
 import com.android.internal.telecom.IConnectionService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.telecom.Log;
 import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneAccountRegistrar.DefaultPhoneAccountHandle;
 
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
 
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
-import android.os.Parcel;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-import android.util.Xml;
-
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.util.Arrays;
+import java.util.Set;
 
 public class PhoneAccountRegistrarTest extends TelecomTestCase {
 
     private static final int MAX_VERSION = Integer.MAX_VALUE;
     private static final String FILE_NAME = "phone-account-registrar-test-1223.xml";
     private PhoneAccountRegistrar mRegistrar;
+    @Mock
+    private TelecomManager mTelecomManager;
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
+        MockitoAnnotations.initMocks(this);
         mComponentContextFixture = new ComponentContextFixture();
+        mComponentContextFixture.setTelecomManager(mTelecomManager);
         new File(
                 mComponentContextFixture.getTestDouble().getApplicationContext().getFilesDir(),
                 FILE_NAME)
@@ -88,9 +99,18 @@
     }
 
     public void testPhoneAccount() throws Exception {
+        Bundle testBundle = new Bundle();
+        testBundle.putInt("EXTRA_INT_1", 1);
+        testBundle.putInt("EXTRA_INT_100", 100);
+        testBundle.putBoolean("EXTRA_BOOL_TRUE", true);
+        testBundle.putBoolean("EXTRA_BOOL_FALSE", false);
+        testBundle.putString("EXTRA_STR1", "Hello");
+        testBundle.putString("EXTRA_STR2", "There");
+
         PhoneAccount input = makeQuickAccountBuilder("id0", 0)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+                .setExtras(testBundle)
                 .build();
         PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
                 mContext);
@@ -98,6 +118,38 @@
         assertPhoneAccountEquals(input, result);
     }
 
+    /**
+     * Test to ensure non-supported balues
+     * @throws Exception
+     */
+    public void testPhoneAccountExtrasEdge() throws Exception {
+        Bundle testBundle = new Bundle();
+        // Ensure null values for string are not persisted.
+        testBundle.putString("EXTRA_STR2", null);
+        //
+
+        // Ensure unsupported data types are not persisted.
+        testBundle.putShort("EXTRA_SHORT", (short) 2);
+        testBundle.putByte("EXTRA_BYTE", (byte) 1);
+        testBundle.putParcelable("EXTRA_PARC", new Rect(1, 1, 1, 1));
+        // Put in something valid so the bundle exists.
+        testBundle.putString("EXTRA_OK", "OK");
+
+        PhoneAccount input = makeQuickAccountBuilder("id0", 0)
+                .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+                .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
+                .setExtras(testBundle)
+                .build();
+        PhoneAccount result = roundTripXml(this, input, PhoneAccountRegistrar.sPhoneAccountXml,
+                mContext);
+
+        Bundle extras = result.getExtras();
+        assertFalse(extras.keySet().contains("EXTRA_STR2"));
+        assertFalse(extras.keySet().contains("EXTRA_SHORT"));
+        assertFalse(extras.keySet().contains("EXTRA_BYTE"));
+        assertFalse(extras.keySet().contains("EXTRA_PARC"));
+    }
+
     public void testState() throws Exception {
         PhoneAccountRegistrar.State input = makeQuickState();
         PhoneAccountRegistrar.State result = roundTripXml(this, input,
@@ -134,10 +186,11 @@
                 .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
                 .build());
 
-        assertEquals(4, mRegistrar.getAllPhoneAccountHandles().size());
-        assertEquals(3, mRegistrar.getCallCapablePhoneAccounts(null, false).size());
-        assertEquals(null, mRegistrar.getSimCallManager());
-        assertEquals(null, mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL));
+        assertEquals(4, mRegistrar.getAllPhoneAccountsOfCurrentUser().size());
+        assertEquals(3, mRegistrar.getCallCapablePhoneAccountsOfCurrentUser(null, false).size());
+        assertEquals(null, mRegistrar.getSimCallManagerOfCurrentUser());
+        assertEquals(null, mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                PhoneAccount.SCHEME_TEL));
     }
 
     public void testSimCallManager() throws Exception {
@@ -150,7 +203,8 @@
                 Mockito.mock(IConnectionService.class));
 
         // By default, there is no default outgoing account (nothing has been registered)
-        assertNull(mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL));
+        assertNull(
+                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
 
         // Register one tel: account
         PhoneAccountHandle telAccount = makeQuickAccountHandle("tel_acct");
@@ -159,7 +213,7 @@
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .build());
         PhoneAccountHandle defaultAccount =
-                mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL);
+                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL);
         assertEquals(telAccount, defaultAccount);
 
         // Add a SIP account, make sure tel: doesn't change
@@ -168,9 +222,11 @@
                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
                 .build());
-        defaultAccount = mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_SIP);
+        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                PhoneAccount.SCHEME_SIP);
         assertEquals(sipAccount, defaultAccount);
-        defaultAccount = mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL);
+        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                PhoneAccount.SCHEME_TEL);
         assertEquals(telAccount, defaultAccount);
 
         // Add a connection manager, make sure tel: doesn't change
@@ -179,12 +235,14 @@
                 .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .build());
-        defaultAccount = mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL);
+        defaultAccount = mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
+                PhoneAccount.SCHEME_TEL);
         assertEquals(telAccount, defaultAccount);
 
         // Unregister the tel: account, make sure there is no tel: default now.
         mRegistrar.unregisterPhoneAccount(telAccount);
-        assertNull(mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL));
+        assertNull(
+                mRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(PhoneAccount.SCHEME_TEL));
     }
 
     public void testPhoneAccountParceling() throws Exception {
@@ -228,7 +286,7 @@
         return new PhoneAccountHandle(
                 makeQuickConnectionServiceComponentName(),
                 id,
-                Binder.getCallingUserHandle());
+                Process.myUserHandle());
     }
 
     private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
@@ -318,6 +376,18 @@
         }
     }
 
+    private static void assertDefaultPhoneAccountHandleEquals(DefaultPhoneAccountHandle a,
+            DefaultPhoneAccountHandle b) {
+        if (a != b) {
+            if (a!= null && b != null) {
+                assertEquals(a.userHandle, b.userHandle);
+                assertPhoneAccountHandleEquals(a.phoneAccountHandle, b.phoneAccountHandle);
+            } else {
+                fail("Default phone account handles are not equal: " + a + ", " + b);
+            }
+        }
+    }
+
     private static void assertPhoneAccountEquals(PhoneAccount a, PhoneAccount b) {
         if (a != b) {
             if (a != null && b != null) {
@@ -330,15 +400,39 @@
                 assertEquals(a.getLabel(), b.getLabel());
                 assertEquals(a.getShortDescription(), b.getShortDescription());
                 assertEquals(a.getSupportedUriSchemes(), b.getSupportedUriSchemes());
+                assertBundlesEqual(a.getExtras(), b.getExtras());
             } else {
                 fail("Phone accounts not equal: " + a + ", " + b);
             }
         }
     }
 
+    private static void assertBundlesEqual(Bundle a, Bundle b) {
+        if (a == null && b == null) {
+            return;
+        }
+
+        assertNotNull(a);
+        assertNotNull(b);
+        Set<String> keySetA = a.keySet();
+        Set<String> keySetB = b.keySet();
+
+        assertTrue("Bundle keys not the same", keySetA.containsAll(keySetB));
+        assertTrue("Bundle keys not the same", keySetB.containsAll(keySetA));
+
+        for (String keyA : keySetA) {
+            assertEquals("Bundle value not the same", a.get(keyA), b.get(keyA));
+        }
+    }
+
     private static void assertStateEquals(
             PhoneAccountRegistrar.State a, PhoneAccountRegistrar.State b) {
-        assertPhoneAccountHandleEquals(a.defaultOutgoing, b.defaultOutgoing);
+        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));
+        }
         assertEquals(a.accounts.size(), b.accounts.size());
         for (int i = 0; i < a.accounts.size(); i++) {
             assertPhoneAccountEquals(a.accounts.get(i), b.accounts.get(i));
@@ -350,7 +444,11 @@
         s.accounts.add(makeQuickAccount("id0", 0));
         s.accounts.add(makeQuickAccount("id1", 1));
         s.accounts.add(makeQuickAccount("id2", 2));
-        s.defaultOutgoing = new PhoneAccountHandle(new ComponentName("pkg0", "cls0"), "id0");
+        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(
+                new ComponentName("pkg0", "cls0"), "id0");
+        UserHandle userHandle = phoneAccountHandle.getUserHandle();
+        s.defaultOutgoingAccountHandles
+                .put(userHandle, new DefaultPhoneAccountHandle(userHandle, phoneAccountHandle));
         return s;
     }
 }
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
new file mode 100644
index 0000000..b8616ee
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.tests;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.Ringtone;
+import android.net.Uri;
+import android.os.Vibrator;
+
+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.CallsManager;
+import com.android.server.telecom.SystemSettingsUtil;
+import com.android.server.telecom.InCallTonePlayer;
+import com.android.server.telecom.Ringer;
+import com.android.server.telecom.RingtoneFactory;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class RingerTest extends TelecomTestCase {
+
+    @Mock Ringtone mMockRingtone;
+    @Mock Vibrator mMockVibrator;
+    @Mock RingtoneFactory mMockRingtoneFactory;
+    @Mock SystemSettingsUtil mMockSystemSettings;
+    @Mock CallsManager mMockCallsManager;
+    @Mock CallAudioManager mMockCallAudioManager;
+    @Mock InCallTonePlayer.Factory mMockToneFactory;
+
+    private Ringer mRingerUnderTest;
+
+    // These tests depend on an async handler to execute play() and stop() on the mock ringtone.
+    // In order to verify these results, the test must wait an arbitrary amount of time to make sure
+    // these methods are called.
+    private static final int TEST_TIMEOUT = 100; //milliseconds
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        // Don't involve CallAudioManager logic in this unit test
+        doNothing().when(mMockCallAudioManager).setIsRinging(any(Call.class), any(boolean.class));
+        // Assume theatre mode is off for these tests
+        when(mMockSystemSettings.isTheaterModeOn(any(Context.class))).thenReturn(false);
+        // Assume the system is set to enable vibration when ringing
+        when(mMockSystemSettings.canVibrateWhenRinging(any(Context.class))).thenReturn(true);
+        when(mMockVibrator.hasVibrator()).thenReturn(true);
+        mRingerUnderTest = new Ringer(
+                mMockCallAudioManager, mMockCallsManager, mMockToneFactory,
+                mComponentContextFixture.getTestDouble().getApplicationContext(),
+                mMockSystemSettings, new AsyncRingtonePlayer(), mMockRingtoneFactory,
+                mMockVibrator);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mRingerUnderTest = null;
+        super.tearDown();
+    }
+
+    private Call createMockRingingCall() {
+        Call mockCall = mock(Call.class);
+        when(mockCall.isIncoming()).thenReturn(true);
+        when(mockCall.getState()).thenReturn(CallState.RINGING);
+        return mockCall;
+    }
+
+    public void testPlayRingtoneOnIncomingCall() throws Exception {
+        Call mockCall = createMockRingingCall();
+        when(mMockRingtoneFactory.getRingtone(any(Uri.class))).thenReturn(mMockRingtone);
+        when(mMockCallsManager.getForegroundCall()).thenReturn(mockCall);
+
+        mRingerUnderTest.onCallAdded(mockCall);
+
+        verify(mMockVibrator).vibrate(any(long[].class), any(int.class),
+                any(AudioAttributes.class));
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).play();
+    }
+
+    public void testCallAnsweredOnIncomingCall() throws Exception {
+        Call mockCall = createMockRingingCall();
+        when(mMockRingtoneFactory.getRingtone(any(Uri.class))).thenReturn(mMockRingtone);
+        when(mMockCallsManager.getForegroundCall()).thenReturn(mockCall);
+
+        // Make sure the ringtone plays for foreground call
+        mRingerUnderTest.onCallAdded(mockCall);
+
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).play();
+        verify(mMockVibrator).vibrate(any(long[].class), any(int.class),
+                any(AudioAttributes.class));
+
+        // Answer Call
+        mRingerUnderTest.onIncomingCallAnswered(mockCall);
+
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).stop();
+        verify(mMockVibrator).cancel();
+    }
+
+    public void testCallWaitingOnBackgroundCall() throws Exception {
+        Call mockForegroundCall = mock(Call.class);
+        Call mockBackgroundCall = createMockRingingCall();
+        // Set foreground call to already answered
+        when(mMockCallsManager.getForegroundCall()).thenReturn(mockForegroundCall);
+        InCallTonePlayer mockInCallTonePlayer = mock(InCallTonePlayer.class);
+        when(mMockToneFactory.createPlayer(any(int.class))).thenReturn(mockInCallTonePlayer);
+
+        // Add new call waiting call
+        mRingerUnderTest.onCallAdded(mockBackgroundCall);
+
+        verify(mockInCallTonePlayer).startTone();
+    }
+
+    public void testCallWaitingOnBackgroundCallDisconnected() throws Exception {
+        Call mockForegroundCall = mock(Call.class);
+        Call mockBackgroundCall = createMockRingingCall();
+        // Set foreground call to already answered
+        when(mMockCallsManager.getForegroundCall()).thenReturn(mockForegroundCall);
+        InCallTonePlayer mockInCallTonePlayer = mock(InCallTonePlayer.class);
+        when(mMockToneFactory.createPlayer(any(int.class))).thenReturn(mockInCallTonePlayer);
+
+        // Add new call waiting call
+        mRingerUnderTest.onCallAdded(mockBackgroundCall);
+
+        verify(mockInCallTonePlayer).startTone();
+
+        // Reject the call waiting call
+        mRingerUnderTest.onIncomingCallRejected(mockBackgroundCall, false, "");
+
+        verify(mockInCallTonePlayer).stopTone();
+    }
+
+    public void testCallDisconnectedWhileRinging() throws Exception {
+        Call mockCall = createMockRingingCall();
+        when(mMockRingtoneFactory.getRingtone(any(Uri.class))).thenReturn(mMockRingtone);
+        when(mMockCallsManager.getForegroundCall()).thenReturn(mockCall);
+
+        // Make sure the ringtone plays for foreground call
+        mRingerUnderTest.onCallAdded(mockCall);
+
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).play();
+        verify(mMockVibrator).vibrate(any(long[].class), any(int.class),
+                any(AudioAttributes.class));
+
+        // Call Disconnected
+        mRingerUnderTest.onCallRemoved(mockCall);
+
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).stop();
+        verify(mMockVibrator).cancel();
+    }
+
+    public void testIncomingCallRejected() throws Exception {
+        Call mockCall = createMockRingingCall();
+        when(mMockRingtoneFactory.getRingtone(any(Uri.class))).thenReturn(mMockRingtone);
+        when(mMockCallsManager.getForegroundCall()).thenReturn(mockCall);
+
+        // Make sure the ringtone plays for foreground call
+        mRingerUnderTest.onCallAdded(mockCall);
+
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).play();
+        verify(mMockVibrator).vibrate(any(long[].class), any(int.class),
+                any(AudioAttributes.class));
+
+        // Answer Call
+        mRingerUnderTest.onIncomingCallRejected(mockCall, false, "");
+
+        verify(mMockRingtone, timeout(TEST_TIMEOUT)).stop();
+        verify(mMockVibrator).cancel();
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 4f96cc7..39aeb5d 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -16,27 +16,33 @@
 
 package com.android.server.telecom.tests;
 
-import com.google.common.base.Predicate;
 
 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.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioManager;
+import android.media.IAudioService;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Handler;
+import android.os.Process;
 import android.os.UserHandle;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
@@ -48,23 +54,39 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
-import android.telephony.TelephonyManager;
 
 import com.android.internal.telecom.IInCallAdapter;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.telecom.Analytics;
+import com.android.server.telecom.BluetoothPhoneServiceImpl;
+import com.android.server.telecom.CallAudioManager;
+import com.android.server.telecom.CallIntentProcessor;
+import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.CallsManagerListenerBase;
+import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.InCallWakeLockControllerFactory;
 import com.android.server.telecom.Log;
 import com.android.server.telecom.MissedCallNotifier;
+import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.components.PrimaryCallReceiver;
+import com.android.server.telecom.components.UserCallIntentProcessor;
+
+import com.google.common.base.Predicate;
 
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
+import java.io.StringWriter;
+import java.util.Map;
 import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.CyclicBarrier;
@@ -74,10 +96,53 @@
     static final int TEST_POLL_INTERVAL = 10;  // milliseconds
     static final int TEST_TIMEOUT = 1000;  // milliseconds
 
-    @Mock MissedCallNotifier mMissedCallNotifier;
+    public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
+        @Override
+        public HeadsetMediaButton create(Context context, CallsManager callsManager,
+                TelecomSystem.SyncRoot lock) {
+            return mHeadsetMediaButton;
+        }
+    }
+
+    public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory {
+        @Override
+        public ProximitySensorManager create(Context context, CallsManager callsManager) {
+            return mProximitySensorManager;
+        }
+    }
+
+    public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory {
+        @Override
+        public InCallWakeLockController create(Context context, CallsManager callsManager) {
+            return mInCallWakeLockController;
+        }
+    }
+
+    public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
+            implements MissedCallNotifier {
+        @Override
+        public void clearMissedCalls() {
+
+        }
+
+        @Override
+        public void showMissedCallNotification(com.android.server.telecom.Call call) {
+
+        }
+
+        @Override
+        public void updateOnStartup(TelecomSystem.SyncRoot lock, CallsManager callsManager,
+                ContactsAsyncHelper contactsAsyncHelper,
+                CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory) {
+
+        }
+    }
+
+    MissedCallNotifier mMissedCallNotifier = new MissedCallNotifierFakeImpl();
     @Mock HeadsetMediaButton mHeadsetMediaButton;
     @Mock ProximitySensorManager mProximitySensorManager;
     @Mock InCallWakeLockController mInCallWakeLockController;
+    @Mock BluetoothPhoneServiceImpl mBluetoothPhoneServiceImpl;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -139,8 +204,12 @@
 
     CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
 
+    IAudioService mAudioService;
+
     TelecomSystem mTelecomSystem;
 
+    private int mNumOutgoingCallsMade;
+
     class IdPair {
         final String mConnectionId;
         final String mCallId;
@@ -154,6 +223,7 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
+        mNumOutgoingCallsMade = 0;
 
         // First set up information about the In-Call services in the mock Context, since
         // Telecom will search for these as soon as it is instantiated
@@ -174,36 +244,42 @@
     }
 
     private void setupTelecomSystem() throws Exception {
+        // Use actual implementations instead of mocking the interface out.
         HeadsetMediaButtonFactory headsetMediaButtonFactory =
-                mock(HeadsetMediaButtonFactory.class);
+                spy(new HeadsetMediaButtonFactoryF());
         ProximitySensorManagerFactory proximitySensorManagerFactory =
-                mock(ProximitySensorManagerFactory.class);
+                spy(new ProximitySensorManagerFactoryF());
         InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
-                mock(InCallWakeLockControllerFactory.class);
+                spy(new InCallWakeLockControllerFactoryF());
+        mAudioService = setupAudioService();
 
         mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
 
-        when(headsetMediaButtonFactory.create(
-                any(Context.class),
-                any(CallsManager.class),
-                any(TelecomSystem.SyncRoot.class)))
-                .thenReturn(mHeadsetMediaButton);
-        when(proximitySensorManagerFactory.create(
-                any(Context.class),
-                any(CallsManager.class)))
-                .thenReturn(mProximitySensorManager);
-        when(inCallWakeLockControllerFactory.create(
-                any(Context.class),
-                any(CallsManager.class)))
-                .thenReturn(mInCallWakeLockController);
-
         mTelecomSystem = new TelecomSystem(
                 mComponentContextFixture.getTestDouble(),
                 mMissedCallNotifier,
                 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
                 headsetMediaButtonFactory,
                 proximitySensorManagerFactory,
-                inCallWakeLockControllerFactory);
+                inCallWakeLockControllerFactory,
+                new CallAudioManager.AudioServiceFactory() {
+                    @Override
+                    public IAudioService getAudioService() {
+                        return mAudioService;
+                    }
+                },
+                new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
+                    @Override
+                    public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
+                            TelecomSystem.SyncRoot lock, CallsManager callsManager,
+                            PhoneAccountRegistrar phoneAccountRegistrar) {
+                        return mBluetoothPhoneServiceImpl;
+                    }
+                });
+
+        mComponentContextFixture.setTelecomManager(new TelecomManager(
+                mComponentContextFixture.getTestDouble(),
+                mTelecomSystem.getTelecomServiceImpl().getBinder()));
 
         verify(headsetMediaButtonFactory).create(
                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
@@ -233,7 +309,7 @@
         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
 
         mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
-                mPhoneAccountA0.getAccountHandle());
+                mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
     }
 
     private void setupInCallServices() throws Exception {
@@ -243,6 +319,8 @@
         mComponentContextFixture.putResource(
                 com.android.server.telecom.R.string.incall_default_class,
                 mInCallServiceComponentNameX.getClassName());
+        mComponentContextFixture.putBooleanResource(
+                com.android.internal.R.bool.config_voice_capable, true);
 
         mInCallServiceFixtureX = new InCallServiceFixture();
         mInCallServiceFixtureY = new InCallServiceFixture();
@@ -255,10 +333,41 @@
                 mInCallServiceFixtureY.getTestDouble());
     }
 
+    /**
+     * Helper method for setting up the fake audio service.
+     * Calls to the fake audio service need to toggle the return
+     * value of AudioManager#isMicrophoneMute.
+     * @return mock of IAudioService
+     */
+    private IAudioService setupAudioService() {
+        IAudioService audioService = mock(IAudioService.class);
+
+        final AudioManager fakeAudioManager =
+                (AudioManager) mComponentContextFixture.getTestDouble()
+                        .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
+
+        try {
+            doAnswer(new Answer() {
+                @Override
+                public Object answer(InvocationOnMock i) {
+                    Object[] args = i.getArguments();
+                    doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute();
+                    return null;
+                }
+            }).when(audioService)
+                    .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class));
+
+        } catch (android.os.RemoteException e) {
+            // Do nothing, leave the faked microphone state as-is
+        }
+        return audioService;
+    }
+
     private IdPair startOutgoingPhoneCall(
             String number,
             PhoneAccountHandle phoneAccountHandle,
-            ConnectionServiceFixture connectionServiceFixture) throws Exception {
+            ConnectionServiceFixture connectionServiceFixture,
+            UserHandle initiatingUser) throws Exception {
         reset(
                 connectionServiceFixture.getTestDouble(),
                 mInCallServiceFixtureX.getTestDouble(),
@@ -271,6 +380,7 @@
                 (mInCallServiceFixtureX.mInCallAdapter != null),
                 (mInCallServiceFixtureY.mInCallAdapter != null));
 
+        mNumOutgoingCallsMade++;
         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
@@ -285,8 +395,19 @@
                     phoneAccountHandle);
         }
 
+        final UserHandle userHandle = initiatingUser;
+        Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
+        new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
+                actionCallIntent, null, true /* hasCallAppOp*/);
+        // 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.
+        verify(localAppContext).sendBroadcastAsUser(actionCallIntent, UserHandle.SYSTEM);
         mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
 
+        assertEquals(userHandle,
+                actionCallIntent.getParcelableExtra(CallIntentProcessor.KEY_INITIATING_USER));
+
         if (!hasInCallAdapter) {
             verify(mInCallServiceFixtureX.getTestDouble())
                     .setInCallAdapter(
@@ -301,7 +422,8 @@
         ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
 
-        verify(mComponentContextFixture.getTestDouble().getApplicationContext())
+        verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
+                times(mNumOutgoingCallsMade))
                 .sendOrderedBroadcastAsUser(
                         newOutgoingCallIntent.capture(),
                         any(UserHandle.class),
@@ -351,6 +473,15 @@
             String number,
             PhoneAccountHandle phoneAccountHandle,
             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
+        return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
+                connectionServiceFixture);
+    }
+
+    private IdPair startIncomingPhoneCall(
+            String number,
+            PhoneAccountHandle phoneAccountHandle,
+            int videoState,
+            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
         reset(
                 connectionServiceFixture.getTestDouble(),
                 mInCallServiceFixtureX.getTestDouble(),
@@ -381,10 +512,15 @@
                 eq(true),
                 eq(false));
 
+        mConnectionServiceFixtureA.mConnectionById.get(
+                connectionServiceFixture.mLatestConnectionId).videoState = videoState;
+
         connectionServiceFixture.sendHandleCreateConnectionComplete(
                 connectionServiceFixture.mLatestConnectionId);
         connectionServiceFixture.sendSetRinging(
                 connectionServiceFixture.mLatestConnectionId);
+        connectionServiceFixture.sendSetVideoState(
+                connectionServiceFixture.mLatestConnectionId);
 
         // 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
@@ -494,7 +630,8 @@
             String number,
             PhoneAccountHandle phoneAccountHandle,
             ConnectionServiceFixture connectionServiceFixture) throws Exception {
-        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
+        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
+                Process.myUserHandle());
 
         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
@@ -507,6 +644,76 @@
         return ids;
     }
 
+    public void testBasicConferenceCall() throws Exception {
+        makeConferenceCall();
+    }
+
+    public void testAddCallToConference1() throws Exception {
+        ParcelableCall conferenceCall = makeConferenceCall();
+        IdPair callId3 = startAndMakeActiveOutgoingCall(
+                "650-555-1214",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+        // testAddCallToConference{1,2} differ in the order of arguments to InCallAdapter#conference
+        mInCallServiceFixtureX.getInCallAdapter().conference(
+                conferenceCall.getId(), callId3.mCallId);
+        Thread.sleep(200);
+
+        ParcelableCall call3 = mInCallServiceFixtureX.getCall(callId3.mCallId);
+        ParcelableCall updatedConference = mInCallServiceFixtureX.getCall(conferenceCall.getId());
+        assertEquals(conferenceCall.getId(), call3.getParentCallId());
+        assertEquals(3, updatedConference.getChildCallIds().size());
+        assertTrue(updatedConference.getChildCallIds().contains(callId3.mCallId));
+    }
+
+    public void testAddCallToConference2() throws Exception {
+        ParcelableCall conferenceCall = makeConferenceCall();
+        IdPair callId3 = startAndMakeActiveOutgoingCall(
+                "650-555-1214",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+        mInCallServiceFixtureX.getInCallAdapter().conference(
+                callId3.mCallId, conferenceCall.getId());
+        Thread.sleep(200);
+
+        ParcelableCall call3 = mInCallServiceFixtureX.getCall(callId3.mCallId);
+        ParcelableCall updatedConference = mInCallServiceFixtureX.getCall(conferenceCall.getId());
+        assertEquals(conferenceCall.getId(), call3.getParentCallId());
+        assertEquals(3, updatedConference.getChildCallIds().size());
+        assertTrue(updatedConference.getChildCallIds().contains(callId3.mCallId));
+    }
+
+    private ParcelableCall makeConferenceCall() throws Exception {
+        IdPair callId1 = startAndMakeActiveOutgoingCall(
+                "650-555-1212",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        IdPair callId2 = startAndMakeActiveOutgoingCall(
+                "650-555-1213",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
+        inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
+        // Wait for wacky non-deterministic behavior
+        Thread.sleep(200);
+        ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId);
+        ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId);
+        // Check that the two calls end up with a parent in the end
+        assertNotNull(call1.getParentCallId());
+        assertNotNull(call2.getParentCallId());
+        assertEquals(call1.getParentCallId(), call2.getParentCallId());
+
+        // Check to make sure that the parent call made it to the in-call service
+        String parentCallId = call1.getParentCallId();
+        ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId);
+        assertEquals(2, conferenceCall.getChildCallIds().size());
+        assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId));
+        assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId));
+        return conferenceCall;
+    }
+
     public void testSingleOutgoingCallLocalDisconnect() throws Exception {
         IdPair ids = startAndMakeActiveOutgoingCall(
                 "650-555-1212",
@@ -537,6 +744,99 @@
                 mInCallServiceFixtureY.getCall(ids.mCallId).getState());
     }
 
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
+     * audio-only call.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingCall() throws Exception {
+        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());
+
+        // Use TelecomManager API to answer the ringing call.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall();
+
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answer(ids.mCallId);
+    }
+
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
+     * video call, which should be answered as video.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
+        // answer using whatever video state the ringing call requests.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall();
+
+        // Answer video API should be called
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall(int)} API.  Tests answering a video call
+     * as an audio call.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall(VideoProfile.STATE_AUDIO_ONLY);
+
+        // The generic answer method on the ConnectionService is used to answer audio-only calls.
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answer(eq(ids.mCallId));
+    }
+
+    /**
+     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
+     * video call, where an attempt is made to answer with an invalid video state.
+     *
+     * @throws Exception
+     */
+    public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
+        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
+                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
+
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
+        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
+
+        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
+        // answer using whatever video state the ringing call requests.
+        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
+                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
+        telecomManager.acceptRingingCall(999 /* invalid videostate */);
+
+        // Answer video API should be called
+        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
+                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
     // A simple incoming call, similar in scope to the previous test
     private IdPair startAndMakeActiveIncomingCall(
             String number,
@@ -594,7 +894,8 @@
         final IdPair ids = startOutgoingPhoneCall(
                 "650-555-1212",
                 mPhoneAccountA0.getAccountHandle(),
-                mConnectionServiceFixtureA);
+                mConnectionServiceFixtureA,
+                Process.myUserHandle());
         rapidFire(
                 new Runnable() {
                     @Override
@@ -663,6 +964,95 @@
                 mInCallServiceFixtureY.getCall(outgoing.mCallId).getState());
     }
 
+    public void testAnalyticsSingleCall() throws Exception {
+        IdPair testCall = startAndMakeActiveIncomingCall(
+                "650-555-1212",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+                Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData();
+
+        assertTrue(analyticsMap.containsKey(testCall.mCallId));
+
+        Analytics.CallInfoImpl callAnalytics = analyticsMap.get(testCall.mCallId);
+        assertTrue(callAnalytics.startTime > 0);
+        assertEquals(0, callAnalytics.endTime);
+        assertEquals(Analytics.INCOMING_DIRECTION, callAnalytics.callDirection);
+        assertFalse(callAnalytics.isInterrupted);
+        assertNull(callAnalytics.callTerminationReason);
+        assertEquals(mConnectionServiceComponentNameA.flattenToShortString(),
+                callAnalytics.connectionService);
+
+        mConnectionServiceFixtureA.
+                sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR);
+
+        analyticsMap = Analytics.cloneData();
+        callAnalytics = analyticsMap.get(testCall.mCallId);
+        assertTrue(callAnalytics.endTime > 0);
+        assertNotNull(callAnalytics.callTerminationReason);
+        assertEquals(DisconnectCause.ERROR, callAnalytics.callTerminationReason.getCode());
+
+        StringWriter sr = new StringWriter();
+        IndentingPrintWriter ip = new IndentingPrintWriter(sr, "    ");
+        Analytics.dump(ip);
+        String dumpResult = sr.toString();
+        String[] expectedFields = {"startTime", "endTime", "direction", "isAdditionalCall",
+                "isInterrupted", "callTechnologies", "callTerminationReason", "connectionServices"};
+        for (String field : expectedFields) {
+            assertTrue(dumpResult.contains(field));
+        }
+    }
+
+    public void testAnalyticsTwoCalls() throws Exception {
+        IdPair testCall1 = startAndMakeActiveIncomingCall(
+                "650-555-1212",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+        IdPair testCall2 = startAndMakeActiveOutgoingCall(
+                "650-555-1213",
+                mPhoneAccountA0.getAccountHandle(),
+                mConnectionServiceFixtureA);
+
+        Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData();
+        assertTrue(analyticsMap.containsKey(testCall1.mCallId));
+        assertTrue(analyticsMap.containsKey(testCall2.mCallId));
+
+        Analytics.CallInfoImpl callAnalytics1 = analyticsMap.get(testCall1.mCallId);
+        Analytics.CallInfoImpl callAnalytics2 = analyticsMap.get(testCall2.mCallId);
+        assertTrue(callAnalytics1.startTime > 0);
+        assertTrue(callAnalytics2.startTime > 0);
+        assertEquals(0, callAnalytics1.endTime);
+        assertEquals(0, callAnalytics2.endTime);
+
+        assertEquals(Analytics.INCOMING_DIRECTION, callAnalytics1.callDirection);
+        assertEquals(Analytics.OUTGOING_DIRECTION, callAnalytics2.callDirection);
+
+        assertTrue(callAnalytics1.isInterrupted);
+        assertTrue(callAnalytics2.isAdditionalCall);
+
+        assertNull(callAnalytics1.callTerminationReason);
+        assertNull(callAnalytics2.callTerminationReason);
+
+        assertEquals(mConnectionServiceComponentNameA.flattenToShortString(),
+                callAnalytics1.connectionService);
+        assertEquals(mConnectionServiceComponentNameA.flattenToShortString(),
+                callAnalytics1.connectionService);
+
+        mConnectionServiceFixtureA.
+                sendSetDisconnected(testCall2.mConnectionId, DisconnectCause.REMOTE);
+        mConnectionServiceFixtureA.
+                sendSetDisconnected(testCall1.mConnectionId, DisconnectCause.ERROR);
+
+        analyticsMap = Analytics.cloneData();
+        callAnalytics1 = analyticsMap.get(testCall1.mCallId);
+        callAnalytics2 = analyticsMap.get(testCall2.mCallId);
+        assertTrue(callAnalytics1.endTime > 0);
+        assertTrue(callAnalytics2.endTime > 0);
+        assertNotNull(callAnalytics1.callTerminationReason);
+        assertNotNull(callAnalytics2.callTerminationReason);
+        assertEquals(DisconnectCause.ERROR, callAnalytics1.callTerminationReason.getCode());
+        assertEquals(DisconnectCause.REMOTE, callAnalytics2.callTerminationReason.getCode());
+    }
+
     public void testAudioManagerOperations() throws Exception {
         AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble()
                 .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
@@ -678,11 +1068,11 @@
                 .setMode(AudioManager.MODE_IN_CALL);
 
         mInCallServiceFixtureX.mInCallAdapter.mute(true);
-        verify(audioManager, timeout(TEST_TIMEOUT))
-                .setMicrophoneMute(true);
+        verify(mAudioService, timeout(TEST_TIMEOUT))
+                .setMicrophoneMute(eq(true), any(String.class), any(Integer.class));
         mInCallServiceFixtureX.mInCallAdapter.mute(false);
-        verify(audioManager, timeout(TEST_TIMEOUT))
-                .setMicrophoneMute(false);
+        verify(mAudioService, timeout(TEST_TIMEOUT))
+                .setMicrophoneMute(eq(false), any(String.class), any(Integer.class));
 
         mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
         verify(audioManager, timeout(TEST_TIMEOUT))
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index 144ef66..9c9d0d8 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -23,7 +23,7 @@
 import android.test.AndroidTestCase;
 
 public abstract class TelecomTestCase extends AndroidTestCase {
-    private static final String TESTING_TAG = "Telecom-TEST";
+    protected static final String TESTING_TAG = "Telecom-TEST";
 
     MockitoHelper mMockitoHelper = new MockitoHelper();
     ComponentContextFixture mComponentContextFixture;