Merge branch 'dev/11/fp3/security-aosp-rvc-release' into int/11/fp3

* dev/11/fp3/security-aosp-rvc-release:
  DO NOT MERGE do not process content uri in call Intents
  Ensure service unbind when receiving a null call screening service in onBind.

Change-Id: Ia12d2c7200357657b3cf5bcce6d80d25c7e9d342
diff --git a/Android.bp b/Android.bp
index 0d89b00..8a1e00d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -17,6 +17,11 @@
 // Build the Telecom service.
 android_app {
     name: "Telecom",
+    libs: [
+      "telephony-common",
+      "telephony-ext",
+      "ims-ext-common",
+    ],
     srcs: [
         ":Telecom-srcs",
         "proto/**/*.proto",
@@ -68,6 +73,9 @@
         "android.test.mock",
         "android.test.base",
         "android.test.runner",
+        "telephony-common",
+        "telephony-ext",
+        "ims-ext-common",
     ],
 
     jni_libs: [
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eb0ac9e..0f9b3af 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -321,5 +321,6 @@
             </intent-filter>
         </service>
 
+        <uses-library android:name="ims-ext-common"/>
     </application>
 </manifest>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 87bde1c..c434ecc 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -47,7 +47,7 @@
     <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"Fehler beim Senden der Nachricht an <xliff:g id="PHONE_NUMBER">%s</xliff:g>"</string>
     <string name="enable_account_preference_title" msgid="6949224486748457976">"Anrufkonten"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"Es sind nur Notrufe erlaubt."</string>
-    <string name="outgoing_call_not_allowed_no_permission" msgid="8590468836581488679">"Diese App darf ohne die Berechtigung \"Standard-App für Telefonie\" keine ausgehenden Anrufe tätigen."</string>
+    <string name="outgoing_call_not_allowed_no_permission" msgid="8590468836581488679">"Diese App darf ohne die Berechtigung \"Telefon-App\" keine ausgehenden Anrufe tätigen."</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="7665135102566099778">"Gib eine gültige Nummer ein."</string>
     <string name="duplicate_video_call_not_allowed" msgid="5754746140185781159">"Der Anruf kann momentan nicht hinzugefügt werden."</string>
     <string name="no_vm_number" msgid="2179959110602180844">"Fehlende Mailbox-Nummer"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 3acab47..bd87d66 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -57,9 +57,9 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8604665314757739550">"デフォルトに設定"</string>
     <string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"キャンセル"</string>
     <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"<xliff:g id="NEW_APP">%s</xliff:g> はすべての通話の発信や制御を行えるようになります。デフォルトの電話アプリに設定するのは信頼できるアプリだけにしてください。"</string>
-    <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"<xliff:g id="NEW_APP">%s</xliff:g> をデフォルトのコール スクリーニング アプリにしますか?"</string>
+    <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"<xliff:g id="NEW_APP">%s</xliff:g> をデフォルトの通話スクリーニング アプリにしますか?"</string>
     <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"<xliff:g id="OLD_APP">%s</xliff:g> では通話をスクリーニングできなくなります。"</string>
-    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"<xliff:g id="NEW_APP">%s</xliff:g> をデフォルトにすると、連絡先に登録されていない発信者の情報を確認したり、そのような発信者からの通話をブロックしたりできるようになります。デフォルトのコール スクリーニング アプリに設定するのは信頼できるアプリだけにしてください。"</string>
+    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"<xliff:g id="NEW_APP">%s</xliff:g> をデフォルトにすると、連絡先に登録されていない発信者の情報を確認したり、そのような発信者からの通話をブロックしたりできるようになります。デフォルトの通話スクリーニング アプリに設定するのは信頼できるアプリだけにしてください。"</string>
     <string name="change_default_call_screening_dialog_affirmative" msgid="7162433828280058647">"デフォルトに設定"</string>
     <string name="change_default_call_screening_dialog_negative" msgid="1839266125623106342">"キャンセル"</string>
     <string name="blocked_numbers" msgid="8322134197039865180">"ブロックした番号"</string>
@@ -112,7 +112,7 @@
     <string name="phone_settings_private_num_summary_txt" msgid="6755758240544021037">"番号非通知の発信者をブロック"</string>
     <string name="phone_settings_payphone_txt" msgid="5003987966052543965">"公衆電話"</string>
     <string name="phone_settings_payphone_summary_txt" msgid="3936631076065563665">"公衆電話からの着信をブロック"</string>
-    <string name="phone_settings_unknown_txt" msgid="3577926178354772728">"不明"</string>
+    <string name="phone_settings_unknown_txt" msgid="3577926178354772728">"不明な発信者"</string>
     <string name="phone_settings_unknown_summary_txt" msgid="5446657192535779645">"不明な発信者からの着信をブロック"</string>
     <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="2895809176537908791">"着信のブロック"</string>
     <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="1713632946174016619">"着信のブロックを無効にしました"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 23c8033..99e211f 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -44,7 +44,7 @@
     <string name="respond_via_sms_setting_summary" msgid="8054571501085436868"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="6579353156073272157">"Брз одговор"</string>
     <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Порака е испратена на <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
-    <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"Пораката не можеше да се испрати на <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
+    <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"Пораката не може да се испрати на <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
     <string name="enable_account_preference_title" msgid="6949224486748457976">"Сметки за повици"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"Дозволени се само итни повици."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="8590468836581488679">"Оваа апликација не може да прави појдовни повици без дозволата Телефон."</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index c941fdd..cdef26b 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -25,7 +25,7 @@
     <string name="notification_missedCallsMsg" msgid="5055782736170916682">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> аваагүй дуудлага"</string>
     <string name="notification_missedCallTicker" msgid="6731461957487087769">"<xliff:g id="MISSED_CALL_FROM">%s</xliff:g>-н аваагүй дуудлага"</string>
     <string name="notification_missedCall_call_back" msgid="7900333283939789732">"Буцааж залгах"</string>
-    <string name="notification_missedCall_message" msgid="4054698824390076431">"Мессэж"</string>
+    <string name="notification_missedCall_message" msgid="4054698824390076431">"Мессеж"</string>
     <string name="notification_disconnectedCall_title" msgid="1790131923692416928">"Дуудлага саллаа"</string>
     <string name="notification_disconnectedCall_body" msgid="600491714584417536">"Яаралтай дуудлага ирсэн тул <xliff:g id="CALLER">%s</xliff:g> руу хийсэн дуудлагыг салгалаа."</string>
     <string name="notification_disconnectedCall_generic_body" msgid="5282765206349184853">"Яаралтай дуудлага ирсэн тул таны дуудлагыг салгалаа."</string>
@@ -43,8 +43,8 @@
     <string name="respond_via_sms_setting_title_2" msgid="4914853536609553457">"Шуурхай хариуг засах"</string>
     <string name="respond_via_sms_setting_summary" msgid="8054571501085436868"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="6579353156073272157">"Шуурхай хариу"</string>
-    <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Зурвасыг <xliff:g id="PHONE_NUMBER">%s</xliff:g> руу илгээв."</string>
-    <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> руу зурвас илгээж чадсангүй."</string>
+    <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Мессежийг <xliff:g id="PHONE_NUMBER">%s</xliff:g> руу илгээв."</string>
+    <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> руу мессеж илгээж чадсангүй."</string>
     <string name="enable_account_preference_title" msgid="6949224486748457976">"Дуудлагын эрхтэй бүртгэлүүд"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"Зөвхөн яаралтай тусламжийн дуудлага хийх боломжтой."</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="8590468836581488679">"Энэ апп нь утасны зөвшөөрөлгүйгээр дуудлага хийх боломжгүй."</string>
@@ -71,11 +71,11 @@
     <string name="add_blocked_number_hint" msgid="8769422085658041097">"Утасны дугаар"</string>
     <string name="block_button" msgid="485080149164258770">"Блоклох"</string>
     <string name="non_primary_user" msgid="315564589279622098">"Зөвхөн энэ төхөөрөмжийн эзэн блоклосон дугаарыг харж, өөрчлөх боломжтой."</string>
-    <string name="delete_icon_description" msgid="5335959254954774373">"Хоригийг тайлах"</string>
+    <string name="delete_icon_description" msgid="5335959254954774373">"Блокоос гаргах"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="582982373755950791">"Дугаар хориглох түр хугацаанд идэвхгүй болсон"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"Түргэн тусламжийн дугаар руу залгах буюу мессеж бичсэний дараа түргэн тусламжаас тантай холбогдох боломжтой байлгахын тулд дугаар хориглохыг идэвхгүй болгоно."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2704456308072489793">"Одоо дахин идэвхжүүлэх"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>-г хориглосон"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>-г блоклосон"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="2933071624674945601">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g>-г блокоос гаргасан"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="4198550501500893890">"Яаралтай дугаарыг хориглох боломжгүй."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="2301270825735665458">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g>-г аль хэдийн хориглосон байна."</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 3b87593..331cf5c 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -30,7 +30,7 @@
     <string name="notification_disconnectedCall_body" msgid="600491714584417536">"आणीबाणी कॉल केल्यामुळे <xliff:g id="CALLER">%s</xliff:g> ला केलेला कॉल डिस्कनेक्ट केला गेला."</string>
     <string name="notification_disconnectedCall_generic_body" msgid="5282765206349184853">"आणीबाणी कॉल केल्यामुळे तुमचा कॉल डिस्कनेक्ट केला गेला आहे."</string>
     <string name="notification_audioProcessing_title" msgid="1619035039880584575">"बॅकग्राउंड कॉल"</string>
-    <string name="notification_audioProcessing_body" msgid="6397005913770420388">"<xliff:g id="AUDIO_PROCESSING_APP_NAME">%s</xliff:g> यांनी कॉल बॅकग्राउंडवर ठेवला आहे हे ॲप कदाचित कॉलद्वारे ऑडिओ ॲक्सेस आणि प्ले करत आहे."</string>
+    <string name="notification_audioProcessing_body" msgid="6397005913770420388">"<xliff:g id="AUDIO_PROCESSING_APP_NAME">%s</xliff:g> यांनी कॉल बॅकग्राउंडवर ठेवला आहे हे अ‍ॅप कदाचित कॉलद्वारे ऑडिओ ॲक्सेस आणि प्ले करत आहे."</string>
     <string name="notification_incallservice_not_responding_title" msgid="5347557574288598548">"<xliff:g id="IN_CALL_SERVICE_APP_NAME">%s</xliff:g> ने प्रतिसाद देणे थांबवले आहे"</string>
     <string name="notification_incallservice_not_responding_body" msgid="9209308270131968623">"तुमच्या कॉलने डिव्हाइससोबत दिलेले फोन अ‍ॅप वापरले आहे"</string>
     <string name="accessibility_call_muted" msgid="2968461092554300779">"कॉल नि.शब्‍द केला."</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index e65fa1d..1e2b32f 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -27,8 +27,8 @@
     <string name="notification_missedCall_call_back" msgid="7900333283939789732">"फेरि कल गर्नुहोस्"</string>
     <string name="notification_missedCall_message" msgid="4054698824390076431">"सन्देश"</string>
     <string name="notification_disconnectedCall_title" msgid="1790131923692416928">"विच्छेद गरिएको कल"</string>
-    <string name="notification_disconnectedCall_body" msgid="600491714584417536">"आपत्कालीन कल गरिएको हुनाले <xliff:g id="CALLER">%s</xliff:g> लाई गरिएको कल विच्छेद गरियो।"</string>
-    <string name="notification_disconnectedCall_generic_body" msgid="5282765206349184853">"आपत्कालीन कल जारी रहेको हुनाले तपाईंको कल विच्छेद गरिएको छ।"</string>
+    <string name="notification_disconnectedCall_body" msgid="600491714584417536">"आपत्‌कालीन कल गरिएको हुनाले <xliff:g id="CALLER">%s</xliff:g> लाई गरिएको कल विच्छेद गरियो।"</string>
+    <string name="notification_disconnectedCall_generic_body" msgid="5282765206349184853">"आपत्‌कालीन कल जारी रहेको हुनाले तपाईंको कल विच्छेद गरिएको छ।"</string>
     <string name="notification_audioProcessing_title" msgid="1619035039880584575">"पृष्ठभूमिको कल"</string>
     <string name="notification_audioProcessing_body" msgid="6397005913770420388">"<xliff:g id="AUDIO_PROCESSING_APP_NAME">%s</xliff:g> ले एउटा कल पृष्ठभूमिमा राखेको छ। कल गरेको बेला यो अनुप्रयोगले अडियोमाथि पहुँच राखेर प्ले गरिरहेको हुन सक्छ।"</string>
     <string name="notification_incallservice_not_responding_title" msgid="5347557574288598548">"<xliff:g id="IN_CALL_SERVICE_APP_NAME">%s</xliff:g> ले काम गर्न छाड्यो"</string>
@@ -46,7 +46,7 @@
     <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> लाई सन्देश पठाइयो।"</string>
     <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> मा सन्देश पठाउन सकिएन।"</string>
     <string name="enable_account_preference_title" msgid="6949224486748457976">"कलिङ खाताहरू"</string>
-    <string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"आपतकालीन कलहरूलाई मात्र अनुमति दिइएको छ।"</string>
+    <string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"आपत्‌कालीन कलहरूलाई मात्र अनुमति दिइएको छ।"</string>
     <string name="outgoing_call_not_allowed_no_permission" msgid="8590468836581488679">"यो अनुप्रयोगले फोनको अनुमति बिना बहिर्गमन कलहरू गर्न सक्दैन।"</string>
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="7665135102566099778">"एक कल गर्नको लागि, एक वैध नम्बर प्रविष्टि गर्नुहोस्।"</string>
     <string name="duplicate_video_call_not_allowed" msgid="5754746140185781159">"यस समयमा कल थप गर्न सकिँदैन।"</string>
@@ -56,14 +56,14 @@
     <string name="change_default_dialer_dialog_title" msgid="5861469279421508060">"तपाईंको पूर्वनिर्धारित फोन एप <xliff:g id="NEW_APP">%s</xliff:g> बनाउने हो?"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8604665314757739550">"पूर्वनिर्धारित रूपमा सेट गर्नुहोस्"</string>
     <string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"रद्द गर्नुहोस्"</string>
-    <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"<xliff:g id="NEW_APP">%s</xliff:g> कलका सबै पक्षहरूलाई स्थापित गर्न र नियन्त्रण गर्न सक्षम हुने छ। तपाईंलाई विश्वास लाग्ने अनुप्रयोगहरूलाई मात्र फोनमा पूर्वनिर्धारित अनुप्रयोगका रूपमा सेट गर्नुपर्छ।"</string>
+    <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"<xliff:g id="NEW_APP">%s</xliff:g> कलका सबै पक्षहरूलाई स्थापित गर्न र नियन्त्रण गर्न सक्षम हुने छ। तपाईंलाई विश्वास लाग्ने एपहरूलाई मात्र फोनमा पूर्वनिर्धारित एपका रूपमा सेट गर्नुपर्छ।"</string>
     <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"तपाईंको पूर्वनिर्धारित कल स्क्रिन एप <xliff:g id="NEW_APP">%s</xliff:g> बनाउने हो?"</string>
     <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"<xliff:g id="OLD_APP">%s</xliff:g> ले अब उप्रान्त कलहरू स्क्रिन गर्न सक्ने छैनन्‌।"</string>
-    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"<xliff:g id="NEW_APP">%s</xliff:g> ले तपाईंको सम्पर्कमा नभएका कल गर्ने व्यक्तिका जानकारी हेर्न सक्छ र तिनीहरूमाथि रोक लगाउन सक्छ। तपाईंलाई विश्वास लाग्ने अनुप्रयोगहरूलाई मात्र कल स्क्रिन पूर्वनिर्धारित अनुप्रयोगका रूपमा सेट गर्नुपर्छ।"</string>
+    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"<xliff:g id="NEW_APP">%s</xliff:g> ले तपाईंको सम्पर्कमा नभएका कल गर्ने व्यक्तिका जानकारी हेर्न सक्छ र तिनीहरूमाथि रोक लगाउन सक्छ। तपाईंलाई विश्वास लाग्ने एपहरूलाई मात्र कल स्क्रिन पूर्वनिर्धारित एपका रूपमा सेट गर्नुपर्छ।"</string>
     <string name="change_default_call_screening_dialog_affirmative" msgid="7162433828280058647">"पूर्वनिर्धारित रूपमा सेट गर्नुहोस्"</string>
     <string name="change_default_call_screening_dialog_negative" msgid="1839266125623106342">"रद्द गर्नुहोस्"</string>
     <string name="blocked_numbers" msgid="8322134197039865180">"रोकिएका नम्बरहरू"</string>
-    <string name="blocked_numbers_msg" msgid="2797422132329662697">"तपाईँले रोक लगाइएका नम्बरहरूबाट फोन वा पाठ सन्देशहरू प्राप्त गर्नुहुने छैन।"</string>
+    <string name="blocked_numbers_msg" msgid="2797422132329662697">"तपाईँले रोक लगाइएका नम्बरहरूबाट फोन वा टेक्स्ट म्यासेजहरू प्राप्त गर्नुहुने छैन।"</string>
     <string name="block_number" msgid="3784343046852802722">"नम्बर थप्नुहोस्"</string>
     <string name="unblock_dialog_body" msgid="2723393535797217261">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> लाई अनब्लक गर्ने हो?"</string>
     <string name="unblock_button" msgid="8732021675729981781">"अनब्लक गर्नुहोस्"</string>
@@ -73,11 +73,11 @@
     <string name="non_primary_user" msgid="315564589279622098">"यन्त्रको मालिकले रोकिएका नम्बरहरूलाई हेर्न र व्यवस्थापन गर्न सक्छ।"</string>
     <string name="delete_icon_description" msgid="5335959254954774373">"अनब्लक गर्नुहोस्"</string>
     <string name="blocked_numbers_butter_bar_title" msgid="582982373755950791">"रोक लगाउने काम अस्थायी रूपमा निष्क्रिय छ"</string>
-    <string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"तपाईँले आपतकालीन नम्बरमा डायल गरेपछि वा पाठ सन्देश पठाएपछि आपतकालीन सेवाहरूले तपाईँलाई सम्पर्क गर्न सकून् भन्ने कुरा सुनिश्चित गर्न कलमाथिको अवरोध निष्क्रिय गरिन्छ।"</string>
+    <string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"तपाईँले आपत्‌कालीन नम्बरमा डायल गरेपछि वा पाठ सन्देश पठाएपछि आपत्‌कालीन सेवाहरूले तपाईँलाई सम्पर्क गर्न सकून् भन्ने कुरा सुनिश्चित गर्न कलमाथिको अवरोध निष्क्रिय गरिन्छ।"</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2704456308072489793">"अब पुन:-सक्रिय गर्नुहोस्"</string>
     <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> माथि रोक लगाइयो"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="2933071624674945601">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> माथिको रोक हटाइयो"</string>
-    <string name="blocked_numbers_block_emergency_number_message" msgid="4198550501500893890">"आपतकालीन नम्बरमाथि रोक लगाउन सकिएन।"</string>
+    <string name="blocked_numbers_block_emergency_number_message" msgid="4198550501500893890">"आपत्‌कालीन नम्बरमाथि रोक लगाउन सकिएन।"</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="2301270825735665458">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> लाई पहिले नै रोकिएको छ।"</string>
     <string name="toast_personal_call_msg" msgid="5817631570381795610">"कल गर्न व्यक्तिगत डायलर प्रयोग गर्दै"</string>
     <string name="notification_incoming_call" msgid="1233481138362230894">"<xliff:g id="CALL_FROM">%2$s</xliff:g> ले गरेको <xliff:g id="CALL_VIA">%1$s</xliff:g> कल"</string>
@@ -116,8 +116,8 @@
     <string name="phone_settings_unknown_summary_txt" msgid="5446657192535779645">"अज्ञात कल गर्ने व्यक्तिहरूको कलमाथि रोक लगाउनुहोस्"</string>
     <string name="phone_strings_call_blocking_turned_off_notification_title_txt" msgid="2895809176537908791">"कलमाथि रोक लगाउने सुविधा"</string>
     <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="1713632946174016619">"कलमाथि रोक लगाउने सुविधालाई असक्षम पारियो"</string>
-    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="6629412508584507377">"आपतकालीन कल गरियो"</string>
-    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="3140411733995271126">"आपतकालीन अवस्थामा उद्दार गर्ने मान्छेहरूलाई तपाईंलाई सम्पर्क गर्न दिन कलमाथि रोक लगाउने सुविधा असक्षम पारिएको छ।"</string>
+    <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="6629412508584507377">"आपत्‌कालीन कल गरियो"</string>
+    <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="3140411733995271126">"आपत्‌कालीन अवस्थामा उद्दार गर्ने मान्छेहरूलाई तपाईंलाई सम्पर्क गर्न दिन कलमाथि रोक लगाउने सुविधा असक्षम पारिएको छ।"</string>
     <string name="developer_title" msgid="9146088855661672353">"टेलिकमको विकासकर्ताको मेनु"</string>
-    <string name="toast_emergency_can_not_pull_call" msgid="9074229465338410869">"आपत्कालीन कल चलिराखेको बेलामा अरु कल स्वीकार गर्न सकिँदैन।"</string>
+    <string name="toast_emergency_can_not_pull_call" msgid="9074229465338410869">"आपत्‌कालीन कल चलिराखेको बेलामा अरु कल स्वीकार गर्न सकिँदैन।"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index e0061a7..e3ca712 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -55,7 +55,7 @@
     <string name="add_vm_number_str" msgid="5179510133063168998">"ନମ୍ବର୍ ଯୋଡ଼ନ୍ତୁ"</string>
     <string name="change_default_dialer_dialog_title" msgid="5861469279421508060">"<xliff:g id="NEW_APP">%s</xliff:g>କୁ ଆପଣଙ୍କ ଫୋନ୍‌ର ଡିଫଲ୍ଟ ଆପ୍ କରିବେ?"</string>
     <string name="change_default_dialer_dialog_affirmative" msgid="8604665314757739550">"ଡିଫଲ୍ଟ ସେଟ୍ କରନ୍ତୁ"</string>
-    <string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"କ୍ୟାନ୍ସଲ୍‍ କରନ୍ତୁ"</string>
+    <string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"ବାତିଲ୍‍ କରନ୍ତୁ"</string>
     <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"<xliff:g id="NEW_APP">%s</xliff:g> କଲ୍ କରିବା ଏବଂ କଲ୍‌ର ସମସ୍ତ ଦିଗକୁ ନିୟନ୍ତ୍ରଣ କରିବାରେ ସକ୍ଷମ ହେବ। କେବଳ ନିଜର ଭରସାଯୋଗ୍ୟ ଆପ୍‌କୁ ଡିଫଲ୍ଟ ଫୋନ୍ ଆପ୍ ଭାବେ ସେଟ୍ କରିବା ଉଚିତ୍।"</string>
     <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"<xliff:g id="NEW_APP">%s</xliff:g>କୁ ଆପଣଙ୍କ ଡିଫଲ୍ଟ କଲ୍ ସ୍କ୍ରିନିଂ ଆପ୍ କରିବେ?"</string>
     <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"<xliff:g id="OLD_APP">%s</xliff:g> କଲ୍ ସ୍କ୍ରିନ୍‌ କରିପାରିରିବେ ନାହିଁ।"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index d59fc79..b646cbb 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -57,9 +57,9 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8604665314757739550">"Ange standard"</string>
     <string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"Avbryt"</string>
     <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"<xliff:g id="NEW_APP">%s</xliff:g> kan ringa och styra allt omkring samtal. Endast appar du litar på bör ställas in som standardtelefonapp."</string>
-    <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"Vill du göra <xliff:g id="NEW_APP">%s</xliff:g> till din standardapp för samtalsspärr?"</string>
-    <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"<xliff:g id="OLD_APP">%s</xliff:g> kommer inte längre att användas för samtalsspärr."</string>
-    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"<xliff:g id="NEW_APP">%s</xliff:g> kommer att kunna se information om uppringare som inte finns i dina kontakter och blockera dessa samtal. Endast appar du litar på bör ställas in som standardapp för samtalsspärr."</string>
+    <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"Vill du göra <xliff:g id="NEW_APP">%s</xliff:g> till din standardapp för samtalsfiltrering?"</string>
+    <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"<xliff:g id="OLD_APP">%s</xliff:g> kommer inte längre att användas för samtalsfiltrering."</string>
+    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"<xliff:g id="NEW_APP">%s</xliff:g> kommer att kunna se information om uppringare som inte finns i dina kontakter och blockera dessa samtal. Endast appar du litar på bör ställas in som standardapp för samtalsfiltrering."</string>
     <string name="change_default_call_screening_dialog_affirmative" msgid="7162433828280058647">"Ange standard"</string>
     <string name="change_default_call_screening_dialog_negative" msgid="1839266125623106342">"Avbryt"</string>
     <string name="blocked_numbers" msgid="8322134197039865180">"Blockerade nummer"</string>
@@ -75,7 +75,7 @@
     <string name="blocked_numbers_butter_bar_title" msgid="582982373755950791">"Blockeringen har inaktiverats tillfälligt"</string>
     <string name="blocked_numbers_butter_bar_body" msgid="1261213114919301485">"När du ringer eller sms:ar ett nödnummer inaktiveras blockering för att säkerställa att räddningstjänsten kan kontakta dig."</string>
     <string name="blocked_numbers_butter_bar_button" msgid="2704456308072489793">"Återaktivera nu"</string>
-    <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> blockerade"</string>
+    <string name="blocked_numbers_number_blocked_message" msgid="4314736791180919167">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> har blockerats"</string>
     <string name="blocked_numbers_number_unblocked_message" msgid="2933071624674945601">"<xliff:g id="UNBLOCKED_NUMBER">%1$s</xliff:g> avblockerat"</string>
     <string name="blocked_numbers_block_emergency_number_message" msgid="4198550501500893890">"Det går inte att blockera nödnummer."</string>
     <string name="blocked_numbers_number_already_blocked_message" msgid="2301270825735665458">"<xliff:g id="BLOCKED_NUMBER">%1$s</xliff:g> är redan blockerat."</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 8b8a40c..d1142f7 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -43,7 +43,7 @@
     <string name="respond_via_sms_setting_title_2" msgid="4914853536609553457">"Hariri majibu ya haraka"</string>
     <string name="respond_via_sms_setting_summary" msgid="8054571501085436868"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="6579353156073272157">"Majibu ya haraka"</string>
-    <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Ujumbe uliotumwa kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g> ."</string>
+    <string name="respond_via_sms_confirmation_format" msgid="2932395476561267842">"Ujumbe umetumwa kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
     <string name="respond_via_sms_failure_format" msgid="5198680980054596391">"Imeshindwa kutuma SMS kwa <xliff:g id="PHONE_NUMBER">%s</xliff:g>."</string>
     <string name="enable_account_preference_title" msgid="6949224486748457976">"Akaunti za simu"</string>
     <string name="outgoing_call_not_allowed_user_restriction" msgid="3424338207838851646">"Piga simu za dharura pekee."</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 94a72e4..58eb225 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -118,6 +118,6 @@
     <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="1713632946174016619">"కాల్ బ్లాక్ చేయడం నిలిపివేయబడింది"</string>
     <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="6629412508584507377">"అత్యవసర కాల్ చేయబడింది"</string>
     <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="3140411733995271126">"మిమ్మల్ని సంప్రదించడానికి అత్యవసర ప్రతిస్పందనదారులను అనుమతించడానికి కాల్ బ్లాక్ చేయడం నిలిపివేయబడింది."</string>
-    <string name="developer_title" msgid="9146088855661672353">"టెలికామ్ డెవలపర్ మెను"</string>
+    <string name="developer_title" msgid="9146088855661672353">"టెలికామ్ డెవలపర్ మెనూ"</string>
     <string name="toast_emergency_can_not_pull_call" msgid="9074229465338410869">"అత్యవసర కాల్‌లో వున్నప్పుడు కాల్‌లను స్వీకరించడానికి వీలుపడదు."</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index ae77978..20cca1b 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -104,7 +104,7 @@
     <string name="alert_redirect_outgoing_call_or_not" msgid="665409645789521636">"یہ کال کرنے کا طریقہ منتخب کریں"</string>
     <string name="alert_place_outgoing_call_with_redirection" msgid="5221065030959024121">"<xliff:g id="OTHER_APP">%1$s</xliff:g> کے ذریعے کال کو ریڈائریکٹ کریں"</string>
     <string name="alert_place_unredirect_outgoing_call" msgid="2467608535225764006">"میرا فون نمبر استعمال کر کے کال کريں"</string>
-    <string name="alert_redirect_outgoing_call_timeout" msgid="5568101425637373060">"<xliff:g id="OTHER_APP">%1$s</xliff:g> کے ذریعے کال نہیں کی جا سکتی۔ کال آگے بڑھانے والی کوئی دوسری ایپ آزما کر یا مدد کے لیے ڈیولپر سے رابطہ کر کے دیکھیں۔"</string>
+    <string name="alert_redirect_outgoing_call_timeout" msgid="5568101425637373060">"<xliff:g id="OTHER_APP">%1$s</xliff:g> کے ذریعے کال نہیں کی جا سکتی۔ کال آگے بڑھانے والی کوئی دوسری ایپ آزما کر یا مدد کے لیے ڈویلپر سے رابطہ کر کے دیکھیں۔"</string>
     <string name="phone_settings_call_blocking_txt" msgid="7311523114822507178">"کال مسدود کرنا"</string>
     <string name="phone_settings_number_not_in_contact_txt" msgid="2602249106007265757">"وہ نمبرز جو رابطوں میں نہیں ہیں"</string>
     <string name="phone_settings_number_not_in_contact_summary_txt" msgid="963327038085718969">"ان نمبرز کو مسدود کریں جو آپ کے رابطوں میں مندرج نہیں ہیں"</string>
@@ -118,6 +118,6 @@
     <string name="phone_strings_call_blocking_turned_off_notification_text_txt" msgid="1713632946174016619">"کال مسدود کرنا غیر فعال ہو گیا ہے"</string>
     <string name="phone_strings_emergency_call_made_dialog_title_txt" msgid="6629412508584507377">"ہنگامی کال کی گئی"</string>
     <string name="phone_strings_emergency_call_made_dialog_call_blocking_text_txt" msgid="3140411733995271126">"ہنگامی حالت میں جواب دہندگان کو آپ سے رابطہ کرنے کی اجازت دینے کیلئے کال مسدود کرنا غیر فعال ہو گیا ہے۔"</string>
-    <string name="developer_title" msgid="9146088855661672353">"ٹیلی کام ڈیولپر مینو"</string>
+    <string name="developer_title" msgid="9146088855661672353">"ٹیلی کام ڈویلپر مینو"</string>
     <string name="toast_emergency_can_not_pull_call" msgid="9074229465338410869">"ہنگامی کال کے دوران کالز نہیں لی جائیں گی۔"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 5f1c946..0f26c9d 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -57,9 +57,9 @@
     <string name="change_default_dialer_dialog_affirmative" msgid="8604665314757739550">"設為預設"</string>
     <string name="change_default_dialer_dialog_negative" msgid="8648669840052697821">"取消"</string>
     <string name="change_default_dialer_warning_message" msgid="8461963987376916114">"「<xliff:g id="NEW_APP">%s</xliff:g>」將可撥打電話並控制所有相關功能。只有您信任的應用程式,才應設為預設手機應用程式。"</string>
-    <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"要將「<xliff:g id="NEW_APP">%s</xliff:g>」設為預設來電篩選應用程式嗎?"</string>
+    <string name="change_default_call_screening_dialog_title" msgid="5365787219927262408">"要將「<xliff:g id="NEW_APP">%s</xliff:g>」設為預設來電過濾應用程式嗎?"</string>
     <string name="change_default_call_screening_warning_message_for_disable_old_app" msgid="2039830033533243164">"「<xliff:g id="OLD_APP">%s</xliff:g>」無法再篩選來電。"</string>
-    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"「<xliff:g id="NEW_APP">%s</xliff:g>」將可查看通訊錄以外來電者的相關資訊,並封鎖這些來電。只有您信任的應用程式才適合設為預設來電篩選應用程式。"</string>
+    <string name="change_default_call_screening_warning_message" msgid="9020537562292754269">"「<xliff:g id="NEW_APP">%s</xliff:g>」將可查看通訊錄以外來電者的相關資訊,並封鎖這些來電。只有您信任的應用程式才適合設為預設來電過濾應用程式。"</string>
     <string name="change_default_call_screening_dialog_affirmative" msgid="7162433828280058647">"設為預設"</string>
     <string name="change_default_call_screening_dialog_negative" msgid="1839266125623106342">"取消"</string>
     <string name="blocked_numbers" msgid="8322134197039865180">"已封鎖的號碼"</string>
diff --git a/src/com/android/server/telecom/BluetoothAdapterProxy.java b/src/com/android/server/telecom/BluetoothAdapterProxy.java
index ee9cde3..914b772 100644
--- a/src/com/android/server/telecom/BluetoothAdapterProxy.java
+++ b/src/com/android/server/telecom/BluetoothAdapterProxy.java
@@ -49,4 +49,11 @@
             return mBluetoothAdapter.removeActiveDevice(profiles);
         }
     }
+
+    public void closeProfileProxy(int profile, BluetoothProfile proxy) {
+        if (mBluetoothAdapter == null) {
+            return;
+        }
+        mBluetoothAdapter.closeProfileProxy(profile, proxy);
+    }
 }
diff --git a/src/com/android/server/telecom/BluetoothHeadsetProxy.java b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
index a43b3cd..061676f 100644
--- a/src/com/android/server/telecom/BluetoothHeadsetProxy.java
+++ b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
@@ -84,4 +84,8 @@
     public boolean isInbandRingingEnabled() {
         return mBluetoothHeadset.isInbandRingingEnabled();
     }
+
+    public BluetoothHeadset getBluetoothHeadsetObj() {
+        return mBluetoothHeadset;
+    }
 }
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index f2ea950..623531d 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -27,18 +27,26 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.telecom.Connection;
 import android.telecom.Log;
 import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.os.UserHandle;
+
+import android.telecom.TelecomManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.server.telecom.CallsManager.CallsManagerListener;
 
+import java.lang.NumberFormatException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -89,6 +97,7 @@
     private int mRingingAddressType = DEFAULT_RINGING_ADDRESS_TYPE;
     private Call mOldHeldCall = null;
     private boolean mIsDisconnectedTonePlaying = false;
+    private boolean isAnswercallInProgress = false;
 
     /**
      * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
@@ -103,10 +112,15 @@
                 Log.startSession("BPSI.aC");
                 long token = Binder.clearCallingIdentity();
                 try {
-                    Log.i(TAG, "BT - answering call");
-                    Call call = mCallsManager.getRingingOrSimulatedRingingCall();
+                    Log.i(TAG, "BT - answering call isAnswercallInProgress "
+                              + isAnswercallInProgress );
+                    Call call = mCallsManager.getFirstCallWithState(CallState.RINGING);
                     if (call != null) {
-                        mCallsManager.answerCall(call, VideoProfile.STATE_AUDIO_ONLY);
+                        if (!isAnswercallInProgress) {
+                            Log.i(TAG, "Making isAnswercallInProgress to true");
+                            isAnswercallInProgress = true;
+                            mCallsManager.answerCall(call, VideoProfile.STATE_AUDIO_ONLY);
+                        }
                         return true;
                     }
                     return false;
@@ -171,14 +185,30 @@
                 long token = Binder.clearCallingIdentity();
                 try {
                     Log.i(TAG, "getNetworkOperator");
+                    String label = null;
                     PhoneAccount account = getBestPhoneAccount();
-                    if (account != null && account.getLabel() != null) {
-                        return account.getLabel().toString();
+                    if (account != null) {
+                        PhoneAccountHandle ph = account.getAccountHandle();
+                        if (ph != null) {
+                            String subId = ph.getId();
+                            int sub;
+                            try {
+                                sub = Integer.parseInt(subId);
+                            } catch (NumberFormatException e){
+                                Log.w(this, " NumberFormatException " + e);
+                                sub = SubscriptionManager.getDefaultVoiceSubscriptionId();
+                            }
+                            label = TelephonyManager.from(mContext)
+                                    .getNetworkOperatorName(sub);
+                        } else {
+                            Log.w(this, "Phone Account Handle is NULL");
+                        }
                     } else {
                         // Finally, just get the network name from telephony.
-                        return mContext.getSystemService(TelephonyManager.class)
+                        label = mContext.getSystemService(TelephonyManager.class)
                                 .getNetworkOperatorName();
                     }
+                    return label;
                 } finally {
                     Binder.restoreCallingIdentity(token);
                     Log.endSession();
@@ -257,6 +287,55 @@
             }
         }
 
+       @Override
+        public boolean isCsCallInProgress()       {
+            boolean isCsCall = false;
+            Call activeCall = mCallsManager.getActiveCall();
+            if (mNumActiveCalls > 0) {
+                isCsCall =  ((activeCall != null) &&
+                 !(activeCall.hasProperty(Connection.PROPERTY_HIGH_DEF_AUDIO) ||
+                 activeCall.hasProperty(Connection.PROPERTY_WIFI)));
+            }
+            Log.i(TAG, "isCsCallInProgress: "+ isCsCall);
+            return isCsCall;
+        }
+
+       /**isHighDefCallInProgress
+        * Returns true  if there is any Call is in Progress with High Definition
+        *               quality
+        *         false otherwise.
+        */
+        @Override
+        public boolean isHighDefCallInProgress() {
+            boolean isHighDef = false;
+            Call ringingCall = mCallsManager.getRingingOrSimulatedRingingCall();
+            Call dialingCall = mCallsManager.getOutgoingCall();
+            Call activeCall = mCallsManager.getActiveCall();
+
+            /* If its an incoming call we will have codec info in dialing state */
+            if (ringingCall != null) {
+                isHighDef = ringingCall.hasProperty(Connection.PROPERTY_HIGH_DEF_AUDIO);
+            } else if (dialingCall != null) { /* CS dialing call has codec info in dialing state */
+                Bundle extras = dialingCall.getExtras();
+                if (extras != null) {
+                    int phoneType = extras.getInt(
+                        TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE);
+                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM
+                        || phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
+                        isHighDef = dialingCall.hasProperty(Connection.PROPERTY_HIGH_DEF_AUDIO);
+                    /* For IMS calls codec info is not present in dialing state */
+                    } else if (phoneType == PhoneConstants.PHONE_TYPE_IMS
+                        || phoneType == PhoneConstants.PHONE_TYPE_CDMA_LTE) {
+                        isHighDef = true;
+                    }
+                }
+            } else if (activeCall != null) {
+                isHighDef = activeCall.hasProperty(Connection.PROPERTY_HIGH_DEF_AUDIO);
+            }
+            Log.i(TAG, "isHighDefCallInProgress: Call is High Def " + isHighDef);
+            return isHighDef;
+        }
+
         @Override
         public boolean processChld(int chld) throws RemoteException {
             synchronized (mLock) {
@@ -336,6 +415,24 @@
             if (call.isExternalCall()) {
                 return;
             }
+
+            Log.i(TAG, "onCallStateChanged oldState: " + oldState + " newState " + newState);
+            if (oldState == CallState.RINGING && newState != oldState) {
+                Log.i(TAG, "making flag isAnswercallInProgress to false");
+                isAnswercallInProgress = false;
+
+                /* When one active call, one RINGING call is present, AT+CHLD=1
+                 * ends active call. RINGING call moves to ANSWERED state. This
+                 * will make BT to update headset that no call is present and
+                 * close SCO. SCO will not be created when call moves to ACTIVE
+                 * state since SCO disconnection may not complete by then.
+                 * Ignore ANSWERED update and update to BT only when call
+                 * moves to ACTIVE state */
+                if (newState == CallState.ANSWERED) {
+                    Log.w(TAG, "ignoring call update from RINGING->ANSWERED state");
+                    return;
+                }
+            }
             // If a call is being put on hold because of a new connecting call, ignore the
             // CONNECTING since the BT state update needs to send out the numHeld = 1 + dialing
             // state atomically.
@@ -353,8 +450,8 @@
             // state. We can assume that the active call will be automatically held which will
             // send another update at which point we will be in the right state.
             if (mCallsManager.getActiveCall() != null
-                    && oldState == CallState.CONNECTING &&
-                    (newState == CallState.DIALING || newState == CallState.PULLING)) {
+                    && oldState == CallState.CONNECTING
+                    && newState == CallState.PULLING) {
                 return;
             }
             updateHeadsetWithCallState(false /* force */);
@@ -411,6 +508,7 @@
                 @Override
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     synchronized (mLock) {
+                        Log.w(TAG, "onServiceConnected: setting mBluetoothHeadset");
                         setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy));
                         updateHeadsetWithCallState(true /* force */);
                     }
@@ -419,7 +517,12 @@
                 @Override
                 public void onServiceDisconnected(int profile) {
                     synchronized (mLock) {
-                        mBluetoothHeadset = null;
+                        if (mBluetoothHeadset != null) {
+                            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET,
+                                    (BluetoothProfile)(mBluetoothHeadset.getBluetoothHeadsetObj()));
+                            Log.w(TAG, "onServiceDisconnected: setting mBluetoothHeadet to null");
+                            mBluetoothHeadset = null;
+                        }
                     }
                 }
             };
@@ -437,10 +540,21 @@
                 Log.d(TAG, "Bluetooth Adapter state: %d", state);
                 if (state == BluetoothAdapter.STATE_ON) {
                     try {
+                        Log.w(TAG, "Calling get Profile proxy");
+                        mBluetoothAdapter.getProfileProxy(context, mProfileListener,
+                                                 BluetoothProfile.HEADSET);
+                        Log.w(TAG, "Done Calling get Profile proxy");
                         mBinder.queryPhoneState();
                     } catch (RemoteException e) {
                         // Remote exception not expected
                     }
+                } else if(state == BluetoothAdapter.STATE_OFF) {
+                    if (mBluetoothHeadset != null) {
+                        mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET,
+                                        (BluetoothProfile)(mBluetoothHeadset.getBluetoothHeadsetObj()));
+                        Log.w(TAG, "setting mBluetoothHeadet to null");
+                        mBluetoothHeadset = null;
+                    }
                 }
             }
         }
@@ -497,7 +611,7 @@
 
     private boolean processChld(int chld) {
         Call activeCall = mCallsManager.getActiveCall();
-        Call ringingCall = mCallsManager.getRingingOrSimulatedRingingCall();
+        Call ringingCall = mCallsManager.getFirstCallWithState(CallState.RINGING);
         Call heldCall = mCallsManager.getHeldCall();
 
         // TODO: Keeping as Log.i for now.  Move to Log.d after L release if BT proves stable.
@@ -512,29 +626,45 @@
                 return true;
             }
         } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) {
+            Log.i(TAG, "CHLD=1: isAnswercallInProgress:" + isAnswercallInProgress);
             if (activeCall == null && ringingCall == null && heldCall == null)
                 return false;
             if (activeCall != null) {
                 mCallsManager.disconnectCall(activeCall);
                 if (ringingCall != null) {
-                    mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
+                    if (!isAnswercallInProgress) {
+                        Log.i(TAG, "making isAnswercallInProgress to true");
+                        isAnswercallInProgress = true;
+                        mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
+                    }
                 }
                 return true;
             }
             if (ringingCall != null) {
-                mCallsManager.answerCall(ringingCall, ringingCall.getVideoState());
+                if (!isAnswercallInProgress) {
+                    Log.i(TAG, "CHLD=1: There is ringing call making "
+                              + "isAnswercallInProgress to true:");
+                    isAnswercallInProgress = true;
+                    mCallsManager.answerCall(ringingCall, ringingCall.getVideoState());
+                }
             } else if (heldCall != null) {
                 mCallsManager.unholdCall(heldCall);
             }
             return true;
         } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
+            Log.i(TAG, "CHLD=2: isAnswercallInProgress:" + isAnswercallInProgress);
             if (activeCall != null && activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
                 activeCall.swapConference();
                 Log.i(TAG, "CDMA calls in conference swapped, updating headset");
                 updateHeadsetWithCallState(true /* force */);
                 return true;
             } else if (ringingCall != null) {
-                mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
+                if (!isAnswercallInProgress) {
+                    Log.i(TAG, "ringing call is present, making isAnswercallInProgress "
+                           + "to true");
+                    isAnswercallInProgress = true;
+                    mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
+                }
                 return true;
             } else if (heldCall != null) {
                 // CallsManager will hold any active calls when unhold() is called on a
@@ -703,6 +833,8 @@
      */
     private void updateHeadsetWithCallState(boolean force) {
         Call activeCall = mCallsManager.getActiveCall();
+
+        /* Treat ANSWERED state call also as ringing call as BT is not aware of this state */
         Call ringingCall = mCallsManager.getRingingOrSimulatedRingingCall();
         Call heldCall = mCallsManager.getHeldCall();
 
@@ -836,6 +968,8 @@
     }
 
     private int getBluetoothCallStateForUpdate() {
+        /* getRingingCall() gets call in RINGING and ANSWERED state. Update BT
+         * about ANSWERED call also as RINGING as BT is not aware of this state */
         Call ringingCall = mCallsManager.getRingingOrSimulatedRingingCall();
         Call dialingCall = mCallsManager.getOutgoingCall();
         boolean hasOnlyDisconnectedCalls = mCallsManager.hasOnlyDisconnectedCalls();
@@ -852,7 +986,7 @@
         int bluetoothCallState = CALL_STATE_IDLE;
         if (ringingCall != null && !ringingCall.isSilentRingingRequested()) {
             bluetoothCallState = CALL_STATE_INCOMING;
-        } else if (dialingCall != null) {
+        } else if (dialingCall != null && dialingCall.getState() == CallState.DIALING) {
             bluetoothCallState = CALL_STATE_ALERTING;
         } else if (hasOnlyDisconnectedCalls || mIsDisconnectedTonePlaying) {
             // Keep the DISCONNECTED state until the disconnect tone's playback is done
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index df6322c..ef0f8e3 100755
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -76,6 +76,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.codeaurora.ims.QtiCallConstants;
 /**
  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
  *  from the time the call intent was received by Telecom (vs. the time the call was
@@ -96,6 +97,8 @@
     public static final int SOURCE_CONNECTION_SERVICE = 1;
     /** Identifies extras changes which originated from an incall service. */
     public static final int SOURCE_INCALL_SERVICE = 2;
+    /** UNKNOWN original call type for video CRS. */
+    public static final int CALL_TYPE_UNKNOWN = -1;
 
     private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
     private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
@@ -367,6 +370,8 @@
 
     private boolean mIsDisconnectingChildCall = false;
 
+    private boolean mIsChildCall = false;
+
     /**
      * Tracks the video states which were applicable over the duration of a call.
      * See {@link VideoProfile} for a list of valid video states.
@@ -855,7 +860,7 @@
         PhoneAccountHandle delegatePhoneAccountHandle = getDelegatePhoneAccountHandle();
         boolean isTargetSameAsRemote = targetPhoneAccountHandle != null
                 && targetPhoneAccountHandle.equals(remotePhoneAccountHandle);
-        if (delegatePhoneAccountHandle.equals(targetPhoneAccountHandle)) {
+        if (Objects.equals(delegatePhoneAccountHandle, targetPhoneAccountHandle)) {
             s.append(">>>");
         }
         s.append("Target");
@@ -1932,6 +1937,11 @@
         return mIsDisconnectingChildCall;
     }
 
+    public boolean isChildCall() {
+        return mIsChildCall;
+    }
+
+
     /**
      * Sets whether this call is a child call.
      */
@@ -2578,6 +2588,25 @@
         return mExtras;
     }
 
+    public boolean isCrsCall() {
+        if (mExtras == null) {
+            return false;
+        }
+        int crsType = mExtras.getInt(QtiCallConstants.EXTRA_CRS_TYPE,
+                QtiCallConstants.CRS_TYPE_INVALID);
+        return (crsType == (QtiCallConstants.CRS_TYPE_VIDEO
+                    | QtiCallConstants.CRS_TYPE_AUDIO))
+            || (crsType == QtiCallConstants.CRS_TYPE_AUDIO);
+    }
+
+    public int getOriginalCallType() {
+        if (mExtras == null) {
+            return CALL_TYPE_UNKNOWN;
+        }
+        return mExtras.getInt(QtiCallConstants.EXTRA_ORIGINAL_CALL_TYPE,
+                CALL_TYPE_UNKNOWN);
+    }
+
     /**
      * Adds extras to the extras bundle associated with this {@link Call}.
      *
@@ -2979,6 +3008,7 @@
     public void setChildOf(Call parentCall) {
         if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
             parentCall.addChildCall(this);
+            mIsChildCall = true;
         }
     }
 
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index a6509b4..5524851 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -16,9 +16,13 @@
 
 package com.android.server.telecom;
 
+import android.content.Context;
 import android.annotation.NonNull;
 import android.media.IAudioService;
+import android.media.AudioManager;
 import android.media.ToneGenerator;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.telecom.CallAudioState;
 import android.telecom.Log;
 import android.telecom.VideoProfile;
@@ -62,6 +66,8 @@
     private boolean mIsTonePlaying = false;
     private boolean mIsDisconnectedTonePlaying = false;
     private InCallTonePlayer mHoldTonePlayer;
+    private boolean mIsInCrsMode = false;
+    private int mOriginalCallType = Call.CALL_TYPE_UNKNOWN;
 
     public CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine,
             CallsManager callsManager,
@@ -122,6 +128,18 @@
             playToneForDisconnectedCall(call);
         }
 
+        if (newState == CallState.ACTIVE && oldState == CallState.DIALING) {
+            playToneAfterCallConnected(call);
+        }
+        //reset CRS mode once call state changed.
+        if (mIsInCrsMode && (newState != CallState.RINGING)) {
+            mIsInCrsMode = false;
+            if ((mOriginalCallType == VideoProfile.STATE_AUDIO_ONLY)
+                    && !mCallsManager.isWiredHandsetInOrBtAvailble()) {
+                setAudioRoute(CallAudioState.ROUTE_EARPIECE, null);
+            }
+            mOriginalCallType = Call.CALL_TYPE_UNKNOWN;
+        }
         onCallLeavingState(call, oldState);
         onCallEnteringState(call, newState);
     }
@@ -444,6 +462,10 @@
                 call.silence();
             }
 
+            if(mIsInCrsMode) {
+                Log.i(this, "Fire silence CRS.");
+                onCallSilenceCrs();
+            }
             mRinger.stopRinging();
             mRinger.stopCallWaiting();
         }
@@ -456,6 +478,11 @@
     @VisibleForTesting
     public boolean startRinging() {
         synchronized (mCallsManager.getLock()) {
+            if (mIsInCrsMode) {
+                Log.i(this, "Start to play CRS.");
+                return mRinger.startPlayCrs(mForegroundCall,
+                        mCallAudioRouteStateMachine.isHfpDeviceAvailable());
+            }
             return mRinger.startRinging(mForegroundCall,
                     mCallAudioRouteStateMachine.isHfpDeviceAvailable());
         }
@@ -596,6 +623,13 @@
                 break;
             case CallState.RINGING:
             case CallState.SIMULATED_RINGING:
+                mIsInCrsMode = call.isCrsCall();
+                mOriginalCallType = call.getOriginalCallType();
+                if(mIsInCrsMode &&
+                        !mCallsManager.isWiredHandsetInOrBtAvailble()) {
+                    Log.i(LOG_TAG, "set Audio Route to SPEAKER");
+                    setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
+                }
                 onCallEnteringRinging();
                 break;
             case CallState.ON_HOLD:
@@ -651,6 +685,14 @@
         }
     }
 
+    private void onCallSilenceCrs() {
+        if (mRingingCalls.size() == 1) {
+            mCallAudioModeStateMachine.sendMessageWithArgs(
+                    CallAudioModeStateMachine.CRS_CHANGE_SILENCE,
+                    makeArgsForModeStateMachine());
+        }
+    }
+
     private void onCallLeavingHold() {
         if (mHoldingCalls.size() == 0) {
             mCallAudioModeStateMachine.sendMessageWithArgs(
@@ -721,7 +763,8 @@
                 .setIsTonePlaying(mIsTonePlaying)
                 .setForegroundCallIsVoip(
                         mForegroundCall != null && mForegroundCall.getIsVoipAudioMode())
-                .setSession(Log.createSubsession()).build();
+                .setSession(Log.createSubsession())
+                .setIsCrsCall(mIsInCrsMode).build();
     }
 
     private HashSet<Call> getBinForCall(Call call) {
@@ -742,6 +785,22 @@
         }
     }
 
+    private void playToneAfterCallConnected(Call call) {
+        final Context context = call.getContext();
+        ToneGenerator toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 90);
+        try {
+            if (Settings.System.getInt(context.getContentResolver(),
+                        Settings.System.CALL_CONNECTED_TONE_ENABLED) == 1) {
+                if (toneGenerator != null) {
+                    Log.i(LOG_TAG, "playing tone");
+                    toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP, 150);
+                }
+            }
+        } catch (SettingNotFoundException e) {
+            Log.e(this, e, "Settings exception when reading playing tone config");
+        }
+    }
+
     private void playToneForDisconnectedCall(Call call) {
         // If this call is being disconnected as a result of being handed over to another call,
         // we will not play a disconnect tone.
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index 2aa9d5d..69fd902 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -45,10 +45,11 @@
         public boolean isTonePlaying;
         public boolean foregroundCallIsVoip;
         public Session session;
+        public boolean isCrsCall;
 
         private MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
                 boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying,
-                boolean foregroundCallIsVoip, Session session) {
+                boolean foregroundCallIsVoip, Session session, boolean isCrsCall) {
             this.hasActiveOrDialingCalls = hasActiveOrDialingCalls;
             this.hasRingingCalls = hasRingingCalls;
             this.hasHoldingCalls = hasHoldingCalls;
@@ -56,6 +57,7 @@
             this.isTonePlaying = isTonePlaying;
             this.foregroundCallIsVoip = foregroundCallIsVoip;
             this.session = session;
+            this.isCrsCall = isCrsCall;
         }
 
         @Override
@@ -68,6 +70,7 @@
                     ", isTonePlaying=" + isTonePlaying +
                     ", foregroundCallIsVoip=" + foregroundCallIsVoip +
                     ", session=" + session +
+                    ", isCrsCall=" + isCrsCall +
                     '}';
         }
 
@@ -79,6 +82,7 @@
             private boolean mIsTonePlaying;
             private boolean mForegroundCallIsVoip;
             private Session mSession;
+            private boolean mIsCrsCall;
 
             public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) {
                 mHasActiveOrDialingCalls = hasActiveOrDialingCalls;
@@ -115,9 +119,15 @@
                 return this;
             }
 
+            public Builder setIsCrsCall(boolean isCrsCall) {
+                mIsCrsCall = isCrsCall;
+                return this;
+            }
+
             public MessageArgs build() {
                 return new MessageArgs(mHasActiveOrDialingCalls, mHasRingingCalls, mHasHoldingCalls,
-                        mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip, mSession);
+                        mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip, mSession,
+                        mIsCrsCall);
             }
         }
     }
@@ -150,6 +160,7 @@
     public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001;
 
     public static final int RINGER_MODE_CHANGE = 5001;
+    public static final int CRS_CHANGE_SILENCE = 5002;
 
     // Used to indicate that Telecom is done doing things to the AudioManager and that it's safe
     // to release focus for other apps to take over.
@@ -177,6 +188,7 @@
         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
         put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE");
         put(AUDIO_OPERATIONS_COMPLETE, "AUDIO_OPERATIONS_COMPLETE");
+        put(CRS_CHANGE_SILENCE, "CRS_CHANGE_SILENCE");
 
         put(RUN_RUNNABLE, "RUN_RUNNABLE");
     }};
@@ -200,7 +212,9 @@
                     transitionTo(mVoipCallFocusState);
                     return HANDLED;
                 case ENTER_RING_FOCUS_FOR_TESTING:
-                    transitionTo(mRingingFocusState);
+                    MessageArgs args = (MessageArgs) msg.obj;
+                    transitionTo(args.isCrsCall ?
+                            mCrsFocusState : mRingingFocusState);
                     return HANDLED;
                 case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING:
                     transitionTo(mOtherFocusState);
@@ -260,7 +274,8 @@
                             ? mVoipCallFocusState : mSimCallFocusState);
                     return HANDLED;
                 case NEW_RINGING_CALL:
-                    transitionTo(mRingingFocusState);
+                    transitionTo(args.isCrsCall ?
+                            mCrsFocusState: mRingingFocusState);
                     return HANDLED;
                 case NEW_AUDIO_PROCESSING_CALL:
                     transitionTo(mAudioProcessingFocusState);
@@ -325,7 +340,7 @@
                             ? mVoipCallFocusState : mSimCallFocusState);
                     return HANDLED;
                 case NEW_RINGING_CALL:
-                    transitionTo(mRingingFocusState);
+                    transitionTo(args.isCrsCall ? mCrsFocusState : mRingingFocusState);
                     return HANDLED;
                 case NEW_HOLDING_CALL:
                     // This really shouldn't happen, but recalculate from args and do the transition
@@ -352,6 +367,119 @@
         }
     }
 
+    private class CrsFocusState extends RingingFocusState {
+        // Keeps track of whether we're ringing with audio focus or if we've just entered the state
+        // without acquiring focus because of a silent ringtone or something.
+        private boolean mHasFocus = false;
+        private void tryStartRinging() {
+            if (mHasFocus) {
+                Log.i(LOG_TAG, "CrsFocusState#tryStartRinging -- audio focus previously acquired.");
+                return;
+            }
+
+            if (mCallAudioManager.startRinging()) {
+                Log.i(LOG_TAG, "RINGING state, try start video CRS");
+                mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
+                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+                mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+                mCallAudioManager.setCallAudioRouteFocusState(
+                        CallAudioRouteStateMachine.ACTIVE_FOCUS);
+                mHasFocus = true;
+                if (mAudioManager.getStreamVolume(AudioManager.STREAM_RING) == 0) {
+                    silenceCrs();
+                }
+            } else {
+                Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
+            }
+        }
+
+        private void silenceCrs() {
+            Log.i(this, "Silence CRS.");
+            mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
+            mAudioManager.setMode(AudioManager.MODE_NORMAL);
+            mHasFocus = false;
+        }
+
+        @Override
+        public void enter() {
+            Log.i(LOG_TAG, "Audio focus entering CRS state");
+            tryStartRinging();
+            mCallAudioManager.stopCallWaiting();
+        }
+
+        @Override
+        public void exit() {
+            // Audio mode and audio stream will be set by the next state.
+            mCallAudioManager.stopRinging();
+            mHasFocus = false;
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            MessageArgs args = (MessageArgs) msg.obj;
+            switch (msg.what) {
+                case NO_MORE_ACTIVE_OR_DIALING_CALLS:
+                    // Do nothing. Loss of an active call should not impact ringer.
+                    return HANDLED;
+                case NO_MORE_HOLDING_CALLS:
+                    // Do nothing and keep ringing.
+                    return HANDLED;
+                case NO_MORE_RINGING_CALLS:
+                    if (args.isCrsCall) {
+                        transitionTo(mUnfocusedState);
+                        return HANDLED;
+                    }
+                    BaseState destState = calculateProperStateFromArgs(args);
+                    if (destState == this) {
+                        Log.w(LOG_TAG, "Got spurious NO_MORE_RINGING_CALLS");
+                    }
+                    transitionTo(destState);
+                    return HANDLED;
+                case NEW_ACTIVE_OR_DIALING_CALL:
+                    // If a call becomes active suddenly, give it priority over ringing.
+                    transitionTo(args.foregroundCallIsVoip
+                            ? mVoipCallFocusState : mSimCallFocusState);
+                    return HANDLED;
+                case NEW_AUDIO_PROCESSING_CALL:
+                    // If we don't have any more ringing calls, transition to audio processing.
+                    if (!args.hasRingingCalls) {
+                        transitionTo(mAudioProcessingFocusState);
+                    } else {
+                        Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
+                                + "ringing");
+                    }
+                case NEW_RINGING_CALL:
+                    // Can happen as a duplicate message
+                    return HANDLED;
+                case NEW_HOLDING_CALL:
+                    // This really shouldn't happen, but transition to the focused state anyway.
+                    Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." +
+                            " Args are: " + args.toString());
+                    transitionTo(mOtherFocusState);
+                    return HANDLED;
+                case RINGER_MODE_CHANGE: {
+                    Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE");
+                    tryStartRinging();
+                    return HANDLED;
+                }
+                case AUDIO_OPERATIONS_COMPLETE:
+                    Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused"
+                            + " state");
+                    return HANDLED;
+                case CRS_CHANGE_SILENCE:
+                    Log.i(LOG_TAG, "CRS state, received CRS_CHANGE_SILENCE");
+                    silenceCrs();
+                    return HANDLED;
+                default:
+                    // The forced focus switch commands are handled by BaseState.
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
     private class RingingFocusState extends BaseState {
         // Keeps track of whether we're ringing with audio focus or if we've just entered the state
         // without acquiring focus because of a silent ringtone or something.
@@ -632,7 +760,7 @@
                         transitionTo(args.foregroundCallIsVoip
                                 ? mVoipCallFocusState : mSimCallFocusState);
                     } else if (args.hasRingingCalls) {
-                        transitionTo(mRingingFocusState);
+                        transitionTo(args.isCrsCall ? mCrsFocusState : mRingingFocusState);
                     } else if (!args.isTonePlaying) {
                         transitionTo(mUnfocusedState);
                     }
@@ -649,7 +777,7 @@
                         mCallAudioManager.startCallWaiting(
                                 "Device is at ear with held call");
                     } else {
-                        transitionTo(mRingingFocusState);
+                        transitionTo(args.isCrsCall ? mCrsFocusState : mRingingFocusState);
                     }
                     return HANDLED;
                 case NEW_HOLDING_CALL:
@@ -677,6 +805,7 @@
 
     private final BaseState mUnfocusedState = new UnfocusedState();
     private final BaseState mRingingFocusState = new RingingFocusState();
+    private final BaseState mCrsFocusState = new CrsFocusState();
     private final BaseState mSimCallFocusState = new SimCallFocusState();
     private final BaseState mVoipCallFocusState = new VoipCallFocusState();
     private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState();
@@ -715,6 +844,7 @@
     private void createStates() {
         addState(mUnfocusedState);
         addState(mRingingFocusState);
+        addState(mCrsFocusState);
         addState(mSimCallFocusState);
         addState(mVoipCallFocusState);
         addState(mAudioProcessingFocusState);
@@ -787,7 +917,7 @@
         } else if (args.hasHoldingCalls) {
             return mOtherFocusState;
         } else if (args.hasRingingCalls) {
-            return mRingingFocusState;
+            return args.isCrsCall ? mCrsFocusState : mRingingFocusState;
         } else if (args.isTonePlaying) {
             return mOtherFocusState;
         } else if (args.hasAudioProcessingCalls) {
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index a562021..37db3c3 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -413,6 +413,8 @@
                     return HANDLED;
                 case SWITCH_SPEAKER:
                 case USER_SWITCH_SPEAKER:
+                    setSpeakerphoneOn(true);
+                    // fall through
                 case SPEAKER_ON:
                     transitionTo(mActiveSpeakerRoute);
                     return HANDLED;
@@ -459,6 +461,8 @@
             switch (msg.what) {
                 case SWITCH_EARPIECE:
                 case USER_SWITCH_EARPIECE:
+                case SPEAKER_ON:
+                    // Ignore speakerphone state changes outside of calls.
                 case SPEAKER_OFF:
                     // Nothing to do here
                     return HANDLED;
@@ -484,7 +488,6 @@
                     return HANDLED;
                 case SWITCH_SPEAKER:
                 case USER_SWITCH_SPEAKER:
-                case SPEAKER_ON:
                     transitionTo(mQuiescentSpeakerRoute);
                     return HANDLED;
                 case SWITCH_FOCUS:
@@ -533,6 +536,7 @@
                     // This may be sent as a confirmation by the BT stack after switch off BT.
                     return HANDLED;
                 case CONNECT_DOCK:
+                    setSpeakerphoneOn(true);
                     sendInternalMessage(SWITCH_SPEAKER);
                     return HANDLED;
                 case DISCONNECT_DOCK:
@@ -611,6 +615,8 @@
                     return HANDLED;
                 case SWITCH_SPEAKER:
                 case USER_SWITCH_SPEAKER:
+                    setSpeakerphoneOn(true);
+                    // fall through
                 case SPEAKER_ON:
                     transitionTo(mActiveSpeakerRoute);
                     return HANDLED;
@@ -677,12 +683,13 @@
                     return HANDLED;
                 case SWITCH_HEADSET:
                 case USER_SWITCH_HEADSET:
+                case SPEAKER_ON:
+                    // Ignore speakerphone state changes outside of calls.
                 case SPEAKER_OFF:
                     // Nothing to do
                     return HANDLED;
                 case SWITCH_SPEAKER:
                 case USER_SWITCH_SPEAKER:
-                case SPEAKER_ON:
                     transitionTo(mQuiescentSpeakerRoute);
                     return HANDLED;
                 case SWITCH_FOCUS:
@@ -725,6 +732,7 @@
                     return HANDLED;
                 case DISCONNECT_WIRED_HEADSET:
                     if (mWasOnSpeaker) {
+                        setSpeakerphoneOn(true);
                         sendInternalMessage(SWITCH_SPEAKER);
                     } else {
                         sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
@@ -796,6 +804,7 @@
                     transitionTo(mActiveHeadsetRoute);
                     break;
                 case SWITCH_SPEAKER:
+                    setSpeakerphoneOn(true);
                     transitionTo(mActiveSpeakerRoute);
                     break;
                 default:
@@ -852,6 +861,8 @@
                     mHasUserExplicitlyLeftBluetooth = true;
                     // fall through
                 case SWITCH_SPEAKER:
+                    setSpeakerphoneOn(true);
+                    // fall through
                 case SPEAKER_ON:
                     setBluetoothOff();
                     transitionTo(mActiveSpeakerRoute);
@@ -947,6 +958,8 @@
                     mHasUserExplicitlyLeftBluetooth = true;
                     // fall through
                 case SWITCH_SPEAKER:
+                    setSpeakerphoneOn(true);
+                    // fall through
                 case SPEAKER_ON:
                     transitionTo(mActiveSpeakerRoute);
                     return HANDLED;
@@ -1012,6 +1025,8 @@
                     return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
+                case SPEAKER_ON:
+                    // Ignore speakerphone state changes outside of calls.
                 case SPEAKER_OFF:
                     // Nothing to do
                     return HANDLED;
@@ -1025,7 +1040,6 @@
                     return HANDLED;
                 case SWITCH_SPEAKER:
                 case USER_SWITCH_SPEAKER:
-                case SPEAKER_ON:
                     transitionTo(mQuiescentSpeakerRoute);
                     return HANDLED;
                 case SWITCH_FOCUS:
@@ -1037,6 +1051,9 @@
                         } else {
                             transitionTo(mRingingBluetoothRoute);
                         }
+                    } else if (msg.arg1 == NO_FOCUS) {
+                        reinitialize();
+                        mCallAudioManager.notifyAudioOperationsComplete();
                     }
                     return HANDLED;
                 case BT_AUDIO_DISCONNECTED:
@@ -1105,8 +1122,9 @@
         @Override
         public void enter() {
             super.enter();
+            // Don't set speakerphone on here -- we might end up in this state by following
+            // the speaker state that some other app commanded.
             mWasOnSpeaker = true;
-            setSpeakerphoneOn(true);
             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
                     mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
             setSystemAudioState(newState, true);
@@ -1593,12 +1611,8 @@
     }
 
     private void setSpeakerphoneOn(boolean on) {
-        if (mAudioManager.isSpeakerphoneOn() != on) {
-            Log.i(this, "turning speaker phone %s", on);
-            mAudioManager.setSpeakerphoneOn(on);
-        } else {
-            Log.i(this, "Ignoring speakerphone request -- already %s", on);
-        }
+        Log.i(this, "turning speaker phone %s", on);
+        mAudioManager.setSpeakerphoneOn(on);
         mStatusBarNotifier.notifySpeakerphone(on);
     }
 
diff --git a/src/com/android/server/telecom/CallIdMapper.java b/src/com/android/server/telecom/CallIdMapper.java
index da2c199..72a51f5 100644
--- a/src/com/android/server/telecom/CallIdMapper.java
+++ b/src/com/android/server/telecom/CallIdMapper.java
@@ -20,6 +20,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.Collection;
 import java.util.Map;
 
 /** Utility to map {@link Call} objects to unique IDs. IDs are generated when a call is added. */
@@ -132,6 +133,10 @@
         return mCalls.getValue(callId);
     }
 
+    Collection<Call> getCalls() {
+        return mCalls.mPrimaryMap.values();
+    }
+
     void clear() {
         mCalls.clear();
     }
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index 7305ab9..c5f3695 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -23,6 +23,8 @@
 
 import java.util.concurrent.CompletableFuture;
 
+import org.codeaurora.ims.QtiCallConstants;
+
 /**
  * Single point of entry for all outgoing and incoming calls.
  * {@link com.android.server.telecom.components.UserCallIntentProcessor} serves as a trampoline that
@@ -128,6 +130,14 @@
         if (clientExtras == null) {
             clientExtras = new Bundle();
         }
+        if (intent.hasExtra(TelecomManager.EXTRA_START_CALL_WITH_RTT)) {
+            boolean isStartRttCall = intent.getBooleanExtra(
+                    TelecomManager.EXTRA_START_CALL_WITH_RTT, false);
+            Log.d(CallIntentProcessor.class, "isStartRttCall = "+isStartRttCall);
+            if (!isStartRttCall) {
+                clientExtras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, isStartRttCall);
+            }
+        }
 
         if (intent.hasExtra(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)) {
             clientExtras.putBoolean(TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL,
@@ -141,6 +151,11 @@
             clientExtras.putString(TelecomManager.EXTRA_CALL_SUBJECT, callsubject);
         }
 
+        final int callDomain = intent.getIntExtra(
+                QtiCallConstants.EXTRA_CALL_DOMAIN, QtiCallConstants.DOMAIN_AUTOMATIC);
+        Log.d(CallIntentProcessor.class, "callDomain = " + callDomain);
+        clientExtras.putInt(QtiCallConstants.EXTRA_CALL_DOMAIN, callDomain);
+
         final int videoState = intent.getIntExtra( TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                 VideoProfile.STATE_AUDIO_ONLY);
         clientExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
@@ -172,6 +187,7 @@
         // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
         NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
         if (disposition.disconnectCause != DisconnectCause.NOT_DISCONNECTED) {
+            callsManager.clearPendingMOEmergencyCall();
             showErrorDialog(context, disposition.disconnectCause);
             return;
         }
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 7a5af14..d83c228 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -213,6 +213,12 @@
         if (oldState == CallState.SELECT_PHONE_ACCOUNT) {
             return false;
         }
+
+        //Not log participant host
+        if (call.hasProperty(Connection.PROPERTY_IS_PARTICIPANT_HOST)) {
+            return false;
+        }
+
         // A conference call which had children should not be logged, unless it was remotely hosted.
         if (call.isConference() && call.hadChildren() &&
                 !call.hasProperty(Connection.PROPERTY_REMOTELY_HOSTED)) {
@@ -323,6 +329,8 @@
             creationTime = call.getCreationTimeMillis();
         }
 
+        creationTime = call.isChildCall() ? call.getConnectTimeMillis()
+                : call.getCreationTimeMillis();
         final long age = call.getAgeMillis();
 
         final String logNumber = getLogNumber(call);
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 5273675..c52da94 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -83,6 +83,7 @@
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -137,6 +138,9 @@
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
 
+import org.codeaurora.ims.QtiCallConstants;
+import org.codeaurora.ims.utils.QtiCarrierConfigHelper;
+import org.codeaurora.ims.utils.QtiImsExtUtils;
 /**
  * Singleton.
  *
@@ -173,6 +177,7 @@
         void onConnectionTimeChanged(Call call);
         void onConferenceStateChanged(Call call, boolean isConference);
         void onCdmaConferenceSwap(Call call);
+        void onSetCamera(Call call, String cameraId);
     }
 
     /** Interface used to define the action which is executed delay under some condition. */
@@ -392,6 +397,14 @@
 
     private boolean mHasActiveRttCall = false;
 
+    // Two global variables used to handle the Emergency Call when there
+    // is no room available for emergency call. Buffer the Emergency Call
+    // in mPendingMOEmerCall until the Current Active call is disconnected
+    // successfully and place the mPendingMOEmerCall followed by clearing
+    // buffer.
+    private Call mPendingMOEmerCall = null;
+    private Call mDisconnectingCall = null;
+
     /**
      * Listener to PhoneAccountRegistrar events.
      */
@@ -589,6 +602,7 @@
         intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
         context.registerReceiver(mReceiver, intentFilter);
         mGraphHandlerThreads = new LinkedList<>();
+        QtiCarrierConfigHelper.getInstance().setup(mContext);
     }
 
     public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -770,6 +784,9 @@
                             "dialing calls.");
                     rejectCallAndLog(incomingCall, result);
                 }
+            } else if (!isIncomingVideoCallAllowed(incomingCall)) {
+                Log.i(this, "onCallFilteringCompleted: MT Video Call rejecting.");
+                rejectCallAndLog(incomingCall, result);
             } else if (result.shouldScreenViaAudio) {
                 Log.i(this, "onCallFilteringCompleted: starting background audio processing");
                 answerCallForAudioProcessing(incomingCall);
@@ -802,6 +819,31 @@
     }
 
     /**
+     * Determines if the incoming video call is allowed or not
+     *
+     * @param Call The incoming call.
+     * @return {@code false} if incoming video call is not allowed.
+     */
+    private static boolean isIncomingVideoCallAllowed(Call call) {
+        Bundle extras = call.getExtras();
+        if (extras == null || (!isIncomingVideoCall(call))) {
+            Log.w(TAG, "isIncomingVideoCallAllowed: null Extras or not an incoming video call " +
+                    "or allow video calls in low battery");
+            return true;
+        }
+
+        final boolean isLowBattery = extras.getBoolean(QtiCallConstants.LOW_BATTERY_EXTRA_KEY,
+                false);
+        Log.d(TAG, "isIncomingVideoCallAllowed: lowbattery = " + isLowBattery);
+        return !isLowBattery;
+    }
+
+    private static boolean isIncomingVideoCall(Call call) {
+        return (!VideoProfile.isAudioOnly(call.getVideoState()) &&
+            call.getState() == CallState.RINGING);
+    }
+
+    /**
      * In the event that the maximum supported calls of a given type is reached, the
      * default behavior is to reject any additional calls of that type.  This checks
      * if the device is configured to silence instead of reject the call, provided
@@ -1011,6 +1053,18 @@
         }
     }
 
+    /**
+     * Handles a change to the currently active camera for a call by notifying listeners.
+     * @param call The call.
+     * @param cameraId The ID of the camera in use, or {@code null} if no camera is in use.
+     */
+    @Override
+    public void onSetCamera(Call call, String cameraId) {
+        for (CallsManagerListener listener : mListeners) {
+            listener.onSetCamera(call, cameraId);
+        }
+    }
+
     public Collection<Call> getCalls() {
         return Collections.unmodifiableCollection(mCalls);
     }
@@ -1214,7 +1268,7 @@
                 call.setIsVoipAudioMode(true);
             } else {
                 // Incoming call is managed, the active call is self-managed and can't be held.
-                // We need to set extras on it to indicate whether answering will cause a 
+                // We need to set extras on it to indicate whether answering will cause a
                 // active self-managed call to drop.
                 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
                 if (activeCall != null && !canHold(activeCall) && activeCall.isSelfManaged()) {
@@ -1730,8 +1784,14 @@
 
                     boolean isVoicemail = isVoicemail(callToUse.getHandle(), accountToUse);
 
+                    int phoneId = SubscriptionManager.getPhoneId(
+                            mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
+                            callToUse.getTargetPhoneAccount()));
                     boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle);
-                    if (!isVoicemail && (isRttSettingOn || (extras != null
+                    if (!isVoicemail && (!VideoProfile.isVideo(callToUse.getVideoState())
+                            || QtiImsExtUtils.isRttSupportedOnVtCalls(
+                            phoneId, mContext))
+                            && (isRttSettingOn || (extras != null
                             && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT,
                             false)))) {
                         Log.d(this, "Outgoing call requesting RTT, rtt setting is %b",
@@ -1750,10 +1810,11 @@
                     setIntentExtrasAndStartTime(callToUse, extras);
                     setCallSourceToAnalytics(callToUse, originalIntent);
 
-                    if (isPotentialMMICode(handle) && !isSelfManaged) {
+                    if (!isSelfManaged && (isPotentialMMICode(handle)
+                            || isPotentialInCallMMICode(handle))) {
                         // Do not add the call if it is a potential MMI code.
                         callToUse.addListener(this);
-                    } else if (!mCalls.contains(callToUse)) {
+                    } else if (!mCalls.contains(callToUse) && mPendingMOEmerCall == null) {
                         // We check if mCalls already contains the call because we could
                         // potentially be reusing
                         // a call which was previously added (See {@link #reuseOutgoingCall}).
@@ -2256,7 +2317,12 @@
                     disconnectSelfManagedCalls("place emerg call" /* reason */);
                 }
 
-                call.startCreateConnection(mPhoneAccountRegistrar);
+                if (mPendingMOEmerCall == null) {
+                    // If the account has been set, proceed to place the outgoing call.
+                    // Otherwise the connection will be initiated when the account is
+                    // set by the user.
+                    call.startCreateConnection(mPhoneAccountRegistrar);
+                }
             }
         } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
                 requireCallCapableAccountByHandle ? callHandleScheme : null, false,
@@ -2421,12 +2487,18 @@
      * @return {@code true} if the speakerphone should be enabled.
      */
     public boolean isSpeakerphoneAutoEnabledForVideoCalls(int videoState) {
-        return VideoProfile.isVideo(videoState) &&
+        return !isVideoCrbtVoLteCall(videoState) &&
+            VideoProfile.isVideo(videoState) &&
             !mWiredHeadsetManager.isPluggedIn() &&
             !mBluetoothRouteManager.isBluetoothAvailable() &&
             isSpeakerEnabledForVideoCalls();
     }
 
+     public boolean isWiredHandsetInOrBtAvailble() {
+        return mWiredHeadsetManager.isPluggedIn()
+            || mBluetoothRouteManager.isBluetoothAvailable();
+     }
+
     /**
      * Determines if the speakerphone should be enabled for when docked.  Speakerphone
      * should be enabled if the device is docked and bluetooth or the wired headset are
@@ -2708,6 +2780,7 @@
         if (handle == null) {
             return Collections.emptyList();
         }
+
         // If we're specifically looking for video capable accounts, then include that capability,
         // otherwise specify no additional capability constraints. When handling the emergency call,
         // it also needs to find the phone accounts excluded by CAPABILITY_EMERGENCY_CALLS_ONLY.
@@ -2717,6 +2790,15 @@
                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user,
                         capabilities,
                         isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY);
+
+        // If no phone account is found, let's query emergency call only account again.
+        // That is happening while emergency account has capability CAPABILITY_EMERGENCY_CALLS_ONLY.
+        if (isEmergency && allAccounts.size() == 0) {
+            Log.v(this, "Try to find an emergency call only phone account");
+            allAccounts =  mPhoneAccountRegistrar.
+                    getEmergencyCallOnlyPhoneAccounts(handle.getScheme(), user);
+        }
+
         if (mMaxNumberOfSimultaneouslyActiveSims < 0) {
             mMaxNumberOfSimultaneouslyActiveSims =
                     getTelephonyManager().getMaxNumberOfSimultaneouslyActiveSims();
@@ -2816,8 +2898,14 @@
     }
 
     private boolean isRttSettingOn(PhoneAccountHandle handle) {
+        int phoneId = SubscriptionManager.getPhoneId(
+                mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle));
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            Log.w(this, "isRttSettingOn: Invalid phone id = " + phoneId);
+            return false;
+        }
         boolean isRttModeSettingOn = Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+                Settings.Secure.RTT_CALLING_MODE + convertRttPhoneId(phoneId), 0) != 0;
         // If the carrier config says that we should ignore the RTT mode setting from the user,
         // assume that it's off (i.e. only make an RTT call if it's requested through the extra).
         boolean shouldIgnoreRttModeSetting = getCarrierConfigForPhoneAccount(handle)
@@ -2825,6 +2913,10 @@
         return isRttModeSettingOn && !shouldIgnoreRttModeSetting;
     }
 
+    private static String convertRttPhoneId(int phoneId) {
+        return phoneId != 0 ? Integer.toString(phoneId) : "";
+    }
+
     private PersistableBundle getCarrierConfigForPhoneAccount(PhoneAccountHandle handle) {
         int subscriptionId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle);
         CarrierConfigManager carrierConfigManager =
@@ -3008,6 +3100,14 @@
             mCallLogManager.logCall(call, Calls.MISSED_TYPE, true, null);
         }
 
+        // Emergency MO call is still pending and current active call is
+        // disconnected succesfully. So initiating pending Emergency call
+        // now and clearing both pending and Disconnectcalls.
+        if (mPendingMOEmerCall != null && mDisconnectingCall == call) {
+            addCall(mPendingMOEmerCall);
+            mPendingMOEmerCall.startCreateConnection(mPhoneAccountRegistrar);
+            clearPendingMOEmergencyCall();
+        }
     }
 
     /**
@@ -3560,6 +3660,10 @@
             if (newState == CallState.ON_HOLD && call.isDtmfTonePlaying()) {
                 stopDtmfTone(call);
             }
+            // Maybe start a vibration for MO call.
+            if (newState == CallState.ACTIVE && !call.isIncoming() && !call.isUnknown()) {
+                mRinger.startVibratingForOutgoingCallActive();
+            }
 
             // Unfortunately, in the telephony world the radio is king. So if the call notifies
             // us that the call is in a particular state, we allow it even if it doesn't make
@@ -3766,7 +3870,11 @@
                     (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
                     dialedNumber.equals("3") ||
                     dialedNumber.equals("4") ||
-                    dialedNumber.equals("5"));
+                    dialedNumber.equals("5") ||
+                    dialedNumber.equals("6") ||
+                    dialedNumber.equals("7") ||
+                    dialedNumber.equals("8") ||
+                    dialedNumber.equals("9") );
         }
         return false;
     }
@@ -4395,6 +4503,20 @@
                 new MissedCallNotifier.CallInfoFactory());
     }
 
+    public boolean isVideoCrbtVoLteCall(int videoState) {
+        Call call = getDialingCall();
+        if (call == null) {
+            return false;
+        }
+        PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
+        int phoneId = SubscriptionManager.getPhoneId(
+                mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle));
+        return QtiImsExtUtils.isCarrierConfigEnabled(phoneId, mContext,
+                "config_enable_video_crbt") && getDialingCall() != null
+            && !VideoProfile.isTransmissionEnabled(videoState)
+            && VideoProfile.isReceptionEnabled(videoState);
+    }
+
     public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
         return isIncomingCallPermitted(null /* excludeCall */, phoneAccountHandle);
     }
@@ -5218,6 +5340,11 @@
         }
     }
 
+    public void clearPendingMOEmergencyCall() {
+        mPendingMOEmerCall = null;
+        mDisconnectingCall = null;
+    }
+
     public void resetConnectionTime(Call call) {
         call.setConnectTimeMillis(System.currentTimeMillis());
         call.setConnectElapsedTimeMillis(SystemClock.elapsedRealtime());
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index e0d2831..55c7b53 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -104,4 +104,8 @@
     @Override
     public void onCdmaConferenceSwap(Call call) {
     }
+
+    @Override
+    public void onSetCamera(Call call, String cameraId) {
+    }
 }
diff --git a/src/com/android/server/telecom/ConnectionServiceFocusManager.java b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
index fbb23f4..df1052c 100644
--- a/src/com/android/server/telecom/ConnectionServiceFocusManager.java
+++ b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
@@ -373,7 +373,8 @@
 
         for (int i = 0; i < PRIORITY_FOCUS_CALL_STATE.length; i++) {
             for (CallFocus call : calls) {
-                if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i]) {
+                if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i] &&
+                       (((Call) call).getParentCall() == null)) {
                     mCurrentFocusCall = call;
                     Log.d(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
                     return;
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
old mode 100755
new mode 100644
index 01acdd1..99acae8
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -39,6 +39,7 @@
 import android.telecom.Logging.Session;
 import android.telecom.ParcelableConference;
 import android.telecom.ParcelableConnection;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.StatusHints;
 import android.telecom.TelecomManager;
@@ -1227,19 +1228,25 @@
                         .setIsAdhocConferenceCall(call.isAdhocConferenceCall())
                         .build();
 
-                try {
-                    mServiceInterface.createConference(
-                            call.getConnectionManagerPhoneAccount(),
-                            callId,
-                            connectionRequest,
-                            call.shouldAttachToExistingConnection(),
-                            call.isUnknown(),
-                            Log.getExternalSession(TELECOM_ABBREVIATION));
+                if (mServiceInterface != null) {
+                    try {
+                        mServiceInterface.createConference(
+                                call.getConnectionManagerPhoneAccount(),
+                                callId,
+                                connectionRequest,
+                                call.shouldAttachToExistingConnection(),
+                                call.isUnknown(),
+                                Log.getExternalSession(TELECOM_ABBREVIATION));
 
-                } catch (RemoteException e) {
-                    Log.e(this, e, "Failure to createConference -- %s", getComponentName());
+                    } catch (RemoteException e) {
+                        Log.e(this, e, "Failure to createConference -- %s", getComponentName());
+                        mPendingResponses.remove(callId).handleCreateConferenceFailure(
+                                new DisconnectCause(DisconnectCause.ERROR, e.toString()));
+                    }
+                } else {
+                    Log.w(this,"Failure to createConference -- %s", getComponentName());
                     mPendingResponses.remove(callId).handleCreateConferenceFailure(
-                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
+                            new DisconnectCause(DisconnectCause.ERROR));
                 }
             }
 
@@ -1316,19 +1323,25 @@
                         .setRttPipeToInCall(call.getCsToInCallRttPipeForCs())
                         .build();
 
-                try {
-                    mServiceInterface.createConnection(
-                            call.getConnectionManagerPhoneAccount(),
-                            callId,
-                            connectionRequest,
-                            call.shouldAttachToExistingConnection(),
-                            call.isUnknown(),
-                            Log.getExternalSession(TELECOM_ABBREVIATION));
-
-                } catch (RemoteException e) {
-                    Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
+                if (mServiceInterface != null) {
+                    try {
+                        mServiceInterface.createConnection(
+                                call.getConnectionManagerPhoneAccount(),
+                                callId,
+                                connectionRequest,
+                                call.shouldAttachToExistingConnection(),
+                                call.isUnknown(),
+                                Log.getExternalSession(TELECOM_ABBREVIATION));
+                    } catch (RemoteException e) {
+                        Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
+                        mPendingResponses.remove(callId).handleCreateConnectionFailure(
+                                new DisconnectCause(DisconnectCause.ERROR, e.toString()));
+                    }
+                } else {
+                    Log.w(this, "Failure to createConnection; no service interface -- %s",
+                            getComponentName());
                     mPendingResponses.remove(callId).handleCreateConnectionFailure(
-                            new DisconnectCause(DisconnectCause.ERROR, e.toString()));
+                            new DisconnectCause(DisconnectCause.ERROR));
                 }
             }
 
@@ -1357,24 +1370,28 @@
                 if (callId != null && isServiceValid("createConnectionFailed")) {
                     Log.addEvent(call, LogUtils.Events.CREATE_CONNECTION_FAILED,
                             Log.piiHandle(call.getHandle()));
-                    try {
-                        logOutgoing("createConnectionFailed %s", callId);
-                        mServiceInterface.createConnectionFailed(
-                                call.getConnectionManagerPhoneAccount(),
-                                callId,
-                                new ConnectionRequest(
-                                        call.getTargetPhoneAccount(),
-                                        call.getHandle(),
-                                        call.getIntentExtras(),
-                                        call.getVideoState(),
-                                        callId,
-                                        false),
-                                call.isIncoming(),
-                                Log.getExternalSession(TELECOM_ABBREVIATION));
-                        call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
-                        call.disconnect();
-                    } catch (RemoteException e) {
+                    if (mServiceInterface != null) {
+                        try {
+                            logOutgoing("createConnectionFailed %s", callId);
+                            mServiceInterface.createConnectionFailed(
+                                    call.getConnectionManagerPhoneAccount(),
+                                    callId,
+                                    new ConnectionRequest(
+                                            call.getTargetPhoneAccount(),
+                                            call.getHandle(),
+                                            call.getIntentExtras(),
+                                            call.getVideoState(),
+                                            callId,
+                                            false),
+                                    call.isIncoming(),
+                                    Log.getExternalSession(TELECOM_ABBREVIATION));
+                        } catch (RemoteException e) {
+                        }
+                    } else {
+                        Log.w(this, "createConnectionFailed - service interface null");
                     }
+                    call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
+                    call.disconnect();
                 }
             }
 
@@ -1403,24 +1420,28 @@
                 if (callId != null && isServiceValid("createConferenceFailed")) {
                     Log.addEvent(call, LogUtils.Events.CREATE_CONFERENCE_FAILED,
                             Log.piiHandle(call.getHandle()));
-                    try {
-                        logOutgoing("createConferenceFailed %s", callId);
-                        mServiceInterface.createConferenceFailed(
-                                call.getConnectionManagerPhoneAccount(),
-                                callId,
-                                new ConnectionRequest(
-                                        call.getTargetPhoneAccount(),
-                                        call.getHandle(),
-                                        call.getIntentExtras(),
-                                        call.getVideoState(),
-                                        callId,
-                                        false),
-                                call.isIncoming(),
-                                Log.getExternalSession(TELECOM_ABBREVIATION));
-                        call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
-                        call.disconnect();
-                    } catch (RemoteException e) {
+                    if (mServiceInterface != null) {
+                        try {
+                            logOutgoing("createConferenceFailed %s", callId);
+                            mServiceInterface.createConferenceFailed(
+                                    call.getConnectionManagerPhoneAccount(),
+                                    callId,
+                                    new ConnectionRequest(
+                                            call.getTargetPhoneAccount(),
+                                            call.getHandle(),
+                                            call.getIntentExtras(),
+                                            call.getVideoState(),
+                                            callId,
+                                            false),
+                                    call.isIncoming(),
+                                    Log.getExternalSession(TELECOM_ABBREVIATION));
+                        } catch (RemoteException e) {
+                        }
+                    } else {
+                        Log.w(this, "createConnectionFailed - service interface null");
                     }
+                    call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED));
+                    call.disconnect();
                 }
             }
 
@@ -1445,19 +1466,21 @@
                 if (callId != null && isServiceValid("handoverFailed")) {
                     Log.addEvent(call, LogUtils.Events.HANDOVER_FAILED,
                             Log.piiHandle(call.getHandle()));
-                    try {
-                        mServiceInterface.handoverFailed(
-                                callId,
-                                new ConnectionRequest(
-                                        call.getTargetPhoneAccount(),
-                                        call.getHandle(),
-                                        call.getIntentExtras(),
-                                        call.getVideoState(),
-                                        callId,
-                                        false),
-                                reason,
-                                Log.getExternalSession(TELECOM_ABBREVIATION));
-                    } catch (RemoteException e) {
+                    if (mServiceInterface != null) {
+                        try {
+                            mServiceInterface.handoverFailed(
+                                    callId,
+                                    new ConnectionRequest(
+                                            call.getTargetPhoneAccount(),
+                                            call.getHandle(),
+                                            call.getIntentExtras(),
+                                            call.getVideoState(),
+                                            callId,
+                                            false),
+                                    reason,
+                                    Log.getExternalSession(TELECOM_ABBREVIATION));
+                        } catch (RemoteException e) {
+                        }
                     }
                 }
             }
@@ -1480,7 +1503,8 @@
             public void onSuccess() {
                 final String callId = mCallIdMapper.getCallId(call);
                 // If still bound, tell the connection service create connection has failed.
-                if (callId != null && isServiceValid("handoverComplete")) {
+                if (callId != null && isServiceValid("handoverComplete")
+                        && mServiceInterface != null) {
                     try {
                         mServiceInterface.handoverComplete(
                                 callId,
@@ -1507,7 +1531,7 @@
         final String callId = mCallIdMapper.getCallId(call);
 
         // If still bound, tell the connection service to abort.
-        if (callId != null && isServiceValid("abort")) {
+        if (callId != null && isServiceValid("abort") && mServiceInterface != null) {
             try {
                 logOutgoing("abort %s", callId);
                 mServiceInterface.abort(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -1521,7 +1545,7 @@
     /** @see IConnectionService#silence(String, Session.Info) */
     void silence(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("silence")) {
+        if (callId != null && isServiceValid("silence") && mServiceInterface != null) {
             try {
                 logOutgoing("silence %s", callId);
                 mServiceInterface.silence(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -1533,7 +1557,7 @@
     /** @see IConnectionService#hold(String, Session.Info) */
     void hold(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("hold")) {
+        if (callId != null && isServiceValid("hold") && mServiceInterface != null) {
             try {
                 logOutgoing("hold %s", callId);
                 mServiceInterface.hold(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -1545,7 +1569,7 @@
     /** @see IConnectionService#unhold(String, Session.Info) */
     void unhold(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("unhold")) {
+        if (callId != null && isServiceValid("unhold") && mServiceInterface != null) {
             try {
                 logOutgoing("unhold %s", callId);
                 mServiceInterface.unhold(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -1558,7 +1582,8 @@
     @VisibleForTesting
     public void onCallAudioStateChanged(Call activeCall, CallAudioState audioState) {
         final String callId = mCallIdMapper.getCallId(activeCall);
-        if (callId != null && isServiceValid("onCallAudioStateChanged")) {
+        if (callId != null && isServiceValid("onCallAudioStateChanged")
+                && mServiceInterface != null) {
             try {
                 logOutgoing("onCallAudioStateChanged %s %s", callId, audioState);
                 mServiceInterface.onCallAudioStateChanged(callId, audioState,
@@ -1571,7 +1596,7 @@
     /** @see IConnectionService#disconnect(String, Session.Info) */
     void disconnect(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("disconnect")) {
+        if (callId != null && isServiceValid("disconnect") && mServiceInterface != null) {
             try {
                 logOutgoing("disconnect %s", callId);
                 mServiceInterface.disconnect(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -1583,7 +1608,7 @@
     /** @see IConnectionService#answer(String, Session.Info) */
     void answer(Call call, int videoState) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("answer")) {
+        if (callId != null && isServiceValid("answer") && mServiceInterface != null) {
             try {
                 logOutgoing("answer %s %d", callId, videoState);
                 if (VideoProfile.isAudioOnly(videoState)) {
@@ -1600,7 +1625,7 @@
     /** @see IConnectionService#deflect(String, Uri , Session.Info) */
     void deflect(Call call, Uri address) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("deflect")) {
+        if (callId != null && isServiceValid("deflect") && mServiceInterface != null) {
             try {
                 logOutgoing("deflect %s", callId);
                 mServiceInterface.deflect(callId, address,
@@ -1613,7 +1638,7 @@
     /** @see IConnectionService#reject(String, Session.Info) */
     void reject(Call call, boolean rejectWithMessage, String message) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("reject")) {
+        if (callId != null && isServiceValid("reject") && mServiceInterface != null) {
             try {
                 logOutgoing("reject %s", callId);
 
@@ -1632,7 +1657,7 @@
     /** @see IConnectionService#reject(String, Session.Info) */
     void rejectWithReason(Call call, @android.telecom.Call.RejectReason int rejectReason) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("rejectReason")) {
+        if (callId != null && isServiceValid("rejectReason") && mServiceInterface != null) {
             try {
                 logOutgoing("rejectReason %s, %d", callId, rejectReason);
 
@@ -1646,7 +1671,7 @@
     /** @see IConnectionService#transfer(String, Uri , boolean, Session.Info) */
     void transfer(Call call, Uri number, boolean isConfirmationRequired) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("transfer")) {
+        if (callId != null && isServiceValid("transfer") && mServiceInterface != null) {
             try {
                 logOutgoing("transfer %s", callId);
                 mServiceInterface.transfer(callId, number, isConfirmationRequired,
@@ -1660,7 +1685,8 @@
     void transfer(Call call, Call otherCall) {
         final String callId = mCallIdMapper.getCallId(call);
         final String otherCallId = mCallIdMapper.getCallId(otherCall);
-        if (callId != null && otherCallId != null && isServiceValid("consultativeTransfer")) {
+        if (callId != null && otherCallId != null && isServiceValid("consultativeTransfer")
+                && mServiceInterface != null) {
             try {
                 logOutgoing("consultativeTransfer %s", callId);
                 mServiceInterface.consultativeTransfer(callId, otherCallId,
@@ -1673,7 +1699,7 @@
     /** @see IConnectionService#playDtmfTone(String, char, Session.Info) */
     void playDtmfTone(Call call, char digit) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("playDtmfTone")) {
+        if (callId != null && isServiceValid("playDtmfTone") && mServiceInterface != null) {
             try {
                 logOutgoing("playDtmfTone %s %c", callId, digit);
                 mServiceInterface.playDtmfTone(callId, digit,
@@ -1686,7 +1712,7 @@
     /** @see IConnectionService#stopDtmfTone(String, Session.Info) */
     void stopDtmfTone(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("stopDtmfTone")) {
+        if (callId != null && isServiceValid("stopDtmfTone") && mServiceInterface != null) {
             try {
                 logOutgoing("stopDtmfTone %s", callId);
                 mServiceInterface.stopDtmfTone(callId,
@@ -1734,7 +1760,7 @@
 
     void onPostDialContinue(Call call, boolean proceed) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("onPostDialContinue")) {
+        if (callId != null && isServiceValid("onPostDialContinue") && mServiceInterface != null) {
             try {
                 logOutgoing("onPostDialContinue %s %b", callId, proceed);
                 mServiceInterface.onPostDialContinue(callId, proceed,
@@ -1747,7 +1773,8 @@
     void conference(final Call call, Call otherCall) {
         final String callId = mCallIdMapper.getCallId(call);
         final String otherCallId = mCallIdMapper.getCallId(otherCall);
-        if (callId != null && otherCallId != null && isServiceValid("conference")) {
+        if (callId != null && otherCallId != null && isServiceValid("conference")
+                && mServiceInterface != null) {
             try {
                 logOutgoing("conference %s %s", callId, otherCallId);
                 mServiceInterface.conference(callId, otherCallId,
@@ -1759,7 +1786,7 @@
 
     void splitFromConference(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("splitFromConference")) {
+        if (callId != null && isServiceValid("splitFromConference") && mServiceInterface != null) {
             try {
                 logOutgoing("splitFromConference %s", callId);
                 mServiceInterface.splitFromConference(callId,
@@ -1771,7 +1798,7 @@
 
     void mergeConference(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("mergeConference")) {
+        if (callId != null && isServiceValid("mergeConference") && mServiceInterface != null) {
             try {
                 logOutgoing("mergeConference %s", callId);
                 mServiceInterface.mergeConference(callId,
@@ -1783,7 +1810,7 @@
 
     void swapConference(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("swapConference")) {
+        if (callId != null && isServiceValid("swapConference") && mServiceInterface != null) {
             try {
                 logOutgoing("swapConference %s", callId);
                 mServiceInterface.swapConference(callId,
@@ -1795,7 +1822,8 @@
 
     void addConferenceParticipants(Call call, List<Uri> participants) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("addConferenceParticipants")) {
+        if (callId != null && isServiceValid("addConferenceParticipants")
+                && mServiceInterface != null) {
             try {
                 logOutgoing("addConferenceParticipants %s", callId);
                 mServiceInterface.addConferenceParticipants(callId, participants,
@@ -1808,7 +1836,7 @@
     @VisibleForTesting
     public void pullExternalCall(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("pullExternalCall")) {
+        if (callId != null && isServiceValid("pullExternalCall") && mServiceInterface != null) {
             try {
                 logOutgoing("pullExternalCall %s", callId);
                 mServiceInterface.pullExternalCall(callId,
@@ -1820,7 +1848,7 @@
 
     void sendCallEvent(Call call, String event, Bundle extras) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("sendCallEvent")) {
+        if (callId != null && isServiceValid("sendCallEvent") && mServiceInterface != null) {
             try {
                 logOutgoing("sendCallEvent %s %s", callId, event);
                 mServiceInterface.sendCallEvent(callId, event, extras,
@@ -1832,7 +1860,7 @@
 
     void onExtrasChanged(Call call, Bundle extras) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("onExtrasChanged")) {
+        if (callId != null && isServiceValid("onExtrasChanged") && mServiceInterface != null) {
             try {
                 logOutgoing("onExtrasChanged %s %s", callId, extras);
                 mServiceInterface.onExtrasChanged(callId, extras,
@@ -1844,7 +1872,7 @@
 
     void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("startRtt")) {
+        if (callId != null && isServiceValid("startRtt") && mServiceInterface != null) {
             try {
                 logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
                 mServiceInterface.startRtt(callId, fromInCall, toInCall,
@@ -1856,7 +1884,7 @@
 
     void stopRtt(Call call) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("stopRtt")) {
+        if (callId != null && isServiceValid("stopRtt") && mServiceInterface != null) {
             try {
                 logOutgoing("stopRtt: %s", callId);
                 mServiceInterface.stopRtt(callId, Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -1868,7 +1896,7 @@
     void respondToRttRequest(
             Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
         final String callId = mCallIdMapper.getCallId(call);
-        if (callId != null && isServiceValid("respondToRttRequest")) {
+        if (callId != null && isServiceValid("respondToRttRequest") && mServiceInterface != null) {
             try {
                 logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
                 mServiceInterface.respondToRttUpgradeRequest(
@@ -1910,11 +1938,15 @@
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
-                try {
-                    mServiceInterface.connectionServiceFocusLost(
-                            Log.getExternalSession(TELECOM_ABBREVIATION));
-                } catch (RemoteException ignored) {
-                    Log.d(this, "failed to inform the focus lost event");
+                if (mServiceInterface != null) {
+                    try {
+                        mServiceInterface.connectionServiceFocusLost(
+                               Log.getExternalSession(TELECOM_ABBREVIATION));
+                    } catch (RemoteException ignored) {
+                        Log.d(this, "failed to inform the focus lost event");
+                    }
+                } else {
+                    Log.w(this, "connectionServiceFocusLost - failed to inform focus lost event");
                 }
             }
 
@@ -1929,11 +1961,15 @@
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
-                try {
-                    mServiceInterface.connectionServiceFocusGained(
-                            Log.getExternalSession(TELECOM_ABBREVIATION));
-                } catch (RemoteException ignored) {
-                    Log.d(this, "failed to inform the focus gained event");
+                if (mServiceInterface != null) {
+                   try {
+                       mServiceInterface.connectionServiceFocusGained(
+                               Log.getExternalSession(TELECOM_ABBREVIATION));
+                    } catch (RemoteException ignored) {
+                        Log.d(this, "failed to inform the focus gained event");
+                    }
+                } else {
+                    Log.w(this, "connectionServiceFocusGained - failed to inform focus lost event");
                 }
             }
 
diff --git a/src/com/android/server/telecom/DeviceIdleControllerAdapter.java b/src/com/android/server/telecom/DeviceIdleControllerAdapter.java
new file mode 100644
index 0000000..d3a798a
--- /dev/null
+++ b/src/com/android/server/telecom/DeviceIdleControllerAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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 android.content.Context;
+import android.os.PowerWhitelistManager;
+import android.os.RemoteException;
+import android.telecom.Log;
+
+import com.android.internal.telecom.IDeviceIdleControllerAdapter;
+
+/**
+ * Telecom is in the same process as the {@link PowerWhitelistManager}, so we can not make direct
+ * calls to the manager interface, since they will fail in the DeviceIdleController
+ * (see {@link Context#enforceCallingPermission(String, String)}). Instead, we must access it
+ * through SystemService#getLocalService, which is only accessible to the Telecom
+ * core loader service (TelecomLoaderService). Unfortunately, due to the architecture, this means
+ * we must use a Binder to allow services such as this to be accessible.
+ */
+public class DeviceIdleControllerAdapter {
+
+    private static final String TAG = "DeviceIdleControllerAdapter";
+
+    private final IDeviceIdleControllerAdapter mAdapter;
+
+    public DeviceIdleControllerAdapter(IDeviceIdleControllerAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Exempts an application from power restrictions for the duration specified. See
+     * DeviceIdleController for more information on how this works.
+     */
+    public void exemptAppTemporarilyForEvent(@NonNull String packageName, long duration,
+            int userHandle, @NonNull String reason) {
+        try {
+            mAdapter.exemptAppTemporarilyForEvent(packageName, duration, userHandle, reason);
+        } catch (RemoteException e) {
+            Log.w(TAG, "exemptAppTemporarilyForEvent e=" + e.getMessage());
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 228cb3d..cb729a7 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -16,8 +16,11 @@
 
 package com.android.server.telecom;
 
+import static android.os.Process.myUid;
+
 import android.Manifest;
 import android.annotation.NonNull;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.ComponentName;
@@ -214,7 +217,7 @@
                 Log.startSession("ICSBC.oSD", Log.getPackageAbbreviation(name));
                 synchronized (mLock) {
                     try {
-                        Log.d(this, "onDisconnected: %s", name);
+                        Log.d(this, "onServiceDisconnected: %s", name);
                         mIsBound = false;
                         onDisconnected();
                     } finally {
@@ -291,8 +294,9 @@
             mIsConnected = true;
             mInCallServiceInfo.setBindingStartTime(mClockProxy.elapsedRealtime());
             if (!mContext.bindServiceAsUser(intent, mServiceConnection,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
-                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE |
+                        Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS |
+                        Context.BIND_ABOVE_CLIENT,
                         UserHandle.CURRENT)) {
                 Log.w(this, "Failed to connect.");
                 mIsConnected = false;
@@ -322,7 +326,10 @@
                 String packageName = mInCallServiceInfo.getComponentName().getPackageName();
                 mContext.unbindService(mServiceConnection);
                 mIsConnected = false;
-                if (mIsNullBinding) {
+                if (mIsNullBinding && mInCallServiceInfo.getType() != IN_CALL_SERVICE_TYPE_NON_UI) {
+                    // Non-UI InCallServices are allowed to return null from onBind if they don't
+                    // want to handle calls at the moment, so don't report them to the user as
+                    // crashed.
                     sendCrashedInCallServiceNotification(packageName);
                 }
                 if (mCall != null) {
@@ -332,6 +339,8 @@
                             mInCallServiceInfo.getDisconnectTime()
                                     - mInCallServiceInfo.getBindingStartTime(), mIsNullBinding);
                 }
+
+                InCallController.this.onDisconnected(mInCallServiceInfo);
             } else {
                 Log.i(InCallController.this, "ICSBC#disconnect: already disconnected; %s",
                         mInCallServiceInfo);
@@ -864,6 +873,7 @@
     private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
 
     private final Context mContext;
+    private final AppOpsManager mAppOpsManager;
     private final TelecomSystem.SyncRoot mLock;
     private final CallsManager mCallsManager;
     private final SystemStateHelper mSystemStateHelper;
@@ -881,12 +891,24 @@
 
     private final CarModeTracker mCarModeTracker;
 
+    /**
+     * The package name of the app which is showing the calling UX.
+     */
+    private String mCurrentUserInterfacePackageName = null;
+
+    /**
+     * {@code true} if InCallController is tracking a managed, not external call which is using the
+     * microphone, {@code false} otherwise.
+     */
+    private boolean mIsCallUsingMicrophone = false;
+
     public InCallController(Context context, TelecomSystem.SyncRoot lock, CallsManager callsManager,
             SystemStateHelper systemStateHelper,
             DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
             EmergencyCallHelper emergencyCallHelper, CarModeTracker carModeTracker,
             ClockProxy clockProxy) {
         mContext = context;
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
         mLock = lock;
         mCallsManager = callsManager;
         mSystemStateHelper = systemStateHelper;
@@ -972,6 +994,7 @@
         }
         call.removeListener(mCallListener);
         mCallIdMapper.removeCall(call);
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     @Override
@@ -1045,10 +1068,12 @@
             }
             Log.i(this, "External call removed from components: %s", componentsUpdated);
         }
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     @Override
     public void onCallStateChanged(Call call, int oldState, int newState) {
+        maybeTrackMicrophoneUse(isMuted());
         updateCall(call);
     }
 
@@ -1066,6 +1091,7 @@
         if (!mInCallServices.isEmpty()) {
             Log.i(this, "Calling onAudioStateChanged, audioState: %s -> %s", oldCallAudioState,
                     newCallAudioState);
+            maybeTrackMicrophoneUse(newCallAudioState.isMuted());
             for (IInCallService inCallService : mInCallServices.values()) {
                 try {
                     inCallService.onCallAudioStateChanged(newCallAudioState);
@@ -1130,6 +1156,23 @@
         updateCall(call);
     }
 
+    /**
+     * Track changes to camera usage for a call.
+     * @param call The call.
+     * @param cameraId The id of the camera to use, or {@code null} if camera is off.
+     */
+    @Override
+    public void onSetCamera(Call call, String cameraId) {
+        Log.i(this, "onSetCamera callId=%s, cameraId=%s", call.getId(), cameraId);
+        if (cameraId != null) {
+            mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
+                    mContext.getOpPackageName(), false, null, null);
+        } else {
+            mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_CAMERA, myUid(),
+                    mContext.getOpPackageName(), null);
+        }
+    }
+
     void bringToForeground(boolean showDialpad) {
         if (!mInCallServices.isEmpty()) {
             for (IInCallService inCallService : mInCallServices.values()) {
@@ -1506,6 +1549,11 @@
     private boolean onConnected(InCallServiceInfo info, IBinder service) {
         Log.i(this, "onConnected to %s", info.getComponentName());
 
+        if (info.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
+                || info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+                || info.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
+            trackCallingUserInterfaceStarted(info);
+        }
         IInCallService inCallService = IInCallService.Stub.asInterface(service);
         mInCallServices.put(info, inCallService);
 
@@ -1556,7 +1604,11 @@
             inCallService.onCanAddCallChanged(mCallsManager.canAddCall());
         } catch (RemoteException ignored) {
         }
-        mBindingFuture.complete(true);
+        // Don't complete the binding future for non-ui incalls
+        if (info.getType() != IN_CALL_SERVICE_TYPE_NON_UI) {
+            mBindingFuture.complete(true);
+        }
+
         Log.i(this, "%s calls sent to InCallService.", numCallsSent);
         return true;
     }
@@ -1568,7 +1620,11 @@
      */
     private void onDisconnected(InCallServiceInfo disconnectedInfo) {
         Log.i(this, "onDisconnected from %s", disconnectedInfo.getComponentName());
-
+        if (disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_CAR_MODE_UI
+                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI
+                || disconnectedInfo.getType() == IN_CALL_SERVICE_TYPE_DIALER_UI) {
+            trackCallingUserInterfaceStopped(disconnectedInfo);
+        }
         mInCallServices.remove(disconnectedInfo);
     }
 
@@ -1634,6 +1690,7 @@
             mCallIdMapper.addCall(call);
             call.addListener(mCallListener);
         }
+        maybeTrackMicrophoneUse(isMuted());
     }
 
     /**
@@ -1808,9 +1865,76 @@
         }
     }
 
+    /**
+     * Tracks start of microphone use on binding to the current calling UX.
+     * @param info
+     */
+    private void trackCallingUserInterfaceStarted(InCallServiceInfo info) {
+        String packageName = info.getComponentName().getPackageName();
+        if (!Objects.equals(mCurrentUserInterfacePackageName, packageName)) {
+            Log.i(this, "trackCallingUserInterfaceStarted: %s is now calling UX.", packageName);
+            mCurrentUserInterfacePackageName = packageName;
+        }
+        maybeTrackMicrophoneUse(isMuted());
+    }
+
+    /**
+     * Tracks stop of microphone use on unbind from the current calling UX.
+     * @param info
+     */
+    private void trackCallingUserInterfaceStopped(InCallServiceInfo info) {
+        maybeTrackMicrophoneUse(isMuted());
+        mCurrentUserInterfacePackageName = null;
+        String packageName = info.getComponentName().getPackageName();
+        Log.i(this, "trackCallingUserInterfaceStopped: %s is no longer calling UX", packageName);
+    }
+
+    /**
+     * As calls are added, removed and change between external and non-external status, track
+     * whether the current active calling UX is using the microphone.  We assume if there is a
+     * managed call present and the mic is not muted that the microphone is in use.
+     */
+    private void maybeTrackMicrophoneUse(boolean isMuted) {
+        boolean wasTrackingManagedCall = mIsCallUsingMicrophone;
+        mIsCallUsingMicrophone = isTrackingManagedAliveCall() && !isMuted;
+        if (wasTrackingManagedCall != mIsCallUsingMicrophone) {
+            if (mIsCallUsingMicrophone) {
+                mAppOpsManager.startOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
+                        mContext.getOpPackageName(), false, null, null);
+            } else {
+                mAppOpsManager.finishOp(AppOpsManager.OP_PHONE_CALL_MICROPHONE, myUid(),
+                        mContext.getOpPackageName(), null);
+            }
+        }
+    }
+
+    /**
+     * @return {@code true} if InCallController is tracking a managed call (i.e. not self managed
+     * and not external) that is active.
+     */
+    private boolean isTrackingManagedAliveCall() {
+        return mCallIdMapper.getCalls().stream().anyMatch(c -> !c.isExternalCall()
+            && !c.isSelfManaged() && c.isAlive() && c.getState() != CallState.ON_HOLD
+                && c.getState() != CallState.AUDIO_PROCESSING);
+    }
+
+    /**
+     * @return {@code true} if the audio is currently muted, {@code false} otherwise.
+     */
+    private boolean isMuted() {
+        if (mCallsManager.getAudioState() == null) {
+            return false;
+        }
+        return mCallsManager.getAudioState().isMuted();
+    }
+
     private void sendCrashedInCallServiceNotification(String packageName) {
         PackageManager packageManager = mContext.getPackageManager();
         CharSequence appName;
+        String systemDialer = mDefaultDialerCache.getSystemDialerApplication();
+        if ((systemDialer != null) && systemDialer.equals(packageName)) {
+            return;
+        }
         try {
             appName = packageManager.getApplicationLabel(
                     packageManager.getApplicationInfo(packageName, 0));
diff --git a/src/com/android/server/telecom/InternalServiceRetrieverAdapter.java b/src/com/android/server/telecom/InternalServiceRetrieverAdapter.java
new file mode 100644
index 0000000..4a2c3d1
--- /dev/null
+++ b/src/com/android/server/telecom/InternalServiceRetrieverAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.os.RemoteException;
+
+import com.android.internal.telecom.IInternalServiceRetriever;
+
+/**
+ * Contains all services that Telecom must access that are only accessible as a local service as
+ * part of the SYSTEM process.
+ */
+public class InternalServiceRetrieverAdapter {
+
+    private final IInternalServiceRetriever mRetriever;
+
+    public InternalServiceRetrieverAdapter(IInternalServiceRetriever retriever) {
+        mRetriever = retriever;
+    }
+
+    public DeviceIdleControllerAdapter getDeviceIdleController() {
+        try {
+            return new DeviceIdleControllerAdapter(mRetriever.getDeviceIdleController());
+        } catch (RemoteException e) {
+            // This should not happen - if it does, there is a bad configuration as this should
+            // all be local.
+            throw new IllegalStateException(e);
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index 5c821a4..314ad9a 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -156,10 +156,7 @@
             properties |= android.telecom.Call.Details.PROPERTY_VOIP_AUDIO_MODE;
         }
 
-        // If this is a single-SIM device, the "default SIM" will always be the only SIM.
-        boolean isDefaultSmsAccount = phoneAccountRegistrar != null &&
-                phoneAccountRegistrar.isUserSelectedSmsPhoneAccount(call.getTargetPhoneAccount());
-        if (call.isRespondViaSmsCapable() && isDefaultSmsAccount) {
+        if (call.isRespondViaSmsCapable()) {
             capabilities |= android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT;
         }
 
@@ -507,7 +504,10 @@
         android.telecom.Call.Details.CAPABILITY_TRANSFER,
 
         Connection.CAPABILITY_TRANSFER_CONSULTATIVE,
-        android.telecom.Call.Details.CAPABILITY_TRANSFER_CONSULTATIVE
+        android.telecom.Call.Details.CAPABILITY_TRANSFER_CONSULTATIVE,
+
+        Connection.CAPABILITY_SUPPORTS_RTT_REMOTE,
+        android.telecom.Call.Details.CAPABILITY_SUPPORTS_RTT_REMOTE
     };
 
     private static int convertConnectionToCallCapabilities(int connectionCapabilities) {
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index b5b22ef..c15fe1a 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -667,6 +667,12 @@
         return getAllPhoneAccounts(mCurrentUserHandle);
     }
 
+    public List<PhoneAccountHandle> getEmergencyCallOnlyPhoneAccounts(String uriScheme,
+            UserHandle userHandle) {
+        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY,
+                0, uriScheme, null, false, userHandle);
+    }
+
     /**
      * Retrieves a list of all phone account call provider phone accounts supporting the
      * specified URI scheme.
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
index 517857b..89b0c77 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
@@ -46,4 +46,4 @@
     public String stripSeparators(String number) {
         return PhoneNumberUtils.stripSeparators(number);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index a769a94..c1852af 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -19,7 +19,10 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.Person;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.VibrationEffect;
 import android.telecom.Log;
 import android.telecom.TelecomManager;
@@ -30,6 +33,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Vibrator;
+import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.LogUtils.EventTimer;
@@ -77,6 +81,8 @@
     private static final int RAMPING_RINGER_VIBRATION_DURATION = 5000;
     private static final int RAMPING_RINGER_DURATION = 10000;
 
+    private static final int OUTGOING_CALL_VIBRATING_DURATION = 100;
+
     static {
         // construct complete pulse pattern
         PULSE_PATTERN = new long[PULSE_PRIMING_PATTERN.length + PULSE_RAMPING_PATTERN.length];
@@ -93,6 +99,10 @@
             PULSE_PRIMING_AMPLITUDE.length, PULSE_RAMPING_AMPLITUDE.length);
     }
 
+    private static final long[] FAIRPHONE_VIBRATION_PATTERN = {0, 600, 900, 500, 800, 800, 1500};
+
+    private static final int[] FAIRPHONE_VIBRATION_AMPLITUDE = {0, 255, 0, 255, 0, 255, 0};
+
     private static final long[] SIMPLE_VIBRATION_PATTERN = {
             0, // No delay before starting
             1000, // How long to vibrate
@@ -159,7 +169,26 @@
     /**
      * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
      */
-    private boolean mIsVibrating = false;
+    private volatile boolean mIsVibrating = false;
+
+    private int mSavedInCallVolume = 0;
+
+    private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent == null ||
+                    !intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+                return;
+            }
+            if (intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1)
+                    != AudioManager.STREAM_VOICE_CALL) {
+                return;
+            }
+            int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+            Log.d(this, "VolumeReceiver and new index is " + index);
+            mSavedInCallVolume = index;
+        }
+    };
 
     /** Initializes the Ringer. */
     @VisibleForTesting
@@ -184,6 +213,10 @@
         mInCallController = inCallController;
         mVibrationEffectProxy = vibrationEffectProxy;
 
+        // TODO: Vibration pattern can't be overlay-ed, hard-code custom pattern here.
+        mDefaultVibrationEffect = VibrationEffect.createWaveform(FAIRPHONE_VIBRATION_PATTERN,
+                    FAIRPHONE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
+        /*
         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
                     SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
@@ -191,6 +224,7 @@
             mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(PULSE_PATTERN,
                     PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
         }
+        */
 
         mIsHapticPlaybackSupportedByDevice =
                 mSystemSettingsUtil.isHapticPlaybackSupported(mContext);
@@ -201,6 +235,154 @@
         mBlockOnRingingFuture = future;
     }
 
+    public boolean startPlayCrs(Call foregroundCall, boolean isHfpDeviceAttached) {
+        if (foregroundCall == null) {
+            Log.wtf(this, "startRinging called with null foreground call.");
+            return false;
+        }
+
+        boolean isCrsCall = foregroundCall.isCrsCall();
+        Log.i(this, "startPlayCrs called with video CRS is :: " + isCrsCall);
+        if (!isCrsCall) {
+            return false;
+        }
+
+        if (foregroundCall.getState() != CallState.RINGING
+                && foregroundCall.getState() != CallState.SIMULATED_RINGING) {
+            // Its possible for bluetooth to connect JUST as a call goes active, which would mean
+            // the call would start ringing again.
+            Log.i(this, "startRinging called for non-ringing foreground callid=%s",
+                    foregroundCall.getId());
+            return false;
+        }
+
+        AudioManager audioManager =
+                mContext.getSystemService(AudioManager.class);
+        LogUtils.EventTimer timer = new EventTimer();
+        boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0;
+        timer.record("isVolumeOverZero");
+        boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri());
+        timer.record("shouldRingForContact");
+        boolean isSelfManaged = foregroundCall.isSelfManaged();
+        timer.record("isSelfManaged");
+        boolean isSilentRingingRequested = foregroundCall.isSilentRingingRequested();
+        timer.record("isSilentRingRequested");
+
+        boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isCrsCall;
+        timer.record("isRingerAudible");
+        boolean hasExternalRinger = hasExternalRinger(foregroundCall);
+        timer.record("hasExternalRinger");
+        // Don't do call waiting operations or vibration unless these are false.
+        boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext);
+        timer.record("isTheaterModeOn");
+        boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging();
+        timer.record("letDialerHandleRinging");
+
+        Log.i(this, "startRinging timings: " + timer);
+        boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
+                hasExternalRinger || isSilentRingingRequested;
+
+        // Acquire audio focus under any of the following conditions:
+        // 1. Should ring for contact and there's an HFP device attached
+        // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
+        //    present, here is CRS from network.
+        // 3. The call is self-managed.
+        boolean shouldAcquireAudioFocus =
+                isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
+
+        if (endEarly) {
+            if (letDialerHandleRinging) {
+                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Dialer handles");
+            }
+            if (isSilentRingingRequested) {
+                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Silent ringing "
+                        + "requested");
+            }
+            Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
+                            "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s",
+                    isTheaterModeOn, letDialerHandleRinging, isSelfManaged, hasExternalRinger,
+                    isSilentRingingRequested);
+            if (mBlockOnRingingFuture != null) {
+                mBlockOnRingingFuture.complete(null);
+            }
+            return shouldAcquireAudioFocus;
+        }
+
+        stopCallWaiting();
+        VibrationEffect effect;
+        // Determine if the settings and DND mode indicate that the vibrator can be used right now.
+        boolean isVibratorEnabled = isVibratorEnabled(mContext, foregroundCall);
+        if (isRingerAudible) {
+            mRingingCall = foregroundCall;
+            Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
+            // Because we wait until a contact info query to complete before processing a
+            // 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.
+            if (mSystemSettingsUtil.applyRampingRinger(mContext)) {
+                Log.i(this, "start ramping ringer.");
+                if (mSystemSettingsUtil.enableAudioCoupledVibrationForRampingRinger()) {
+                    effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
+                } else {
+                    effect = mDefaultVibrationEffect;
+                }
+            } else {
+                effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
+            }
+        } else {
+            String reason = String.format(
+                    "isVolumeOverZero=%s, shouldRingForContact=%s, isCrsCall=%s",
+                    isVolumeOverZero, shouldRingForContact, isCrsCall);
+            Log.i(this, "startRinging: skipping because ringer would not be audible. " + reason);
+            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " + reason);
+            effect = mDefaultVibrationEffect;
+        }
+
+        Log.i(this, "isHfpDeviceAttached=%s, isVibratorEnabled=%s, isRingerAudible=%s, ",
+                isHfpDeviceAttached, isVibratorEnabled, isRingerAudible);
+        int ringVolumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_RING);
+        if (ringVolumeLevel != 0 && isRingerAudible) {
+            Log.i(this, "Start play CRS with volume :: " + ringVolumeLevel);
+            // Set the CRS volume with local ring volume  and save the old volume setting.
+            mSavedInCallVolume = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+            mContext.registerReceiver(mVolumeReceiver, filter);
+            audioManager.setStreamVolume(
+                    AudioManager.STREAM_VOICE_CALL,
+                    convertVolumeLevelFromRingToCrs(ringVolumeLevel), 0);
+        }
+        if (mBlockOnRingingFuture != null) {
+            mBlockOnRingingFuture.complete(null);
+        }
+        maybeStartVibration(foregroundCall, shouldRingForContact,
+                effect, isVibratorEnabled, isRingerAudible);
+
+        return shouldAcquireAudioFocus;
+    }
+
+    private static int convertVolumeLevelFromRingToCrs(int ringVolume) {
+        //CRS volume is same as call volume, MinVolume is 1 and MaxVolume 5.
+        //The range of local ring volume is from 0 to 7, telephony needs to align
+        //volume level between local ring and CRS.
+        //local ring level <---> CRS volume level
+        // 7/6             <--->      5
+        // 5/4             <--->      4
+        // 3               <--->      3
+        // 2               <--->      2
+        // 1               <--->      1
+        // 0               <--->  silence CRS
+        final int upperBound  = 5;
+        final int middleBound = 4;
+        if (ringVolume < middleBound) { // Linear mapping for lower bound.
+            return ringVolume;
+        } else if (ringVolume > upperBound  ) {  // Saturating mapping upper bound.
+            return upperBound;
+        } else {
+            return middleBound;
+        }
+    }
+
     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
         if (foregroundCall == null) {
             Log.wtf(this, "startRinging called with null foreground call.");
@@ -423,7 +605,35 @@
         }
     }
 
+    private void stopPlayCrs() {
+        if (mRingingCall != null) {
+            Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
+            AudioManager audioManager =  mContext.getSystemService(AudioManager.class);
+            audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, mSavedInCallVolume, 0);
+            mContext.unregisterReceiver(mVolumeReceiver);
+            mSavedInCallVolume = 0;
+            mRingingCall = null;
+        }
+        // If we haven't started vibrating because we were waiting for the haptics info, cancel
+        // it and don't vibrate at all.
+        if (mVibrateFuture != null) {
+            mVibrateFuture.cancel(true);
+        }
+
+        if (mIsVibrating) {
+            Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
+            mVibrator.cancel();
+            mIsVibrating = false;
+            mVibratingCall = null;
+        }
+    }
+
     public void stopRinging() {
+        if (mRingingCall != null && mRingingCall.isCrsCall()) {
+            stopPlayCrs();
+            return;
+        }
+
         if (mRingingCall != null) {
             Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER);
             mRingingCall = null;
@@ -517,4 +727,25 @@
         return mSystemSettingsUtil.canVibrateWhenRinging(context)
             || mSystemSettingsUtil.applyRampingRinger(context);
     }
+
+    public void startVibratingForOutgoingCallActive() {
+        if (!mIsVibrating
+                && Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.VIBRATING_FOR_OUTGOING_CALL_ACCEPTED, 1) == 1) {
+            mIsVibrating = true;
+            java.util.concurrent.Executors.defaultThreadFactory().newThread(() -> {
+                final VibrationEffect vibrationEffect =
+                        mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN,
+                        SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
+                final AudioAttributes vibrationAttributes = new AudioAttributes.Builder()
+                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+                        .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
+                        .build();
+                mVibrator.vibrate(vibrationEffect, vibrationAttributes);
+                android.os.SystemClock.sleep(OUTGOING_CALL_VIBRATING_DURATION);
+                mVibrator.cancel();
+                mIsVibrating = false;
+            }).start();
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 807cc2d..08389b9 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -207,7 +207,8 @@
             ClockProxy clockProxy,
             RoleManagerAdapter roleManagerAdapter,
             IncomingCallFilter.Factory incomingCallFilterFactory,
-            ContactsAsyncHelper.Factory contactsAsyncHelperFactory) {
+            ContactsAsyncHelper.Factory contactsAsyncHelperFactory,
+            DeviceIdleControllerAdapter deviceIdleControllerAdapter) {
         mContext = context.getApplicationContext();
         LogUtils.initLogging(mContext);
         DefaultDialerManagerAdapter defaultDialerAdapter =
@@ -241,7 +242,8 @@
         SystemStateHelper systemStateHelper = new SystemStateHelper(mContext);
 
         mMissedCallNotifier = missedCallNotifierImplFactory
-                .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar, defaultDialerCache);
+                .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar, defaultDialerCache,
+                        deviceIdleControllerAdapter);
         DisconnectedCallNotifier.Factory disconnectedCallNotifierFactory =
                 new DisconnectedCallNotifier.Default();
 
diff --git a/src/com/android/server/telecom/TelephonyUtil.java b/src/com/android/server/telecom/TelephonyUtil.java
index 7eb08d7..01317b2 100644
--- a/src/com/android/server/telecom/TelephonyUtil.java
+++ b/src/com/android/server/telecom/TelephonyUtil.java
@@ -19,6 +19,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.net.Uri;
+import android.os.ServiceManager;
+import android.os.RemoteException;
+import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.PhoneNumberUtils;
@@ -26,6 +29,7 @@
 import android.telephony.TelephonyManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import org.codeaurora.internal.IExtTelephony;
 
 import java.util.Collections;
 import java.util.Comparator;
@@ -41,6 +45,8 @@
     private static final String PSTN_CALL_SERVICE_CLASS_NAME =
             "com.android.services.telephony.TelephonyConnectionService";
 
+    private static final String LOG_TAG = "TelephonyUtil";
+
     private static final PhoneAccountHandle DEFAULT_EMERGENCY_PHONE_ACCOUNT_HANDLE =
             new PhoneAccountHandle(
                     new ComponentName(TELEPHONY_PACKAGE_NAME, PSTN_CALL_SERVICE_CLASS_NAME), "E");
@@ -78,4 +84,32 @@
             return false;
         }
     }
+
+    public static boolean isLocalEmergencyNumber(String address) {
+        IExtTelephony mIExtTelephony =
+            IExtTelephony.Stub.asInterface(ServiceManager.getService("qti.radio.extphone"));
+        boolean result = false;
+        try {
+            result = mIExtTelephony.isLocalEmergencyNumber(address);
+        } catch (RemoteException ex) {
+            Log.e(LOG_TAG, ex, "RemoteException");
+        } catch (NullPointerException ex) {
+            Log.e(LOG_TAG, ex, "NullPointerException");
+        }
+        return result;
+    }
+
+    public static boolean isPotentialLocalEmergencyNumber(String address) {
+        IExtTelephony mIExtTelephony =
+            IExtTelephony.Stub.asInterface(ServiceManager.getService("qti.radio.extphone"));
+        boolean result = false;
+        try {
+            result = mIExtTelephony.isPotentialLocalEmergencyNumber(address);
+        } catch (RemoteException ex) {
+            Log.e(LOG_TAG, ex, "RemoteException");
+        } catch (NullPointerException ex) {
+            Log.e(LOG_TAG, ex, "NullPointerException");
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/server/telecom/Timeouts.java b/src/com/android/server/telecom/Timeouts.java
index a701b88..2309596 100644
--- a/src/com/android/server/telecom/Timeouts.java
+++ b/src/com/android/server/telecom/Timeouts.java
@@ -211,4 +211,15 @@
     public static long getCallRecordingToneRepeatIntervalMillis(ContentResolver contentResolver) {
         return get(contentResolver, "call_recording_tone_repeat_interval", 15000L /* 15 seconds */);
     }
+
+    /**
+     * Returns the number of milliseconds for which the system should exempt the default dialer from
+     * power save restrictions due to the dialer needing to handle a missed call notification
+     * (update call log, check VVM, etc...).
+     */
+    public static long getDialerMissedCallPowerSaveExemptionTimeMillis(
+            ContentResolver contentResolver) {
+        return get(contentResolver, "dialer_missed_call_power_save_exemption_time_millis",
+                30000L /*30 seconds*/);
+    }
 }
diff --git a/src/com/android/server/telecom/TtyManager.java b/src/com/android/server/telecom/TtyManager.java
index 2d04234..dfddb8f 100644
--- a/src/com/android/server/telecom/TtyManager.java
+++ b/src/com/android/server/telecom/TtyManager.java
@@ -49,7 +49,9 @@
 
         IntentFilter intentFilter = new IntentFilter(
                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
-        mContext.registerReceiver(mReceiver, intentFilter);
+        mContext.registerReceiver(mReceiver, intentFilter,
+                android.Manifest.permission.MODIFY_PHONE_STATE,
+                null);
 
         updateCurrentTtyMode();
     }
diff --git a/src/com/android/server/telecom/VideoProviderProxy.java b/src/com/android/server/telecom/VideoProviderProxy.java
index 364e0f4..df11403 100644
--- a/src/com/android/server/telecom/VideoProviderProxy.java
+++ b/src/com/android/server/telecom/VideoProviderProxy.java
@@ -55,6 +55,7 @@
      */
     public interface Listener {
         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
+        void onSetCamera(Call call, String cameraId);
     }
 
     /**
@@ -346,6 +347,12 @@
                     return;
                 }
             }
+
+            // Inform other Telecom components of the change in camera status.
+            for (Listener listener : mListeners) {
+                listener.onSetCamera(mCall, cameraId);
+            }
+
             try {
                 mConectionServiceVideoProvider.setCamera(cameraId, callingPackage,
                         targetSdkVersion);
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 7211990..d52087d 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -225,8 +225,6 @@
             sendMessageDelayed(CONNECTION_TIMEOUT, args,
                     mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
                             mContext.getContentResolver()));
-            // Pretend like audio is connected when communicating w/ CARSM.
-            mListener.onBluetoothAudioConnected();
         }
 
         @Override
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
index 721a269..bf8c6f9 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
@@ -291,7 +291,12 @@
 
     public void unbindCallScreeningService() {
         if (mConnection != null) {
-            mContext.unbindService(mConnection);
+            try {
+                mContext.unbindService(mConnection);
+            } catch (IllegalArgumentException e) {
+                Log.i(this, "Exception when unbind service %s : %s", mConnection,
+                        e.getMessage());
+            }
         }
         mConnection = null;
     }
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index e20da80..b6705f4 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -30,6 +30,10 @@
 import android.telecom.Log;
 
 import android.telecom.CallerInfoAsyncQuery;
+
+import com.android.internal.telecom.IInternalServiceRetriever;
+import com.android.internal.telecom.ITelecomLoader;
+import com.android.internal.telecom.ITelecomService;
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.BluetoothAdapterProxy;
 import com.android.server.telecom.BluetoothPhoneServiceImpl;
@@ -41,16 +45,17 @@
 import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.DeviceIdleControllerAdapter;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
 import com.android.server.telecom.InCallWakeLockControllerFactory;
 import com.android.server.telecom.CallAudioManager;
+import com.android.server.telecom.InternalServiceRetrieverAdapter;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.ProximitySensorManager;
-import com.android.server.telecom.R;
 import com.android.server.telecom.RoleManagerAdapterImpl;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TelecomWakeLock;
@@ -68,10 +73,17 @@
     @Override
     public IBinder onBind(Intent intent) {
         Log.d(this, "onBind");
-        initializeTelecomSystem(this);
-        synchronized (getTelecomSystem().getLock()) {
-            return getTelecomSystem().getTelecomServiceImpl().getBinder();
-        }
+        return new ITelecomLoader.Stub() {
+            @Override
+            public ITelecomService createTelecomService(IInternalServiceRetriever retriever) {
+                InternalServiceRetrieverAdapter adapter =
+                        new InternalServiceRetrieverAdapter(retriever);
+                initializeTelecomSystem(TelecomService.this, adapter);
+                synchronized (getTelecomSystem().getLock()) {
+                    return getTelecomSystem().getTelecomServiceImpl().getBinder();
+                }
+            }
+        };
     }
 
     /**
@@ -84,7 +96,8 @@
      *
      * @param context
      */
-    static void initializeTelecomSystem(Context context) {
+    static void initializeTelecomSystem(Context context,
+            InternalServiceRetrieverAdapter internalServiceRetriever) {
         if (TelecomSystem.getInstance() == null) {
             NotificationChannelManager notificationChannelManager =
                     new NotificationChannelManager();
@@ -98,9 +111,11 @@
                                 public MissedCallNotifierImpl makeMissedCallNotifierImpl(
                                         Context context,
                                         PhoneAccountRegistrar phoneAccountRegistrar,
-                                        DefaultDialerCache defaultDialerCache) {
+                                        DefaultDialerCache defaultDialerCache,
+                                        DeviceIdleControllerAdapter idleControllerAdapter) {
                                     return new MissedCallNotifierImpl(context,
-                                            phoneAccountRegistrar, defaultDialerCache);
+                                            phoneAccountRegistrar, defaultDialerCache,
+                                            idleControllerAdapter);
                                 }
                             },
                             new CallerInfoAsyncQueryFactory() {
@@ -191,7 +206,8 @@
                             new RoleManagerAdapterImpl(context,
                                     (RoleManager) context.getSystemService(Context.ROLE_SERVICE)),
                             new IncomingCallFilter.Factory(),
-                            new ContactsAsyncHelper.Factory()));
+                            new ContactsAsyncHelper.Factory(),
+                            internalServiceRetriever.getDeviceIdleController()));
         }
         if (BluetoothAdapter.getDefaultAdapter() != null) {
             context.startService(new Intent(context, BluetoothPhoneService.class));
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index a0eca8f..e37fe6b 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -19,8 +19,10 @@
 import static android.Manifest.permission.READ_PHONE_STATE;
 
 import android.annotation.NonNull;
+import android.app.BroadcastOptions;
 import android.content.ContentProvider;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
 import android.telecom.Logging.Runnable;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -29,11 +31,13 @@
 import com.android.server.telecom.CallsManagerListenerBase;
 import com.android.server.telecom.Constants;
 import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.DeviceIdleControllerAdapter;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.R;
 import com.android.server.telecom.TelecomBroadcastIntentProcessor;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.Timeouts;
 import com.android.server.telecom.components.TelecomBroadcastReceiver;
 
 import android.app.Notification;
@@ -86,7 +90,8 @@
     public interface MissedCallNotifierImplFactory {
         MissedCallNotifier makeMissedCallNotifierImpl(Context context,
                 PhoneAccountRegistrar phoneAccountRegistrar,
-                DefaultDialerCache defaultDialerCache);
+                DefaultDialerCache defaultDialerCache,
+                DeviceIdleControllerAdapter deviceIdleControllerAdapter);
     }
 
     public interface NotificationBuilderFactory {
@@ -124,12 +129,14 @@
 
     private static final int MISSED_CALL_NOTIFICATION_ID = 1;
     private static final String NOTIFICATION_TAG = MissedCallNotifierImpl.class.getSimpleName();
+    private static final String MISSED_CALL_POWER_SAVE_REASON = "missed-call";
 
     private final Context mContext;
     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
     private final NotificationManager mNotificationManager;
     private final NotificationBuilderFactory mNotificationBuilderFactory;
     private final DefaultDialerCache mDefaultDialerCache;
+    private final DeviceIdleControllerAdapter mDeviceIdleControllerAdapter;
     private UserHandle mCurrentUserHandle;
 
     // Used to track the number of missed calls.
@@ -138,19 +145,22 @@
     private List<UserHandle> mUsersToLoadAfterBootComplete = new ArrayList<>();
 
     public MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
-            DefaultDialerCache defaultDialerCache) {
+            DefaultDialerCache defaultDialerCache,
+            DeviceIdleControllerAdapter deviceIdleControllerAdapter) {
         this(context, phoneAccountRegistrar, defaultDialerCache,
-                new DefaultNotificationBuilderFactory());
+                new DefaultNotificationBuilderFactory(), deviceIdleControllerAdapter);
     }
 
     public MissedCallNotifierImpl(Context context,
             PhoneAccountRegistrar phoneAccountRegistrar,
             DefaultDialerCache defaultDialerCache,
-            NotificationBuilderFactory notificationBuilderFactory) {
+            NotificationBuilderFactory notificationBuilderFactory,
+            DeviceIdleControllerAdapter deviceIdleControllerAdapter) {
         mContext = context;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
         mNotificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mDeviceIdleControllerAdapter = deviceIdleControllerAdapter;
         mDefaultDialerCache = defaultDialerCache;
 
         mNotificationBuilderFactory = notificationBuilderFactory;
@@ -162,7 +172,8 @@
     public void clearMissedCalls(UserHandle userHandle) {
         // If the default dialer is showing the missed call notification then it will modify the
         // call log and we don't have to do anything here.
-        if (!shouldManageNotificationThroughDefaultDialer(userHandle)) {
+        String dialerPackage = getDefaultDialerPackage(userHandle);
+        if (!shouldManageNotificationThroughDefaultDialer(dialerPackage, userHandle)) {
             markMissedCallsAsRead(userHandle);
         }
         cancelMissedCallNotification(userHandle);
@@ -194,6 +205,15 @@
         }.prepare());
     }
 
+    private String getDefaultDialerPackage(UserHandle userHandle) {
+        String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication(
+                userHandle.getIdentifier());
+        if (TextUtils.isEmpty(dialerPackage)) {
+            return null;
+        }
+        return dialerPackage;
+    }
+
     /**
      * Returns the missed-call notification intent to send to the default dialer for the given user.
      * Note, the passed in userHandle is always the non-managed user for SIM calls (multi-user
@@ -204,18 +224,16 @@
      * handle of the phone account. This could be a managed user. In that case we return the default
      * dialer for the given user which could be a managed (work profile) dialer.
      */
-    private Intent getShowMissedCallIntentForDefaultDialer(UserHandle userHandle) {
-        String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication(
-                userHandle.getIdentifier());
-        if (TextUtils.isEmpty(dialerPackage)) {
-            return null;
-        }
+    private Intent getShowMissedCallIntentForDefaultDialer(String dialerPackage) {
         return new Intent(TelecomManager.ACTION_SHOW_MISSED_CALLS_NOTIFICATION)
             .setPackage(dialerPackage);
     }
 
-    private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) {
-        Intent intent = getShowMissedCallIntentForDefaultDialer(userHandle);
+    private boolean shouldManageNotificationThroughDefaultDialer(String dialerPackage,
+            UserHandle userHandle) {
+        if (TextUtils.isEmpty(dialerPackage)) return false;
+
+        Intent intent = getShowMissedCallIntentForDefaultDialer(dialerPackage);
         if (intent == null) {
             return false;
         }
@@ -225,9 +243,29 @@
         return receivers.size() > 0;
     }
 
-    private void sendNotificationThroughDefaultDialer(CallInfo callInfo, UserHandle userHandle) {
+    /**
+     * For dialers that manage missed call handling themselves, we must temporarily add them to the
+     * power save exemption list, as they must perform operations such as modifying the call log and
+     * power save restrictions can cause these types of operations to not complete (sometimes
+     * causing ANRs).
+     */
+    private Bundle exemptFromPowerSavingTemporarily(String dialerPackage, UserHandle handle) {
+        if (TextUtils.isEmpty(dialerPackage)) {
+            return null;
+        }
+        BroadcastOptions bopts = BroadcastOptions.makeBasic();
+        long duration = Timeouts.getDialerMissedCallPowerSaveExemptionTimeMillis(
+                mContext.getContentResolver());
+        mDeviceIdleControllerAdapter.exemptAppTemporarilyForEvent(dialerPackage, duration,
+                handle.getIdentifier(), MISSED_CALL_POWER_SAVE_REASON);
+        bopts.setTemporaryAppWhitelistDuration(duration);
+        return bopts.toBundle();
+    }
+
+    private void sendNotificationThroughDefaultDialer(String dialerPackage, CallInfo callInfo,
+            UserHandle userHandle) {
         int count = mMissedCallCounts.get(userHandle).get();
-        Intent intent = getShowMissedCallIntentForDefaultDialer(userHandle)
+        Intent intent = getShowMissedCallIntentForDefaultDialer(dialerPackage)
             .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
             .putExtra(TelecomManager.EXTRA_CLEAR_MISSED_CALLS_INTENT,
                     createClearMissedCallsPendingIntent(userHandle))
@@ -248,7 +286,8 @@
 
 
         Log.w(this, "Showing missed calls through default dialer.");
-        mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);
+        Bundle options = exemptFromPowerSavingTemporarily(dialerPackage, userHandle);
+        mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE, options);
     }
 
     /**
@@ -276,8 +315,9 @@
         mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0));
         int missCallCounts = mMissedCallCounts.get(userHandle).incrementAndGet();
 
-        if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
-            sendNotificationThroughDefaultDialer(callInfo, userHandle);
+        String dialerPackage = getDefaultDialerPackage(userHandle);
+        if (shouldManageNotificationThroughDefaultDialer(dialerPackage, userHandle)) {
+            sendNotificationThroughDefaultDialer(dialerPackage, callInfo, userHandle);
             return;
         }
 
@@ -393,8 +433,9 @@
         mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0));
         mMissedCallCounts.get(userHandle).set(0);
 
-        if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
-            sendNotificationThroughDefaultDialer(null, userHandle);
+        String dialerPackage = getDefaultDialerPackage(userHandle);
+        if (shouldManageNotificationThroughDefaultDialer(dialerPackage, userHandle)) {
+            sendNotificationThroughDefaultDialer(dialerPackage, null, userHandle);
             return;
         }
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 22f5348..6e848ee 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -39,6 +39,7 @@
     <application android:label="@string/app_name"
                  android:debuggable="true">
         <uses-library android:name="android.test.runner" />
+        <uses-library android:name="ims-ext-common"/>
     </application>
 
     <!--
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 58f1ee7..879ed0f 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -384,8 +384,12 @@
         // rest of the system
         verifyNoSystemAudioChanges();
 
+        // Special case for SPEAKER_ON -- we don't expect any route transitions to happen when
+        // there are no calls, so set the expected state to the initial route.
+        int expectedRoute = (mParams.action == CallAudioRouteStateMachine.SPEAKER_ON)
+                ? mParams.initialRoute : mParams.expectedRoute;
         // Verify the end state
-        CallAudioState expectedState = new CallAudioState(false, mParams.expectedRoute,
+        CallAudioState expectedState = new CallAudioState(false, expectedRoute,
                 mParams.expectedAvailableRoutes | CallAudioState.ROUTE_SPEAKER,
                 mParams.expectedBluetoothDevice, mParams.availableBluetoothDevices);
         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
@@ -811,7 +815,7 @@
                 "Speakerphone turned off externally during speaker", // name
                 CallAudioState.ROUTE_SPEAKER, // initialRoute
                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH, // availableRoutes
-                NONE, // speakerInteraction
+                OFF, // speakerInteraction
                 ON, // bluetoothInteraction
                 CallAudioRouteStateMachine.SPEAKER_OFF, // action
                 CallAudioState.ROUTE_BLUETOOTH, // expectedRoute
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
index 1345c01..c7b3a7e 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -211,6 +212,19 @@
 
     @SmallTest
     @Test
+    public void testUnbindingException() {
+        // Make sure that exceptions when unbinding won't make the device crash.
+        doThrow(new IllegalArgumentException()).when(mContext)
+                .unbindService(nullable(ServiceConnection.class));
+        CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, PKG_NAME,
+                CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, mCallsManager,
+                mAppLabelProxy, mParcelableCallUtilsConverter);
+        filter.startFilterLookup(inputResult);
+        filter.unbindCallScreeningService();
+    }
+
+    @SmallTest
+    @Test
     public void testAllowCall() throws Exception {
         CallScreeningServiceFilter filter = new CallScreeningServiceFilter(mCall, PKG_NAME,
                 CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, mCallsManager,
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 7effc47..af062d7 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -39,6 +39,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -52,6 +53,7 @@
 import android.os.Handler;
 import android.os.IInterface;
 import android.os.PersistableBundle;
+import android.os.PowerWhitelistManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telecom.CallAudioState;
@@ -320,6 +322,12 @@
         }
 
         @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+                Bundle options) {
+            // Override so that this can be verified via spy.
+        }
+
+        @Override
         public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
                 String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
                 int initialCode, String initialData, Bundle initialExtras) {
@@ -434,6 +442,7 @@
             ArrayListMultimap.create();
     private final Map<ComponentName, IInterface> mServiceByComponentName = new HashMap<>();
     private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName = new HashMap<>();
+    private final Map<ComponentName, ActivityInfo> mActivityInfoByComponentName = new HashMap<>();
     private final Map<IInterface, ComponentName> mComponentNameByService = new HashMap<>();
     private final Map<ServiceConnection, IInterface> mServiceByServiceConnection = new HashMap<>();
 
@@ -507,6 +516,24 @@
             }
         }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), anyInt());
 
+        doAnswer(new Answer<List<ResolveInfo>>() {
+            @Override
+            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
+                return doQueryIntentReceivers(
+                        (Intent) invocation.getArguments()[0],
+                        (Integer) invocation.getArguments()[1]);
+            }
+        }).when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
+
+        doAnswer(new Answer<List<ResolveInfo>>() {
+            @Override
+            public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
+                return doQueryIntentReceivers(
+                        (Intent) invocation.getArguments()[0],
+                        (Integer) invocation.getArguments()[1]);
+            }
+        }).when(mPackageManager).queryBroadcastReceiversAsUser((Intent) any(), anyInt(), anyInt());
+
         // By default, tests use non-ui apps instead of 3rd party companion apps.
         when(mPackageManager.checkPermission(
                 matches(Manifest.permission.CALL_COMPANION_APP), anyString()))
@@ -585,6 +612,14 @@
                 eq(componentName.getPackageName()))).thenReturn(PackageManager.PERMISSION_GRANTED);
     }
 
+    public void addIntentReceiver(String action, ComponentName name) {
+        mComponentNamesByAction.put(action, name);
+        ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.packageName = name.getPackageName();
+        activityInfo.name = name.getClassName();
+        mActivityInfoByComponentName.put(name, activityInfo);
+    }
+
     public void putResource(int id, final String value) {
         when(mResources.getText(eq(id))).thenReturn(value);
         when(mResources.getString(eq(id))).thenReturn(value);
@@ -635,4 +670,14 @@
         }
         return result;
     }
+
+    private List<ResolveInfo> doQueryIntentReceivers(Intent intent, int flags) {
+        List<ResolveInfo> result = new ArrayList<>();
+        for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
+            ResolveInfo resolveInfo = new ResolveInfo();
+            resolveInfo.activityInfo = mActivityInfoByComponentName.get(componentName);
+            result.add(resolveInfo);
+        }
+        return result;
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index e16b598..78b3332 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.matches;
 import static org.mockito.ArgumentMatchers.nullable;
@@ -39,6 +40,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.UiModeManager;
@@ -58,8 +60,8 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.telecom.CallAudioState;
 import android.telecom.InCallService;
-import android.telecom.Log;
 import android.telecom.ParcelableCall;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -98,6 +100,7 @@
 
 import java.util.Collections;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 
 @RunWith(JUnit4.class)
@@ -109,6 +112,7 @@
     @Mock PackageManager mMockPackageManager;
     @Mock Call mMockCall;
     @Mock Resources mMockResources;
+    @Mock AppOpsManager mMockAppOpsManager;
     @Mock MockContext mMockContext;
     @Mock Timeouts.Adapter mTimeoutsAdapter;
     @Mock DefaultDialerCache mDefaultDialerCache;
@@ -133,6 +137,10 @@
     private static final String CAR2_CLASS = "carcls";
     private static final int CAR_UID = 4;
     private static final int CAR2_UID = 5;
+    private static final String NONUI_PKG = "nonui_pkg";
+    private static final String NONUI_CLASS = "nonui_cls";
+    private static final int NONUI_UID = 6;
+
     private static final PhoneAccountHandle PA_HANDLE =
             new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"), "pa_id");
 
@@ -148,6 +156,7 @@
         MockitoAnnotations.initMocks(this);
         when(mMockCall.getAnalytics()).thenReturn(new Analytics.CallInfo());
         doReturn(mMockResources).when(mMockContext).getResources();
+        doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
         doReturn(SYS_PKG).when(mMockResources).getString(
                 com.android.internal.R.string.config_defaultDialer);
         doReturn(SYS_CLASS).when(mMockResources).getString(R.string.incall_default_class);
@@ -158,11 +167,11 @@
         mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, mDefaultDialerCache,
                 mTimeoutsAdapter);
         when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter);
+        when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
+                .thenReturn(mNotificationManager);
         mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
                 mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
                 mEmergencyCallHelper, new CarModeTracker(), mClockProxy);
-        when(mMockContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
-                .thenReturn(mNotificationManager);
         // Companion Apps don't have CONTROL_INCALL_EXPERIENCE permission.
         doAnswer(invocation -> {
             int uid = invocation.getArgument(0);
@@ -177,6 +186,8 @@
                     return new String[] { CAR_PKG };
                 case CAR2_UID:
                     return new String[] { CAR2_PKG };
+                case NONUI_UID:
+                    return new String[] { NONUI_PKG };
             }
             return null;
         }).when(mMockPackageManager).getPackagesForUid(anyInt());
@@ -189,6 +200,10 @@
         when(mMockPackageManager.checkPermission(
                 matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
                 matches(CAR2_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mMockPackageManager.checkPermission(
+                matches(Manifest.permission.CONTROL_INCALL_EXPERIENCE),
+                matches(NONUI_PKG))).thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
     }
 
     @Override
@@ -581,6 +596,7 @@
                 any(Intent.class), any(ServiceConnection.class), anyInt(), any(UserHandle.class)))
                 .thenReturn(true);
         when(mMockContext.getApplicationInfo()).thenReturn(applicationInfo);
+        when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID)).thenReturn(DEF_PKG);
 
         setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
         mInCallController.bindToServices(mMockCall);
@@ -607,7 +623,7 @@
 
         verify(mNotificationManager).notify(eq(NOTIFICATION_TAG),
                 eq(IN_CALL_SERVICE_NOTIFICATION_ID), any(Notification.class));
-        verify(mCallInfo).addInCallService(eq(sysDialerComponentName.flattenToShortString()),
+        verify(mCallInfo).addInCallService(eq(defDialerComponentName.flattenToShortString()),
                 anyInt(), anyLong(), eq(true));
 
         ArgumentCaptor<Intent> bindIntentCaptor2 = ArgumentCaptor.forClass(Intent.class);
@@ -617,6 +633,7 @@
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS),
                 eq(UserHandle.CURRENT));
+        assertEquals(sysDialerComponentName, bindIntentCaptor2.getValue().getComponent());
     }
 
     /**
@@ -884,13 +901,15 @@
                 nullable(ContentResolver.class))).thenReturn(500L);
 
         when(mMockCallsManager.getCalls()).thenReturn(Collections.singletonList(mMockCall));
-        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        setupMockPackageManager(true /* default */, true /* nonui */, true /* system */,
+                false /* external calls */,
+                false /* self mgd in default*/, false /* self mgd in car*/);
         mInCallController.bindToServices(mMockCall);
 
         ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
                 ArgumentCaptor.forClass(ServiceConnection.class);
-        verify(mMockContext, times(1)).bindServiceAsUser(
+        verify(mMockContext, times(2)).bindServiceAsUser(
                 bindIntentCaptor.capture(),
                 serviceConnectionCaptor.capture(),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
@@ -903,13 +922,39 @@
 
         // Start the connection, make sure we don't unbind, and make sure that we don't send
         // anything to the in-call service yet.
-        ServiceConnection serviceConnection = serviceConnectionCaptor.getValue();
+        List<ServiceConnection> serviceConnections = serviceConnectionCaptor.getAllValues();
+        List<Intent> intents = bindIntentCaptor.getAllValues();
+
+        // Find the non-ui service and have it connect first.
+        int nonUiIdx = findFirstIndexMatching(intents,
+                i -> NONUI_PKG.equals(i.getComponent().getPackageName()));
+        if (nonUiIdx < 0) {
+            fail("Did not bind to non-ui incall");
+        }
+
+        {
+            ComponentName nonUiComponentName = new ComponentName(NONUI_PKG, NONUI_CLASS);
+            IBinder mockBinder = mock(IBinder.class);
+            IInCallService mockInCallService = mock(IInCallService.class);
+            when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
+            serviceConnections.get(nonUiIdx).onServiceConnected(nonUiComponentName, mockBinder);
+
+            // Make sure the non-ui binding didn't trigger the future.
+            assertFalse(bindTimeout.isDone());
+        }
+
+        int defDialerIdx = findFirstIndexMatching(intents,
+                i -> DEF_PKG.equals(i.getComponent().getPackageName()));
+        if (defDialerIdx < 0) {
+            fail("Did not bind to default dialer incall");
+        }
+
         ComponentName defDialerComponentName = new ComponentName(DEF_PKG, DEF_CLASS);
         IBinder mockBinder = mock(IBinder.class);
         IInCallService mockInCallService = mock(IInCallService.class);
         when(mockBinder.queryLocalInterface(anyString())).thenReturn(mockInCallService);
 
-        serviceConnection.onServiceConnected(defDialerComponentName, mockBinder);
+        serviceConnections.get(defDialerIdx).onServiceConnected(defDialerComponentName, mockBinder);
         verify(mockInCallService).setInCallAdapter(nullable(IInCallAdapter.class));
 
         // Make sure that the future completed without timing out.
@@ -1101,9 +1146,21 @@
         }};
     }
 
+    private ResolveInfo getNonUiResolveinfo() {
+        return new ResolveInfo() {{
+            serviceInfo = new ServiceInfo();
+            serviceInfo.packageName = NONUI_PKG;
+            serviceInfo.name = NONUI_CLASS;
+            serviceInfo.applicationInfo = new ApplicationInfo();
+            serviceInfo.applicationInfo.uid = NONUI_UID;
+            serviceInfo.enabled = true;
+            serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
+        }};
+    }
+
     private void setupMockPackageManager(final boolean useDefaultDialer,
             final boolean useSystemDialer, final boolean includeExternalCalls) {
-        setupMockPackageManager(useDefaultDialer, useSystemDialer, includeExternalCalls,
+        setupMockPackageManager(useDefaultDialer, false, useSystemDialer, includeExternalCalls,
                 false /* self mgd */, false /* self mgd */);
     }
 
@@ -1111,6 +1168,16 @@
             final boolean useSystemDialer, final boolean includeExternalCalls,
             final boolean includeSelfManagedCallsInDefaultDialer,
             final boolean includeSelfManagedCallsInCarModeDialer) {
+        setupMockPackageManager(useDefaultDialer, false /* nonui */, useSystemDialer,
+                includeExternalCalls, includeSelfManagedCallsInDefaultDialer,
+                includeSelfManagedCallsInCarModeDialer);
+    }
+
+    private void setupMockPackageManager(final boolean useDefaultDialer,
+            final boolean useNonUiInCalls,
+            final boolean useSystemDialer, final boolean includeExternalCalls,
+            final boolean includeSelfManagedCallsInDefaultDialer,
+            final boolean includeSelfManagedCallsInCarModeDialer) {
         doAnswer(new Answer() {
             @Override
             public Object answer(InvocationOnMock invocation) throws Throwable {
@@ -1145,7 +1212,13 @@
                         resolveInfo.add(getCarModeResolveinfo(CAR2_PKG, CAR2_CLASS,
                                 includeExternalCalls, includeSelfManagedCallsInCarModeDialer));
                     }
+                } else {
+                    // InCallController uses a blank package name when querying for non-ui incalls
+                    if (useNonUiInCalls) {
+                        resolveInfo.add(getNonUiResolveinfo());
+                    }
                 }
+
                 return resolveInfo;
             }
         }).when(mMockPackageManager).queryIntentServicesAsUser(
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
index 10a0194..9dd90f5 100644
--- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -16,6 +16,10 @@
 
 package com.android.server.telecom.tests;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.BroadcastOptions;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -43,6 +47,7 @@
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.Constants;
 import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.DeviceIdleControllerAdapter;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelecomBroadcastIntentProcessor;
@@ -67,6 +72,8 @@
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
@@ -143,6 +150,9 @@
     private static final UserHandle SECONARY_USER = UserHandle.of(12);
     private static final int NO_CAPABILITY = 0;
     private static final int TEST_TIMEOUT = 1000;
+    private static final long TEST_POWER_EXEMPT_TIME_MS = 1000;
+    private static final ComponentName COMPONENT_NAME = new ComponentName(
+            "com.anything", "com.whatever");
 
     @Mock
     private NotificationManager mNotificationManager;
@@ -155,6 +165,7 @@
 
     @Mock TelecomSystem mTelecomSystem;
     @Mock private DefaultDialerCache mDefaultDialerCache;
+    @Mock private DeviceIdleControllerAdapter mDeviceIdleControllerAdapter;
 
     @Override
     @Before
@@ -246,7 +257,8 @@
                 makeNotificationBuilderFactory(builders);
 
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory);
+                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory,
+                mDeviceIdleControllerAdapter);
 
         missedCallNotifier.showMissedCallNotification(fakeCall);
         missedCallNotifier.showMissedCallNotification(fakeCall);
@@ -401,7 +413,8 @@
                 makeNotificationBuilderFactory(builder1);
 
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory);
+                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory,
+                mDeviceIdleControllerAdapter);
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
 
         MissedCallNotifier.CallInfo fakeCall =
@@ -461,7 +474,8 @@
                 makeNotificationBuilderFactory(builder1);
 
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory);
+                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory,
+                mDeviceIdleControllerAdapter);
 
         // AsyncQueryHandler used in reloadFromDatabase interacts poorly with the below
         // timeout-verify, so run this in a new handler to mitigate that.
@@ -530,7 +544,8 @@
                 makeNotificationBuilderFactory(builder1);
 
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory);
+                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory,
+                mDeviceIdleControllerAdapter);
 
         // AsyncQueryHandler used in reloadFromDatabase interacts poorly with the below
         // timeout-verify, so run this in a new handler to mitigate that.
@@ -560,6 +575,49 @@
                 nullable(Notification.class), eq(PRIMARY_USER));
     }
 
+    @SmallTest
+    @Test
+    public void testDialerHandleMissedCall() {
+        // Configure Notifier to send missed call intent and let dialer handle
+        enableDialerHandlesMissedCall();
+
+        Notification.Builder builder1 = makeNotificationBuilder("builder1");
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builder1);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory,
+                mDeviceIdleControllerAdapter);
+        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
+
+        MissedCallNotifier.CallInfo fakeCall =
+                makeFakeCallInfo(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
+                        phoneAccount.getAccountHandle());
+        missedCallNotifier.showMissedCallNotification(fakeCall);
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        ArgumentCaptor<Bundle> bundleCaptor =
+                ArgumentCaptor.forClass(Bundle.class);
+        verify(mDeviceIdleControllerAdapter).exemptAppTemporarilyForEvent(
+                eq(COMPONENT_NAME.getPackageName()), anyLong(), anyInt(), any());
+        verify(mContext).sendBroadcastAsUser(
+                intentCaptor.capture(),
+                any(),
+                eq(android.Manifest.permission.READ_PHONE_STATE), bundleCaptor.capture());
+        assertNotNull("Not expecting null intent", intentCaptor.getValue());
+        assertEquals("Incorrect intent received",
+                TelecomManager.ACTION_SHOW_MISSED_CALLS_NOTIFICATION,
+                intentCaptor.getValue().getAction());
+        assertNotNull("Not expecting null options bundle", bundleCaptor.getValue());
+        BroadcastOptions options = new BroadcastOptions(bundleCaptor.getValue());
+        assertTrue("App must have a temporary exemption set.",
+                options.getTemporaryAppWhitelistDuration() > 0);
+
+        // A notification should never be posted by Telecom
+        verify(mNotificationManager, never()).notifyAsUser(nullable(String.class), anyInt(),
+                nullable(Notification.class), eq(PRIMARY_USER));
+    }
+
     private Notification.Builder makeNotificationBuilder(String label) {
         Notification.Builder builder = spy(new Notification.Builder(mContext));
         Notification notification = mock(Notification.class);
@@ -592,14 +650,14 @@
     private MissedCallNotifier makeMissedCallNotifier(
             NotificationBuilderFactory fakeBuilderFactory, UserHandle currentUser) {
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory);
+                mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory,
+                mDeviceIdleControllerAdapter);
         missedCallNotifier.setCurrentUserHandle(currentUser);
         return missedCallNotifier;
     }
 
     private PhoneAccount makePhoneAccount(UserHandle userHandle, int capability) {
-        ComponentName componentName = new ComponentName("com.anything", "com.whatever");
-        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(componentName, "id",
+        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(COMPONENT_NAME, "id",
                 userHandle);
         PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, "test");
         builder.setCapabilities(capability);
@@ -609,6 +667,13 @@
         return phoneAccount;
     }
 
+    private void enableDialerHandlesMissedCall() {
+        doReturn(COMPONENT_NAME.getPackageName()).when(mDefaultDialerCache).
+                getDefaultDialerApplication(anyInt());
+        mComponentContextFixture.addIntentReceiver(
+                TelecomManager.ACTION_SHOW_MISSED_CALLS_NOTIFICATION, COMPONENT_NAME);
+    }
+
     private IContentProvider getContentProviderForUser(int userId) {
         return mContext.getContentResolver().acquireProvider(userId + "@call_log");
     }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 442c310..68b10c6 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -78,6 +79,7 @@
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ContactsAsyncHelper;
+import com.android.server.telecom.DeviceIdleControllerAdapter;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
 import com.android.server.telecom.InCallWakeLockController;
@@ -207,6 +209,7 @@
     @Mock ClockProxy mClockProxy;
     @Mock RoleManagerAdapter mRoleManagerAdapter;
     @Mock ToneGenerator mToneGenerator;
+    @Mock DeviceIdleControllerAdapter mDeviceIdleControllerAdapter;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -348,6 +351,8 @@
         doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
         doNothing().when(mSpyContext).sendBroadcastAsUser(any(), any(), any());
 
+        doReturn(mock(AppOpsManager.class)).when(mSpyContext).getSystemService(AppOpsManager.class);
+
         mHandlerThread = new HandlerThread("TelecomHandlerThread");
         mHandlerThread.start();
 
@@ -475,7 +480,8 @@
         when(mRoleManagerAdapter.getDefaultCallScreeningApp()).thenReturn(null);
         mTelecomSystem = new TelecomSystem(
                 mComponentContextFixture.getTestDouble(),
-                (context, phoneAccountRegistrar, defaultDialerCache) -> mMissedCallNotifier,
+                (context, phoneAccountRegistrar, defaultDialerCache, mDeviceIdleControllerAdapter)
+                        -> mMissedCallNotifier,
                 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
                 headsetMediaButtonFactory,
                 proximitySensorManagerFactory,
@@ -535,7 +541,7 @@
                             ContactsAsyncHelper.ContentResolverAdapter adapter) {
                         return new ContactsAsyncHelper(adapter, mHandlerThread.getLooper());
                     }
-                });
+                }, mDeviceIdleControllerAdapter);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index b0b1ec0..264e087 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -25,8 +25,10 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 
 public abstract class TelecomTestCase {
     protected static final String TESTING_TAG = "Telecom-TEST";
@@ -75,4 +77,13 @@
             }
         }
     }
+
+    protected static <T> int findFirstIndexMatching(List<T> items, Predicate<T> matcher) {
+        for (int i = 0; i < items.size(); i++) {
+            if (matcher.test(items.get(i))) {
+                return i;
+            }
+        }
+        return -1;
+    }
 }