Merge "Revert "Revert "Ensure MediaSession is ONLY made active when routed to wired headset.""" into udc-dev
diff --git a/Android.bp b/Android.bp
index 1b422aa..c5141ca 100644
--- a/Android.bp
+++ b/Android.bp
@@ -28,6 +28,9 @@
     static_libs: [
         "androidx.annotation_annotation",
     ],
+    libs: [
+        "services",
+    ],
     resource_dirs: ["res"],
     proto: {
         type: "nano",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 648b4a9..d42dcff 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -64,6 +64,7 @@
     <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
     <uses-permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"/>
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
 
     <permission android:name="android.permission.BROADCAST_CALLLOG_INFO"
          android:label="Broadcast the call type/duration information"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9874044..489fab7 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,3 @@
-[Hook Scripts]
-aosp_hook = ${REPO_ROOT}/packages/services/Telecomm/scripts/aosp_tag_preupload.py ${PREUPLOAD_COMMIT}
+# Uncomment to re-enable aosp warning.
+#[Hook Scripts]
+#aosp_hook = ${REPO_ROOT}/packages/services/Telecomm/scripts/aosp_tag_preupload.py ${PREUPLOAD_COMMIT}
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index fc36464..79f29bf 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -90,7 +90,7 @@
     <string name="answering_ends_other_managed_video_call" msgid="1988508241432031327">"መመለስ እየተካሄደ ያለ የቪዲዮ ጥሪዎን ይጨርሳል"</string>
     <string name="answer_incoming_call" msgid="2045888814782215326">"ይመልሱ"</string>
     <string name="decline_incoming_call" msgid="922147089348451310">"አትቀበል"</string>
-    <string name="cant_call_due_to_no_supported_service" msgid="1635626384149947077">"የዚህን አይነት ጥሪዎች የሚደግፉ መደወያ መለያዎች ስለሌሉ ጥሪ መደረግ አይችልም።"</string>
+    <string name="cant_call_due_to_no_supported_service" msgid="1635626384149947077">"የዚህን ዓይነት ጥሪዎች የሚደግፉ መደወያ መለያዎች ስለሌሉ ጥሪ መደረግ አይችልም።"</string>
     <string name="cant_call_due_to_ongoing_call" msgid="8004235328451385493">"በ<xliff:g id="OTHER_CALL">%1$s</xliff:g> ጥሪዎ ምክንያት ጥሪ መደረግ አይችልም።"</string>
     <string name="cant_call_due_to_ongoing_calls" msgid="6379163795277824868">"በ<xliff:g id="OTHER_CALL">%1$s</xliff:g> ጥሪዎችዎ ምክንያት ጥሪዎች መደረግ አይችሉም።"</string>
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="8243532328969433172">"በሌላ መተግበሪያ ውስጥ ባለ ጥሪ ምክንያት ጥሪ መደረግ አይችልም።"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b6db5c4..0b279b4 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -123,7 +123,7 @@
     <string name="developer_title" msgid="9146088855661672353">"Menu do programador de telecomunicações"</string>
     <string name="toast_emergency_can_not_pull_call" msgid="9074229465338410869">"Não é possível atender chamadas durante uma chamada de emergência."</string>
     <string name="cancel" msgid="6733466216239934756">"Cancelar"</string>
-    <string name="back" msgid="6915955601805550206">"Voltar atrás"</string>
+    <string name="back" msgid="6915955601805550206">"Anterior"</string>
     <string name="callendpoint_name_earpiece" msgid="7047285080319678594">"Auricular"</string>
     <string name="callendpoint_name_bluetooth" msgid="210210953208913172">"Bluetooth"</string>
     <string name="callendpoint_name_wiredheadset" msgid="6860787176412079742">"Auscultadores com microfone integrado com fios"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 98115db..5ed2ebe 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -51,7 +51,7 @@
     <string name="outgoing_call_error_no_phone_number_supplied" msgid="7665135102566099778">"కాల్ చేయడానికి, చెల్లుబాటు అయ్యే నంబర్‌ను నమోదు చేయండి."</string>
     <string name="duplicate_video_call_not_allowed" msgid="5754746140185781159">"ఈ సమయంలో కాల్‌ను జోడించడం సాధ్యపడదు."</string>
     <string name="no_vm_number" msgid="2179959110602180844">"వాయిస్ మెయిల్ నంబర్ లేదు"</string>
-    <string name="no_vm_number_msg" msgid="1339245731058529388">"సిమ్ కార్డులో వాయిస్ మెయిల్ నంబర్ ఏదీ నిల్వ చేయబడలేదు."</string>
+    <string name="no_vm_number_msg" msgid="1339245731058529388">"సిమ్ కార్డులో వాయిస్ మెయిల్ నంబర్ ఏదీ స్టోరేజ్‌ చేయబడలేదు."</string>
     <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>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 7033f36..689828e 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/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="8811420157964118913">"<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>
@@ -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>
@@ -118,10 +118,10 @@
     <string name="phone_settings_unavailable_summary_txt" msgid="8221686031038282633">"کالز کو مسدود کریں جہاں یہ نمبر دستیاب نہ ہو"</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="developer_title" msgid="9146088855661672353">"ٹیلی کام ڈویلپر مینو"</string>
-    <string name="toast_emergency_can_not_pull_call" msgid="9074229465338410869">"ہنگامی کال کے دوران کالز نہیں لی جائیں گی۔"</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="cancel" msgid="6733466216239934756">"منسوخ کریں"</string>
     <string name="back" msgid="6915955601805550206">"پیچھے"</string>
     <string name="callendpoint_name_earpiece" msgid="7047285080319678594">"ایئر پیس"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index e7b1a07..b422c73 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -28,11 +28,11 @@
     <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_generic_body" msgid="5282765206349184853">"因撥打緊急電話緣故,你的通話已中斷。"</string>
     <string name="notification_audioProcessing_title" msgid="1619035039880584575">"背景通話"</string>
     <string name="notification_audioProcessing_body" msgid="8811420157964118913">"「<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="notification_incallservice_not_responding_body" msgid="9209308270131968623">"你使用了裝置隨付的手機應用程式來通話"</string>
     <string name="accessibility_call_muted" msgid="2968461092554300779">"通話已靜音。"</string>
     <string name="accessibility_speakerphone_enabled" msgid="555386652061614267">"擴音器已啟用"</string>
     <string name="respond_via_sms_canned_response_1" msgid="6332561460870382561">"我現在不方便通話,有什麼事呢?"</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,7 +73,7 @@
     <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>
@@ -82,17 +82,17 @@
     <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>
     <string name="notification_incoming_video_call" msgid="5795968314037063900">"來自<xliff:g id="CALL_FROM">%2$s</xliff:g>的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 視像通話"</string>
-    <string name="answering_ends_other_call" msgid="8653544281903986641">"如果接聽,您的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 通話將會結束"</string>
-    <string name="answering_ends_other_calls" msgid="3702302838456922535">"如果接聽,您的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 通話將會結束"</string>
-    <string name="answering_ends_other_video_call" msgid="8572022039304239958">"如果接聽,您的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 視像通話將會結束"</string>
-    <string name="answering_ends_other_managed_call" msgid="4031778317409881805">"如果接聽,您進行中的通話將會結束"</string>
-    <string name="answering_ends_other_managed_calls" msgid="3974069768615307659">"如果接聽,您進行中的通話將會結束"</string>
-    <string name="answering_ends_other_managed_video_call" msgid="1988508241432031327">"如果接聽,您進行中的視像通話將會結束"</string>
+    <string name="answering_ends_other_call" msgid="8653544281903986641">"如果接聽,你的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 通話將會結束"</string>
+    <string name="answering_ends_other_calls" msgid="3702302838456922535">"如果接聽,你的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 通話將會結束"</string>
+    <string name="answering_ends_other_video_call" msgid="8572022039304239958">"如果接聽,你的 <xliff:g id="CALL_VIA">%1$s</xliff:g> 視像通話將會結束"</string>
+    <string name="answering_ends_other_managed_call" msgid="4031778317409881805">"如果接聽,你進行中的通話將會結束"</string>
+    <string name="answering_ends_other_managed_calls" msgid="3974069768615307659">"如果接聽,你進行中的通話將會結束"</string>
+    <string name="answering_ends_other_managed_video_call" msgid="1988508241432031327">"如果接聽,你進行中的視像通話將會結束"</string>
     <string name="answer_incoming_call" msgid="2045888814782215326">"接聽"</string>
     <string name="decline_incoming_call" msgid="922147089348451310">"拒絕"</string>
     <string name="cant_call_due_to_no_supported_service" msgid="1635626384149947077">"沒有通話帳戶支援這類通話,因此無法撥打電話。"</string>
-    <string name="cant_call_due_to_ongoing_call" msgid="8004235328451385493">"由於您已在進行 <xliff:g id="OTHER_CALL">%1$s</xliff:g> 通話,因此無法撥打電話。"</string>
-    <string name="cant_call_due_to_ongoing_calls" msgid="6379163795277824868">"由於您已在進行 <xliff:g id="OTHER_CALL">%1$s</xliff:g> 通話,因此無法撥打電話。"</string>
+    <string name="cant_call_due_to_ongoing_call" msgid="8004235328451385493">"由於你已在進行 <xliff:g id="OTHER_CALL">%1$s</xliff:g> 通話,因此無法撥打電話。"</string>
+    <string name="cant_call_due_to_ongoing_calls" msgid="6379163795277824868">"由於你已在進行 <xliff:g id="OTHER_CALL">%1$s</xliff:g> 通話,因此無法撥打電話。"</string>
     <string name="cant_call_due_to_ongoing_unknown_call" msgid="8243532328969433172">"由於已在另一個應用程式中進行通話,因此無法撥打電話。"</string>
     <string name="notification_channel_incoming_call" msgid="5245550964701715662">"來電"</string>
     <string name="notification_channel_missed_call" msgid="7168893015283909012">"未接來電"</string>
@@ -100,11 +100,11 @@
     <string name="notification_channel_background_calls" msgid="7785659903711350506">"背景通話"</string>
     <string name="notification_channel_disconnected_calls" msgid="8228636543997645757">"已中斷的通話"</string>
     <string name="notification_channel_in_call_service_crash" msgid="7313237519166984267">"當機的手機應用程式"</string>
-    <string name="alert_outgoing_call" msgid="5319895109298927431">"如果撥打此電話,您的 <xliff:g id="OTHER_APP">%1$s</xliff:g> 通話將會結束。"</string>
+    <string name="alert_outgoing_call" msgid="5319895109298927431">"如果撥打此電話,你的 <xliff:g id="OTHER_APP">%1$s</xliff:g> 通話將會結束。"</string>
     <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>
@@ -119,7 +119,7 @@
     <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_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="cancel" msgid="6733466216239934756">"取消"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index b0e50b0..15f765b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -73,4 +73,8 @@
     <!-- When set, Telecom will attempt to bind to the {@link CallDiagnosticService} implementation
          defined by the app with this package name. -->
     <string name="call_diagnostic_service_package_name"></string>
+
+    <!-- When true, the options in the call blocking settings to block unavailable and unknown
+     callers are combined into a single toggle. -->
+    <bool name="combine_options_to_block_unavailable_and_unknown_callers">true</bool>
 </resources>
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 24a65b7..3fbac1f 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -18,9 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.media.AudioAttributes;
 import android.media.Ringtone;
-import android.media.RingtoneManager;
 import android.media.VolumeShaper;
 import android.net.Uri;
 import android.os.Handler;
@@ -32,6 +30,9 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
 /**
  * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
  * used from the main thread.
@@ -57,13 +58,16 @@
      * If {@link VolumeShaper.Configuration} is specified, it is applied to the ringtone to change
      * the volume of the ringtone as it plays.
      *
-     * @param ringtone The {@link Ringtone}.
+     * @param ringtoneSupplier The {@link Ringtone} factory.
+     * @param ringtoneConsumer The {@link Ringtone} post-creation callback (to start the vibration).
      */
-    public void play(@NonNull Ringtone ringtone) {
+    public void play(@NonNull Supplier<Ringtone> ringtoneSupplier,
+            BiConsumer<Ringtone, Boolean> ringtoneConsumer) {
         Log.d(this, "Posting play.");
         SomeArgs args = SomeArgs.obtain();
-        args.arg1 = ringtone;
-        args.arg2 = Log.createSubsession();
+        args.arg1 = ringtoneSupplier;
+        args.arg2 = ringtoneConsumer;
+        args.arg3 = Log.createSubsession();
         postMessage(EVENT_PLAY, true /* shouldCreateHandler */, args);
     }
 
@@ -122,30 +126,50 @@
      * Starts the actual playback of the ringtone. Executes on ringtone-thread.
      */
     private void handlePlay(SomeArgs args) {
-        Ringtone ringtone = (Ringtone) args.arg1;
-        Session session = (Session) args.arg2;
+        Supplier<Ringtone> ringtoneSupplier = (Supplier<Ringtone>) args.arg1;
+        BiConsumer<Ringtone, Boolean> ringtoneConsumer = (BiConsumer<Ringtone, Boolean>) args.arg2;
+        Session session = (Session) args.arg3;
         args.recycle();
 
         Log.continueSession(session, "ARP.hP");
         try {
-            // don't bother with any of this if there is an EVENT_STOP waiting.
+            // Don't bother with any of this if there is an EVENT_STOP waiting, but give the
+            // consumer a chance to do anything no matter what.
             if (mHandler.hasMessages(EVENT_STOP)) {
+                ringtoneConsumer.accept(null, /* stopped= */ true);
                 return;
             }
-
-            ThreadUtil.checkNotOnMainThread();
-
-            setRingtone(ringtone);
-            Uri uri = mRingtone.getUri();
-            String uriString = (uri != null ? uri.toSafeString() : "");
-            Log.i(this, "handlePlay: Play ringtone. Uri: " + uriString);
-            mRingtone.setLooping(true);
-            if (mRingtone.isPlaying()) {
-                Log.d(this, "Ringtone already playing.");
-                return;
+            Ringtone ringtone = null;
+            boolean hasStopped = false;
+            try {
+                ringtone = ringtoneSupplier.get();
+                // Ringtone supply can be slow. Re-check for stop event.
+                if (mHandler.hasMessages(EVENT_STOP)) {
+                    hasStopped = true;
+                    ringtone.stop();  // proactively release the ringtone.
+                    return;
+                }
+                // setRingtone even if null - it also stops any current ringtone to be consistent
+                // with the overall state.
+                setRingtone(ringtone);
+                if (mRingtone == null) {
+                    // The ringtoneConsumer can still vibrate at this stage.
+                    Log.w(this, "No ringtone was found bail out from playing.");
+                    return;
+                }
+                Uri uri = mRingtone.getUri();
+                String uriString = (uri != null ? uri.toSafeString() : "");
+                Log.i(this, "handlePlay: Play ringtone. Uri: " + uriString);
+                mRingtone.setLooping(true);
+                if (mRingtone.isPlaying()) {
+                    Log.d(this, "Ringtone already playing.");
+                    return;
+                }
+                mRingtone.play();
+                Log.i(this, "Play ringtone, looping.");
+            } finally {
+                ringtoneConsumer.accept(ringtone, hasStopped);
             }
-            mRingtone.play();
-            Log.i(this, "Play ringtone, looping.");
         } finally {
             Log.cancelSubsession(session);
         }
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index 02dff13..e2d8489 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom;
 
 import static android.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED;
+import static android.telecom.Call.EVENT_DISPLAY_SOS_MESSAGE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,6 +39,7 @@
 import android.provider.CallLog;
 import android.provider.ContactsContract.Contacts;
 import android.telecom.BluetoothCallQualityReport;
+import android.telecom.CallAttributes;
 import android.telecom.CallAudioState;
 import android.telecom.CallDiagnosticService;
 import android.telecom.CallDiagnostics;
@@ -316,6 +318,12 @@
      */
     private long mCreationTimeMillis;
 
+    /**
+     * The elapsed realtime millis when this call was created; this can be used to determine how
+     * long has elapsed since the call was first created.
+     */
+    private long mCreationElapsedRealtimeMillis;
+
     /** The time this call was made active. */
     private long mConnectTimeMillis = 0;
 
@@ -555,6 +563,25 @@
     private boolean mIsSelfManaged = false;
 
     private boolean mIsTransactionalCall = false;
+    private CallingPackageIdentity mCallingPackageIdentity = new CallingPackageIdentity();
+
+    /**
+     * CallingPackageIdentity is responsible for storing properties about the calling package that
+     * initiated the call. For example, if MyVoipApp requests to add a call with Telecom, we can
+     * store their UID and PID when we are still bound to that package.
+     */
+    public static class CallingPackageIdentity {
+        public int mCallingPackageUid = -1;
+        public int mCallingPackagePid = -1;
+
+        public CallingPackageIdentity() {
+        }
+
+        CallingPackageIdentity(Bundle extras) {
+            mCallingPackageUid = extras.getInt(CallAttributes.CALLER_UID_KEY, -1);
+            mCallingPackagePid = extras.getInt(CallAttributes.CALLER_PID_KEY, -1);
+        }
+    }
 
     /**
      * Indicates whether this call is streaming.
@@ -801,6 +828,7 @@
         mClockProxy = clockProxy;
         mToastFactory = toastFactory;
         mCreationTimeMillis = mClockProxy.currentTimeMillis();
+        mCreationElapsedRealtimeMillis = mClockProxy.elapsedRealtime();
         mMissedReason = MISSED_REASON_NOT_MISSED;
         mStartRingTime = 0;
 
@@ -1830,6 +1858,17 @@
         setConnectionProperties(getConnectionProperties());
     }
 
+    public void setCallingPackageIdentity(Bundle extras) {
+        mCallingPackageIdentity = new CallingPackageIdentity(extras);
+        // These extras should NOT be propagated to Dialer and should be removed.
+        extras.remove(CallAttributes.CALLER_PID_KEY);
+        extras.remove(CallAttributes.CALLER_UID_KEY);
+    }
+
+    public CallingPackageIdentity getCallingPackageIdentity() {
+        return mCallingPackageIdentity;
+    }
+
     public void setTransactionServiceWrapper(TransactionalServiceWrapper service) {
         mTransactionalService = service;
     }
@@ -2019,8 +2058,12 @@
         return mCreationTimeMillis;
     }
 
-    public void setCreationTimeMillis(long time) {
-        mCreationTimeMillis = time;
+    /**
+     * @return The elapsed realtime millis when the call was created; ONLY useful for determining
+     * how long has elapsed since the call was first created.
+     */
+    public long getCreationElapsedRealtimeMillis() {
+        return mCreationElapsedRealtimeMillis;
     }
 
     public long getConnectTimeMillis() {
@@ -2397,6 +2440,8 @@
 
     @Override
     public void handleCreateConferenceFailure(DisconnectCause disconnectCause) {
+        Log.i(this, "handleCreateConferenceFailure; callid=%s, disconnectCause=%s",
+                getId(), disconnectCause);
         clearConnectionService();
         setDisconnectCause(disconnectCause);
         mCallsManager.markCallAsDisconnected(this, disconnectCause);
@@ -2417,6 +2462,8 @@
 
     @Override
     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
+        Log.i(this, "handleCreateConnectionFailure; callid=%s, disconnectCause=%s",
+                getId(), disconnectCause);
         clearConnectionService();
         setDisconnectCause(disconnectCause);
         mCallsManager.markCallAsDisconnected(this, disconnectCause);
@@ -3655,6 +3702,7 @@
         if (mTransactionalService != null) {
             Log.i(this, "stopRtt: called on TransactionalService. doing nothing");
         } else if (mConnectionService != null) {
+            Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "stop");
             mConnectionService.stopRtt(this);
         } else {
             // If this gets called by the in-call app before the connection service is set, we'll
@@ -3668,6 +3716,7 @@
             Log.i(this, "sendRttRequest: called on TransactionalService. doing nothing");
             return;
         }
+        Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "start");
         createRttStreams();
         mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
     }
@@ -3691,12 +3740,14 @@
 
     public void onRttConnectionFailure(int reason) {
         Log.i(this, "Got RTT initiation failure with reason %d", reason);
+        Log.addEvent(this, LogUtils.Events.ON_RTT_FAILED, "reason="  + reason);
         for (Listener l : mListeners) {
             l.onRttInitiationFailure(this, reason);
         }
     }
 
     public void onRemoteRttRequest() {
+        Log.addEvent(this, LogUtils.Events.ON_RTT_REQUEST);
         if (isRttCall()) {
             Log.w(this, "Remote RTT request on a call that's already RTT");
             return;
@@ -3721,6 +3772,8 @@
             Log.i(this, "handleRttRequestResponse: called on TransactionalService. doing nothing");
             return;
         }
+        Log.addEvent(this, LogUtils.Events.RESPOND_TO_RTT_REQUEST, "id=" + id + ", accept="
+                + accept);
         if (accept) {
             createRttStreams();
             Log.i(this, "RTT request %d accepted.", id);
@@ -3903,6 +3956,10 @@
         return mCallDirection == CALL_DIRECTION_UNKNOWN;
     }
 
+    public boolean isOutgoing() {
+        return mCallDirection == CALL_DIRECTION_OUTGOING;
+    }
+
     /**
      * Determines if this call is in a disconnecting state.
      *
@@ -3997,7 +4054,8 @@
 
     public void setRttMode(int mode) {
         mRttMode = mode;
-        // TODO: hook this up to CallAudioManager
+        Log.addEvent(this, LogUtils.Events.SET_RRT_MODE, "mode=" + mode);
+        // TODO: hook this up to CallAudioManager.
     }
 
     /**
@@ -4072,6 +4130,12 @@
                 l.onReceivedCallQualityReport(this, callQuality);
             }
         } else {
+            if (event.equals(EVENT_DISPLAY_SOS_MESSAGE) && !isEmergencyCall()) {
+                Log.w(this, "onConnectionEvent: EVENT_DISPLAY_SOS_MESSAGE is sent "
+                        + "without an emergency call");
+                return;
+            }
+
             for (Listener l : mListeners) {
                 l.onConnectionEvent(this, event, extras);
             }
diff --git a/src/com/android/server/telecom/CallAnomalyWatchdog.java b/src/com/android/server/telecom/CallAnomalyWatchdog.java
index d3dbbc9..045671e 100644
--- a/src/com/android/server/telecom/CallAnomalyWatchdog.java
+++ b/src/com/android/server/telecom/CallAnomalyWatchdog.java
@@ -43,6 +43,8 @@
  * Watchdog class responsible for detecting potential anomalous conditions for {@link Call}s.
  */
 public class CallAnomalyWatchdog extends CallsManagerListenerBase implements Call.Listener {
+    private final EmergencyCallDiagnosticLogger mEmergencyCallDiagnosticLogger;
+
     /**
      * Class used to track the call state as it pertains to the watchdog. The watchdog cares about
      * both the call state and whether a {@link ConnectionService} has finished creating the
@@ -146,15 +148,21 @@
 
     public CallAnomalyWatchdog(ScheduledExecutorService executorService,
             TelecomSystem.SyncRoot lock,
-            Timeouts.Adapter timeoutAdapter, ClockProxy clockProxy) {
+            Timeouts.Adapter timeoutAdapter, ClockProxy clockProxy,
+            EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger) {
         mScheduledExecutorService = executorService;
         mLock = lock;
         mTimeoutAdapter = timeoutAdapter;
         mClockProxy = clockProxy;
+        mEmergencyCallDiagnosticLogger = emergencyCallDiagnosticLogger;
     }
 
+    /**
+     * Start tracking a call that we're waiting for a ConnectionService to create.
+     * @param call the call.
+     */
     @Override
-    public void onCallCreated(Call call) {
+    public void onStartCreateConnection(Call call) {
         maybeTrackCall(call);
         call.addListener(this);
     }
@@ -165,23 +173,24 @@
     }
 
     /**
+     * Override of {@link CallsManagerListenerBase} to track when calls have failed to be created by
+     * a ConnectionService.  These calls should no longer be tracked by the CallAnomalyWatchdog.
+     * @param call the call
+     */
+    @Override
+    public void onCreateConnectionFailed(Call call) {
+        Log.i(this, "onCreateConnectionFailed: call=%s", call.toString());
+        stopTrackingCall(call);
+    }
+
+    /**
      * Override of {@link CallsManagerListenerBase} to track when calls are removed
      * @param call the call
      */
     @Override
     public void onCallRemoved(Call call) {
-        if (mScheduledFutureMap.containsKey(call)) {
-            ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
-            existingTimeout.cancel(false /* cancelIfRunning */);
-            mScheduledFutureMap.remove(call);
-        }
-        if (mCallsPendingDestruction.contains(call)) {
-            mCallsPendingDestruction.remove(call);
-        }
-        if (mWatchdogCallStateMap.containsKey(call)) {
-            mWatchdogCallStateMap.remove(call);
-        }
-        call.removeListener(this);
+        Log.i(this, "onCallRemoved: call=%s", call.toString());
+        stopTrackingCall(call);
     }
 
     /**
@@ -192,7 +201,10 @@
      * @param newState the new state
      */
     @Override
-    public void onCallStateChanged(Call call, int oldState, int newState) { maybeTrackCall(call); }
+    public void onCallStateChanged(Call call, int oldState, int newState) {
+        Log.i(this, "onCallStateChanged: call=%s", call.toString());
+        maybeTrackCall(call);
+    }
 
     /**
      * Override of {@link Call.Listener} so we can capture successful creation of calls.
@@ -211,7 +223,8 @@
      */
     @Override
     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
-        maybeTrackCall(call);
+        Log.i(this, "onFailedOutgoingCall: call=%s", call.toString());
+        stopTrackingCall(call);
     }
 
     /**
@@ -229,7 +242,27 @@
      */
     @Override
     public void onFailedIncomingCall(Call call) {
-        maybeTrackCall(call);
+        Log.i(this, "onFailedIncomingCall: call=%s", call.toString());
+        stopTrackingCall(call);
+    }
+
+    /**
+     * Helper method used to stop CallAnomalyWatchdog from tracking or destroying the call.
+     * @param call the call.
+     */
+    private void stopTrackingCall(Call call) {
+        if (mScheduledFutureMap.containsKey(call)) {
+            ScheduledFuture<?> existingTimeout = mScheduledFutureMap.get(call);
+            existingTimeout.cancel(false /* cancelIfRunning */);
+            mScheduledFutureMap.remove(call);
+        }
+        if (mCallsPendingDestruction.contains(call)) {
+            mCallsPendingDestruction.remove(call);
+        }
+        if (mWatchdogCallStateMap.containsKey(call)) {
+            mWatchdogCallStateMap.remove(call);
+        }
+        call.removeListener(this);
     }
 
     /**
@@ -273,7 +306,7 @@
         }
     }
 
-    private long getTimeoutMillis(Call call, WatchdogCallState state) {
+    public long getTimeoutMillis(Call call, WatchdogCallState state) {
         boolean isVoip = call.getIsVoipAudioMode();
         boolean isEmergency = call.isEmergencyCall();
 
@@ -328,6 +361,7 @@
                         mAnomalyReporter.reportAnomaly(
                                 WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_UUID,
                                 WATCHDOG_DISCONNECTED_STUCK_EMERGENCY_CALL_MSG);
+                        mEmergencyCallDiagnosticLogger.reportStuckCall(call);
                     } else {
                         mAnomalyReporter.reportAnomaly(
                                 WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index f56320f..531f42e 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -49,6 +49,7 @@
 import java.util.HashMap;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * This class describes the available routes of a call as a state machine.
@@ -81,14 +82,16 @@
                 WiredHeadsetManager wiredHeadsetManager,
                 StatusBarNotifier statusBarNotifier,
                 CallAudioManager.AudioServiceFactory audioServiceFactory,
-                int earpieceControl) {
+                int earpieceControl,
+                Executor asyncTaskExecutor) {
             return new CallAudioRouteStateMachine(context,
                     callsManager,
                     bluetoothManager,
                     wiredHeadsetManager,
                     statusBarNotifier,
                     audioServiceFactory,
-                    earpieceControl);
+                    earpieceControl,
+                    asyncTaskExecutor);
         }
     }
     /** Values for CallAudioRouteStateMachine constructor's earPieceRouting arg. */
@@ -554,6 +557,7 @@
                     return HANDLED;
                 case STREAMING_FORCE_ENABLED:
                     transitionTo(mStreamingState);
+                    return HANDLED;
                 default:
                     return NOT_HANDLED;
             }
@@ -1478,6 +1482,8 @@
     private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute();
     private final StreamingState mStreamingState = new StreamingState();
 
+    private final Executor mAsyncTaskExecutor;
+
     /**
      * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
      * states
@@ -1516,7 +1522,8 @@
             WiredHeadsetManager wiredHeadsetManager,
             StatusBarNotifier statusBarNotifier,
             CallAudioManager.AudioServiceFactory audioServiceFactory,
-            int earpieceControl) {
+            int earpieceControl,
+            Executor asyncTaskExecutor) {
         super(NAME);
         mContext = context;
         mCallsManager = callsManager;
@@ -1526,7 +1533,7 @@
         mStatusBarNotifier = statusBarNotifier;
         mAudioServiceFactory = audioServiceFactory;
         mLock = callsManager.getLock();
-
+        mAsyncTaskExecutor = asyncTaskExecutor;
         createStates(earpieceControl);
     }
 
@@ -1538,7 +1545,7 @@
             WiredHeadsetManager wiredHeadsetManager,
             StatusBarNotifier statusBarNotifier,
             CallAudioManager.AudioServiceFactory audioServiceFactory,
-            int earpieceControl, Looper looper) {
+            int earpieceControl, Looper looper, Executor asyncTaskExecutor) {
         super(NAME, looper);
         mContext = context;
         mCallsManager = callsManager;
@@ -1548,6 +1555,7 @@
         mStatusBarNotifier = statusBarNotifier;
         mAudioServiceFactory = audioServiceFactory;
         mLock = callsManager.getLock();
+        mAsyncTaskExecutor = asyncTaskExecutor;
 
         createStates(earpieceControl);
     }
@@ -1629,7 +1637,8 @@
                 new IntentFilter(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED));
 
         mStatusBarNotifier.notifyMute(initState.isMuted());
-        mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
+        // We used to call mStatusBarNotifier.notifySpeakerphone, but that makes no sense as there
+        // is never a call at this boot (init) time.
         setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute()));
         start();
     }
@@ -1722,26 +1731,32 @@
 
     private void setSpeakerphoneOn(boolean on) {
         Log.i(this, "turning speaker phone %s", on);
-        AudioDeviceInfo speakerDevice = null;
-        for (AudioDeviceInfo info : mAudioManager.getAvailableCommunicationDevices()) {
-            if (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
-                speakerDevice = info;
-                break;
+        final boolean hasAnyCalls = mCallsManager.hasAnyCalls();
+        // These APIs are all via two-way binder calls so can potentially block Telecom.  Since none
+        // of this has to happen in the Telecom lock we'll offload it to the async executor.
+        mAsyncTaskExecutor.execute(() -> {
+            AudioDeviceInfo speakerDevice = null;
+            for (AudioDeviceInfo info : mAudioManager.getAvailableCommunicationDevices()) {
+                if (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+                    speakerDevice = info;
+                    break;
+                }
             }
-        }
-        boolean speakerOn = false;
-        if (speakerDevice != null && on) {
-            boolean result = mAudioManager.setCommunicationDevice(speakerDevice);
-            if (result) {
-                speakerOn = true;
+            boolean speakerOn = false;
+            if (speakerDevice != null && on) {
+                boolean result = mAudioManager.setCommunicationDevice(speakerDevice);
+                if (result) {
+                    speakerOn = true;
+                }
+            } else {
+                AudioDeviceInfo curDevice = mAudioManager.getCommunicationDevice();
+                if (curDevice != null
+                        && curDevice.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
+                    mAudioManager.clearCommunicationDevice();
+                }
             }
-        } else {
-            AudioDeviceInfo curDevice = mAudioManager.getCommunicationDevice();
-            if (curDevice != null && curDevice.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
-                mAudioManager.clearCommunicationDevice();
-            }
-        }
-        mStatusBarNotifier.notifySpeakerphone(speakerOn);
+            mStatusBarNotifier.notifySpeakerphone(hasAnyCalls && speakerOn);
+        });
     }
 
     private void setBluetoothOn(String address) {
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index 7f864b8..7953324 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -182,9 +182,10 @@
         boolean isPrivilegedDialer = defaultDialerCache.isDefaultOrSystemDialer(callingPackage,
                 initiatingUser.getIdentifier());
 
+
         NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                 context, callsManager, intent, callsManager.getPhoneNumberUtilsAdapter(),
-                isPrivilegedDialer, defaultDialerCache);
+                isPrivilegedDialer, defaultDialerCache, new MmiUtils());
 
         // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
         NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
old mode 100755
new mode 100644
index 0ec2362..a7bbf84
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -386,7 +386,12 @@
         if (okayToLog) {
             AddCallArgs args = new AddCallArgs(mContext, paramBuilder.build(),
                     logCallCompletedListener);
+            Log.addEvent(call, LogUtils.Events.LOG_CALL, "number=" + Log.piiHandle(logNumber)
+                    + ",postDial=" + Log.piiHandle(call.getPostDialDigits()) + ",pres="
+                    + call.getHandlePresentation());
             logCallAsync(args);
+        } else {
+            Log.addEvent(call, LogUtils.Events.SKIP_CALL_LOG);
         }
     }
 
diff --git a/src/com/android/server/telecom/CallStreamingController.java b/src/com/android/server/telecom/CallStreamingController.java
index cd8a690..31b2235 100644
--- a/src/com/android/server/telecom/CallStreamingController.java
+++ b/src/com/android/server/telecom/CallStreamingController.java
@@ -54,10 +54,12 @@
     private CallStreamingServiceConnection mConnection;
     private boolean mIsStreaming;
     private final Object mLock;
+    private TelecomSystem.SyncRoot mTelecomLock;
 
-    public CallStreamingController(Context context) {
+    public CallStreamingController(Context context, TelecomSystem.SyncRoot telecomLock) {
         mLock = new Object();
         mContext = context;
+        mTelecomLock = telecomLock;
     }
 
     private void onConnectedInternal(Call call, TransactionalServiceWrapper wrapper,
@@ -101,6 +103,7 @@
         private final CallsManager mCallsManager;
 
         public QueryCallStreamingTransaction(CallsManager callsManager) {
+            super(callsManager.getLock());
             mCallsManager = callsManager;
         }
 
@@ -128,7 +131,9 @@
         private Call mCall;
         private boolean mEnterInterception;
 
-        public AudioInterceptionTransaction(Call call, boolean enterInterception) {
+        public AudioInterceptionTransaction(Call call, boolean enterInterception,
+                TelecomSystem.SyncRoot lock) {
+            super(lock);
             mCall = call;
             mEnterInterception = enterInterception;
         }
@@ -164,6 +169,7 @@
 
         public StreamingServiceTransaction(Context context, TransactionalServiceWrapper wrapper,
                 Call call) {
+            super(mTelecomLock);
             mWrapper = wrapper;
             mCall = call;
             mUserHandle = mCall.getInitiatingUser();
@@ -177,7 +183,7 @@
             CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
 
             RoleManager roleManager = mContext.getSystemService(RoleManager.class);
-            PackageManager packageManager = mContext.getSystemService(PackageManager.class);
+            PackageManager packageManager = mContext.getPackageManager();
             if (roleManager == null || packageManager == null) {
                 Log.e(TAG, "Can't find system service");
                 future.complete(new VoipCallTransactionResult(
@@ -185,9 +191,8 @@
                 return future;
             }
 
-            // TODO: change this role to RoleManager.ROLE_STREAMING
-            List<String> holders = roleManager.getRoleHoldersAsUser(RoleManager.ROLE_DIALER,
-                    mUserHandle);
+            List<String> holders = roleManager.getRoleHoldersAsUser(
+                    RoleManager.ROLE_SYSTEM_CALL_STREAMING, mUserHandle);
             if (holders.isEmpty()) {
                 Log.e(TAG, "Can't find streaming app");
                 future.complete(new VoipCallTransactionResult(
@@ -241,6 +246,7 @@
         private static final String TAG = "UnbindStreamingServiceTransaction";
 
         public UnbindStreamingServiceTransaction() {
+            super(mTelecomLock);
         }
 
         @SuppressLint("LongLogTag")
@@ -306,6 +312,7 @@
         @StreamingCall.StreamingCallState int mState;
 
         public CallStreamingStateChangeTransaction(@StreamingCall.StreamingCallState int state) {
+            super(mTelecomLock);
             mState = state;
         }
 
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 09d8787..ccc8e59 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -60,8 +60,6 @@
 import android.media.MediaPlayer;
 import android.media.ToneGenerator;
 import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -78,6 +76,7 @@
 import android.provider.BlockedNumberContract;
 import android.provider.BlockedNumberContract.SystemContract;
 import android.provider.CallLog.Calls;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
 import android.telecom.CallAttributes;
@@ -101,14 +100,15 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
+import android.telephony.CellIdentity;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
 import android.widget.Button;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -117,6 +117,7 @@
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
 import com.android.server.telecom.callfiltering.BlockCheckerFilter;
+import com.android.server.telecom.callfiltering.BlockedNumbersAdapter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
@@ -127,7 +128,6 @@
 import com.android.server.telecom.callredirection.CallRedirectionProcessor;
 import com.android.server.telecom.components.ErrorDialogActivity;
 import com.android.server.telecom.components.TelecomBroadcastReceiver;
-import com.android.server.telecom.settings.BlockedNumbersUtil;
 import com.android.server.telecom.stats.CallFailureCause;
 import com.android.server.telecom.ui.AudioProcessingNotification;
 import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity;
@@ -135,6 +135,8 @@
 import com.android.server.telecom.ui.DisconnectedCallNotifier;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.ToastFactory;
+import com.android.server.telecom.voip.TransactionManager;
+import com.android.server.telecom.voip.VoipCallMonitor;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -175,13 +177,13 @@
     @VisibleForTesting
     public interface CallsManagerListener {
         /**
-         * Informs listeners when a {@link Call} is newly created but not yet added to
-         * {@link #mCalls}.  This is where the call has not yet been created by the underlying
-         * {@link ConnectionService}.
+         * Informs listeners when a {@link Call} is newly created, but not yet returned by a
+         * {@link android.telecom.ConnectionService} implementation.
          * @param call the call.
          */
-        default void onCallCreated(Call call) {}
+        default void onStartCreateConnection(Call call) {}
         void onCallAdded(Call call);
+        void onCreateConnectionFailed(Call call);
         void onCallRemoved(Call call);
         void onCallStateChanged(Call call, int oldState, int newState);
         void onConnectionServiceChanged(
@@ -279,6 +281,15 @@
     public static final String EXCEPTION_RETRIEVING_PHONE_ACCOUNTS_EMERGENCY_ERROR_MSG =
             "Exception thrown while retrieving list of potential phone accounts when placing an "
                     + "emergency call.";
+    public static final UUID EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_UUID =
+            UUID.fromString("f9a916c8-8d61-4550-9ad3-11c2e84f6364");
+    public static final String EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_MSG =
+            "An emergency call was disconnected after the connection was created but before the "
+                    + "call was successfully added to CallsManager.";
+    public static final UUID EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_UUID =
+            UUID.fromString("2e994acb-1997-4345-8bf3-bad04303de26");
+    public static final String EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_MSG =
+            "An emergency call was aborted since there were no available phone accounts.";
 
     private static final int[] OUTGOING_CALL_STATES =
             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
@@ -427,9 +438,14 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final EmergencyCallHelper mEmergencyCallHelper;
     private final RoleManagerAdapter mRoleManagerAdapter;
+    private final VoipCallMonitor mVoipCallMonitor;
     private final CallEndpointController mCallEndpointController;
     private final CallAnomalyWatchdog mCallAnomalyWatchdog;
+
+    private final EmergencyCallDiagnosticLogger mEmergencyCallDiagnosticLogger;
     private final CallStreamingController mCallStreamingController;
+    private final BlockedNumbersAdapter mBlockedNumbersAdapter;
+    private final TransactionManager mTransactionManager;
 
     private final ConnectionServiceFocusManager.CallsManagerRequester mRequester =
             new ConnectionServiceFocusManager.CallsManagerRequester() {
@@ -450,8 +466,6 @@
 
     private boolean mCanAddCall = true;
 
-    private int mMaxNumberOfSimultaneouslyActiveSims = -1;
-
     private Runnable mStopTone;
 
     private LinkedList<HandlerThread> mGraphHandlerThreads;
@@ -463,6 +477,7 @@
 
     private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
 
+    private final MmiUtils mMmiUtils = new MmiUtils();
     /**
      * Listener to PhoneAccountRegistrar events.
      */
@@ -493,34 +508,14 @@
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            Log.startSession("CM.CCCR");
             String action = intent.getAction();
             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)
                     || SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED.equals(action)) {
-                new UpdateEmergencyCallNotificationTask().doInBackground(
-                        Pair.create(context, Log.createSubsession()));
+                updateEmergencyCallNotificationAsync(context);
             }
         }
     };
 
-    private static class UpdateEmergencyCallNotificationTask
-            extends AsyncTask<Pair<Context, Session>, Void, Void> {
-        @SafeVarargs
-        @Override
-        protected final Void doInBackground(Pair<Context, Session>... args) {
-            if (args == null || args.length != 1 || args[0] == null) {
-                Log.e(this, new IllegalArgumentException(), "Incorrect invocation");
-                return null;
-            }
-            Log.continueSession(args[0].second, "CM.UECNT");
-            Context context = args[0].first;
-            BlockedNumbersUtil.updateEmergencyCallNotification(context,
-                    SystemContract.shouldShowEmergencyCallNotification(context));
-            Log.endSession();
-            return null;
-        }
-    }
-
     /**
      * Initializes the required Telecom components.
      */
@@ -558,8 +553,12 @@
             ToastFactory toastFactory,
             CallEndpointControllerFactory callEndpointControllerFactory,
             CallAnomalyWatchdog callAnomalyWatchdog,
+            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
             Executor asyncTaskExecutor,
-            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter) {
+            BlockedNumbersAdapter blockedNumbersAdapter,
+            TransactionManager transactionManager,
+            EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger) {
+
         mContext = context;
         mLock = lock;
         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -576,6 +575,7 @@
         mTimeoutsAdapter = timeoutsAdapter;
         mEmergencyCallHelper = emergencyCallHelper;
         mCallerInfoLookupHelper = callerInfoLookupHelper;
+        mEmergencyCallDiagnosticLogger = emergencyCallDiagnosticLogger;
 
         mDtmfLocalTonePlayer =
                 new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy());
@@ -587,7 +587,8 @@
                         wiredHeadsetManager,
                         statusBarNotifier,
                         audioServiceFactory,
-                        CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT
+                        CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
+                        asyncTaskExecutor
                 );
         callAudioRouteStateMachine.initialize();
 
@@ -641,7 +642,10 @@
         mClockProxy = clockProxy;
         mToastFactory = toastFactory;
         mRoleManagerAdapter = roleManagerAdapter;
-        mCallStreamingController = new CallStreamingController(mContext);
+        mTransactionManager = transactionManager;
+        mBlockedNumbersAdapter = blockedNumbersAdapter;
+        mCallStreamingController = new CallStreamingController(mContext, mLock);
+        mVoipCallMonitor = new VoipCallMonitor(mContext, mLock);
 
         mListeners.add(mInCallWakeLockController);
         mListeners.add(statusBarNotifier);
@@ -657,10 +661,14 @@
         mListeners.add(mProximitySensorManager);
         mListeners.add(audioProcessingNotification);
         mListeners.add(callAnomalyWatchdog);
+        mListeners.add(mEmergencyCallDiagnosticLogger);
         mListeners.add(mCallStreamingController);
 
         // this needs to be after the mCallAudioManager
         mListeners.add(mPhoneStateBroadcaster);
+        mListeners.add(mVoipCallMonitor);
+
+        mVoipCallMonitor.startMonitor();
 
         // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
         final UserManager userManager = UserManager.get(mContext);
@@ -735,8 +743,7 @@
 
     @Override
     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
-        Log.v(this, "onFailedOutgoingCall, call: %s", call);
-
+        Log.i(this, "onFailedOutgoingCall for call %s", call);
         markCallAsRemoved(call);
     }
 
@@ -862,6 +869,9 @@
             return;
         }
 
+        // Store the shouldSuppress value in the call object which will be passed to InCallServices
+        incomingCall.setCallIsSuppressedByDoNotDisturb(result.shouldSuppressCallDueToDndStatus);
+
         // Inform our connection service that call filtering is done (if it was performed at all).
         if (incomingCall.isUsingCallFiltering()) {
             boolean isInContacts = incomingCall.getCallerInfo() != null
@@ -991,14 +1001,15 @@
 
     @Override
     public void onFailedIncomingCall(Call call) {
+        Log.i(this, "onFailedIncomingCall for call %s", call);
         setCallState(call, CallState.DISCONNECTED, "failed incoming call");
         call.removeListener(this);
     }
 
     @Override
     public void onSuccessfulUnknownCall(Call call, int callState) {
-        setCallState(call, callState, "successful unknown call");
         Log.i(this, "onSuccessfulUnknownCall for call %s", call);
+        setCallState(call, callState, "successful unknown call");
         addCall(call);
     }
 
@@ -1278,6 +1289,10 @@
         return mEmergencyCallHelper;
     }
 
+    EmergencyCallDiagnosticLogger getEmergencyCallDiagnosticLogger() {
+        return mEmergencyCallDiagnosticLogger;
+    }
+
     public DefaultDialerCache getDefaultDialerCache() {
         return mDefaultDialerCache;
     }
@@ -1387,11 +1402,11 @@
                 isConference, /* isConference */
                 mClockProxy,
                 mToastFactory);
-        notifyCallCreated(call);
 
         // set properties for transactional call
         if (extras.containsKey(TelecomManager.TRANSACTION_CALL_ID_KEY)) {
             call.setIsTransactionalCall(true);
+            call.setCallingPackageIdentity(extras);
             call.setConnectionCapabilities(
                     extras.getInt(CallAttributes.CALL_CAPABILITIES_KEY,
                             CallAttributes.SUPPORTS_SET_INACTIVE), true);
@@ -1551,8 +1566,11 @@
             // transactional calls should skip Call#startCreateConnection below
             // as that is meant for Call objects with a ConnectionServiceWrapper
             call.setState(CallState.RINGING, "explicitly set new incoming to ringing");
+            // Transactional calls don't get created via a connection service; they are added now.
+            call.setIsCreateConnectionComplete(true);
             addCall(call);
         } else {
+            notifyStartCreateConnection(call);
             call.startCreateConnection(mPhoneAccountRegistrar);
         }
         return call;
@@ -1579,12 +1597,11 @@
                 false, /* isConference */
                 mClockProxy,
                 mToastFactory);
-        notifyCallCreated(call);
-
         call.initAnalytics();
 
         setIntentExtrasAndStartTime(call, extras);
         call.addListener(this);
+        notifyStartCreateConnection(call);
         call.startCreateConnection(mPhoneAccountRegistrar);
     }
 
@@ -1700,6 +1717,7 @@
 
             if (extras.containsKey(TelecomManager.TRANSACTION_CALL_ID_KEY)) {
                 call.setIsTransactionalCall(true);
+                call.setCallingPackageIdentity(extras);
                 call.setConnectionCapabilities(
                         extras.getInt(CallAttributes.CALL_CAPABILITIES_KEY,
                                 CallAttributes.SUPPORTS_SET_INACTIVE), true);
@@ -1707,8 +1725,23 @@
             }
 
             call.initAnalytics(callingPackage, creationLogs.toString());
-            // Let listeners know that we just created a new call but haven't added it yet.
-            notifyCallCreated(call);
+
+            // Log info for emergency call
+            if (call.isEmergencyCall()) {
+                String simNumeric = "";
+                String networkNumeric = "";
+                int defaultVoiceSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+                if (defaultVoiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                    TelephonyManager tm = getTelephonyManager().createForSubscriptionId(
+                            defaultVoiceSubId);
+                    CellIdentity cellIdentity = tm.getLastKnownCellIdentity();
+                    simNumeric = tm.getSimOperatorNumeric();
+                    networkNumeric = (cellIdentity != null) ? cellIdentity.getPlmn() : "";
+                }
+                TelecomStatsLog.write(TelecomStatsLog.EMERGENCY_NUMBER_DIALED,
+                            handle.getSchemeSpecificPart(),
+                            callingPackage, simNumeric, networkNumeric);
+            }
 
             // Ensure new calls related to self-managed calls/connections are set as such.  This
             // will be overridden when the actual connection is returned in startCreateConnection,
@@ -1831,7 +1864,7 @@
         CompletableFuture<Call> makeRoomForCall = setAccountHandle.thenComposeAsync(
                 potentialPhoneAccounts -> {
                     Log.i(CallsManager.this, "make room for outgoing call stage");
-                    if (isPotentialInCallMMICode(handle) && !isSelfManaged) {
+                    if (mMmiUtils.isPotentialInCallMMICode(handle) && !isSelfManaged) {
                         return CompletableFuture.completedFuture(finalCall);
                     }
                     // If a call is being reused, then it has already passed the
@@ -1923,19 +1956,23 @@
                                 return CompletableFuture.completedFuture(null);
                             }
                             if (accountSuggestions == null || accountSuggestions.isEmpty()) {
-                                Uri callUri = callToPlace.getHandle();
-                                if (PhoneAccount.SCHEME_TEL.equals(callUri.getScheme())) {
-                                    int managedProfileUserId = getManagedProfileUserId(mContext,
-                                            initiatingUser.getIdentifier());
-                                    if (managedProfileUserId != UserHandle.USER_NULL
-                                            && mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
-                                            handle.getScheme(), false,
-                                            UserHandle.of(managedProfileUserId), false).size()
-                                            != 0) {
-                                        boolean dialogShown = showSwitchToManagedProfileDialog(
-                                                callUri, initiatingUser, managedProfileUserId);
-                                        if (dialogShown) {
-                                            return CompletableFuture.completedFuture(null);
+                                if (isSwitchToManagedProfileDialogFlagEnabled()) {
+                                    Uri callUri = callToPlace.getHandle();
+                                    if (PhoneAccount.SCHEME_TEL.equals(callUri.getScheme())) {
+                                        int managedProfileUserId = getManagedProfileUserId(mContext,
+                                                initiatingUser.getIdentifier());
+                                        if (managedProfileUserId != UserHandle.USER_NULL
+                                                &&
+                                                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
+                                                        handle.getScheme(), false,
+                                                        UserHandle.of(managedProfileUserId),
+                                                        false).size()
+                                                        != 0) {
+                                            boolean dialogShown = showSwitchToManagedProfileDialog(
+                                                    callUri, initiatingUser, managedProfileUserId);
+                                            if (dialogShown) {
+                                                return CompletableFuture.completedFuture(null);
+                                            }
                                         }
                                     }
                                 }
@@ -1943,6 +1980,12 @@
                                 Log.i(CallsManager.this, "Aborting call since there are no"
                                         + " available accounts.");
                                 showErrorMessage(R.string.cant_call_due_to_no_supported_service);
+                                mListeners.forEach(l -> l.onCreateConnectionFailed(callToPlace));
+                                if (callToPlace.isEmergencyCall()){
+                                    mAnomalyReporter.reportAnomaly(
+                                            EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_UUID,
+                                            EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_MSG);
+                                }
                                 return CompletableFuture.completedFuture(null);
                             }
                             boolean needsAccountSelection = accountSuggestions.size() > 1
@@ -2064,7 +2107,7 @@
                     setIntentExtrasAndStartTime(callToUse, extras);
                     setCallSourceToAnalytics(callToUse, originalIntent);
 
-                    if (isPotentialMMICode(handle) && !isSelfManaged) {
+                    if (mMmiUtils.isPotentialMMICode(handle) && !isSelfManaged) {
                         // Do not add the call if it is a potential MMI code.
                         callToUse.addListener(this);
                     } else if (!mCalls.contains(callToUse)) {
@@ -2092,6 +2135,11 @@
         return UserHandle.USER_NULL;
     }
 
+    private boolean isSwitchToManagedProfileDialogFlagEnabled() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
+                "enable_switch_to_managed_profile_dialog", false);
+    }
+
     private boolean showSwitchToManagedProfileDialog(Uri callUri, UserHandle initiatingUser,
             int managedProfileUserId) {
         try {
@@ -2644,6 +2692,7 @@
                     disconnectSelfManagedCalls("place emerg call" /* reason */);
                 }
                 try {
+                    notifyStartCreateConnection(call);
                     call.startCreateConnection(mPhoneAccountRegistrar);
                 } catch (Exception exception) {
                     // If an exceptions is thrown while creating the connection, prompt the user to
@@ -3117,6 +3166,18 @@
         return constructPossiblePhoneAccounts(handle, user, isVideo, isEmergency, false);
     }
 
+    // Returns whether the device is capable of 2 simultaneous active voice calls on different subs.
+    private boolean isDsdaCallingPossible() {
+        try {
+            return getTelephonyManager().getMaxNumberOfSimultaneouslyActiveSims() > 1
+                    || getTelephonyManager().getPhoneCapability()
+                           .getMaxActiveVoiceSubscriptions() > 1;
+        } catch (Exception e) {
+            Log.w(this, "exception in isDsdaCallingPossible(): ", e);
+            return false;
+        }
+    }
+
     public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user,
             boolean isVideo, boolean isEmergency, boolean isConference) {
 
@@ -3133,13 +3194,12 @@
                         capabilities,
                         isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY,
                         isEmergency);
-        if (mMaxNumberOfSimultaneouslyActiveSims < 0) {
-            mMaxNumberOfSimultaneouslyActiveSims =
-                    getTelephonyManager().getMaxNumberOfSimultaneouslyActiveSims();
-        }
         // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount
         // should be available if a call is already active on the SIM account.
-        if (mMaxNumberOfSimultaneouslyActiveSims == 1) {
+        // Similarly, the emergency call should be attempted over the same PhoneAccount as the
+        // ongoing call. However, if the ongoing call is over cross-SIM registration, then the
+        // emergency call will be attempted over a different Phone object at a later stage.
+        if (isEmergency || !isDsdaCallingPossible()) {
             List<PhoneAccountHandle> simAccounts =
                     mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser();
             PhoneAccountHandle ongoingCallAccount = null;
@@ -3491,6 +3551,8 @@
      * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
      */
     public void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
+        Log.i(this, "markCallAsDisconnected: call=%s; disconnectCause=%s",
+                call.toString(), disconnectCause.toString());
         int oldState = call.getState();
         if (call.getState() == CallState.SIMULATED_RINGING
                 && disconnectCause.getCode() == DisconnectCause.REMOTE) {
@@ -3515,6 +3577,17 @@
             }
         }
 
+        // Notify listeners that the call was disconnected before being added to CallsManager.
+        // Listeners will not receive onAdded or onRemoved callbacks.
+        if (!mCalls.contains(call)) {
+            if (call.isEmergencyCall()) {
+                mAnomalyReporter.reportAnomaly(
+                        EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_UUID,
+                        EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_MSG);
+            }
+            mListeners.forEach(l -> l.onCreateConnectionFailed(call));
+        }
+
         // If a call diagnostic service is in use, we will log the original telephony-provided
         // disconnect cause, inform the CDS of the disconnection, and then chain the update of the
         // call state until AFTER the CDS reports it's result back.
@@ -3584,40 +3657,57 @@
      * @param call The call.
      */
     private void performRemoval(Call call) {
-        mInCallController.getBindingFuture().thenRunAsync(() -> {
-            call.maybeCleanupHandover();
-            removeCall(call);
-            Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
-            if (mLocallyDisconnectingCalls.contains(call)) {
-                boolean isDisconnectingChildCall = call.isDisconnectingChildCall();
-                Log.v(this, "performRemoval: isDisconnectingChildCall = "
-                        + isDisconnectingChildCall + "call -> %s", call);
-                mLocallyDisconnectingCalls.remove(call);
-                // Auto-unhold the foreground call due to a locally disconnected call, except if the
-                // call which was disconnected is a member of a conference (don't want to auto
-                // un-hold the conference if we remove a member of the conference).
-                if (!isDisconnectingChildCall && foregroundCall != null
-                        && foregroundCall.getState() == CallState.ON_HOLD) {
-                    foregroundCall.unhold();
-                }
-            } else if (foregroundCall != null &&
-                    !foregroundCall.can(Connection.CAPABILITY_SUPPORT_HOLD) &&
-                    foregroundCall.getState() == CallState.ON_HOLD) {
+        if (mInCallController.getBindingFuture() != null) {
+            mInCallController.getBindingFuture().thenRunAsync(() -> {
+                        doRemoval(call);
+                    }, new LoggedHandlerExecutor(mHandler, "CM.pR", mLock))
+                    .exceptionally((throwable) -> {
+                        Log.e(TAG, throwable, "Error while executing call removal");
+                        mAnomalyReporter.reportAnomaly(CALL_REMOVAL_EXECUTION_ERROR_UUID,
+                                CALL_REMOVAL_EXECUTION_ERROR_MSG);
+                        return null;
+                    });
+        } else {
+            doRemoval(call);
+        }
+    }
 
-                // The new foreground call is on hold, however the carrier does not display the hold
-                // button in the UI.  Therefore, we need to auto unhold the held call since the user
-                // has no means of unholding it themselves.
-                Log.i(this, "performRemoval: Auto-unholding held foreground call (call doesn't "
-                        + "support hold)");
+    /**
+     * Code to perform removal of a call.  Called above from {@link #performRemoval(Call)} either
+     * async (in live code) or sync (in testing).
+     * @param call the call to remove.
+     */
+    private void doRemoval(Call call) {
+        call.maybeCleanupHandover();
+        removeCall(call);
+        Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
+        if (mLocallyDisconnectingCalls.contains(call)) {
+            boolean isDisconnectingChildCall = call.isDisconnectingChildCall();
+            Log.v(this, "performRemoval: isDisconnectingChildCall = "
+                    + isDisconnectingChildCall + "call -> %s", call);
+            mLocallyDisconnectingCalls.remove(call);
+            // Auto-unhold the foreground call due to a locally disconnected call, except if the
+            // call which was disconnected is a member of a conference (don't want to auto
+            // un-hold the conference if we remove a member of the conference).
+            // Also, ensure that the call we're removing is from the same ConnectionService as
+            // the one we're removing.  We don't want to auto-unhold between ConnectionService
+            // implementations, especially if one is managed and the other is a VoIP CS.
+            if (!isDisconnectingChildCall && foregroundCall != null
+                    && foregroundCall.getState() == CallState.ON_HOLD
+                    && areFromSameSource(foregroundCall, call)) {
                 foregroundCall.unhold();
             }
-        }, new LoggedHandlerExecutor(mHandler, "CM.pR", mLock))
-                .exceptionally((throwable) -> {
-                    Log.e(TAG, throwable, "Error while executing call removal");
-                    mAnomalyReporter.reportAnomaly(CALL_REMOVAL_EXECUTION_ERROR_UUID,
-                            CALL_REMOVAL_EXECUTION_ERROR_MSG);
-                    return null;
-                });
+        } else if (foregroundCall != null &&
+                !foregroundCall.can(Connection.CAPABILITY_SUPPORT_HOLD) &&
+                foregroundCall.getState() == CallState.ON_HOLD) {
+
+            // The new foreground call is on hold, however the carrier does not display the hold
+            // button in the UI.  Therefore, we need to auto unhold the held call since the user
+            // has no means of unholding it themselves.
+            Log.i(this, "performRemoval: Auto-unholding held foreground call (call doesn't "
+                    + "support hold)");
+            foregroundCall.unhold();
+        }
     }
 
     /**
@@ -3930,7 +4020,11 @@
                 connectElapsedTime,
                 mClockProxy,
                 mToastFactory);
-        notifyCallCreated(call);
+
+        // Unlike connections, conferences are not created first and then notified as create
+        // connection complete from the CS.  They originate from the CS and are reported directly to
+        // telecom where they're added (see below).
+        call.setIsCreateConnectionComplete(true);
 
         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
                 "new conference call");
@@ -4327,37 +4421,6 @@
         }
     }
 
-    private boolean isPotentialMMICode(Uri handle) {
-        return (handle != null && handle.getSchemeSpecificPart() != null
-                && handle.getSchemeSpecificPart().contains("#"));
-    }
-
-    /**
-     * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
-     * MMI codes which can be dialed when one or more calls are in progress.
-     * <P>
-     * Checks for numbers formatted similar to the MMI codes defined in:
-     * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)}
-     *
-     * @param handle The URI to call.
-     * @return {@code True} if the URI represents a number which could be an in-call MMI code.
-     */
-    private boolean isPotentialInCallMMICode(Uri handle) {
-        if (handle != null && handle.getSchemeSpecificPart() != null &&
-                handle.getScheme() != null &&
-                handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
-
-            String dialedNumber = handle.getSchemeSpecificPart();
-            return (dialedNumber.equals("0") ||
-                    (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
-                    (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
-                    dialedNumber.equals("3") ||
-                    dialedNumber.equals("4") ||
-                    dialedNumber.equals("5"));
-        }
-        return false;
-    }
-
     /**
      * Determines if there are any ongoing self managed calls for the given package/user.
      * @param packageName The package name to check.
@@ -4788,9 +4851,18 @@
             return true;
         }
 
-        // If the live call is stuck in a connecting state, then we should disconnect it in favor
-        // of the new outgoing call and prompt the user to generate a bugreport.
-        if (liveCall.getState() == CallState.CONNECTING) {
+        // If the live call is stuck in a connecting state for longer than the transitory timeout,
+        // then we should disconnect it in favor of the new outgoing call and prompt the user to
+        // generate a bugreport.
+        // TODO: In the future we should let the CallAnomalyWatchDog do this disconnection of the
+        // live call stuck in the connecting state.  Unfortunately that code will get tripped up by
+        // calls that have a longer than expected new outgoing call broadcast response time.  This
+        // mitigation is intended to catch calls stuck in a CONNECTING state for a long time that
+        // block outgoing calls.  However, if the user dials two calls in quick succession it will
+        // result in both calls getting disconnected, which is not optimal.
+        if (liveCall.getState() == CallState.CONNECTING
+                && ((mClockProxy.elapsedRealtime() - liveCall.getCreationElapsedRealtimeMillis())
+                > mTimeoutsAdapter.getNonVoipCallTransitoryStateTimeoutMillis())) {
             mAnomalyReporter.reportAnomaly(LIVE_CALL_STUCK_CONNECTING_ERROR_UUID,
                     LIVE_CALL_STUCK_CONNECTING_ERROR_MSG);
             liveCall.disconnect("Force disconnect CONNECTING call.");
@@ -4926,6 +4998,25 @@
     }
 
     /**
+     * Asynchronously updates the emergency call notification.
+     * @param context the context for the update.
+     */
+    private void updateEmergencyCallNotificationAsync(Context context) {
+        mAsyncTaskExecutor.execute(() -> {
+            Log.startSession("CM.UEMCNA");
+            try {
+                boolean shouldShow = mBlockedNumbersAdapter.shouldShowEmergencyCallNotification(
+                        context);
+                Log.i(CallsManager.this, "updateEmergencyCallNotificationAsync; show=%b",
+                        shouldShow);
+                mBlockedNumbersAdapter.updateEmergencyCallNotification(context, shouldShow);
+            } finally {
+                Log.endSession();
+            }
+        });
+    }
+
+    /**
      * Creates a new call for an existing connection.
      *
      * @param callId The id of the new call.
@@ -4989,6 +5080,9 @@
                 call.setParentCall(parentCall);
             }
         }
+        // Existing connections originate from a connection service, so they are completed creation
+        // by the ConnectionService implicitly.
+        call.setIsCreateConnectionComplete(true);
         addCall(call);
         if (parentCall != null) {
             // Now, set the call as a child of the parent since it has been added to Telecom.  This
@@ -5300,7 +5394,7 @@
      *
      * @param pw The {@code IndentingPrintWriter} to write the state to.
      */
-    public void dump(IndentingPrintWriter pw) {
+    public void dump(IndentingPrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
         if (mCalls != null) {
             pw.println("mCalls: ");
@@ -5362,6 +5456,14 @@
             mCallAnomalyWatchdog.dump(pw);
             pw.decreaseIndent();
         }
+
+        if (mEmergencyCallDiagnosticLogger != null) {
+            pw.println("mEmergencyCallDiagnosticLogger:");
+            pw.increaseIndent();
+            mEmergencyCallDiagnosticLogger.dump(pw, args);
+            pw.decreaseIndent();
+        }
+
         if (mDefaultDialerCache != null) {
             pw.println("mDefaultDialerCache:");
             pw.increaseIndent();
@@ -5391,8 +5493,10 @@
     * @param call The call.
     */
     private void maybeShowErrorDialogOnDisconnect(Call call) {
-        if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle())
-                || isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(call)) {
+        if (call.getState() == CallState.DISCONNECTED && (mMmiUtils.isPotentialMMICode(
+                call.getHandle())
+                || mMmiUtils.isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(
+                call)) {
             DisconnectCause disconnectCause = call.getDisconnectCause();
             if (!TextUtils.isEmpty(disconnectCause.getDescription()) && ((disconnectCause.getCode()
                     == DisconnectCause.ERROR) || (disconnectCause.getCode()
@@ -5468,6 +5572,9 @@
         } else {
             call.setConnectionService(service);
             service.createConnectionFailed(call);
+            if (!mCalls.contains(call)){
+                mListeners.forEach(l -> l.onCreateConnectionFailed(call));
+            }
         }
     }
 
@@ -5490,16 +5597,19 @@
         } else {
             call.setConnectionService(service);
             service.createConferenceFailed(call);
+            if (!mCalls.contains(call)){
+                mListeners.forEach(l -> l.onCreateConnectionFailed(call));
+            }
         }
     }
 
     /**
-     * Notify interested parties that a new call has been created, but not yet added to
-     * CallsManager.
+     * Notify interested parties that a new call is about to be handed off to a ConnectionService to
+     * be created.
      * @param theCall the new call.
      */
-    private void notifyCallCreated(final Call theCall) {
-        mListeners.forEach(l -> l.onCallCreated(theCall));
+    private void notifyStartCreateConnection(final Call theCall) {
+        mListeners.forEach(l -> l.onStartCreateConnection(theCall));
     }
 
     /**
@@ -5664,7 +5774,7 @@
                 // Disconnect all self-managed calls to make priority for emergency call.
                 disconnectSelfManagedCalls("emergency call");
             }
-
+            notifyStartCreateConnection(call);
             call.startCreateConnection(mPhoneAccountRegistrar);
         }
 
@@ -5841,7 +5951,7 @@
         extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true);
         extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT,
                 fromCall.getTargetPhoneAccount());
-
+        notifyStartCreateConnection(call);
         call.startCreateConnection(mPhoneAccountRegistrar);
     }
 
@@ -5961,34 +6071,53 @@
     }
 
     /**
-     * Intended for ongoing or new calls that would like to go active/answered and need to
-     * update the mConnectionSvrFocusMgr before setting the state
+     * This helper mainly requests mConnectionSvrFocusMgr to update the call focus via a
+     * {@link TransactionalFocusRequestCallback}.  However, in the case of a held call, the
+     * state must be set first and then a request must be made.
+     *
+     * @param newCallFocus          to set active/answered
+     * @param resultCallback        that back propagates the focusManager result
+     *
+     * Note: This method should only be called if there are no active calls.
      */
-    public void transactionRequestNewFocusCall(Call call, int newCallState,
-            OutcomeReceiver<Boolean, CallException> callback) {
-        Log.d(this, "transactionRequestNewFocusCall");
-        PendingAction pendingAction = new ActionSetCallState(call, newCallState,
-                "transactional ActionSetCallState");
+    public void requestNewCallFocusAndVerify(Call newCallFocus,
+            OutcomeReceiver<Boolean, CallException> resultCallback) {
+        int currentCallState = newCallFocus.getState();
+        PendingAction pendingAction = null;
+
+        // if the current call is in a state that can become the new call focus, we can set the
+        // state afterwards...
+        if (ConnectionServiceFocusManager.PRIORITY_FOCUS_CALL_STATE.contains(currentCallState)) {
+            pendingAction = new ActionSetCallState(newCallFocus, CallState.ACTIVE,
+                    "vCFC: pending action set state");
+        } else {
+            // However, HELD calls need to be set to ACTIVE before requesting call focus.
+            setCallState(newCallFocus, CallState.ACTIVE, "vCFC: immediately set active");
+        }
+
         mConnectionSvrFocusMgr
-                .requestFocus(call,
-                        new TransactionalFocusRequestCallback(pendingAction, call, callback));
+                .requestFocus(newCallFocus,
+                        new TransactionalFocusRequestCallback(pendingAction, currentCallState,
+                                newCallFocus, resultCallback));
     }
 
     /**
      * Request a new call focus and ensure the request was successful via an OutcomeReceiver. Also,
-     * include a PendingAction that will execute if the call focus change is successful.
+     * conditionally include a PendingAction that will execute if and only if the call focus change
+     * is successful.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public class TransactionalFocusRequestCallback implements
             ConnectionServiceFocusManager.RequestFocusCallback {
         private PendingAction mPendingAction;
-        @NonNull
-        private Call mTargetCallFocus;
+        private int mPreviousCallState;
+        @NonNull private Call mTargetCallFocus;
         private OutcomeReceiver<Boolean, CallException> mCallback;
 
-        TransactionalFocusRequestCallback(PendingAction pendingAction, @NonNull Call call,
-                OutcomeReceiver<Boolean, CallException> callback) {
+        TransactionalFocusRequestCallback(PendingAction pendingAction, int previousState,
+                @NonNull Call call, OutcomeReceiver<Boolean, CallException> callback) {
             mPendingAction = pendingAction;
+            mPreviousCallState = previousState;
             mTargetCallFocus = call;
             mCallback = callback;
         }
@@ -6001,12 +6130,18 @@
                     mTargetCallFocus, currentCallFocus);
             if (currentCallFocus == null ||
                     !currentCallFocus.getId().equals(mTargetCallFocus.getId())) {
+                // possibly reset the call state
+                if (mTargetCallFocus.getState() != mPreviousCallState) {
+                    mTargetCallFocus.setState(mPreviousCallState, "resetting call state");
+                }
                 mCallback.onError(new CallException("failed to switch focus to requested call",
                         CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
                 return;
             }
             // at this point, we know the FocusManager is able to update successfully
-            mPendingAction.performAction(); // set the call state
+            if (mPendingAction != null) {
+                mPendingAction.performAction(); // set the call state
+            }
             mCallback.onResult(true); // complete the transaction
         }
     }
@@ -6174,6 +6309,10 @@
         return mRinger;
     }
 
+    @VisibleForTesting
+    public VoipCallMonitor getVoipCallMonitor() {
+        return mVoipCallMonitor;
+    }
 
     /**
      * This method should only be used for testing.
diff --git a/src/com/android/server/telecom/CallsManagerListenerBase.java b/src/com/android/server/telecom/CallsManagerListenerBase.java
index 6b8e2fe..43f3b90 100644
--- a/src/com/android/server/telecom/CallsManagerListenerBase.java
+++ b/src/com/android/server/telecom/CallsManagerListenerBase.java
@@ -35,6 +35,10 @@
     }
 
     @Override
+    public void onCreateConnectionFailed(Call call) {
+    }
+
+    @Override
     public void onCallStateChanged(Call call, int oldState, int newState) {
     }
 
diff --git a/src/com/android/server/telecom/ConnectionServiceFocusManager.java b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
index 8db98e9..6fbc494 100644
--- a/src/com/android/server/telecom/ConnectionServiceFocusManager.java
+++ b/src/com/android/server/telecom/ConnectionServiceFocusManager.java
@@ -32,6 +32,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -153,10 +154,9 @@
         void setCallsManagerListener(CallsManager.CallsManagerListener listener);
     }
 
-    private static final int[] PRIORITY_FOCUS_CALL_STATE = new int[] {
-            CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING, CallState.AUDIO_PROCESSING,
-            CallState.RINGING
-    };
+    public static final Set<Integer> PRIORITY_FOCUS_CALL_STATE
+            = Set.of(CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING,
+            CallState.AUDIO_PROCESSING, CallState.RINGING);
 
     private static final int MSG_REQUEST_FOCUS = 1;
     private static final int MSG_RELEASE_CONNECTION_FOCUS = 2;
@@ -374,17 +374,15 @@
                         && call.isFocusable())
                 .collect(Collectors.toList());
 
-        for (int i = 0; i < PRIORITY_FOCUS_CALL_STATE.length; i++) {
-            for (CallFocus call : calls) {
-                if (call.getState() == PRIORITY_FOCUS_CALL_STATE[i]) {
-                    mCurrentFocusCall = call;
-                    Log.d(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
-                    return;
-                }
+        for (CallFocus call : calls) {
+            if (PRIORITY_FOCUS_CALL_STATE.contains(call.getState())) {
+                mCurrentFocusCall = call;
+                Log.i(this, "updateCurrentFocusCall %s", mCurrentFocusCall);
+                return;
             }
         }
 
-        Log.d(this, "updateCurrentFocusCall = null");
+        Log.i(this, "updateCurrentFocusCall = null");
     }
 
     private void onRequestFocusDone(FocusRequest focusRequest) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 874b77f..cc5932c 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -89,7 +89,6 @@
         ConnectionServiceFocusManager.ConnectionServiceFocus {
 
     private static final String TELECOM_ABBREVIATION = "cast";
-
     private CompletableFuture<Pair<Integer, Location>> mQueryLocationFuture = null;
     private @Nullable CancellationSignal mOngoingQueryLocationRequest = null;
     private final ExecutorService mQueryLocationExecutor = Executors.newSingleThreadExecutor();
@@ -1432,7 +1431,11 @@
                 callback.send(0,
                         getQueryLocationErrorResult(QueryLocationException.ERROR_UNSPECIFIED));
             }
+            //make sure we don't pass mock locations diretly, always reset() mock locations
             if (result.second != null) {
+                if(result.second.isMock()) {
+                    result.second.reset();
+                }
                 callback.send(1, getQueryLocationResult(result.second));
             } else {
                 callback.send(0, getQueryLocationErrorResult(result.first));
@@ -1597,7 +1600,7 @@
             public void onSuccess() {
                 String callId = mCallIdMapper.getCallId(call);
                 if (callId == null) {
-                    Log.w(ConnectionServiceWrapper.this, "Call not present"
+                    Log.i(ConnectionServiceWrapper.this, "Call not present"
                             + " in call id mapper, maybe it was aborted before the bind"
                             + " completed successfully?");
                     response.handleCreateConnectionFailure(
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 331c32b..6702f03 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -429,19 +429,28 @@
             // Get user preferred PA if it exists.
             PhoneAccount preferredPA = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
                     preferredPAH);
-            // Next, add all SIM phone accounts which can place emergency calls.
-            sortSimPhoneAccountsForEmergency(allAccounts, preferredPA);
-            // and pick the first one that can place emergency calls.
-            for (PhoneAccount phoneAccount : allAccounts) {
-                if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
-                        && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
-                    PhoneAccountHandle phoneAccountHandle = phoneAccount.getAccountHandle();
-                    Log.i(this, "Will try PSTN account %s for emergency", phoneAccountHandle);
-                    mAttemptRecords.add(new CallAttemptRecord(phoneAccountHandle,
-                            phoneAccountHandle));
-                    // Add only one emergency SIM PhoneAccount to the attempt list, telephony will
-                    // perform retries if the call fails.
-                    break;
+            if (mCall.isIncoming() && preferredPA != null) {
+                // The phone account for the incoming call should be used.
+                mAttemptRecords.add(new CallAttemptRecord(preferredPA.getAccountHandle(),
+                        preferredPA.getAccountHandle()));
+            } else {
+                // Next, add all SIM phone accounts which can place emergency calls.
+                sortSimPhoneAccountsForEmergency(allAccounts, preferredPA);
+                Log.i(this, "The preferred PA is: %s", preferredPA);
+                // and pick the first one that can place emergency calls.
+                for (PhoneAccount phoneAccount : allAccounts) {
+                    if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
+                            && phoneAccount.hasCapabilities(
+                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+                        PhoneAccountHandle phoneAccountHandle = phoneAccount.getAccountHandle();
+                        Log.i(this, "Will try PSTN account %s for emergency",
+                                phoneAccountHandle);
+                        mAttemptRecords.add(new CallAttemptRecord(phoneAccountHandle,
+                                phoneAccountHandle));
+                        // Add only one emergency SIM PhoneAccount to the attempt list, telephony
+                        // will perform retries if the call fails.
+                        break;
+                    }
                 }
             }
 
diff --git a/src/com/android/server/telecom/DefaultDialerCache.java b/src/com/android/server/telecom/DefaultDialerCache.java
index a4a0242..3ce394e 100644
--- a/src/com/android/server/telecom/DefaultDialerCache.java
+++ b/src/com/android/server/telecom/DefaultDialerCache.java
@@ -265,7 +265,7 @@
             if (packageName == null ||
                     Objects.equals(packageName, mCurrentDefaultDialerPerUser.get(userId))) {
                 String newDefaultDialer = refreshCacheForUser(userId);
-                Log.i(LOG_TAG, "Refreshing default dialer for user %d: now %s",
+                Log.v(LOG_TAG, "Refreshing default dialer for user %d: now %s",
                         userId, newDefaultDialer);
             }
         }
diff --git a/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java b/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java
new file mode 100644
index 0000000..af79da3
--- /dev/null
+++ b/src/com/android/server/telecom/EmergencyCallDiagnosticLogger.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom;
+
+import static android.telephony.TelephonyManager.EmergencyCallDiagnosticParams;
+
+import android.os.BugreportManager;
+import android.os.DropBoxManager;
+import android.provider.DeviceConfig;
+import android.telecom.DisconnectCause;
+import android.telecom.Log;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+
+/**
+ * The EmergencyCallDiagnosticsLogger monitors information required to diagnose potential outgoing
+ * ecall failures on the device. When a potential failure is detected, it calls a Telephony API to
+ * persist relevant information (dumpsys, logcat etc.) to the dropbox. This acts as a central place
+ * to determine when and what to collect.
+ *
+ * <p>When a bugreport is triggered, this module will read the dropbox entries and add them to the
+ * telecom dump.
+ */
+public class EmergencyCallDiagnosticLogger extends CallsManagerListenerBase
+        implements Call.Listener {
+
+    public static final int REPORT_REASON_RANGE_START = -1; //!!DO NOT CHANGE
+    public static final int REPORT_REASON_RANGE_END = 5; //increment this and add new reason above
+    public static final int COLLECTION_TYPE_BUGREPORT = 10;
+    public static final int COLLECTION_TYPE_TELECOM_STATE = 11;
+    public static final int COLLECTION_TYPE_TELEPHONY_STATE = 12;
+    public static final int COLLECTION_TYPE_LOGCAT_BUFFERS = 13;
+    private static final int REPORT_REASON_STUCK_CALL_DETECTED = 0;
+    private static final int REPORT_REASON_INACTIVE_CALL_TERMINATED_BY_USER_AFTER_DELAY = 1;
+    private static final int REPORT_REASON_CALL_FAILED = 2;
+    private static final int REPORT_REASON_CALL_CREATED_BUT_NEVER_ADDED = 3;
+    private static final int REPORT_REASON_SHORT_DURATION_AFTER_GOING_ACTIVE = 4;
+    private static final String DROPBOX_TAG = "ecall_diagnostic_data";
+    private static final String ENABLE_BUGREPORT_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS =
+            "enable_bugreport_collection_for_emergency_call_diagnostics";
+    private static final String ENABLE_TELECOM_DUMP_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS =
+            "enable_telecom_dump_collection_for_emergency_call_diagnostics";
+
+    private static final String ENABLE_LOGCAT_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS =
+            "enable_logcat_collection_for_emergency_call_diagnostics";
+    private static final String ENABLE_TELEPHONY_DUMP_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS =
+            "enable_telephony_dump_collection_for_emergency_call_diagnostics";
+
+    private static final String DUMPSYS_ARG_FOR_DIAGNOSTICS = "EmergencyDiagnostics";
+
+    // max text size to read from dropbox entry
+    private static final int DEFAULT_MAX_READ_BYTES_PER_DROP_BOX_ENTRY = 500000;
+    private static final String MAX_BYTES_PER_DROP_BOX_ENTRY = "max_bytes_per_dropbox_entry";
+    private static final int MAX_DROPBOX_ENTRIES_TO_DUMP = 6;
+
+    private final Timeouts.Adapter mTimeoutAdapter;
+    // This map holds all calls, but keeps pruning non-emergency calls when we can determine it
+    private final Map<Call, CallEventTimestamps> mEmergencyCallsMap = new ConcurrentHashMap<>(2);
+    private final DropBoxManager mDropBoxManager;
+    private final LocalLog mLocalLog = new LocalLog(10);
+    private final TelephonyManager mTelephonyManager;
+    private final BugreportManager mBugreportManager;
+    private final Executor mAsyncTaskExecutor;
+    private final ClockProxy mClockProxy;
+
+    public EmergencyCallDiagnosticLogger(
+            TelephonyManager tm,
+            BugreportManager brm,
+            Timeouts.Adapter timeoutAdapter, DropBoxManager dropBoxManager,
+            Executor asyncTaskExecutor, ClockProxy clockProxy) {
+        mTimeoutAdapter = timeoutAdapter;
+        mDropBoxManager = dropBoxManager;
+        mTelephonyManager = tm;
+        mBugreportManager = brm;
+        mAsyncTaskExecutor = asyncTaskExecutor;
+        mClockProxy = clockProxy;
+    }
+
+    // this calculates time from ACTIVE --> removed
+    private static long getCallTimeInActiveStateSec(CallEventTimestamps ts) {
+        if (ts.getCallActiveTime() == 0 || ts.getCallRemovedTime() == 0) {
+            return 0;
+        } else {
+            return (ts.getCallRemovedTime() - ts.getCallActiveTime()) / 1000;
+        }
+    }
+
+    // this calculates time from call created --> removed
+    private static long getTotalCallTimeSec(CallEventTimestamps ts) {
+        if (ts.getCallRemovedTime() == 0 || ts.getCallCreatedTime() == 0) {
+            return 0;
+        } else {
+            return (ts.getCallRemovedTime() - ts.getCallCreatedTime()) / 1000;
+        }
+    }
+
+    //determines what to collect based on fail reason
+    //if COLLECTION_TYPE_BUGREPORT is present in the returned list, then that
+    //should be the only collection type in the list
+    @VisibleForTesting
+    public static List<Integer> getDataCollectionTypes(int reason) {
+        switch (reason) {
+            case REPORT_REASON_SHORT_DURATION_AFTER_GOING_ACTIVE:
+                return Arrays.asList(COLLECTION_TYPE_TELECOM_STATE);
+            case REPORT_REASON_CALL_CREATED_BUT_NEVER_ADDED:
+                return Arrays.asList(
+                        COLLECTION_TYPE_TELECOM_STATE, COLLECTION_TYPE_TELEPHONY_STATE);
+            case REPORT_REASON_CALL_FAILED:
+            case REPORT_REASON_INACTIVE_CALL_TERMINATED_BY_USER_AFTER_DELAY:
+            case REPORT_REASON_STUCK_CALL_DETECTED:
+                return Arrays.asList(
+                        COLLECTION_TYPE_TELECOM_STATE,
+                        COLLECTION_TYPE_TELEPHONY_STATE,
+                        COLLECTION_TYPE_LOGCAT_BUFFERS);
+            default:
+        }
+        return new ArrayList<>();
+    }
+
+    private int getMaxBytesPerDropboxEntry() {
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
+                MAX_BYTES_PER_DROP_BOX_ENTRY, DEFAULT_MAX_READ_BYTES_PER_DROP_BOX_ENTRY);
+    }
+
+    @VisibleForTesting
+    public Map<Call, CallEventTimestamps> getEmergencyCallsMap() {
+        return mEmergencyCallsMap;
+    }
+
+    private void triggerDiagnosticsCollection(Call call, int reason) {
+        Log.i(this, "Triggering diagnostics for call %s reason: %d", call.getId(), reason);
+        List<Integer> dataCollectionTypes = getDataCollectionTypes(reason);
+        boolean invokeTelephonyPersistApi = false;
+        CallEventTimestamps ts = mEmergencyCallsMap.get(call);
+        EmergencyCallDiagnosticParams dp =
+                new EmergencyCallDiagnosticParams();
+        for (Integer dataCollectionType : dataCollectionTypes) {
+            switch (dataCollectionType) {
+                case COLLECTION_TYPE_TELECOM_STATE:
+                    if (isTelecomDumpCollectionEnabled()) {
+                        dp.setTelecomDumpSysCollection(true);
+                        invokeTelephonyPersistApi = true;
+                    }
+                    break;
+                case COLLECTION_TYPE_TELEPHONY_STATE:
+                    if (isTelephonyDumpCollectionEnabled()) {
+                        dp.setTelephonyDumpSysCollection(true);
+                        invokeTelephonyPersistApi = true;
+                    }
+                    break;
+                case COLLECTION_TYPE_LOGCAT_BUFFERS:
+                    if (isLogcatCollectionEnabled()) {
+                        dp.setLogcatCollection(true, ts.getCallCreatedTime());
+                        invokeTelephonyPersistApi = true;
+                    }
+                    break;
+                case COLLECTION_TYPE_BUGREPORT:
+                    if (isBugreportCollectionEnabled()) {
+                        mAsyncTaskExecutor.execute(new Runnable() {
+                            @Override
+                            public void run() {
+                                persistBugreport();
+                            }
+                        });
+                    }
+                    break;
+                default:
+            }
+        }
+        if (invokeTelephonyPersistApi) {
+            mAsyncTaskExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    Log.i(this, "Requesting Telephony to persist data %s", dp.toString());
+                    try {
+                        mTelephonyManager.persistEmergencyCallDiagnosticData(DROPBOX_TAG, dp);
+                    } catch (Exception e) {
+                        Log.w(this,
+                                "Exception while invoking "
+                                        + "Telephony#persistEmergencyCallDiagnosticData  %s",
+                                e.toString());
+                    }
+                }
+            });
+        }
+    }
+
+    private boolean isBugreportCollectionEnabled() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TELEPHONY,
+                ENABLE_BUGREPORT_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS,
+                false);
+    }
+
+    private boolean isTelecomDumpCollectionEnabled() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TELEPHONY,
+                ENABLE_TELECOM_DUMP_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS,
+                true);
+    }
+
+    private boolean isLogcatCollectionEnabled() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TELEPHONY,
+                ENABLE_LOGCAT_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS,
+                true);
+    }
+
+    private boolean isTelephonyDumpCollectionEnabled() {
+        return DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_TELEPHONY,
+                ENABLE_TELEPHONY_DUMP_COLLECTION_FOR_EMERGENCY_CALL_DIAGNOSTICS,
+                true);
+    }
+
+    private void persistBugreport() {
+        if (isBugreportCollectionEnabled()) {
+            // TODO:
+        }
+    }
+
+    private boolean shouldTrackCall(Call call) {
+        return (call != null && call.isEmergencyCall() && call.isOutgoing());
+    }
+
+    public void reportStuckCall(Call call) {
+        if (shouldTrackCall(call)) {
+            Log.i(this, "Triggering diagnostics for stuck call %s", call.getId());
+            triggerDiagnosticsCollection(call, REPORT_REASON_STUCK_CALL_DETECTED);
+            call.removeListener(this);
+            mEmergencyCallsMap.remove(call);
+        }
+    }
+
+    @Override
+    public void onStartCreateConnection(Call call) {
+        if (shouldTrackCall(call)) {
+            long currentTime = mClockProxy.currentTimeMillis();
+            call.addListener(this);
+            Log.i(this, "Tracking call %s timestamp: %d", call.getId(), currentTime);
+            mEmergencyCallsMap.put(call, new CallEventTimestamps(currentTime));
+        }
+    }
+
+    @Override
+    public void onCreateConnectionFailed(Call call) {
+        if (shouldTrackCall(call)) {
+            Log.i(this, "Triggering diagnostics for  call %s that was never added", call.getId());
+            triggerDiagnosticsCollection(call, REPORT_REASON_CALL_CREATED_BUT_NEVER_ADDED);
+            call.removeListener(this);
+            mEmergencyCallsMap.remove(call);
+        }
+    }
+
+    /**
+     * Override of {@link CallsManagerListenerBase} to track when calls are removed
+     *
+     * @param call the call
+     */
+    @Override
+    public void onCallRemoved(Call call) {
+        if (call != null && (mEmergencyCallsMap.get(call) != null)) {
+            call.removeListener(this);
+
+            CallEventTimestamps ts = mEmergencyCallsMap.get(call);
+            long currentTime = mClockProxy.currentTimeMillis();
+            ts.setCallRemovedTime(currentTime);
+
+            maybeTriggerDiagnosticsCollection(call, ts);
+            mEmergencyCallsMap.remove(call);
+        }
+    }
+
+    // !NOTE!: this method should only be called after we get onCallRemoved
+    private void maybeTriggerDiagnosticsCollection(Call removedCall, CallEventTimestamps ts) {
+        Log.i(this, "Evaluating emergency call for diagnostic logging: %s", removedCall.getId());
+        boolean wentActive = (ts.getCallActiveTime() != 0);
+        long callActiveTimeSec = (wentActive ? getCallTimeInActiveStateSec(ts) : 0);
+        long timeSinceCallCreatedSec = getTotalCallTimeSec(ts);
+        int dc = removedCall.getDisconnectCause().getCode();
+
+        if (wentActive) {
+            if (callActiveTimeSec
+                    < mTimeoutAdapter.getEmergencyCallActiveTimeThresholdMillis() / 1000) {
+                // call connected but did not go on for long
+                triggerDiagnosticsCollection(
+                        removedCall, REPORT_REASON_SHORT_DURATION_AFTER_GOING_ACTIVE);
+            }
+        } else {
+
+            if (dc == DisconnectCause.LOCAL
+                    && timeSinceCallCreatedSec
+                    > mTimeoutAdapter.getEmergencyCallTimeBeforeUserDisconnectThresholdMillis()
+                    / 1000) {
+                // call was disconnected by the user (but not immediately)
+                triggerDiagnosticsCollection(
+                        removedCall, REPORT_REASON_INACTIVE_CALL_TERMINATED_BY_USER_AFTER_DELAY);
+            } else if (dc != DisconnectCause.LOCAL) {
+                // this can be a case for a full bugreport
+                triggerDiagnosticsCollection(removedCall, REPORT_REASON_CALL_FAILED);
+            }
+        }
+    }
+
+    /**
+     * Override of {@link com.android.server.telecom.CallsManager.CallsManagerListener} to track
+     * call state changes.
+     *
+     * @param call     the call
+     * @param oldState its old state
+     * @param newState the new state
+     */
+    @Override
+    public void onCallStateChanged(Call call, int oldState, int newState) {
+
+        if (call != null && mEmergencyCallsMap.get(call) != null && newState == CallState.ACTIVE) {
+            CallEventTimestamps ts = mEmergencyCallsMap.get(call);
+            if (ts != null) {
+                long currentTime = mClockProxy.currentTimeMillis();
+                ts.setCallActiveTime(currentTime);
+            }
+        }
+    }
+
+    private void dumpDiagnosticDataFromDropbox(IndentingPrintWriter pw) {
+        pw.increaseIndent();
+        pw.println("PERSISTED DIAGNOSTIC DATA FROM DROP BOX");
+        int totalEntriesDumped = 0;
+        long currentTime = mClockProxy.currentTimeMillis();
+        long entriesAfterTime =
+                currentTime - (mTimeoutAdapter.getDaysBackToSearchEmergencyDiagnosticEntries() * 24
+                        * 60L * 60L * 1000L);
+        Log.i(this, "current time: %d entriesafter: %d", currentTime, entriesAfterTime);
+        DropBoxManager.Entry entry;
+        entry = mDropBoxManager.getNextEntry(DROPBOX_TAG, entriesAfterTime);
+        while (entry != null) {
+            Log.i(this, "found entry with ts: %d", entry.getTimeMillis());
+            String content[] = entry.getText(getMaxBytesPerDropboxEntry()).split(
+                    System.lineSeparator());
+            long entryTime = entry.getTimeMillis();
+            if (content != null) {
+                pw.increaseIndent();
+                pw.println("------------BEGIN ENTRY (" + entryTime + ")--------");
+                for (String line : content) {
+                    pw.println(line);
+                }
+                pw.println("--------END ENTRY--------");
+                pw.decreaseIndent();
+                totalEntriesDumped++;
+            }
+            entry = mDropBoxManager.getNextEntry(DROPBOX_TAG, entryTime);
+            if (totalEntriesDumped > MAX_DROPBOX_ENTRIES_TO_DUMP) {
+                /*
+                Since Emergency calls are a rare/once in a lifetime time occurrence for most users,
+                we should not be seeing too many entries. This code just guards against edge case
+                like load testing, b2b failures etc. We may accumulate a lot of dropbox entries in
+                such cases, but we limit to dumping only MAX_DROPBOX_ENTRIES_TO_DUMP in the
+                bugreport
+
+                The Dropbox API in its current state does not allow to query Entries in reverse
+                chronological order efficiently.
+                 */
+
+                Log.i(this, "Skipping dump for remaining entries. dumped :%d", totalEntriesDumped);
+                break;
+            }
+        }
+        pw.println("END OF PERSISTED DIAGNOSTIC DATA FROM DROP BOX");
+        pw.decreaseIndent();
+    }
+
+    public void dump(IndentingPrintWriter pw, String[] args) {
+        pw.increaseIndent();
+        mLocalLog.dump(pw);
+        pw.decreaseIndent();
+        if (args != null && args.length > 0 && args[0].equals(DUMPSYS_ARG_FOR_DIAGNOSTICS)) {
+            //dont read dropbox entries since this dump is triggered by telephony for diagnostics
+            Log.i(this, "skipped dumping diagnostic data");
+            return;
+        }
+        dumpDiagnosticDataFromDropbox(pw);
+    }
+
+    private static class CallEventTimestamps {
+
+        private final long mCallCreatedTime;
+        private long mCallActiveTime;
+        private long mCallRemovedTime;
+
+        public CallEventTimestamps(long createdTime) {
+            mCallCreatedTime = createdTime;
+        }
+
+        public long getCallActiveTime() {
+            return mCallActiveTime;
+        }
+
+        public void setCallActiveTime(long callActiveTime) {
+            this.mCallActiveTime = callActiveTime;
+        }
+
+        public long getCallCreatedTime() {
+            return mCallCreatedTime;
+        }
+
+        public long getCallRemovedTime() {
+            return mCallRemovedTime;
+        }
+
+        public void setCallRemovedTime(long callRemovedTime) {
+            this.mCallRemovedTime = callRemovedTime;
+        }
+    }
+}
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index f5214ed..9ce10bd 100755
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -695,7 +695,7 @@
     @Override
     public void sendRttRequest(String callId) {
         try {
-            Log.startSession("ICA.sRR");
+            Log.startSession("ICA.sRR", mOwnerPackageAbbreviation);
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -717,7 +717,7 @@
     @Override
     public void respondToRttRequest(String callId, int id, boolean accept) {
         try {
-            Log.startSession("ICA.rTRR");
+            Log.startSession("ICA.rTRR", mOwnerPackageAbbreviation);
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -739,7 +739,7 @@
     @Override
     public void stopRtt(String callId) {
         try {
-            Log.startSession("ICA.sRTT");
+            Log.startSession("ICA.sRTT", mOwnerPackageAbbreviation);
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -761,11 +761,16 @@
     @Override
     public void setRttMode(String callId, int mode) {
         try {
-            Log.startSession("ICA.sRM");
+            Log.startSession("ICA.sRM", mOwnerPackageAbbreviation);
             long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    // TODO
+                    Call call = mCallIdMapper.getCall(callId);
+                    if (call != null) {
+                        call.setRttMode(mode);
+                    } else {
+                        Log.w(this, "setRttMode(): call %s not found", callId);
+                    }
                 }
             } finally {
                 Binder.restoreCallingIdentity(token);
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 33ddf0a..3215605 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -98,14 +98,6 @@
             UUID.fromString("0c2adf96-353a-433c-afe9-1e5564f304f9");
     public static final String SET_IN_CALL_ADAPTER_ERROR_MSG =
             "Exception thrown while setting the in-call adapter.";
-    public static final UUID BIND_TO_IN_CALL_ERROR_UUID =
-            UUID.fromString("1261231d-b16a-4e0c-a322-623f8bb8e599");
-    public static final String BIND_TO_IN_CALL_ERROR_MSG =
-            "Failed to connect when attempting to bind to InCall.";
-    public static final UUID BIND_TO_IN_CALL_EMERGENCY_ERROR_UUID =
-            UUID.fromString("9ec8f1f0-3f0b-4079-9e9f-325f1262a8c7");
-    public static final String BIND_TO_IN_CALL_EMERGENCY_ERROR_MSG =
-            "Outgoing emergency call failed to connect when attempting to bind to InCall.";
 
     @VisibleForTesting
     public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
@@ -344,18 +336,15 @@
             mIsConnected = true;
             mInCallServiceInfo.setBindingStartTime(mClockProxy.elapsedRealtime());
             UserHandle userToBind = getUserFromCall(call);
+            boolean isManagedProfile = UserUtil.isManagedProfile(mContext, userToBind);
+            // Note that UserHandle.CURRENT fails to capture the work profile, so we need to handle
+            // it separately to ensure that the ICS is bound to the appropriate user.
+            userToBind = isManagedProfile ? userToBind : UserHandle.CURRENT;
             if (!mContext.bindServiceAsUser(intent, mServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                         | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
                         | Context.BIND_SCHEDULE_LIKE_TOP_APP, userToBind)) {
                 Log.w(this, "Failed to connect.");
-                if (call != null && call.isEmergencyCall()) {
-                    mAnomalyReporter.reportAnomaly(BIND_TO_IN_CALL_EMERGENCY_ERROR_UUID,
-                            BIND_TO_IN_CALL_EMERGENCY_ERROR_MSG);
-                } else {
-                    mAnomalyReporter.reportAnomaly(BIND_TO_IN_CALL_ERROR_UUID,
-                            BIND_TO_IN_CALL_ERROR_MSG);
-                }
                 mIsConnected = false;
             }
 
@@ -816,6 +805,9 @@
             }
             Call callToConnectWith = mCallIdMapper.getCalls().iterator().next();
             for (InCallServiceBindingConnection newConnection : newConnections) {
+                // Ensure we track the new sub-connection so that when we later disconnect we will
+                // be able to disconnect it.
+                mSubConnections.add(newConnection);
                 newConnection.connect(callToConnectWith);
             }
         }
@@ -2204,7 +2196,8 @@
      * Adds the call to the list of calls tracked by the {@link InCallController}.
      * @param call The call to add.
      */
-    private void addCall(Call call) {
+    @VisibleForTesting
+    public void addCall(Call call) {
         if (mCallIdMapper.getCalls().size() == 0) {
             mAppOpsManager.startWatchingActive(new String[] { OPSTR_RECORD_AUDIO },
                     java.lang.Runnable::run, this);
@@ -2600,8 +2593,17 @@
 
     private UserHandle getUserFromCall(Call call) {
         // Call may never be specified, so we can fall back to using the CallManager current user.
-        return call == null
-                ? mCallsManager.getCurrentUserHandle()
-                : call.getUserHandleFromTargetPhoneAccount();
+        if (call == null) {
+            return mCallsManager.getCurrentUserHandle();
+        } else {
+            UserHandle userFromCall = call.getUserHandleFromTargetPhoneAccount();
+            UserManager userManager = mContext.getSystemService(UserManager.class);
+            // Emergency call should never be blocked, so if the user associated with call is in
+            // quite mode, use the primary user for the emergency call.
+            if (call.isEmergencyCall() && userManager.isQuietModeEnabled(userFromCall)) {
+                return mCallsManager.getCurrentUserHandle();
+            }
+            return userFromCall;
+        }
     }
 }
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 0957eb4..3cc4aac 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -170,7 +170,7 @@
 
     private static final int RELATIVE_VOLUME_EMERGENCY = 100;
     private static final int RELATIVE_VOLUME_HIPRI = 80;
-    private static final int RELATIVE_VOLUME_LOPRI = 50;
+    private static final int RELATIVE_VOLUME_LOPRI = 30;
     private static final int RELATIVE_VOLUME_UNDEFINED = -1;
 
     // Buffer time (in msec) to add on to the tone timeout value. Needed mainly when the timeout
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 35f8d1e..8ce5dc3 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -105,10 +105,17 @@
         public static final String SET_RINGING = "SET_RINGING";
         public static final String SET_ANSWERED = "SET_ANSWERED";
         public static final String SET_DISCONNECTED = "SET_DISCONNECTED";
+        public static final String SKIP_CALL_LOG = "SKIP_CALL_LOG";
+        public static final String LOG_CALL = "LOG_CALL";
         public static final String SET_DISCONNECTING = "SET_DISCONNECTING";
         public static final String SET_SELECT_PHONE_ACCOUNT = "SET_SELECT_PHONE_ACCOUNT";
         public static final String SET_AUDIO_PROCESSING = "SET_AUDIO_PROCESSING";
         public static final String SET_SIMULATED_RINGING = "SET_SIMULATED_RINGING";
+        public static final String REQUEST_RTT = "REQUEST_RTT";
+        public static final String RESPOND_TO_RTT_REQUEST = "RESPOND_TO_RTT_REQUEST";
+        public static final String SET_RRT_MODE = "SET_RTT_MODE";
+        public static final String ON_RTT_FAILED = "ON_RTT_FAILED";
+        public static final String ON_RTT_REQUEST = "ON_RTT_REQUEST";
         public static final String REQUEST_HOLD = "REQUEST_HOLD";
         public static final String REQUEST_UNHOLD = "REQUEST_UNHOLD";
         public static final String REQUEST_DISCONNECT = "REQUEST_DISCONNECT";
@@ -216,6 +223,8 @@
         public static final String ICS_EXTRAS_CHANGED = "ICS_EXTRAS_CHANGED";
         public static final String FLASH_NOTIFICATION_START = "FLASH_NOTIFICATION_START";
         public static final String FLASH_NOTIFICATION_STOP = "FLASH_NOTIFICATION_STOP";
+        public static final String GAINED_FGS_DELEGATION = "GAINED_FGS_DELEGATION";
+        public static final String LOST_FGS_DELEGATION = "LOST_FGS_DELEGATION";
 
         public static class Timings {
             public static final String ACCEPT_TIMING = "accept";
diff --git a/src/com/android/server/telecom/MmiUtils.java b/src/com/android/server/telecom/MmiUtils.java
new file mode 100644
index 0000000..11f6d59
--- /dev/null
+++ b/src/com/android/server/telecom/MmiUtils.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 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.net.Uri;
+import android.telecom.PhoneAccount;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class MmiUtils {
+    // See TS 22.030 6.5.2 "Structure of the MMI"
+
+    private static Pattern sPatternSuppService = Pattern.compile(
+            "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+        /*       1  2                    3          4  5       6   7         8    9     10  11
+              12
+
+                 1 = Full string up to and including #
+                 2 = action (activation/interrogation/registration/erasure)
+                 3 = service code
+                 5 = SIA
+                 7 = SIB
+                 9 = SIC
+                 10 = dialing number
+        */
+    //regex groups
+    static final int MATCH_GROUP_POUND_STRING = 1;
+    static final int MATCH_GROUP_ACTION = 2; //(activation/interrogation/registration/erasure)
+    static final int MATCH_GROUP_SERVICE_CODE = 3;
+    static final int MATCH_GROUP_SIA = 5;
+    static final int MATCH_GROUP_SIB = 7;
+    static final int MATCH_GROUP_SIC = 9;
+    static final int MATCH_GROUP_PWD_CONFIRM = 11;
+    static final int MATCH_GROUP_DIALING_NUMBER = 12;
+    // Call Forwarding service codes
+    static final String SC_CFU = "21";
+    static final String SC_CFB = "67";
+    static final String SC_CFNRy = "61";
+    static final String SC_CFNR = "62";
+    static final String SC_CF_All = "002";
+    static final String SC_CF_All_Conditional = "004";
+
+    //see: https://nationalnanpa.com/number_resource_info/vsc_assignments.html
+    @SuppressWarnings("DoubleBraceInitialization")
+    private static Set<String> sDangerousVerticalServiceCodes = new HashSet<String>()
+    {{
+        add("*09"); //Selective Call Blocking/Reporting
+        add("*42"); //Change Forward-To Number for Cust Programmable Call Forwarding Don't Answer
+        add("*56"); //Change Forward-To Number for ISDN Call Forwarding
+        add("*60"); //Selective Call Rejection Activation
+        add("*63"); //Selective Call Forwarding Activation
+        add("*64"); //Selective Call Acceptance Activation
+        add("*68"); //Call Forwarding Busy Line/Don't Answer Activation
+        add("*72"); //Call Forwarding Activation
+        add("*77"); //Anonymous Call Rejection Activation
+        add("*78"); //Do Not Disturb Activation
+    }};
+    private final int mMinLenInDangerousSet;
+    private final int mMaxLenInDangerousSet;
+
+    public MmiUtils() {
+        mMinLenInDangerousSet = sDangerousVerticalServiceCodes.stream()
+                .mapToInt(String::length)
+                .min()
+                .getAsInt();
+        mMaxLenInDangerousSet = sDangerousVerticalServiceCodes.stream()
+                .mapToInt(String::length)
+                .max()
+                .getAsInt();
+    }
+
+    /**
+     * Determines if the Uri represents a call forwarding related mmi code
+     *
+     * @param handle The URI to call.
+     * @return {@code True} if the URI represents a call forwarding related MMI
+     */
+    private static boolean isCallForwardingMmiCode(Uri handle) {
+        Matcher m;
+        String dialString = handle.getSchemeSpecificPart();
+        m = sPatternSuppService.matcher(dialString);
+
+        if (m.matches()) {
+            String sc = m.group(MATCH_GROUP_SERVICE_CODE);
+            return sc != null &&
+                    (sc.equals(SC_CFU)
+                            || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
+                            || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
+                            || sc.equals(SC_CF_All_Conditional));
+        }
+
+        return false;
+
+    }
+
+    private static boolean isTelScheme(Uri handle) {
+        return (handle != null && handle.getSchemeSpecificPart() != null &&
+                handle.getScheme() != null &&
+                handle.getScheme().equals(PhoneAccount.SCHEME_TEL));
+    }
+
+    private boolean isDangerousVerticalServiceCode(Uri handle) {
+        if (isTelScheme(handle)) {
+            String dialedNumber = handle.getSchemeSpecificPart();
+            if (dialedNumber.length() >= mMinLenInDangerousSet && dialedNumber.charAt(0) == '*') {
+                //we only check vertical codes defined by The North American Numbering Plan Admin
+                //see: https://nationalnanpa.com/number_resource_info/vsc_assignments.html
+                //only two or 3-digit codes are valid as of today, but the code is generic enough.
+                for (int prefixLen = mMaxLenInDangerousSet; prefixLen <= mMaxLenInDangerousSet;
+                        prefixLen++) {
+                    String prefix = dialedNumber.substring(0, prefixLen);
+                    if (sDangerousVerticalServiceCodes.contains(prefix)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
+     * MMI codes which can be dialed when one or more calls are in progress.
+     * <P>
+     * Checks for numbers formatted similar to the MMI codes defined in:
+     * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)}
+     *
+     * @param handle The URI to call.
+     * @return {@code True} if the URI represents a number which could be an in-call MMI code.
+     */
+    public boolean isPotentialInCallMMICode(Uri handle) {
+        if (isTelScheme(handle)) {
+            String dialedNumber = handle.getSchemeSpecificPart();
+            return (dialedNumber.equals("0") ||
+                    (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
+                    (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
+                    dialedNumber.equals("3") ||
+                    dialedNumber.equals("4") ||
+                    dialedNumber.equals("5"));
+        }
+        return false;
+    }
+
+    public boolean isPotentialMMICode(Uri handle) {
+        return (handle != null && handle.getSchemeSpecificPart() != null
+                && handle.getSchemeSpecificPart().contains("#"));
+    }
+
+    /**
+     * Determines if the Uri represents a dangerous MMI code or Vertical Service code. Dangerous
+     * codes are ones, for which,
+     * we normally expect the user to be aware that an application has dialed them
+     *
+     * @param handle The URI to call.
+     * @return {@code True} if the URI represents a dangerous code
+     */
+    public boolean isDangerousMmiOrVerticalCode(Uri handle) {
+        if (isPotentialMMICode(handle)) {
+            return isCallForwardingMmiCode(handle);
+            //since some dangerous mmi codes could be carrier specific, in the future,
+            //we can add a carrier config item which can list carrier specific dangerous mmi codes
+        } else if (isDangerousVerticalServiceCode(handle)) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index 41aa2fb..8426d1f 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -16,14 +16,12 @@
 
 package com.android.server.telecom;
 
-import android.app.AppOpsManager;
-
 import android.app.Activity;
+import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Trace;
@@ -78,6 +76,7 @@
     private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
     private final TelecomSystem.SyncRoot mLock;
     private final DefaultDialerCache mDefaultDialerCache;
+    private final MmiUtils mMmiUtils;
 
     /*
      * Whether or not the outgoing call intent originated from the default phone application. If
@@ -101,7 +100,7 @@
     @VisibleForTesting
     public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager,
             Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
-            boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache) {
+            boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache, MmiUtils mmiUtils) {
         mContext = context;
         mCallsManager = callsManager;
         mIntent = intent;
@@ -109,6 +108,7 @@
         mIsDefaultOrSystemPhoneApp = isDefaultPhoneApp;
         mLock = mCallsManager.getLock();
         mDefaultDialerCache = defaultDialerCache;
+        mMmiUtils = mmiUtils;
     }
 
     /**
@@ -291,6 +291,16 @@
                     result.callImmediately = true;
                     result.requestRedirection = false;
                 }
+            } else if (mMmiUtils.isDangerousMmiOrVerticalCode(intent.getData())) {
+                if (!mIsDefaultOrSystemPhoneApp) {
+                    Log.w(this,
+                            "Potentially dangerous MMI code %s with CALL Intent %s can only be "
+                                    + "sent if caller is the system or default dialer",
+                            number, intent);
+                    launchSystemDialer(intent.getData());
+                    result.disconnectCause = DisconnectCause.OUTGOING_CANCELED;
+                    return result;
+                }
             }
         } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
             if (!isEmergencyNumber) {
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index a7824b4..f5a3450 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -873,13 +873,17 @@
     public void registerPhoneAccount(PhoneAccount account) {
         // Enforce the requirement that a connection service for a phone account has the correct
         // permission.
-        if (!hasTransactionalCallCapabilites(account) &&
+        if (!hasTransactionalCallCapabilities(account) &&
                 !phoneAccountRequiresBindPermission(account.getAccountHandle())) {
             Log.w(this,
                     "Phone account %s does not have BIND_TELECOM_CONNECTION_SERVICE permission.",
                     account.getAccountHandle());
-            throw new SecurityException("PhoneAccount connection service requires "
-                    + "BIND_TELECOM_CONNECTION_SERVICE permission.");
+            throw new SecurityException("Registering a PhoneAccount requires either: "
+                    + "(1) The Service definition requires that the ConnectionService is guarded"
+                    + " with the BIND_TELECOM_CONNECTION_SERVICE, which can be defined using the"
+                    + " android:permission tag as part of the Service definition. "
+                    + "(2) The PhoneAccount capability called"
+                    + " CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS.");
         }
         enforceCharacterLimit(account);
         enforceIconSizeLimit(account);
@@ -962,18 +966,20 @@
 
         String[] fields =
                 {"Package Name", "Class Name", "PhoneAccountHandle Id", "Label", "ShortDescription",
-                        "GroupId", "Address"};
+                        "GroupId", "Address", "SubscriptionAddress"};
         CharSequence[] args = {handle.getComponentName().getPackageName(),
                 handle.getComponentName().getClassName(), handle.getId(), account.getLabel(),
                 account.getShortDescription(), account.getGroupId(),
-                (account.getAddress() != null ? account.getAddress().toString() : "")};
+                (account.getAddress() != null ? account.getAddress().toString() : ""),
+                (account.getSubscriptionAddress() != null ?
+                        account.getSubscriptionAddress().toString() : "")};
 
         for (int i = 0; i < fields.length; i++) {
             if (args[i] != null && args[i].length() > MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT) {
                 EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
                         "enforceCharacterLimit");
-                throw new IllegalArgumentException("The PhoneAccount or PhoneAccountHandle"
-                        + fields[i] + " field has an invalid character count. PhoneAccount and "
+                throw new IllegalArgumentException("The PhoneAccount or PhoneAccountHandle ["
+                        + fields[i] + "] field has an invalid character count. PhoneAccount and "
                         + "PhoneAccountHandle String and Char-Sequence fields are limited to "
                         + MAX_PHONE_ACCOUNT_FIELD_CHAR_LIMIT + " characters.");
             }
@@ -1060,7 +1066,7 @@
         boolean isNewAccount;
 
         // add self-managed capability for transactional accounts that are missing it
-        if (hasTransactionalCallCapabilites(account) &&
+        if (hasTransactionalCallCapabilities(account) &&
                 !account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
             account = account.toBuilder()
                     .setCapabilities(account.getCapabilities()
@@ -1371,10 +1377,6 @@
             return false;
         }
 
-        if (hasTransactionalCallCapabilites(getPhoneAccountUnchecked(phoneAccountHandle))) {
-            return false;
-        }
-
         for (ResolveInfo resolveInfo : resolveInfos) {
             ServiceInfo serviceInfo = resolveInfo.serviceInfo;
             if (serviceInfo == null) {
@@ -1394,7 +1396,7 @@
     }
 
     @VisibleForTesting
-    public boolean hasTransactionalCallCapabilites(PhoneAccount phoneAccount) {
+    public boolean hasTransactionalCallCapabilities(PhoneAccount phoneAccount) {
         if (phoneAccount == null) {
             return false;
         }
@@ -1528,7 +1530,10 @@
             }
             PhoneAccountHandle handle = m.getAccountHandle();
 
-            if (resolveComponent(handle).isEmpty()) {
+            // PhoneAccounts with CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS do not require a
+            // ConnectionService and will fail [resolveComponent(PhoneAccountHandle)]. Bypass
+            // the [resolveComponent(PhoneAccountHandle)] for transactional accounts.
+            if (!hasTransactionalCallCapabilities(m) && resolveComponent(handle).isEmpty()) {
                 // This component cannot be resolved anymore; skip this one.
                 continue;
             }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index c04cf6b..cdacab0 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -33,6 +33,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -49,6 +51,8 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
 
 /**
  * Controls the ringtone player.
@@ -172,7 +176,7 @@
 
     /**
      * Call objects that are ringing, vibrating or call-waiting. These are used only for logging
-     * purposes.
+     * purposes (except mVibratingCall is also used to ensure consistency).
      */
     private Call mRingingCall;
     private Call mVibratingCall;
@@ -244,195 +248,257 @@
     }
 
     public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) {
-        if (foregroundCall == null) {
-            Log.wtf(this, "startRinging called with null foreground call.");
-            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;
-        }
-
-        // Use completable future to establish a timeout, not intent to make these work outside the
-        // main thread asynchronously
-        // TODO: moving these RingerAttributes calculation out of Telecom lock to avoid blocking.
-        CompletableFuture<RingerAttributes> ringerAttributesFuture = CompletableFuture
-                .supplyAsync(() -> getRingerAttributes(foregroundCall, isHfpDeviceAttached),
-                        new LoggedHandlerExecutor(getHandler(), "R.sR", null));
-
-        RingerAttributes attributes = null;
+        boolean deferBlockOnRingingFuture = false;
+        // try-finally to ensure that the block on ringing future is always called.
         try {
-            mAttributesLatch = new CountDownLatch(1);
-            attributes = ringerAttributesFuture.get(
-                    RINGER_ATTRIBUTES_TIMEOUT, TimeUnit.MILLISECONDS);
-        } catch (ExecutionException | InterruptedException | TimeoutException e) {
-            // Keep attributes as null
-            Log.i(this, "getAttributes error: " + e);
-        }
-
-        if (attributes == null) {
-            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "RingerAttributes error");
-            return false;
-        }
-
-        if (attributes.isEndEarly()) {
-            boolean acquireAudioFocus = attributes.shouldAcquireAudioFocus();
-            if (attributes.letDialerHandleRinging()) {
-                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Dialer handles");
-                // Dialer will setup a ringtone, provide the audio focus if its audible.
-                acquireAudioFocus |= attributes.isRingerAudible();
+            if (foregroundCall == null) {
+                Log.wtf(this, "startRinging called with null foreground call.");
+                return false;
             }
 
-            if (attributes.isSilentRingingRequested()) {
-                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Silent ringing "
-                        + "requested");
+            if (foregroundCall.getState() != CallState.RINGING
+                    && foregroundCall.getState() != CallState.SIMULATED_RINGING) {
+                // It's 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;
             }
-            if (mBlockOnRingingFuture != null) {
-                mBlockOnRingingFuture.complete(null);
+
+            // Use completable future to establish a timeout, not intent to make these work outside
+            // the main thread asynchronously
+            // TODO: moving these RingerAttributes calculation out of Telecom lock to avoid blocking
+            CompletableFuture<RingerAttributes> ringerAttributesFuture = CompletableFuture
+                    .supplyAsync(() -> getRingerAttributes(foregroundCall, isHfpDeviceAttached),
+                            new LoggedHandlerExecutor(getHandler(), "R.sR", null));
+
+            RingerAttributes attributes = null;
+            try {
+                mAttributesLatch = new CountDownLatch(1);
+                attributes = ringerAttributesFuture.get(
+                        RINGER_ATTRIBUTES_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (ExecutionException | InterruptedException | TimeoutException e) {
+                // Keep attributes as null
+                Log.i(this, "getAttributes error: " + e);
             }
-            return acquireAudioFocus;
-        }
 
-        stopCallWaiting();
+            if (attributes == null) {
+                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
+                        "RingerAttributes error");
+                return false;
+            }
 
-        final boolean shouldFlash = attributes.shouldRingForContact();
-        if (mAccessibilityManagerAdapter != null && shouldFlash) {
-            Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
-            getHandler().post(() ->
-                    mAccessibilityManagerAdapter.startFlashNotificationSequence(mContext,
-                        AccessibilityManager.FLASH_REASON_CALL));
-        }
+            if (attributes.isEndEarly()) {
+                boolean acquireAudioFocus = attributes.shouldAcquireAudioFocus();
+                if (attributes.letDialerHandleRinging()) {
+                    Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Dialer handles");
+                    // Dialer will setup a ringtone, provide the audio focus if its audible.
+                    acquireAudioFocus |= attributes.isRingerAudible();
+                }
 
-        // Determine if the settings and DND mode indicate that the vibrator can be used right now.
-        final boolean isVibratorEnabled =
-            isVibratorEnabled(mContext, attributes.shouldRingForContact());
-        boolean shouldApplyRampingRinger =
-                isVibratorEnabled && mSystemSettingsUtil.isRampingRingerEnabled(mContext);
+                if (attributes.isSilentRingingRequested()) {
+                    Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Silent ringing "
+                            + "requested");
+                }
+                if (attributes.isWorkProfileInQuietMode()) {
+                    Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
+                            "Work profile in quiet mode");
+                }
+                return acquireAudioFocus;
+            }
 
-        boolean isHapticOnly = false;
-        boolean useCustomVibrationEffect = false;
+            stopCallWaiting();
 
-        mVolumeShaperConfig = null;
+            final boolean shouldFlash = attributes.shouldRingForContact();
+            if (mAccessibilityManagerAdapter != null && shouldFlash) {
+                Log.addEvent(foregroundCall, LogUtils.Events.FLASH_NOTIFICATION_START);
+                getHandler().post(() ->
+                        mAccessibilityManagerAdapter.startFlashNotificationSequence(mContext,
+                                AccessibilityManager.FLASH_REASON_CALL));
+            }
 
-        if (attributes.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 (shouldApplyRampingRinger) {
-                Log.i(this, "create ramping ringer.");
-                float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION)
-                    / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
-                mVolumeShaperConfig =
-                    new VolumeShaper.Configuration.Builder()
-                        .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION)
-                        .setCurve(
-                            new float[] {0.f, silencePoint + EPSILON /*keep monotonicity*/, 1.f},
-                            new float[] {0.f, 0.f, 1.f})
-                        .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
-                        .build();
-                if (mSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled()) {
-                  useCustomVibrationEffect = true;
+            // Determine if the settings and DND mode indicate that the vibrator can be used right
+            // now.
+            final boolean isVibratorEnabled =
+                    isVibratorEnabled(mContext, attributes.shouldRingForContact());
+            boolean shouldApplyRampingRinger =
+                    isVibratorEnabled && mSystemSettingsUtil.isRampingRingerEnabled(mContext);
+
+            boolean isHapticOnly = false;
+            boolean useCustomVibrationEffect = false;
+
+            mVolumeShaperConfig = null;
+
+            if (attributes.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 (shouldApplyRampingRinger) {
+                    Log.i(this, "create ramping ringer.");
+                    float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION)
+                            / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION);
+                    mVolumeShaperConfig =
+                            new VolumeShaper.Configuration.Builder()
+                                    .setDuration(RAMPING_RINGER_VIBRATION_DURATION
+                                            + RAMPING_RINGER_DURATION)
+                                    .setCurve(
+                                            new float[]{0.f, silencePoint + EPSILON
+                                                    /*keep monotonicity*/, 1.f},
+                                            new float[]{0.f, 0.f, 1.f})
+                                    .setInterpolatorType(
+                                            VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)
+                                    .build();
+                    if (mSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled()) {
+                        useCustomVibrationEffect = true;
+                    }
+                } else {
+                    if (DEBUG_RINGER) {
+                        Log.i(this, "Create ringer with custom vibration effect");
+                    }
+                    // Ramping ringtone is not enabled.
+                    useCustomVibrationEffect = true;
                 }
             } else {
-                if (DEBUG_RINGER) {
-                  Log.i(this, "Create ringer with custom vibration effect");
-                }
-                // Ramping ringtone is not enabled.
-                useCustomVibrationEffect = true;
-            }
-        } else {
-            Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
-                "Inaudible: " + attributes.getInaudibleReason()
-                    + " isVibratorEnabled=" + isVibratorEnabled);
+                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING,
+                        "Inaudible: " + attributes.getInaudibleReason()
+                                + " isVibratorEnabled=" + isVibratorEnabled);
 
-            if (isVibratorEnabled) {
-                // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
-                // Use haptic-only ringtone or do not play anything.
-                isHapticOnly = true;
-                if (DEBUG_RINGER) {
-                  Log.i(this, "Set ringtone as haptic only: " + isHapticOnly);
+                if (isVibratorEnabled) {
+                    // If ringer is not audible for this call, then the phone is in "Vibrate" mode.
+                    // Use haptic-only ringtone or do not play anything.
+                    isHapticOnly = true;
+                    if (DEBUG_RINGER) {
+                        Log.i(this, "Set ringtone as haptic only: " + isHapticOnly);
+                    }
+                } else {
+                    foregroundCall.setUserMissed(USER_MISSED_NO_VIBRATE);
+                    return attributes.shouldAcquireAudioFocus(); // ringer not audible
+                }
+            }
+
+            boolean hapticChannelsMuted = !isVibratorEnabled || !mIsHapticPlaybackSupportedByDevice;
+            if (shouldApplyRampingRinger
+                    && !mSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled()
+                    && isVibratorEnabled) {
+                Log.i(this, "Muted haptic channels since audio coupled ramping ringer is disabled");
+                hapticChannelsMuted = true;
+            } else if (hapticChannelsMuted) {
+                Log.i(this,
+                        "Muted haptic channels isVibratorEnabled=%s, hapticPlaybackSupported=%s",
+                        isVibratorEnabled, mIsHapticPlaybackSupportedByDevice);
+            }
+            // Defer ringtone creation to the async player thread.
+            Supplier<Ringtone> ringtoneSupplier;
+            final boolean finalHapticChannelsMuted = hapticChannelsMuted;
+            if (isHapticOnly) {
+                if (hapticChannelsMuted) {
+                    Log.i(this,
+                            "want haptic only ringtone but haptics are muted, skip ringtone play");
+                    ringtoneSupplier = null;
+                } else {
+                    ringtoneSupplier = mRingtoneFactory::getHapticOnlyRingtone;
                 }
             } else {
-                if (mBlockOnRingingFuture != null) {
-                  mBlockOnRingingFuture.complete(null);
-                }
+                ringtoneSupplier = () -> mRingtoneFactory.getRingtone(
+                        foregroundCall, mVolumeShaperConfig, finalHapticChannelsMuted);
+            }
+
+            // If vibration will be done, reserve the vibrator.
+            boolean vibratorReserved = isVibratorEnabled && attributes.shouldRingForContact()
+                && tryReserveVibration(foregroundCall);
+            if (!vibratorReserved) {
                 foregroundCall.setUserMissed(USER_MISSED_NO_VIBRATE);
-                return attributes.shouldAcquireAudioFocus(); // ringer not audible
+                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION,
+                        "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, "
+                                + "isVibratorEnabled=%b",
+                        mVibrator.hasVibrator(),
+                        mSystemSettingsUtil.isRingVibrationEnabled(mContext),
+                        mAudioManager.getRingerMode(), isVibratorEnabled);
             }
-        }
 
-        boolean hapticChannelsMuted = !isVibratorEnabled || !mIsHapticPlaybackSupportedByDevice;
-        if (shouldApplyRampingRinger
-            && !mSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled()
-            && isVibratorEnabled) {
-            Log.i(this, "Muted haptic channels since audio coupled ramping ringer is disabled");
-            hapticChannelsMuted = true;
-        } else if (hapticChannelsMuted) {
-            Log.i(this, "Muted haptic channels isVibratorEnabled=%s, hapticPlaybackSupported=%s",
-                isVibratorEnabled, mIsHapticPlaybackSupportedByDevice);
-        }
-        Ringtone ringtone;
-        if (isHapticOnly) {
-            if (hapticChannelsMuted) {
-                Log.i(this, "want haptic only ringtone but haptics are muted, skip ringtone play");
-                ringtone = null;
+            // The vibration logic depends on the loaded ringtone, but we need to defer the ringtone
+            // load to the async ringtone thread. Hence, we bundle up the final part of this method
+            // for that thread to run after loading the ringtone. This logic is intended to run even
+            // if the loaded ringtone is null. However if a stop event arrives before the ringtone
+            // creation finishes, then this consumer can be skipped.
+            final boolean finalUseCustomVibrationEffect = useCustomVibrationEffect;
+            BiConsumer<Ringtone, Boolean> afterRingtoneLogic =
+                    (Ringtone ringtone, Boolean stopped) -> {
+                try {
+                    if (stopped.booleanValue() || !vibratorReserved) {
+                        // don't start vibration if the ringing is already abandoned, or the
+                        // vibrator wasn't reserved. This still triggers the mBlockOnRingingFuture.
+                        return;
+                    }
+                    final VibrationEffect vibrationEffect;
+                    if (ringtone != null && finalUseCustomVibrationEffect) {
+                        if (DEBUG_RINGER) {
+                            Log.d(this, "Using ringtone defined vibration effect.");
+                        }
+                        vibrationEffect = getVibrationEffectForRingtone(ringtone);
+                    } else {
+                        vibrationEffect = mDefaultVibrationEffect;
+                    }
+
+                    boolean isUsingAudioCoupledHaptics =
+                            !finalHapticChannelsMuted && ringtone != null
+                                    && ringtone.hasHapticChannels();
+                    vibrateIfNeeded(isUsingAudioCoupledHaptics, foregroundCall, vibrationEffect);
+                } finally {
+                    // This is used to signal to tests that the async play() call has completed.
+                    if (mBlockOnRingingFuture != null) {
+                        mBlockOnRingingFuture.complete(null);
+                    }
+                }
+            };
+            deferBlockOnRingingFuture = true;  // Run in vibrationLogic.
+            if (ringtoneSupplier != null) {
+                mRingtonePlayer.play(ringtoneSupplier, afterRingtoneLogic);
             } else {
-                ringtone = mRingtoneFactory.getHapticOnlyRingtone();
+                afterRingtoneLogic.accept(/* ringtone= */ null, /* stopped= */ false);
             }
-        } else {
-            ringtone = mRingtoneFactory.getRingtone(
-                foregroundCall, mVolumeShaperConfig, hapticChannelsMuted);
-        }
 
-        final VibrationEffect vibrationEffect;
-        if (useCustomVibrationEffect) {
-            if (DEBUG_RINGER) {
-                Log.d(this, "Using ringtone defined vibration effect.");
-            }
-            vibrationEffect = getVibrationEffectForRingtone(ringtone);
-        } else {
-            vibrationEffect = mDefaultVibrationEffect;
-        }
-
-        if (ringtone == null) {
-            Log.w(this, "No ringtone was found bail out from playing.");
-            // No ringtone was found, try as a last resort to check if vibration can be performed.
-            vibrateIfNeeded(/* isUsingAudioCoupledHaptics */ false, attributes, foregroundCall,
-                vibrationEffect, isVibratorEnabled);
-            if (mBlockOnRingingFuture != null) {
+            // shouldAcquireAudioFocus is meant to be true, but that check is deferred to here
+            // because until now is when we actually know if the ringtone loading worked.
+            return attributes.shouldAcquireAudioFocus()
+                    || (!isHapticOnly && attributes.isRingerAudible());
+        } finally {
+            // This is used to signal to tests that the async play() call has completed. It can
+            // be deferred into AsyncRingtonePlayer
+            if (mBlockOnRingingFuture != null && !deferBlockOnRingingFuture) {
                 mBlockOnRingingFuture.complete(null);
             }
-            return attributes.shouldAcquireAudioFocus();
         }
-
-        boolean isUsingAudioCoupledHaptics = !hapticChannelsMuted && ringtone.hasHapticChannels();
-
-        // There is a ringtone and we want to play it.
-        mRingtonePlayer.play(ringtone);
-
-        vibrateIfNeeded(isUsingAudioCoupledHaptics, attributes, foregroundCall, vibrationEffect,
-            isVibratorEnabled);
-        if (mBlockOnRingingFuture != null) {
-            mBlockOnRingingFuture.complete(null);
-        }
-
-        // shouldAcquireAudioFocus is meant to be true, but that check is deferred to here
-        // because until now is when we actually know if the ringtone loading worked.
-        return attributes.shouldAcquireAudioFocus() || attributes.isRingerAudible();
     }
 
-    private void vibrateIfNeeded(boolean isUsingAudioCoupledHaptics, RingerAttributes attributes,
-        Call foregroundCall, VibrationEffect effect, boolean isVibratorEnabled) {
-        final boolean shouldRingForContact = attributes.shouldRingForContact();
+    /**
+     * Try to reserve the vibrator for this call, returning false if it's already committed.
+     * The vibration will be started by AsyncRingtonePlayer to ensure timing is aligned with the
+     * audio. The logic uses mVibratingCall to say which call is currently getting ready to vibrate,
+     * or actually vibrating (indicated by mIsVibrating).
+     *
+     * Once reserved, the vibrateIfNeeded method is expected to be called. Note that if
+     * audio-coupled haptics were used instead of vibrator, the reservation still stays until
+     * ringing is stopped, because the vibrator is exclusive to a single vibration source.
+     *
+     * Note that this "reservation" is only local to the Ringer - it's not locking the vibrator, so
+     * if it's busy with some other important vibration, this ringer's one may not displace it.
+     */
+    private boolean tryReserveVibration(Call foregroundCall) {
+        synchronized (mLock) {
+            if (mVibratingCall != null || mIsVibrating) {
+                return false;
+            }
+            mVibratingCall = foregroundCall;
+            return true;
+        }
+   }
 
+    private void vibrateIfNeeded(boolean isUsingAudioCoupledHaptics, Call foregroundCall,
+            VibrationEffect effect) {
         if (isUsingAudioCoupledHaptics) {
             Log.addEvent(
                 foregroundCall, LogUtils.Events.SKIP_VIBRATION, "using audio-coupled haptics");
@@ -440,48 +506,40 @@
         }
 
         synchronized (mLock) {
-            if (isVibratorEnabled && !mIsVibrating && shouldRingForContact) {
+            // Ensure the reservation is live. The mIsVibrating check should be redundant.
+            if (foregroundCall == mVibratingCall && !mIsVibrating) {
                 Log.addEvent(foregroundCall, LogUtils.Events.START_VIBRATOR,
                     "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
                     mVibrator.hasVibrator(), mSystemSettingsUtil.isRingVibrationEnabled(mContext),
                     mAudioManager.getRingerMode(), mIsVibrating);
-                mVibratingCall = foregroundCall;
                 mIsVibrating = true;
                 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
                 Log.i(this, "start vibration.");
-            } else {
-                foregroundCall.setUserMissed(USER_MISSED_NO_VIBRATE);
-                Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION,
-                    "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b",
-                    mVibrator.hasVibrator(), mSystemSettingsUtil.isRingVibrationEnabled(mContext),
-                    mAudioManager.getRingerMode(), mIsVibrating);
             }
+            // else stopped already: this isn't started unless a reservation was made.
         }
     }
 
-    private VibrationEffect getVibrationEffectForRingtone(Ringtone ringtone) {
-        VibrationEffect effect = null;
-        Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null;
-        if (ringtoneUri != null) {
-            try {
-                effect = mVibrationEffectProxy.get(ringtoneUri, mContext);
-                if (effect == null) {
-                  Log.i(this, "did not find vibration effect, falling back to default vibration");
-                }
-            } catch (IllegalArgumentException iae) {
-                // Deep in the bowels of the VibrationEffect class it is possible for an
-                // IllegalArgumentException to be thrown if there is an invalid URI specified in the
-                // device config, or a content provider failure.  Rather than crashing the Telecom
-                // process we will just use the default vibration effect.
-                Log.e(this, iae, "getVibrationEffectForRingtone: failed to get vibration effect");
-                effect = null;
+    private VibrationEffect getVibrationEffectForRingtone(@NonNull Ringtone ringtone) {
+        Uri ringtoneUri = ringtone.getUri();
+        if (ringtoneUri == null) {
+            return mDefaultVibrationEffect;
+        }
+        try {
+            VibrationEffect effect = mVibrationEffectProxy.get(ringtoneUri, mContext);
+            if (effect == null) {
+              Log.i(this, "did not find vibration effect, falling back to default vibration");
+              return mDefaultVibrationEffect;
             }
+            return effect;
+        } catch (IllegalArgumentException iae) {
+            // Deep in the bowels of the VibrationEffect class it is possible for an
+            // IllegalArgumentException to be thrown if there is an invalid URI specified in the
+            // device config, or a content provider failure.  Rather than crashing the Telecom
+            // process we will just use the default vibration effect.
+            Log.e(this, iae, "getVibrationEffectForRingtone: failed to get vibration effect");
+            return mDefaultVibrationEffect;
         }
-
-        if (effect == null) {
-            effect = mDefaultVibrationEffect;
-        }
-        return effect;
     }
 
     public void startCallWaiting(Call call) {
@@ -537,8 +595,8 @@
                 Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR);
                 mVibrator.cancel();
                 mIsVibrating = false;
-                mVibratingCall = null;
             }
+            mVibratingCall = null;  // Prevents vibrations from starting via AsyncRingtonePlayer.
         }
     }
 
@@ -640,16 +698,20 @@
         boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging(
                 call.getUserHandleFromTargetPhoneAccount());
         timer.record("letDialerHandleRinging");
+        boolean isWorkProfileInQuietMode =
+                isProfileInQuietMode(call.getUserHandleFromTargetPhoneAccount());
+        timer.record("isWorkProfileInQuietMode");
 
         Log.i(this, "startRinging timings: " + timer);
         boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged ||
-                hasExternalRinger || isSilentRingingRequested;
+                hasExternalRinger || isSilentRingingRequested || isWorkProfileInQuietMode;
 
         if (endEarly) {
             Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " +
-                            "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s",
+                            "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s, " +
+                            "isWorkProfileInQuietMode=%s",
                     isTheaterModeOn, letDialerHandleRinging, isSelfManaged, hasExternalRinger,
-                    isSilentRingingRequested);
+                    isSilentRingingRequested, isWorkProfileInQuietMode);
         }
 
         // Acquire audio focus under any of the following conditions:
@@ -657,8 +719,8 @@
         // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone
         //    present. (This check is deferred until ringer knows the ringtone)
         // 3. The call is self-managed.
-        boolean shouldAcquireAudioFocus =
-            (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged;
+        boolean shouldAcquireAudioFocus = !isWorkProfileInQuietMode &&
+                ((isHfpDeviceAttached && shouldRingForContact) || isSelfManaged);
 
         // Set missed reason according to attributes
         if (!isVolumeOverZero) {
@@ -676,9 +738,15 @@
                 .setInaudibleReason(inaudibleReason)
                 .setShouldRingForContact(shouldRingForContact)
                 .setSilentRingingRequested(isSilentRingingRequested)
+                .setWorkProfileQuietMode(isWorkProfileInQuietMode)
                 .build();
     }
 
+    private boolean isProfileInQuietMode(UserHandle user) {
+        UserManager um = mContext.getSystemService(UserManager.class);
+        return um.isManagedProfile(user.getIdentifier()) && um.isQuietModeEnabled(user);
+    }
+
     private Handler getHandler() {
         if (mHandler == null) {
             HandlerThread handlerThread = new HandlerThread("Ringer");
diff --git a/src/com/android/server/telecom/RingerAttributes.java b/src/com/android/server/telecom/RingerAttributes.java
index 840d815..e0d3e1c 100644
--- a/src/com/android/server/telecom/RingerAttributes.java
+++ b/src/com/android/server/telecom/RingerAttributes.java
@@ -25,6 +25,7 @@
         private String mInaudibleReason;
         private boolean mShouldRingForContact;
         private boolean mSilentRingingRequested;
+        private boolean mWorkProfileQuietMode;
 
         public RingerAttributes.Builder setEndEarly(boolean endEarly) {
             mEndEarly = endEarly;
@@ -61,10 +62,15 @@
             return this;
         }
 
+        public RingerAttributes.Builder setWorkProfileQuietMode(boolean workProfileQuietMode) {
+            mWorkProfileQuietMode = workProfileQuietMode;
+            return this;
+        }
+
         public RingerAttributes build() {
             return new RingerAttributes(mEndEarly, mLetDialerHandleRinging, mAcquireAudioFocus,
                     mRingerAudible, mInaudibleReason, mShouldRingForContact,
-                    mSilentRingingRequested);
+                    mSilentRingingRequested, mWorkProfileQuietMode);
         }
     }
 
@@ -75,10 +81,12 @@
     private String mInaudibleReason;
     private boolean mShouldRingForContact;
     private boolean mSilentRingingRequested;
+    private boolean mWorkProfileQuietMode;
 
     private RingerAttributes(boolean endEarly, boolean letDialerHandleRinging,
             boolean acquireAudioFocus, boolean ringerAudible, String inaudibleReason,
-            boolean shouldRingForContact, boolean silentRingingRequested) {
+            boolean shouldRingForContact, boolean silentRingingRequested,
+            boolean workProfileQuietMode) {
         mEndEarly = endEarly;
         mLetDialerHandleRinging = letDialerHandleRinging;
         mAcquireAudioFocus = acquireAudioFocus;
@@ -86,6 +94,7 @@
         mInaudibleReason = inaudibleReason;
         mShouldRingForContact = shouldRingForContact;
         mSilentRingingRequested = silentRingingRequested;
+        mWorkProfileQuietMode = workProfileQuietMode;
     }
 
     public boolean isEndEarly() {
@@ -115,4 +124,8 @@
     public boolean isSilentRingingRequested() {
         return mSilentRingingRequested;
     }
+
+    public boolean isWorkProfileInQuietMode() {
+        return mWorkProfileQuietMode;
+    }
 }
diff --git a/src/com/android/server/telecom/RingtoneFactory.java b/src/com/android/server/telecom/RingtoneFactory.java
index 68c95a6..309c86e 100644
--- a/src/com/android/server/telecom/RingtoneFactory.java
+++ b/src/com/android/server/telecom/RingtoneFactory.java
@@ -66,12 +66,16 @@
 
     public Ringtone getRingtone(Call incomingCall,
             @Nullable VolumeShaper.Configuration volumeShaperConfig, boolean hapticChannelsMuted) {
+        // Initializing ringtones on the main thread can deadlock
+        ThreadUtil.checkNotOnMainThread();
+
         AudioAttributes audioAttrs = getDefaultRingtoneAudioAttributes(hapticChannelsMuted);
 
         // Use the default ringtone of the work profile if the contact is a work profile contact.
+        // or the default ringtone of the receiving user.
         Context userContext = isWorkContact(incomingCall) ?
                 getWorkProfileContextForUser(mCallsManager.getCurrentUserHandle()) :
-                getContextForUserHandle(mCallsManager.getCurrentUserHandle());
+                getContextForUserHandle(incomingCall.getUserHandleFromTargetPhoneAccount());
         Uri ringtoneUri = incomingCall.getRingtone();
         Ringtone ringtone = null;
 
@@ -116,7 +120,7 @@
         return ringtone;
     }
 
-    public AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) {
+    private AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) {
         return new AudioAttributes.Builder()
             .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -127,6 +131,8 @@
     /** Returns a ringtone to be used when ringer is not audible for the incoming call. */
     @Nullable
     public Ringtone getHapticOnlyRingtone() {
+        // Initializing ringtones on the main thread can deadlock
+        ThreadUtil.checkNotOnMainThread();
         Uri ringtoneUri = Uri.parse("file://" + mContext.getString(
                 com.android.internal.R.string.config_defaultRingtoneVibrationSound));
         AudioAttributes audioAttrs = getDefaultRingtoneAudioAttributes(
diff --git a/src/com/android/server/telecom/StatusBarNotifier.java b/src/com/android/server/telecom/StatusBarNotifier.java
index d1de958..772335e 100644
--- a/src/com/android/server/telecom/StatusBarNotifier.java
+++ b/src/com/android/server/telecom/StatusBarNotifier.java
@@ -79,13 +79,15 @@
         mIsShowingMute = isMuted;
     }
 
+    /**
+     * Update the status bar manager with the new speakerphone state.
+     *
+     * IMPORTANT: DO NOT call into any Telecom code here; this is usually scheduled on an async
+     * executor to save Telecom from blocking on outgoing binder calls.
+     * @param isSpeakerphone
+     */
     @VisibleForTesting
     public void notifySpeakerphone(boolean isSpeakerphone) {
-        // Never display anything if there are no calls.
-        if (!mCallsManager.hasAnyCalls()) {
-            isSpeakerphone = false;
-        }
-
         if (mIsShowingSpeakerphone == isSpeakerphone) {
             return;
         }
diff --git a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
index e1f2d08..0be90e0 100644
--- a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
+++ b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
@@ -16,10 +16,12 @@
 
 package com.android.server.telecom;
 
+import android.app.BroadcastOptions;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.telecom.Log;
 import android.widget.Toast;
@@ -247,8 +249,14 @@
      * Closes open system dialogs and the notification shade.
      */
     private void closeSystemDialogs(Context context) {
-        Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        context.sendBroadcastAsUser(intent, UserHandle.ALL);
+        Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        Bundle options = BroadcastOptions.makeBasic()
+                .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+                .toBundle();
+        context.sendBroadcastAsUser(intent, UserHandle.ALL, null /* receiverPermission */,
+                options);
     }
 
     private void sendSmsIntent(Intent intent, UserHandle userHandle) {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 6826290..99a8d3d 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CALL_PHONE;
 import static android.Manifest.permission.CALL_PRIVILEGED;
 import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.MANAGE_OWN_CALLS;
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
 import static android.Manifest.permission.READ_PHONE_NUMBERS;
 import static android.Manifest.permission.READ_PHONE_STATE;
@@ -26,11 +27,10 @@
 import static android.Manifest.permission.READ_SMS;
 import static android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.Manifest.permission.MANAGE_OWN_CALLS;
 import static android.telecom.CallAttributes.DIRECTION_INCOMING;
 import static android.telecom.CallAttributes.DIRECTION_OUTGOING;
-import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
 import static android.telecom.CallException.CODE_ERROR_UNKNOWN;
+import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -47,6 +47,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -58,7 +59,6 @@
 import android.provider.BlockedNumberContract;
 import android.provider.Settings;
 import android.telecom.CallAttributes;
-
 import android.telecom.CallException;
 import android.telecom.Log;
 import android.telecom.PhoneAccount;
@@ -90,6 +90,7 @@
 import java.io.PrintWriter;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 
@@ -193,16 +194,21 @@
                 enforcePhoneAccountIsRegisteredEnabled(handle, handle.getUserHandle());
                 enforceCallingPackage(callingPackage, "addCall");
 
+                // add extras about info used for FGS delegation
+                Bundle extras = new Bundle();
+                extras.putInt(CallAttributes.CALLER_UID_KEY, Binder.getCallingUid());
+                extras.putInt(CallAttributes.CALLER_PID_KEY, Binder.getCallingPid());
+
                 VoipCallTransaction transaction = null;
                 // create transaction based on the call direction
                 switch (callAttributes.getDirection()) {
                     case DIRECTION_OUTGOING:
                         transaction = new OutgoingCallTransaction(callId, mContext, callAttributes,
-                                mCallsManager);
+                                mCallsManager, extras);
                         break;
                     case DIRECTION_INCOMING:
                         transaction = new IncomingCallTransaction(callId, callAttributes,
-                                mCallsManager);
+                                mCallsManager, extras);
                         break;
                     default:
                         throw new IllegalArgumentException(String.format("Invalid Call Direction. "
@@ -362,12 +368,13 @@
                 }
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    boolean crossUserAccess = hasInAppCrossUserPermission();
                     long token = Binder.clearCallingIdentity();
                     try {
                         return new ParceledListSlice<>(
                                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null,
                                         includeDisabledAccounts, callingUserHandle,
-                                        hasInAppCrossUserPermission()));
+                                        crossUserAccess));
                     } catch (Exception e) {
                         Log.e(this, e, "getCallCapablePhoneAccounts");
                         mAnomalyReporter.reportAnomaly(GET_CALL_CAPABLE_ACCOUNTS_ERROR_UUID,
@@ -540,15 +547,18 @@
                             throw e;
                         }
                     }
+                    Set<String> permissions = computePermissionsForBoundPackage(
+                            Set.of(MODIFY_PHONE_STATE), null);
                     long token = Binder.clearCallingIdentity();
                     try {
                         // In ideal case, we should not resolve the handle across profiles. But
                         // given the fact that profile's call is handled by its parent user's
                         // in-call UI, parent user's in call UI need to be able to get phone account
                         // from the profile's phone account handle.
-                        return mPhoneAccountRegistrar
+                        PhoneAccount account = mPhoneAccountRegistrar
                                 .getPhoneAccount(accountHandle, callingUserHandle,
                                         /* acrossProfiles */ true);
+                        return maybeCleansePhoneAccount(account, permissions);
                     } catch (Exception e) {
                         Log.e(this, e, "getPhoneAccount %s", accountHandle);
                         mAnomalyReporter.reportAnomaly(GET_PHONE_ACCOUNT_ERROR_UUID,
@@ -637,11 +647,12 @@
 
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    boolean crossUserAccess = hasInAppCrossUserPermission();
                     long token = Binder.clearCallingIdentity();
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
                                 .getAllPhoneAccountHandles(callingUserHandle,
-                                        hasInAppCrossUserPermission()));
+                                        crossUserAccess));
                     } catch (Exception e) {
                         Log.e(this, e, "getAllPhoneAccounts");
                         throw e;
@@ -767,6 +778,9 @@
                                     .build();
                         }
 
+                        // Validate the profile boundary of the given image URI.
+                        validateAccountIconUserBoundary(account.getIcon());
+
                         final long token = Binder.clearCallingIdentity();
                         try {
                             Log.i(this, "registerPhoneAccount: account=%s",
@@ -945,12 +959,13 @@
                 synchronized (mLock) {
                     enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
                     UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    boolean crossUserAccess = hasInAppCrossUserPermission();
                     long token = Binder.clearCallingIdentity();
                     try {
                         Log.i(this, "Silence Ringer requested by %s", callingPackage);
                         Set<UserHandle> userHandles = mCallsManager.getCallAudioManager().
                                 silenceRingers(mContext, callingUserHandle,
-                                        hasInAppCrossUserPermission());
+                                        crossUserAccess);
                         mCallsManager.getInCallController().silenceRinger(userHandles);
                     } finally {
                         Binder.restoreCallingIdentity(token);
@@ -1739,7 +1754,6 @@
                 enforceCallingPackage(callingPackage, "placeCall");
 
                 PhoneAccountHandle phoneAccountHandle = null;
-                boolean clearPhoneAccountHandleExtra = false;
                 if (extras != null) {
                     phoneAccountHandle = extras.getParcelable(
                             TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
@@ -1748,33 +1762,42 @@
                         extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
                     }
                 }
-                boolean isSelfManaged = phoneAccountHandle != null &&
+                ComponentName phoneAccountComponentName = phoneAccountHandle != null
+                        ? phoneAccountHandle.getComponentName() : null;
+                String phoneAccountPackageName = phoneAccountComponentName != null
+                        ? phoneAccountComponentName.getPackageName() : null;
+                boolean isCallerOwnerOfPhoneAccount =
+                        callingPackage.equals(phoneAccountPackageName);
+                boolean isSelfManagedPhoneAccount =
                         isSelfManagedConnectionService(phoneAccountHandle);
-                if (isSelfManaged) {
-                    try {
-                        mContext.enforceCallingOrSelfPermission(
-                                Manifest.permission.MANAGE_OWN_CALLS,
-                                "Self-managed ConnectionServices require "
-                                        + "MANAGE_OWN_CALLS permission.");
-                    } catch (SecurityException e) {
-                        // Fallback to use mobile network to avoid disclosing phone account handle
-                        // package information
-                        clearPhoneAccountHandleExtra = true;
-                    }
-
-                    if (!clearPhoneAccountHandleExtra && !callingPackage.equals(
-                            phoneAccountHandle.getComponentName().getPackageName())
-                            && !canCallPhone(callingPackage, callingFeatureId,
-                            "CALL_PHONE permission required to place calls.")) {
-                        // The caller is not allowed to place calls, so fallback to use mobile
-                        // network.
-                        clearPhoneAccountHandleExtra = true;
-                    }
-                } else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) {
+                // Ensure the app's calling package matches the PhoneAccount package name before
+                // checking self-managed status so that we do not leak installed package
+                // information.
+                boolean isSelfManagedRequest = isCallerOwnerOfPhoneAccount &&
+                        isSelfManagedPhoneAccount;
+                if (isSelfManagedRequest) {
+                    // The package name of the caller matches the package name of the
+                    // PhoneAccountHandle, so ensure the app has MANAGE_OWN_CALLS permission if
+                    // self-managed.
+                    mContext.enforceCallingOrSelfPermission(
+                            Manifest.permission.MANAGE_OWN_CALLS,
+                            "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
+                } else if (!canCallPhone(callingPackage, callingFeatureId,
+                        "CALL_PHONE permission required to place calls.")) {
+                    // not self-managed, so CALL_PHONE is required.
                     mAnomalyReporter.reportAnomaly(PLACE_CALL_SECURITY_EXCEPTION_ERROR_UUID,
                             PLACE_CALL_SECURITY_EXCEPTION_ERROR_MSG);
-                    throw new SecurityException("Package " + callingPackage
-                            + " is not allowed to place phone calls");
+                    throw new SecurityException(
+                            "CALL_PHONE permission required to place calls.");
+                }
+
+                // An application can not place a call with a self-managed PhoneAccount that
+                // they do not own. If this is the case (and the app has CALL_PHONE permission),
+                // remove the PhoneAccount from the request and place the call as if it was a
+                // managed call request with no PhoneAccount specified.
+                if (!isCallerOwnerOfPhoneAccount && isSelfManagedPhoneAccount) {
+                    // extras can not be null if isSelfManagedPhoneAccount is true
+                    extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                 }
 
                 // Note: we can still get here for the default/system dialer, even if the Phone
@@ -1805,16 +1828,12 @@
                         final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                 Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                         if (extras != null) {
-                            if (clearPhoneAccountHandleExtra) {
-                                extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
-                            }
                             extras.setDefusable(true);
                             intent.putExtras(extras);
                         }
                         mUserCallIntentProcessorFactory.create(mContext, userHandle)
-                                .processIntent(
-                                        intent, callingPackage, isSelfManaged ||
-                                                (hasCallAppOp && hasCallPermission),
+                                .processIntent(intent, callingPackage, isSelfManagedRequest,
+                                        (hasCallAppOp && hasCallPermission),
                                         true /* isLocalInvocation */);
                     } finally {
                         Binder.restoreCallingIdentity(token);
@@ -1921,19 +1940,21 @@
             }
 
 
-            if (args.length > 0 && Analytics.ANALYTICS_DUMPSYS_ARG.equals(args[0])) {
+            if (args != null && args.length > 0 && Analytics.ANALYTICS_DUMPSYS_ARG.equals(
+                    args[0])) {
                 Binder.withCleanCallingIdentity(() ->
                         Analytics.dumpToEncodedProto(mContext, writer, args));
                 return;
             }
 
-            boolean isTimeLineView = (args.length > 0 && TIME_LINE_ARG.equalsIgnoreCase(args[0]));
+            boolean isTimeLineView =
+                    (args != null && args.length > 0 && TIME_LINE_ARG.equalsIgnoreCase(args[0]));
 
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
             if (mCallsManager != null) {
                 pw.println("CallsManager: ");
                 pw.increaseIndent();
-                mCallsManager.dump(pw);
+                mCallsManager.dump(pw, args);
                 pw.decreaseIndent();
 
                 pw.println("PhoneAccountRegistrar: ");
@@ -2711,6 +2732,46 @@
         return packageUid == callingUid;
     }
 
+    /**
+     * Note: This method should be called BEFORE clearing the binder identity.
+     *
+     * @param permissionsToValidate      set of permissions that should be checked
+     * @param alreadyComputedPermissions a list of permissions that were already checked
+     * @return all the permissions that
+     */
+    private Set<String> computePermissionsForBoundPackage(
+            Set<String> permissionsToValidate,
+            Set<String> alreadyComputedPermissions) {
+        Set<String> permissions = Objects.requireNonNullElseGet(alreadyComputedPermissions,
+                HashSet::new);
+        for (String permission : permissionsToValidate) {
+            if (mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+                permissions.add(permission);
+            }
+        }
+        return permissions;
+    }
+
+    /**
+     * This method should be used to clear {@link PhoneAccount} properties based on a
+     * callingPackages permissions.
+     *
+     * @param account     to clear properties from
+     * @param permissions the list of permissions the callingPackge has
+     * @return the account that callingPackage will receive
+     */
+    private PhoneAccount maybeCleansePhoneAccount(PhoneAccount account,
+            Set<String> permissions) {
+        if (account == null) {
+            return null;
+        }
+        PhoneAccount.Builder accountBuilder = new PhoneAccount.Builder(account);
+        if (!permissions.contains(MODIFY_PHONE_STATE)) {
+            accountBuilder.setGroupId("***");
+        }
+        return accountBuilder.build();
+    }
+
     private void enforceTelecomFeature() {
         PackageManager pm = mContext.getPackageManager();
         if (!pm.hasSystemFeature(PackageManager.FEATURE_TELECOM)
@@ -3077,4 +3138,22 @@
             mContext.sendBroadcast(intent);
         }
     }
+
+    private void validateAccountIconUserBoundary(Icon icon) {
+        // Refer to Icon#getUriString for context. The URI string is invalid for icons of
+        // incompatible types.
+        if (icon != null && (icon.getType() == Icon.TYPE_URI
+                || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
+            String encodedUser = icon.getUri().getEncodedUserInfo();
+            // If there is no encoded user, the URI is calling into the calling user space
+            if (encodedUser != null) {
+                int userId = Integer.parseInt(encodedUser);
+                if (userId != UserHandle.getUserId(Binder.getCallingUid())) {
+                    // If we are transcending the profile boundary, throw an error.
+                    throw new IllegalArgumentException("Attempting to register a phone account with"
+                            + " an image icon belonging to another user.");
+                }
+            }
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index f9c2508..8477d49 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -26,12 +26,14 @@
 import android.content.ServiceConnection;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
+import android.os.BugreportManager;
+import android.os.DropBoxManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.AnomalyReporter;
-import android.view.accessibility.AccessibilityManager;
+import android.telephony.TelephonyManager;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -42,6 +44,7 @@
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+import com.android.server.telecom.callfiltering.BlockedNumbersAdapter;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.ui.AudioProcessingNotification;
@@ -49,10 +52,12 @@
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
 import com.android.server.telecom.ui.ToastFactory;
+import com.android.server.telecom.voip.TransactionManager;
 
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
 /**
@@ -210,9 +215,12 @@
             RoleManagerAdapter roleManagerAdapter,
             ContactsAsyncHelper.Factory contactsAsyncHelperFactory,
             DeviceIdleControllerAdapter deviceIdleControllerAdapter,
-            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter) {
+            Ringer.AccessibilityManagerAdapter accessibilityManagerAdapter,
+            Executor asyncTaskExecutor,
+            BlockedNumbersAdapter blockedNumbersAdapter) {
         mContext = context.getApplicationContext();
         LogUtils.initLogging(mContext);
+        android.telecom.Log.setLock(mLock);
         AnomalyReporter.initialize(mContext);
         DefaultDialerManagerAdapter defaultDialerAdapter =
                 new DefaultDialerCache.DefaultDialerManagerAdapterImpl();
@@ -331,10 +339,17 @@
                 }
             };
 
+            EmergencyCallDiagnosticLogger emergencyCallDiagnosticLogger =
+                    new EmergencyCallDiagnosticLogger(mContext.getSystemService(
+                            TelephonyManager.class), mContext.getSystemService(
+                            BugreportManager.class), timeoutsAdapter, mContext.getSystemService(
+                            DropBoxManager.class), asyncTaskExecutor, clockProxy);
+
             CallAnomalyWatchdog callAnomalyWatchdog = new CallAnomalyWatchdog(
                     Executors.newSingleThreadScheduledExecutor(),
-                    mLock, timeoutsAdapter, clockProxy);
+                    mLock, timeoutsAdapter, clockProxy, emergencyCallDiagnosticLogger);
 
+            TransactionManager transactionManager = TransactionManager.getInstance();
             mCallsManager = new CallsManager(
                     mContext,
                     mLock,
@@ -367,8 +382,11 @@
                     toastFactory,
                     callEndpointControllerFactory,
                     callAnomalyWatchdog,
-                    Executors.newSingleThreadExecutor(),
-                    accessibilityManagerAdapter);
+                    accessibilityManagerAdapter,
+                    asyncTaskExecutor,
+                    blockedNumbersAdapter,
+                    transactionManager,
+                    emergencyCallDiagnosticLogger);
 
             mIncomingCallNotifier = incomingCallNotifier;
             incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
diff --git a/src/com/android/server/telecom/Timeouts.java b/src/com/android/server/telecom/Timeouts.java
index d4713e8..c5fdd4c 100644
--- a/src/com/android/server/telecom/Timeouts.java
+++ b/src/com/android/server/telecom/Timeouts.java
@@ -20,8 +20,8 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.telecom.CallDiagnosticService;
-import android.telecom.CallRedirectionService;
 import android.telecom.CallDiagnostics;
+import android.telecom.CallRedirectionService;
 import android.telephony.ims.ImsReasonInfo;
 
 import java.util.concurrent.TimeUnit;
@@ -112,12 +112,46 @@
         public long getNonVoipEmergencyCallIntermediateStateTimeoutMillis() {
             return Timeouts.getNonVoipEmergencyCallIntermediateStateTimeoutMillis();
         }
+
+        public long getEmergencyCallTimeBeforeUserDisconnectThresholdMillis(){
+            return Timeouts.getEmergencyCallTimeBeforeUserDisconnectThresholdMillis();
+        }
+
+        public long getEmergencyCallActiveTimeThresholdMillis(){
+            return Timeouts.getEmergencyCallActiveTimeThresholdMillis();
+        }
+
+        public int getDaysBackToSearchEmergencyDiagnosticEntries(){
+            return Timeouts.getDaysBackToSearchEmergencyDiagnosticEntries();
+
+        }
     }
 
     /** A prefix to use for all keys so to not clobber the global namespace. */
     private static final String PREFIX = "telecom.";
 
     /**
+     * threshold used to filter out ecalls that the user may have dialed by mistake
+     * It is used only when the disconnect cause is LOCAL by EmergencyDiagnosticLogger
+     */
+    private static final String EMERGENCY_CALL_TIME_BEFORE_USER_DISCONNECT_THRESHOLD_MILLIS =
+            "emergency_call_time_before_user_disconnect_threshold_millis";
+
+    /**
+     * Returns the threshold used to detect ecalls that transition to active but only for a very
+     * short duration. These short duration active calls can result in Diagnostic data collection.
+     */
+    private static final String EMERGENCY_CALL_ACTIVE_TIME_THRESHOLD_MILLIS =
+            "emergency_call_active_time_threshold_millis";
+
+    /**
+     * Time in Days that is used to filter out old dropbox entries for emergency call diagnostic
+     * data. Entries older than this are ignored
+     */
+    private static final String DAYS_BACK_TO_SEARCH_EMERGENCY_DROP_BOX_ENTRIES =
+            "days_back_to_search_emergency_drop_box_entries";
+
+    /**
      * A prefix to use for {@link DeviceConfig} for the transitory state timeout of
      * VoIP Call, in millis.
      */
@@ -335,6 +369,36 @@
                 TRANSITORY_STATE_VOIP_NORMAL_TIMEOUT_MILLIS, 5000L);
     }
 
+
+    /**
+     * Returns the threshold used to filter out ecalls that the user may have dialed by mistake
+     * It is used only when the disconnect cause is LOCAL by EmergencyDiagnosticLogger
+     * @return the threshold in milliseconds
+     */
+    public static long getEmergencyCallTimeBeforeUserDisconnectThresholdMillis() {
+        return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+                EMERGENCY_CALL_TIME_BEFORE_USER_DISCONNECT_THRESHOLD_MILLIS, 20000L);
+    }
+
+    /**
+     * Returns the threshold used to detect ecalls that transition to active but only for a very
+     * short duration. These short duration active calls can result in Diagnostic data collection.
+     * @return the threshold in milliseconds
+     */
+    public static long getEmergencyCallActiveTimeThresholdMillis() {
+        return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
+                EMERGENCY_CALL_ACTIVE_TIME_THRESHOLD_MILLIS, 15000L);
+    }
+
+    /**
+     * Time in Days that is used to filter out old dropbox entries for emergency call diagnostic
+     * data. Entries older than this are ignored
+     */
+    public static int getDaysBackToSearchEmergencyDiagnosticEntries() {
+        return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
+                DAYS_BACK_TO_SEARCH_EMERGENCY_DROP_BOX_ENTRIES, 30);
+    }
+
     /**
      * Returns the duration of time an emergency VoIP call can be in a transitory state before
      * Telecom will try to clean up the call.
@@ -352,7 +416,7 @@
      */
     public static long getNonVoipCallTransitoryStateTimeoutMillis() {
         return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
-                TRANSITORY_STATE_NON_VOIP_NORMAL_TIMEOUT_MILLIS, 5000L);
+                TRANSITORY_STATE_NON_VOIP_NORMAL_TIMEOUT_MILLIS, 10000L);
     }
 
     /**
@@ -362,7 +426,7 @@
      */
     public static long getNonVoipEmergencyCallTransitoryStateTimeoutMillis() {
         return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
-                TRANSITORY_STATE_NON_VOIP_EMERGENCY_TIMEOUT_MILLIS, 5000L);
+                TRANSITORY_STATE_NON_VOIP_EMERGENCY_TIMEOUT_MILLIS, 10000L);
     }
 
     /**
@@ -392,7 +456,7 @@
      */
     public static long getNonVoipCallIntermediateStateTimeoutMillis() {
         return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
-                INTERMEDIATE_STATE_NON_VOIP_NORMAL_TIMEOUT_MILLIS, 60000L);
+                INTERMEDIATE_STATE_NON_VOIP_NORMAL_TIMEOUT_MILLIS, 120000L);
     }
 
     /**
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index d97d192..1e6403e 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -38,14 +38,13 @@
 
 import com.android.internal.telecom.ICallControl;
 import com.android.internal.telecom.ICallEventCallback;
-import com.android.server.telecom.voip.AnswerCallTransaction;
 import com.android.server.telecom.voip.CallEventCallbackAckTransaction;
 import com.android.server.telecom.voip.EndpointChangeTransaction;
 import com.android.server.telecom.voip.HoldCallTransaction;
 import com.android.server.telecom.voip.EndCallTransaction;
-import com.android.server.telecom.voip.HoldActiveCallForNewCallTransaction;
+import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
 import com.android.server.telecom.voip.ParallelTransaction;
-import com.android.server.telecom.voip.RequestFocusTransaction;
+import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
 import com.android.server.telecom.voip.SerialTransaction;
 import com.android.server.telecom.voip.TransactionManager;
 import com.android.server.telecom.voip.VoipCallTransaction;
@@ -86,7 +85,7 @@
     private ConnectionServiceFocusManager.ConnectionServiceFocusListener mConnSvrFocusListener;
     // init when constructor is called
     private final Hashtable<String, Call> mTrackedCalls = new Hashtable<>();
-    private final Object mLock;
+    private final TelecomSystem.SyncRoot mLock;
     private final String mPackageName;
     // needs to be non-final for testing
     private TransactionManager mTransactionManager;
@@ -105,7 +104,7 @@
         mPackageName = phoneAccountHandle.getComponentName().getPackageName();
         mTransactionManager = TransactionManager.getInstance();
         mStreamingController = mCallsManager.getCallStreamingController();
-        mLock = new Object();
+        mLock = mCallsManager.getLock();
     }
 
     @VisibleForTesting
@@ -247,11 +246,12 @@
             if (call != null) {
                 switch (action) {
                     case SET_ACTIVE:
-                        addTransactionsToManager(createSetActiveTransactions(call), callback);
+                        handleCallControlNewCallFocusTransactions(call, SET_ACTIVE,
+                                false /* isAnswer */, 0/*VideoState (ignored)*/, callback);
                         break;
                     case ANSWER:
-                        addTransactionsToManager(createSetAnswerTransactions(call,
-                                (int) objects[0]), callback);
+                        handleCallControlNewCallFocusTransactions(call, ANSWER,
+                                true /* isAnswer */, (int) objects[0] /*VideoState*/, callback);
                         break;
                     case DISCONNECT:
                         addTransactionsToManager(new EndCallTransaction(mCallsManager,
@@ -278,6 +278,32 @@
             }
         }
 
+        // The client is request their VoIP call state go ACTIVE/ANSWERED.
+        // This request is originating from the VoIP application.
+        private void handleCallControlNewCallFocusTransactions(Call call, String action,
+                boolean isAnswer, int potentiallyNewVideoState, ResultReceiver callback) {
+            mTransactionManager.addTransaction(createSetActiveTransactions(call),
+                    new OutcomeReceiver<>() {
+                        @Override
+                        public void onResult(VoipCallTransactionResult result) {
+                            Log.i(TAG, String.format(Locale.US,
+                                    "%s: onResult: callId=[%s]", action, call.getId()));
+                            if (isAnswer) {
+                                call.setVideoState(potentiallyNewVideoState);
+                            }
+                            callback.send(TELECOM_TRANSACTION_SUCCESS, new Bundle());
+                        }
+
+                        @Override
+                        public void onError(CallException exception) {
+                            Bundle extras = new Bundle();
+                            extras.putParcelable(TRANSACTION_EXCEPTION_KEY, exception);
+                            callback.send(exception == null ? CallException.CODE_ERROR_UNKNOWN :
+                                    exception.getCode(), extras);
+                        }
+                    });
+        }
+
         @Override
         public void requestCallEndpointChange(CallEndpoint endpoint, ResultReceiver callback) {
             try {
@@ -299,14 +325,12 @@
                 Call call = mTrackedCalls.get(callId);
                 if (call != null) {
                     call.onConnectionEvent(event, extras);
-                }
-                else{
+                } else {
                     Log.i(TAG,
                             "sendEvent: was called but there is no call with id=[%s] cannot be "
                                     + "found. Most likely the call has been disconnected");
                 }
-            }
-            finally {
+            } finally {
                 Log.endSession();
             }
         }
@@ -348,7 +372,8 @@
         try {
             Log.startSession("TSW.oSA");
             Log.d(TAG, String.format(Locale.US, "onSetActive: callId=[%s]", call.getId()));
-            handleNewActiveCallCallbacks(call, ON_SET_ACTIVE, 0);
+            handleCallEventCallbackNewFocus(call, ON_SET_ACTIVE, false /*isAnswerRequest*/,
+                    0 /*VideoState*/);
         } finally {
             Log.endSession();
         }
@@ -358,42 +383,51 @@
         try {
             Log.startSession("TSW.oA");
             Log.d(TAG, String.format(Locale.US, "onAnswer: callId=[%s]", call.getId()));
-            handleNewActiveCallCallbacks(call, ON_ANSWER, videoState);
+            handleCallEventCallbackNewFocus(call, ON_ANSWER, true /*isAnswerRequest*/,
+                    videoState /*VideoState*/);
         } finally {
             Log.endSession();
         }
     }
 
-    // need to create multiple transactions for onSetActive and onAnswer which both seek to set
-    // the call to active
-    private void handleNewActiveCallCallbacks(Call call, String action, int videoState) {
+    // handle a CallEventCallback to set a call ACTIVE/ANSWERED. Must get ack from client since the
+    // request has come from another source (ex. Android Auto is requesting a call to go active)
+    private void handleCallEventCallbackNewFocus(Call call, String action, boolean isAnswerRequest,
+            int potentiallyNewVideoState) {
         // save CallsManager state before sending client state changes
         Call foregroundCallBeforeSwap = mCallsManager.getForegroundCall();
         boolean wasActive = foregroundCallBeforeSwap != null && foregroundCallBeforeSwap.isActive();
 
-        // create 3 serial transactions:
-        // -- hold active
-        // -- set newCall as active
-        // -- ack from client
         SerialTransaction serialTransactions = createSetActiveTransactions(call);
-        serialTransactions.appendTransaction(
-                new CallEventCallbackAckTransaction(mICallEventCallback,
-                        action, call.getId(), videoState));
+        // 3. get ack from client (that the requested call can go active)
+        if (isAnswerRequest) {
+            serialTransactions.appendTransaction(
+                    new CallEventCallbackAckTransaction(mICallEventCallback,
+                            action, call.getId(), potentiallyNewVideoState, mLock));
+        } else {
+            serialTransactions.appendTransaction(
+                    new CallEventCallbackAckTransaction(mICallEventCallback,
+                            action, call.getId(), mLock));
+        }
 
         // do CallsManager workload before asking client and
         //   reset CallsManager state if client does NOT ack
-        mTransactionManager.addTransaction(serialTransactions, new OutcomeReceiver<>() {
-            @Override
-            public void onResult(VoipCallTransactionResult result) {
-                Log.i(TAG, String.format(Locale.US,
-                        "%s: onResult: callId=[%s]", action, call.getId()));
-            }
+        mTransactionManager.addTransaction(serialTransactions,
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(VoipCallTransactionResult result) {
+                        Log.i(TAG, String.format(Locale.US,
+                                "%s: onResult: callId=[%s]", action, call.getId()));
+                        if (isAnswerRequest) {
+                            call.setVideoState(potentiallyNewVideoState);
+                        }
+                    }
 
-            @Override
-            public void onError(CallException exception) {
-                maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
-            }
-        });
+                    @Override
+                    public void onError(CallException exception) {
+                        maybeResetForegroundCall(foregroundCallBeforeSwap, wasActive);
+                    }
+                });
     }
 
 
@@ -403,7 +437,7 @@
             Log.i(TAG, String.format(Locale.US, "onSetInactive: callId=[%s]", call.getId()));
             mTransactionManager.addTransaction(
                     new CallEventCallbackAckTransaction(mICallEventCallback,
-                            ON_SET_INACTIVE, call.getId()), new OutcomeReceiver<>() {
+                            ON_SET_INACTIVE, call.getId(), mLock), new OutcomeReceiver<>() {
                         @Override
                         public void onResult(VoipCallTransactionResult result) {
                             mCallsManager.markCallAsOnHold(call);
@@ -426,7 +460,7 @@
 
             mTransactionManager.addTransaction(
                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_DISCONNECT,
-                            call.getId(), cause), new OutcomeReceiver<>() {
+                            call.getId(), cause, mLock), new OutcomeReceiver<>() {
                         @Override
                         public void onResult(VoipCallTransactionResult result) {
                             removeCallFromCallsManager(call, cause);
@@ -451,7 +485,7 @@
 
             mTransactionManager.addTransaction(
                     new CallEventCallbackAckTransaction(mICallEventCallback, ON_STREAMING_STARTED,
-                            call.getId()), new OutcomeReceiver<>() {
+                            call.getId(), mLock), new OutcomeReceiver<>() {
                         @Override
                         public void onResult(VoipCallTransactionResult result) {
                         }
@@ -519,7 +553,7 @@
         }
     }
 
-    public void onEvent(Call call, String event, Bundle extras){
+    public void onEvent(Call call, String event, Bundle extras) {
         if (call != null) {
             try {
                 mICallEventCallback.onEvent(call.getId(), event, extras);
@@ -553,28 +587,12 @@
         // create list for multiple transactions
         List<VoipCallTransaction> transactions = new ArrayList<>();
 
-        // add t1. hold potential active call
-        transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
+        // potentially hold the current active call in order to set a new call (active/answered)
+        transactions.add(new MaybeHoldCallForNewCallTransaction(mCallsManager, call));
+        // And request a new focus call update
+        transactions.add(new RequestNewActiveCallTransaction(mCallsManager, call));
 
-        // add t2. send request to set the current call active
-        transactions.add(new RequestFocusTransaction(mCallsManager, call));
-
-        // send off to Transaction Manager to process
-        return new SerialTransaction(transactions);
-    }
-
-    private SerialTransaction createSetAnswerTransactions(Call call, int videoState) {
-        // create list for multiple transactions
-        List<VoipCallTransaction> transactions = new ArrayList<>();
-
-        // add t1. hold potential active call
-        transactions.add(new HoldActiveCallForNewCallTransaction(mCallsManager, call));
-
-        // add t2. answer current call
-        transactions.add(new AnswerCallTransaction(mCallsManager, call, videoState));
-
-        // send off to Transaction Manager to process
-        return new SerialTransaction(transactions);
+        return new SerialTransaction(transactions, mLock);
     }
 
     /***
@@ -631,12 +649,13 @@
         subTransactions.add(mStreamingController.getCallStreamingServiceTransaction(
                 mCallsManager.getContext(), this, call));
         // add t2.2 audio route operations
-        subTransactions.add(new CallStreamingController.AudioInterceptionTransaction(call, true));
+        subTransactions.add(new CallStreamingController.AudioInterceptionTransaction(call,
+                true, mLock));
 
         // add t2
-        transactions.add(new ParallelTransaction(subTransactions));
+        transactions.add(new ParallelTransaction(subTransactions, mLock));
         // send off to Transaction Manager to process
-        return new SerialTransaction(transactions);
+        return new SerialTransaction(transactions, mLock);
     }
 
     private VoipCallTransaction createStopStreamingTransaction(Call call) {
@@ -647,8 +666,9 @@
         // 1. unbind to call streaming service
         transactions.add(mStreamingController.getUnbindStreamingServiceTransaction());
         // 2. audio route operations
-        transactions.add(new CallStreamingController.AudioInterceptionTransaction(call, false));
-        return new ParallelTransaction(transactions);
+        transactions.add(new CallStreamingController.AudioInterceptionTransaction(call,
+                false, mLock));
+        return new ParallelTransaction(transactions, mLock);
     }
 
 
diff --git a/src/com/android/server/telecom/callfiltering/BlockedNumbersAdapter.java b/src/com/android/server/telecom/callfiltering/BlockedNumbersAdapter.java
new file mode 100644
index 0000000..b8658d8
--- /dev/null
+++ b/src/com/android/server/telecom/callfiltering/BlockedNumbersAdapter.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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.callfiltering;
+
+import android.content.Context;
+
+/**
+ * Adapter interface that wraps methods from
+ * {@link android.provider.BlockedNumberContract.SystemContract} and
+ * {@link com.android.server.telecom.settings.BlockedNumbersUtil} to make things testable.
+ */
+public interface BlockedNumbersAdapter {
+    boolean shouldShowEmergencyCallNotification (Context context);
+    void updateEmergencyCallNotification(Context context, boolean showNotification);
+}
diff --git a/src/com/android/server/telecom/callfiltering/DndCallFilter.java b/src/com/android/server/telecom/callfiltering/DndCallFilter.java
index a100bad..f6ed646 100644
--- a/src/com/android/server/telecom/callfiltering/DndCallFilter.java
+++ b/src/com/android/server/telecom/callfiltering/DndCallFilter.java
@@ -55,9 +55,6 @@
         // query NotificationManager to determine if the call should ring or be suppressed
         boolean shouldSuppress = !mRinger.shouldRingForContact(mCall);
 
-        // store the shouldSuppress value in the call object which will be passed to InCallServices
-        mCall.setCallIsSuppressedByDoNotDisturb(shouldSuppress);
-
         // end timer
         Log.addEvent(mCall, LogUtils.Events.DND_PRE_CHECK_COMPLETED, shouldSuppress);
 
diff --git a/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java b/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java
index 039526b..963e923 100644
--- a/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java
+++ b/src/com/android/server/telecom/callredirection/CallRedirectionProcessor.java
@@ -162,6 +162,19 @@
                     Log.endSession();
                 }
             }
+
+            @Override
+            public void onNullBinding(ComponentName componentName) {
+                // Make sure we unbind the service if onBind returns null
+                Log.startSession("CRSC.oNB");
+                try {
+                    synchronized (mTelecomLock) {
+                        finishCallRedirection();
+                    }
+                } finally {
+                    Log.endSession();
+                }
+            }
         }
 
         private class CallRedirectionAdapter extends ICallRedirectionAdapter.Stub {
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 3881154..ef85fc7 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -26,6 +26,7 @@
 import android.os.PowerManager;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.provider.BlockedNumberContract;
 import android.telecom.Log;
 
 import android.telecom.CallerInfoAsyncQuery;
@@ -59,10 +60,14 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TelecomWakeLock;
 import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.callfiltering.BlockedNumbersAdapter;
+import com.android.server.telecom.settings.BlockedNumbersUtil;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.MissedCallNotifierImpl;
 import com.android.server.telecom.ui.NotificationChannelManager;
 
+import java.util.concurrent.Executors;
+
 /**
  * Implementation of the ITelecom interface.
  */
@@ -156,7 +161,7 @@
                             new InCallWakeLockControllerFactory() {
                                 @Override
                                 public InCallWakeLockController create(Context context,
-                                                                       CallsManager callsManager) {
+                                        CallsManager callsManager) {
                                     return new InCallWakeLockController(
                                             new TelecomWakeLock(context,
                                                     PowerManager.FULL_WAKE_LOCK,
@@ -208,6 +213,22 @@
                                     return context.getSystemService(AccessibilityManager.class)
                                             .stopFlashNotificationSequence(context);
                                 }
+                            },
+                            Executors.newCachedThreadPool(),
+                            new BlockedNumbersAdapter() {
+                                @Override
+                                public boolean shouldShowEmergencyCallNotification(Context
+                                        context) {
+                                    return BlockedNumberContract.SystemContract
+                                            .shouldShowEmergencyCallNotification(context);
+                                }
+
+                                @Override
+                                public void updateEmergencyCallNotification(Context context,
+                                        boolean showNotification) {
+                                    BlockedNumbersUtil.updateEmergencyCallNotification(context,
+                                            showNotification);
+                                }
                             }));
         }
     }
diff --git a/src/com/android/server/telecom/components/UserCallActivity.java b/src/com/android/server/telecom/components/UserCallActivity.java
index 1d85884..d7b2001 100644
--- a/src/com/android/server/telecom/components/UserCallActivity.java
+++ b/src/com/android/server/telecom/components/UserCallActivity.java
@@ -74,7 +74,8 @@
             // ActivityThread.ActivityClientRecord#intent directly.
             // Modifying directly may be a potential risk when relaunching this activity.
             new UserCallIntentProcessor(this, userHandle).processIntent(new Intent(intent),
-                    getCallingPackage(), true /* hasCallAppOp*/, false /* isLocalInvocation */);
+                    getCallingPackage(), false, true /* hasCallAppOp*/,
+                    false /* isLocalInvocation */);
         } finally {
             Log.endSession();
             wakelock.release();
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index cad7b4c..a4602c1 100755
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -69,6 +69,7 @@
      *
      * @param intent The intent.
      * @param callingPackageName The package name of the calling app.
+     * @param isSelfManaged      {@code true} if SelfManaged profile enabled.
      * @param canCallNonEmergency {@code true} if the caller is permitted to call non-emergency
      *                            numbers.
      * @param isLocalInvocation {@code true} if the caller is within the system service (i.e. the
@@ -79,19 +80,21 @@
      *                            service resides.
      */
     public void processIntent(Intent intent, String callingPackageName,
-            boolean canCallNonEmergency, boolean isLocalInvocation) {
+            boolean isSelfManaged, boolean canCallNonEmergency,
+            boolean isLocalInvocation) {
         String action = intent.getAction();
 
         if (Intent.ACTION_CALL.equals(action) ||
                 Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
                 Intent.ACTION_CALL_EMERGENCY.equals(action)) {
-            processOutgoingCallIntent(intent, callingPackageName, canCallNonEmergency,
-                    isLocalInvocation);
+            processOutgoingCallIntent(intent, callingPackageName, isSelfManaged,
+                    canCallNonEmergency, isLocalInvocation);
         }
     }
 
     private void processOutgoingCallIntent(Intent intent, String callingPackageName,
-            boolean canCallNonEmergency, boolean isLocalInvocation) {
+            boolean isSelfManaged, boolean canCallNonEmergency,
+            boolean isLocalInvocation) {
         Uri handle = intent.getData();
         if (handle == null) return;
         String scheme = handle.getScheme();
@@ -102,40 +105,43 @@
             handle = Uri.fromParts(PhoneAccount.SCHEME_SIP, uriString, null);
         }
 
-        // Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this check in a managed
-        // profile user because this check can always be bypassed by copying and pasting the phone
-        // number into the personal dialer.
-        if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
-            // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
-            // restriction.
-            if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
-                final UserManager userManager = (UserManager) mContext.getSystemService(
-                        Context.USER_SERVICE);
-                if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
-                        mUserHandle)) {
-                    showErrorDialogForRestrictedOutgoingCall(mContext,
-                            R.string.outgoing_call_not_allowed_user_restriction);
-                    Log.w(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
-                            + "restriction");
-                    return;
-                } else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
-                        mUserHandle)) {
-                    final DevicePolicyManager dpm =
-                            mContext.getSystemService(DevicePolicyManager.class);
-                    if (dpm == null) {
+       if(!isSelfManaged) {
+            // Check DISALLOW_OUTGOING_CALLS restriction. Note: We are skipping this
+            // check in a managed profile user because this check can always be bypassed
+            // by copying and pasting the phone number into the personal dialer.
+            if (!UserUtil.isManagedProfile(mContext, mUserHandle)) {
+                // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
+                // restriction.
+                if (!TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
+                    final UserManager userManager =
+                            (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+                    if (userManager.hasBaseUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
+                            mUserHandle)) {
+                        showErrorDialogForRestrictedOutgoingCall(mContext,
+                                R.string.outgoing_call_not_allowed_user_restriction);
+                        Log.w(this, "Rejecting non-emergency phone call "
+                                + "due to DISALLOW_OUTGOING_CALLS restriction");
+                        return;
+                    } else if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
+                            mUserHandle)) {
+                        final DevicePolicyManager dpm =
+                                mContext.getSystemService(DevicePolicyManager.class);
+                        if (dpm == null) {
+                            return;
+                        }
+                        final Intent adminSupportIntent = dpm.createAdminSupportIntent(
+                                UserManager.DISALLOW_OUTGOING_CALLS);
+                        if (adminSupportIntent != null) {
+                            mContext.startActivity(adminSupportIntent);
+                        }
                         return;
                     }
-                    final Intent adminSupportIntent = dpm.createAdminSupportIntent(
-                            UserManager.DISALLOW_OUTGOING_CALLS);
-                    if (adminSupportIntent != null) {
-                        mContext.startActivity(adminSupportIntent);
-                    }
-                    return;
                 }
             }
         }
 
-        if (!canCallNonEmergency && !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
+        if (!isSelfManaged && !canCallNonEmergency &&
+                !TelephonyUtil.shouldProcessAsEmergency(mContext, handle)) {
             showErrorDialogForRestrictedOutgoingCall(mContext,
                     R.string.outgoing_call_not_allowed_no_permission);
             Log.w(this, "Rejecting non-emergency phone call because "
diff --git a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
index 6e2eb97..7345a67 100644
--- a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
+++ b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
@@ -40,7 +40,6 @@
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
@@ -156,7 +155,8 @@
             }
         };
         registerReceiver(mBlockingStatusReceiver, new IntentFilter(
-                BlockedNumberContract.SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED));
+                BlockedNumberContract.SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED),
+                Context.RECEIVER_EXPORTED);
 
         getLoaderManager().initLoader(0, null, this);
     }
diff --git a/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java b/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java
index c0bb56a..b1a1b0e 100644
--- a/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java
+++ b/src/com/android/server/telecom/settings/EnhancedCallBlockingFragment.java
@@ -45,6 +45,7 @@
     private static final String BLOCK_UNAVAILABLE_NUMBERS_KEY =
             "block_unavailable_calls_setting";
     private boolean mIsCombiningRestrictedAndUnknownOption = false;
+    private boolean mIsCombiningUnavailableAndUnknownOption = false;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -94,11 +95,17 @@
                         R.bool.combine_options_to_block_restricted_and_unknown_callers);
         if (mIsCombiningRestrictedAndUnknownOption) {
             Preference restricted_pref = findPreference(BLOCK_RESTRICTED_NUMBERS_KEY);
-            Preference unavailable_pref = findPreference(BLOCK_UNAVAILABLE_NUMBERS_KEY);
             screen.removePreference(restricted_pref);
-            screen.removePreference(unavailable_pref);
             Log.i(this, "onCreate: removed block restricted preference.");
         }
+
+        mIsCombiningUnavailableAndUnknownOption = getResources().getBoolean(
+                R.bool.combine_options_to_block_unavailable_and_unknown_callers);
+        if (mIsCombiningUnavailableAndUnknownOption) {
+            Preference unavailable_pref = findPreference(BLOCK_UNAVAILABLE_NUMBERS_KEY);
+            screen.removePreference(unavailable_pref);
+            Log.i(this, "onCreate: removed block unavailable preference.");
+        }
     }
 
     /**
@@ -136,14 +143,20 @@
 
     @Override
     public boolean onPreferenceChange(Preference preference, Object objValue) {
-        if (mIsCombiningRestrictedAndUnknownOption
-                && preference.getKey().equals(BLOCK_UNKNOWN_NUMBERS_KEY)) {
-            Log.i(this, "onPreferenceChange: changing %s and %s to %b",
-                    preference.getKey(), BLOCK_RESTRICTED_NUMBERS_KEY, (boolean) objValue);
-            BlockedNumbersUtil.setEnhancedBlockSetting(getActivity(), BLOCK_RESTRICTED_NUMBERS_KEY,
-                    (boolean) objValue);
-            BlockedNumbersUtil.setEnhancedBlockSetting(getActivity(),
-                    BLOCK_UNAVAILABLE_NUMBERS_KEY, (boolean) objValue);
+        if (preference.getKey().equals(BLOCK_UNKNOWN_NUMBERS_KEY)) {
+            if (mIsCombiningRestrictedAndUnknownOption) {
+                Log.i(this, "onPreferenceChange: changing %s and %s to %b",
+                        preference.getKey(), BLOCK_RESTRICTED_NUMBERS_KEY, (boolean) objValue);
+                BlockedNumbersUtil.setEnhancedBlockSetting(getActivity(),
+                        BLOCK_RESTRICTED_NUMBERS_KEY, (boolean) objValue);
+            }
+
+            if (mIsCombiningUnavailableAndUnknownOption) {
+                Log.i(this, "onPreferenceChange: changing %s and %s to %b",
+                        preference.getKey(), BLOCK_UNAVAILABLE_NUMBERS_KEY, (boolean) objValue);
+                BlockedNumbersUtil.setEnhancedBlockSetting(getActivity(),
+                        BLOCK_UNAVAILABLE_NUMBERS_KEY, (boolean) objValue);
+            }
         }
         BlockedNumbersUtil.setEnhancedBlockSetting(getActivity(), preference.getKey(),
                 (boolean) objValue);
diff --git a/src/com/android/server/telecom/voip/AnswerCallTransaction.java b/src/com/android/server/telecom/voip/AnswerCallTransaction.java
deleted file mode 100644
index 1eb34c4..0000000
--- a/src/com/android/server/telecom/voip/AnswerCallTransaction.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2023 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.voip;
-
-import android.os.OutcomeReceiver;
-import android.telecom.CallException;
-import android.util.Log;
-
-import com.android.server.telecom.Call;
-import com.android.server.telecom.CallState;
-import com.android.server.telecom.CallsManager;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
-/**
- * This transaction should be created for new incoming calls that request to go from
- * CallState.Ringing to CallState.Answered.  Before changing the CallState, the focus manager must
- * be updated. Once the focus manager updates, the call state will be set.  If there is an issue
- * answering the call, the transaction will fail.
- */
-public class AnswerCallTransaction extends VoipCallTransaction {
-
-    private static final String TAG = AnswerCallTransaction.class.getSimpleName();
-    private final CallsManager mCallsManager;
-    private final Call mCall;
-    private final int mVideoState;
-
-    public AnswerCallTransaction(CallsManager callsManager, Call call, int videoState) {
-        mCallsManager = callsManager;
-        mCall = call;
-        mVideoState = videoState;
-    }
-
-    @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
-        Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
-
-        mCall.setVideoState(mVideoState);
-
-        mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ANSWERED,
-                new OutcomeReceiver<>() {
-            @Override
-            public void onResult(Boolean result) {
-                Log.d(TAG, "processTransaction: onResult");
-                future.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
-            }
-
-            @Override
-            public void onError(CallException exception) {
-                Log.d(TAG, "processTransaction: onError");
-                future.complete(new VoipCallTransactionResult(
-                        exception.getCode(), exception.getMessage()));
-            }
-        });
-
-        return future;
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
index f47e4c5..8b4ffed 100644
--- a/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
+++ b/src/com/android/server/telecom/voip/CallEventCallbackAckTransaction.java
@@ -22,10 +22,12 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.telecom.CallAttributes;
 import android.telecom.DisconnectCause;
 import android.util.Log;
 
 import com.android.internal.telecom.ICallEventCallback;
+import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TransactionalServiceWrapper;
 
 import java.util.concurrent.CompletableFuture;
@@ -43,7 +45,7 @@
     private final String mAction;
     private final String mCallId;
     // optional values
-    private int mVideoState = 0;
+    private int mVideoState = CallAttributes.AUDIO_CALL;
     private DisconnectCause mDisconnectCause = null;
 
     private final VoipCallTransactionResult TRANSACTION_FAILED = new VoipCallTransactionResult(
@@ -66,7 +68,8 @@
     }
 
     public CallEventCallbackAckTransaction(ICallEventCallback service, String action,
-            String callId) {
+            String callId, TelecomSystem.SyncRoot lock) {
+        super(lock);
         mICallEventCallback = service;
         mAction = action;
         mCallId = callId;
@@ -74,7 +77,8 @@
 
 
     public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId,
-            int videoState) {
+            int videoState, TelecomSystem.SyncRoot lock) {
+        super(lock);
         mICallEventCallback = service;
         mAction = action;
         mCallId = callId;
@@ -82,7 +86,8 @@
     }
 
     public CallEventCallbackAckTransaction(ICallEventCallback service, String action, String callId,
-            DisconnectCause cause) {
+            DisconnectCause cause, TelecomSystem.SyncRoot lock) {
+        super(lock);
         mICallEventCallback = service;
         mAction = action;
         mCallId = callId;
diff --git a/src/com/android/server/telecom/voip/EndCallTransaction.java b/src/com/android/server/telecom/voip/EndCallTransaction.java
index 7a5006a..0cb7458 100644
--- a/src/com/android/server/telecom/voip/EndCallTransaction.java
+++ b/src/com/android/server/telecom/voip/EndCallTransaction.java
@@ -36,6 +36,7 @@
     private DisconnectCause mCause;
 
     public EndCallTransaction(CallsManager callsManager, DisconnectCause cause, Call call) {
+        super(callsManager.getLock());
         mCallsManager = callsManager;
         mCause = cause;
         mCall = call;
diff --git a/src/com/android/server/telecom/voip/EndpointChangeTransaction.java b/src/com/android/server/telecom/voip/EndpointChangeTransaction.java
index 88630c8..e037a79 100644
--- a/src/com/android/server/telecom/voip/EndpointChangeTransaction.java
+++ b/src/com/android/server/telecom/voip/EndpointChangeTransaction.java
@@ -32,6 +32,7 @@
     private final CallsManager mCallsManager;
 
     public EndpointChangeTransaction(CallEndpoint endpoint, CallsManager callsManager) {
+        super(callsManager.getLock());
         mCallEndpoint = endpoint;
         mCallsManager = callsManager;
     }
diff --git a/src/com/android/server/telecom/voip/HoldCallTransaction.java b/src/com/android/server/telecom/voip/HoldCallTransaction.java
index bf0805e..6c4e8b7 100644
--- a/src/com/android/server/telecom/voip/HoldCallTransaction.java
+++ b/src/com/android/server/telecom/voip/HoldCallTransaction.java
@@ -32,6 +32,7 @@
     private final Call mCall;
 
     public HoldCallTransaction(CallsManager callsManager, Call call) {
+        super(callsManager.getLock());
         mCallsManager = callsManager;
         mCall = call;
     }
diff --git a/src/com/android/server/telecom/voip/IncomingCallTransaction.java b/src/com/android/server/telecom/voip/IncomingCallTransaction.java
index 7bb9736..c0bb93d 100644
--- a/src/com/android/server/telecom/voip/IncomingCallTransaction.java
+++ b/src/com/android/server/telecom/voip/IncomingCallTransaction.java
@@ -20,7 +20,6 @@
 
 import android.os.Bundle;
 import android.telecom.CallAttributes;
-import android.telecom.CallControl;
 import android.telecom.CallException;
 import android.telecom.TelecomManager;
 import android.util.Log;
@@ -37,14 +36,22 @@
     private final String mCallId;
     private final CallAttributes mCallAttributes;
     private final CallsManager mCallsManager;
+    private final Bundle mExtras;
 
     public IncomingCallTransaction(String callId, CallAttributes callAttributes,
-            CallsManager callsManager) {
+            CallsManager callsManager, Bundle extras) {
+        super(callsManager.getLock());
+        mExtras = extras;
         mCallId = callId;
         mCallAttributes = callAttributes;
         mCallsManager = callsManager;
     }
 
+    public IncomingCallTransaction(String callId, CallAttributes callAttributes,
+            CallsManager callsManager) {
+        this(callId, callAttributes, callsManager, new Bundle());
+    }
+
     @Override
     public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
         Log.d(TAG, "processTransaction");
@@ -69,11 +76,10 @@
         }
     }
 
-    private Bundle generateExtras(CallAttributes callAttributes){
-        Bundle extras = new Bundle();
-        extras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
-        extras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
-        extras.putInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE, callAttributes.getCallType());
-        return extras;
+    private Bundle generateExtras(CallAttributes callAttributes) {
+        mExtras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
+        mExtras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
+        mExtras.putInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE, callAttributes.getCallType());
+        return mExtras;
     }
 }
diff --git a/src/com/android/server/telecom/voip/HoldActiveCallForNewCallTransaction.java b/src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
similarity index 87%
rename from src/com/android/server/telecom/voip/HoldActiveCallForNewCallTransaction.java
rename to src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
index 5d269b1..a245c1c 100644
--- a/src/com/android/server/telecom/voip/HoldActiveCallForNewCallTransaction.java
+++ b/src/com/android/server/telecom/voip/MaybeHoldCallForNewCallTransaction.java
@@ -26,13 +26,14 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
-public class HoldActiveCallForNewCallTransaction extends VoipCallTransaction {
+public class MaybeHoldCallForNewCallTransaction extends VoipCallTransaction {
 
-    private static final String TAG = HoldActiveCallForNewCallTransaction.class.getSimpleName();
+    private static final String TAG = MaybeHoldCallForNewCallTransaction.class.getSimpleName();
     private final CallsManager mCallsManager;
     private final Call mCall;
 
-    public HoldActiveCallForNewCallTransaction(CallsManager callsManager, Call call) {
+    public MaybeHoldCallForNewCallTransaction(CallsManager callsManager, Call call) {
+        super(callsManager.getLock());
         mCallsManager = callsManager;
         mCall = call;
     }
diff --git a/src/com/android/server/telecom/voip/OutgoingCallTransaction.java b/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
index 169fc48..0b17da2 100644
--- a/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
+++ b/src/com/android/server/telecom/voip/OutgoingCallTransaction.java
@@ -43,14 +43,22 @@
     private final String mCallingPackage;
     private final CallAttributes mCallAttributes;
     private final CallsManager mCallsManager;
+    private final Bundle mExtras;
+
+    public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes,
+            CallsManager callsManager, Bundle extras) {
+        super(callsManager.getLock());
+        mCallId = callId;
+        mContext = context;
+        mCallAttributes = callAttributes;
+        mCallsManager = callsManager;
+        mExtras = extras;
+        mCallingPackage = mContext.getOpPackageName();
+    }
 
     public OutgoingCallTransaction(String callId, Context context, CallAttributes callAttributes,
             CallsManager callsManager) {
-        mCallId = callId;
-        mContext = context;
-        mCallingPackage = mContext.getOpPackageName();
-        mCallAttributes = callAttributes;
-        mCallsManager = callsManager;
+        this(callId, context, callAttributes, callsManager, new Bundle());
     }
 
     @Override
@@ -112,13 +120,12 @@
         }
     }
 
-    private Bundle generateExtras(CallAttributes callAttributes){
-        Bundle extras = new Bundle();
-        extras.setDefusable(true);
-        extras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
-        extras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
-        extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
+    private Bundle generateExtras(CallAttributes callAttributes) {
+        mExtras.setDefusable(true);
+        mExtras.putString(TelecomManager.TRANSACTION_CALL_ID_KEY, mCallId);
+        mExtras.putInt(CALL_CAPABILITIES_KEY, callAttributes.getCallCapabilities());
+        mExtras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                 callAttributes.getCallType());
-        return extras;
+        return mExtras;
     }
 }
diff --git a/src/com/android/server/telecom/voip/ParallelTransaction.java b/src/com/android/server/telecom/voip/ParallelTransaction.java
index c2d532c..2762949 100644
--- a/src/com/android/server/telecom/voip/ParallelTransaction.java
+++ b/src/com/android/server/telecom/voip/ParallelTransaction.java
@@ -16,6 +16,8 @@
 
 package com.android.server.telecom.voip;
 
+import com.android.server.telecom.TelecomSystem;
+
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -23,8 +25,9 @@
  * A VoipCallTransaction implementation that its sub transactions will be executed in parallel
  */
 public class ParallelTransaction extends VoipCallTransaction {
-    public ParallelTransaction(List<VoipCallTransaction> subTransactions) {
-        super(subTransactions);
+    public ParallelTransaction(List<VoipCallTransaction> subTransactions,
+            TelecomSystem.SyncRoot lock) {
+        super(subTransactions, lock);
     }
 
     @Override
diff --git a/src/com/android/server/telecom/voip/RequestFocusTransaction.java b/src/com/android/server/telecom/voip/RequestFocusTransaction.java
deleted file mode 100644
index 5dedbda..0000000
--- a/src/com/android/server/telecom/voip/RequestFocusTransaction.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2022 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.voip;
-
-import android.os.OutcomeReceiver;
-import android.telecom.CallException;
-import android.util.Log;
-
-import com.android.server.telecom.Call;
-import com.android.server.telecom.CallState;
-import com.android.server.telecom.CallsManager;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
-public class RequestFocusTransaction extends VoipCallTransaction {
-
-    private static final String TAG = RequestFocusTransaction.class.getSimpleName();
-    private final CallsManager mCallsManager;
-    private final Call mCall;
-
-    public RequestFocusTransaction(CallsManager callsManager, Call call) {
-        mCallsManager = callsManager;
-        mCall = call;
-    }
-
-    @Override
-    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
-        Log.d(TAG, "processTransaction");
-        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
-
-        mCallsManager.transactionRequestNewFocusCall(mCall, CallState.ACTIVE,
-                new OutcomeReceiver<>() {
-            @Override
-            public void onResult(Boolean result) {
-                Log.d(TAG, "processTransaction: onResult");
-                future.complete(new VoipCallTransactionResult(
-                        VoipCallTransactionResult.RESULT_SUCCEED, null));
-            }
-
-            @Override
-            public void onError(CallException exception) {
-                Log.d(TAG, "processTransaction: onError");
-                future.complete(new VoipCallTransactionResult(
-                        exception.getCode(), exception.getMessage()));
-            }
-        });
-
-        return future;
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java b/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java
new file mode 100644
index 0000000..f586cc3
--- /dev/null
+++ b/src/com/android/server/telecom/voip/RequestNewActiveCallTransaction.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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.voip;
+
+import android.os.OutcomeReceiver;
+import android.telecom.CallAttributes;
+import android.telecom.CallException;
+import android.util.Log;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ConnectionServiceFocusManager;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * This transaction should be created when a requesting call would like to go from a valid inactive
+ * state (ex. HELD, RINGING, DIALING) to ACTIVE.
+ *
+ * This class performs some pre-checks to spot a failure in requesting a new call focus and sends
+ * the official request to transition the requested call to ACTIVE.
+ *
+ * Note:
+ * - This Transaction is used for CallControl and CallEventCallbacks, do not put logic in the
+ * onResult/onError that pertains to one direction.
+ * - MaybeHoldCallForNewCallTransaction was performed before this so any potential active calls
+ * should be held now.
+ */
+public class RequestNewActiveCallTransaction extends VoipCallTransaction {
+
+    private static final String TAG = RequestNewActiveCallTransaction.class.getSimpleName();
+    private final CallsManager mCallsManager;
+    private final Call mCall;
+
+    public RequestNewActiveCallTransaction(CallsManager callsManager, Call call) {
+        super(callsManager.getLock());
+        mCallsManager = callsManager;
+        mCall = call;
+    }
+
+    @Override
+    public CompletionStage<VoipCallTransactionResult> processTransaction(Void v) {
+        Log.d(TAG, "processTransaction");
+        CompletableFuture<VoipCallTransactionResult> future = new CompletableFuture<>();
+        int currentCallState = mCall.getState();
+
+        // certain calls cannot go active/answered (ex. disconnect calls, etc.)
+        if (!canBecomeNewCallFocus(currentCallState)) {
+            future.complete(new VoipCallTransactionResult(
+                    CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
+                    "CallState cannot be set to active or answered due to current call"
+                            + " state being in invalid state"));
+            return future;
+        }
+
+        if (mCallsManager.getActiveCall() != null) {
+            future.complete(new VoipCallTransactionResult(
+                    CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
+                    "Already an active call. Request hold on current active call."));
+            return future;
+        }
+
+        mCallsManager.requestNewCallFocusAndVerify(mCall, new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        Log.d(TAG, "processTransaction: onResult");
+                        future.complete(new VoipCallTransactionResult(
+                                VoipCallTransactionResult.RESULT_SUCCEED, null));
+                    }
+
+                    @Override
+                    public void onError(CallException exception) {
+                        Log.d(TAG, "processTransaction: onError");
+                        future.complete(new VoipCallTransactionResult(
+                                exception.getCode(), exception.getMessage()));
+                    }
+                });
+
+        return future;
+    }
+
+    private boolean isPriorityCallingState(int currentCallState) {
+        return ConnectionServiceFocusManager.PRIORITY_FOCUS_CALL_STATE.contains(currentCallState);
+    }
+
+    private boolean canBecomeNewCallFocus(int currentCallState) {
+        return isPriorityCallingState(currentCallState) || currentCallState == CallState.ON_HOLD;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/voip/SerialTransaction.java b/src/com/android/server/telecom/voip/SerialTransaction.java
index 87bc9ec..4c6d02e 100644
--- a/src/com/android/server/telecom/voip/SerialTransaction.java
+++ b/src/com/android/server/telecom/voip/SerialTransaction.java
@@ -16,14 +16,17 @@
 
 package com.android.server.telecom.voip;
 
+import com.android.server.telecom.TelecomSystem;
+
 import java.util.List;
 
 /**
  * A VoipCallTransaction implementation that its sub transactions will be executed in serial
  */
 public class SerialTransaction extends VoipCallTransaction {
-    public SerialTransaction(List<VoipCallTransaction> subTransactions) {
-        super(subTransactions);
+    public SerialTransaction(List<VoipCallTransaction> subTransactions,
+            TelecomSystem.SyncRoot lock) {
+        super(subTransactions, lock);
     }
 
     public void appendTransaction(VoipCallTransaction transaction){
diff --git a/src/com/android/server/telecom/voip/TransactionManager.java b/src/com/android/server/telecom/voip/TransactionManager.java
index a0955c8..98faf3d 100644
--- a/src/com/android/server/telecom/voip/TransactionManager.java
+++ b/src/com/android/server/telecom/voip/TransactionManager.java
@@ -16,6 +16,8 @@
 
 package com.android.server.telecom.voip;
 
+import static android.telecom.CallException.CODE_OPERATION_TIMED_OUT;
+
 import android.os.OutcomeReceiver;
 import android.telecom.TelecomManager;
 import android.telecom.CallException;
@@ -79,8 +81,8 @@
 
                 @Override
                 public void onTransactionTimeout(String transactionName){
-                    receiver.onResult(new VoipCallTransactionResult(
-                            VoipCallTransactionResult.RESULT_FAILED, transactionName + " timeout"));
+                    receiver.onError(new CallException(transactionName + " timeout",
+                            CODE_OPERATION_TIMED_OUT));
                     finishTransaction();
                 }
             });
diff --git a/src/com/android/server/telecom/voip/VoipCallMonitor.java b/src/com/android/server/telecom/voip/VoipCallMonitor.java
new file mode 100644
index 0000000..d0304a9
--- /dev/null
+++ b/src/com/android/server/telecom/voip/VoipCallMonitor.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2022 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.voip;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ForegroundServiceDelegationOptions;
+import android.app.Notification;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.telecom.Log;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManagerListenerBase;
+import com.android.server.telecom.LogUtils;
+import com.android.server.telecom.TelecomSystem;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class VoipCallMonitor extends CallsManagerListenerBase {
+
+    private final List<Call> mPendingCalls;
+    // Same notification may be passed as different object in onNotificationPosted and
+    // onNotificationRemoved. Use its string as key to cache ongoing notifications.
+    private final Map<String, Call> mNotifications;
+    private final Map<PhoneAccountHandle, Set<Call>> mPhoneAccountHandleListMap;
+    private ActivityManagerInternal mActivityManagerInternal;
+    private final Map<PhoneAccountHandle, ServiceConnection> mServices;
+    private NotificationListenerService mNotificationListener;
+    private final Object mLock = new Object();
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
+    private final Context mContext;
+    private List<StatusBarNotification> mPendingSBN;
+
+    public VoipCallMonitor(Context context, TelecomSystem.SyncRoot lock) {
+        mContext = context;
+        mHandlerThread = new HandlerThread(this.getClass().getSimpleName());
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mPendingCalls = new ArrayList<>();
+        mPendingSBN = new ArrayList<>();
+        mNotifications = new HashMap<>();
+        mServices = new HashMap<>();
+        mPhoneAccountHandleListMap = new HashMap<>();
+        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+
+        mNotificationListener = new NotificationListenerService() {
+            @Override
+            public void onNotificationPosted(StatusBarNotification sbn) {
+                synchronized (mLock) {
+                    if (sbn.getNotification().isStyle(Notification.CallStyle.class)) {
+                        boolean sbnMatched = false;
+                        for (Call call : mPendingCalls) {
+                            if (notificationMatchedCall(sbn, call)) {
+                                mPendingCalls.remove(call);
+                                mNotifications.put(sbn.toString(), call);
+                                sbnMatched = true;
+                                break;
+                            }
+                        }
+                        if (!sbnMatched) {
+                            // notification may posted before we started to monitor the call, cache
+                            // this notification and try to match it later with new added call.
+                            mPendingSBN.add(sbn);
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public void onNotificationRemoved(StatusBarNotification sbn) {
+                synchronized (mLock) {
+                    mPendingSBN.remove(sbn);
+                    if (mNotifications.isEmpty()) {
+                        return;
+                    }
+                    Call call = mNotifications.getOrDefault(sbn.toString(), null);
+                    if (call != null) {
+                        mNotifications.remove(sbn.toString(), call);
+                        stopFGSDelegation(call.getTargetPhoneAccount());
+                    }
+                }
+            }
+        };
+
+    }
+
+    public void startMonitor() {
+        try {
+            mNotificationListener.registerAsSystemService(mContext,
+                    new ComponentName(this.getClass().getPackageName(),
+                            this.getClass().getCanonicalName()), ActivityManager.getCurrentUser());
+        } catch (RemoteException e) {
+            Log.e(this, e, "Cannot register notification listener");
+        }
+    }
+
+    public void stopMonitor() {
+        try {
+            mNotificationListener.unregisterAsSystemService();
+        } catch (RemoteException e) {
+            Log.e(this, e, "Cannot unregister notification listener");
+        }
+    }
+
+    @Override
+    public void onCallAdded(Call call) {
+        if (!call.isTransactionalCall()) {
+            return;
+        }
+
+        synchronized (mLock) {
+            PhoneAccountHandle phoneAccountHandle = call.getTargetPhoneAccount();
+            Set<Call> callList = mPhoneAccountHandleListMap.computeIfAbsent(phoneAccountHandle,
+                    k -> new HashSet<>());
+            callList.add(call);
+
+            mHandler.post(
+                    () -> startFGSDelegation(call.getCallingPackageIdentity().mCallingPackagePid,
+                            call.getCallingPackageIdentity().mCallingPackageUid, call));
+        }
+    }
+
+    @Override
+    public void onCallRemoved(Call call) {
+        if (!call.isTransactionalCall()) {
+            return;
+        }
+
+        synchronized (mLock) {
+            stopMonitorWorks(call);
+            PhoneAccountHandle phoneAccountHandle = call.getTargetPhoneAccount();
+            Set<Call> callList = mPhoneAccountHandleListMap.computeIfAbsent(phoneAccountHandle,
+                    k -> new HashSet<>());
+            callList.remove(call);
+
+            if (callList.isEmpty()) {
+                stopFGSDelegation(phoneAccountHandle);
+            }
+        }
+    }
+
+    private void startFGSDelegation(int pid, int uid, Call call) {
+        Log.i(this, "startFGSDelegation for call %s", call.getId());
+        if (mActivityManagerInternal != null) {
+            PhoneAccountHandle handle = call.getTargetPhoneAccount();
+            ForegroundServiceDelegationOptions options = new ForegroundServiceDelegationOptions(pid,
+                    uid, handle.getComponentName().getPackageName(), null /* clientAppThread */,
+                    false /* isSticky */, String.valueOf(handle.hashCode()),
+                    0 /* foregroundServiceType */,
+                    ForegroundServiceDelegationOptions.DELEGATION_SERVICE_PHONE_CALL);
+            ServiceConnection fgsConnection = new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName name, IBinder service) {
+                    Log.addEvent(call, LogUtils.Events.GAINED_FGS_DELEGATION);
+                    mServices.put(handle, this);
+                    startMonitorWorks(call);
+                }
+
+                @Override
+                public void onServiceDisconnected(ComponentName name) {
+                    Log.addEvent(call, LogUtils.Events.LOST_FGS_DELEGATION);
+                    mServices.remove(handle);
+                }
+            };
+            try {
+                mActivityManagerInternal.startForegroundServiceDelegate(options, fgsConnection);
+            } catch (Exception e) {
+                Log.i(this, "startForegroundServiceDelegate failed due to: " + e);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public void stopFGSDelegation(PhoneAccountHandle handle) {
+        synchronized (mLock) {
+            Log.i(this, "stopFGSDelegation of handle %s", handle);
+            Set<Call> calls = mPhoneAccountHandleListMap.get(handle);
+            for (Call call : calls) {
+                stopMonitorWorks(call);
+            }
+            mPhoneAccountHandleListMap.remove(handle);
+
+            if (mActivityManagerInternal != null) {
+                ServiceConnection fgsConnection = mServices.get(handle);
+                if (fgsConnection != null) {
+                    mActivityManagerInternal.stopForegroundServiceDelegate(fgsConnection);
+                }
+            }
+        }
+    }
+
+    private void startMonitorWorks(Call call) {
+        startMonitorNotification(call);
+    }
+
+    private void stopMonitorWorks(Call call) {
+        stopMonitorNotification(call);
+    }
+
+    private void startMonitorNotification(Call call) {
+        synchronized (mLock) {
+            boolean sbnMatched = false;
+            for (StatusBarNotification sbn : mPendingSBN) {
+                if (notificationMatchedCall(sbn, call)) {
+                    mPendingSBN.remove(sbn);
+                    mNotifications.put(sbn.toString(), call);
+                    sbnMatched = true;
+                    break;
+                }
+            }
+            if (!sbnMatched) {
+                // Only continue to
+                mPendingCalls.add(call);
+                mHandler.postDelayed(() -> {
+                    synchronized (mLock) {
+                        if (mPendingCalls.contains(call)) {
+                            Log.i(this, "Notification for voip-call %s haven't "
+                                    + "posted in time, stop delegation.", call.getId());
+                            stopFGSDelegation(call.getTargetPhoneAccount());
+                            mPendingCalls.remove(call);
+                        }
+                    }
+                }, 5000L);
+            }
+        }
+    }
+
+    private void stopMonitorNotification(Call call) {
+        mPendingCalls.remove(call);
+    }
+
+    @VisibleForTesting
+    public void setActivityManagerInternal(ActivityManagerInternal ami) {
+        mActivityManagerInternal = ami;
+    }
+
+    @VisibleForTesting
+    public void setNotificationListenerService(NotificationListenerService listener) {
+        mNotificationListener = listener;
+    }
+
+    private boolean notificationMatchedCall(StatusBarNotification sbn, Call call) {
+        String packageName = sbn.getPackageName();
+        UserHandle userHandle = sbn.getUser();
+        PhoneAccountHandle accountHandle = call.getTargetPhoneAccount();
+
+        return packageName != null &&
+                packageName.equals(call.getTargetPhoneAccount()
+                        .getComponentName().getPackageName())
+                && userHandle != null
+                && userHandle.equals(accountHandle.getUserHandle());
+    }
+}
diff --git a/src/com/android/server/telecom/voip/VoipCallTransaction.java b/src/com/android/server/telecom/voip/VoipCallTransaction.java
index 413a1cd..a1cc13c 100644
--- a/src/com/android/server/telecom/voip/VoipCallTransaction.java
+++ b/src/com/android/server/telecom/voip/VoipCallTransaction.java
@@ -20,6 +20,7 @@
 import android.os.HandlerThread;
 
 import com.android.server.telecom.LoggedHandlerExecutor;
+import com.android.server.telecom.TelecomSystem;
 
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -36,17 +37,19 @@
     protected Handler mHandler;
     protected TransactionManager.TransactionCompleteListener mCompleteListener;
     protected List<VoipCallTransaction> mSubTransactions;
+    private TelecomSystem.SyncRoot mLock;
 
     public VoipCallTransaction(
-            List<VoipCallTransaction> subTransactions) {
+            List<VoipCallTransaction> subTransactions, TelecomSystem.SyncRoot lock) {
         mSubTransactions = subTransactions;
         mHandlerThread = new HandlerThread(this.toString());
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
+        mLock = lock;
     }
 
-    public VoipCallTransaction() {
-        this(null /** mSubTransactions */);
+    public VoipCallTransaction(TelecomSystem.SyncRoot lock) {
+        this(null /** mSubTransactions */, lock);
     }
 
     public void start() {
@@ -68,7 +71,7 @@
         CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
         future.thenComposeAsync(this::processTransaction,
                         new LoggedHandlerExecutor(mHandler, mTransactionName + "@"
-                                + hashCode() + ".pT", null))
+                                + hashCode() + ".pT", mLock))
                 .thenApplyAsync(
                         (Function<VoipCallTransactionResult, Void>) result -> {
                             mCompleted.set(true);
diff --git a/testapps/transactionalVoipApp/AndroidManifest.xml b/testapps/transactionalVoipApp/AndroidManifest.xml
index d0aa50b..e4968db 100644
--- a/testapps/transactionalVoipApp/AndroidManifest.xml
+++ b/testapps/transactionalVoipApp/AndroidManifest.xml
@@ -15,13 +15,20 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-     coreApp="true"
-     package="com.android.server.telecom.transactionalVoipApp">
+          coreApp="true"
+          package="com.android.server.telecom.transactionalVoipApp">
 
     <uses-sdk android:minSdkVersion="28"
-         android:targetSdkVersion="33"/>
+              android:targetSdkVersion="33"/>
 
-    <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
+    <uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
+    <!-- Needed to test media/audio -->
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <!-- Needed for foreground services -->
+    <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"/>
 
     <application android:label="Transactional Voip">
         <uses-library android:name="android.test.runner"/>
@@ -30,10 +37,26 @@
                   android:exported="true"
                   android:label="Transactional Voip">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.server.telecom.transactionalVoipApp.InCallActivity"
+                  android:exported="true"
+                  android:launchMode="singleInstance"
+                  android:label="InCall VoIP Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <service
+            android:name=".BackgroundIncomingCallService"
+            android:foregroundServiceType="phoneCall"
+            android:exported="false"
+        />
+
     </application>
 </manifest>
diff --git a/testapps/transactionalVoipApp/res/layout/in_call_activity.xml b/testapps/transactionalVoipApp/res/layout/in_call_activity.xml
new file mode 100644
index 0000000..54d467e
--- /dev/null
+++ b/testapps/transactionalVoipApp/res/layout/in_call_activity.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/getCallIdTextView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/get_call_id"
+    />
+
+    <Button
+        android:id="@+id/answer_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/answer"/>
+
+    <Button
+        android:id="@+id/set_call_active_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/set_call_active"/>
+
+    <Button
+        android:id="@+id/set_call_inactive_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/set_call_inactive"/>
+
+    <Button
+        android:id="@+id/disconnect_call_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/disconnect_call"/>
+
+    <Button
+        android:id="@+id/start_stream_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/start_stream"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/current_endpoint"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+        />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/request_earpiece"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/request_earpiece_endpoint"/>
+
+            <Button
+                android:id="@+id/request_speaker"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/request_speaker_endpoint"/>
+
+            <Button
+                android:id="@+id/request_bluetooth"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/request_bluetooth_endpoint"/>
+        </LinearLayout>
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/testapps/transactionalVoipApp/res/layout/main_activity.xml b/testapps/transactionalVoipApp/res/layout/main_activity.xml
index 86d8e20..28f0744 100644
--- a/testapps/transactionalVoipApp/res/layout/main_activity.xml
+++ b/testapps/transactionalVoipApp/res/layout/main_activity.xml
@@ -38,53 +38,31 @@
             android:layout_height="wrap_content"
             android:text="@string/register_phone_account"/>
 
-        <ToggleButton
-            android:id="@+id/callDirectionButton"
+        <Button
+            android:id="@+id/startForegroundService"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textOff="@string/direction_outgoing"
-            android:textOn="@string/direction_incoming"
+            android:text="@string/start_foreground_service"
         />
 
         <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:orientation="horizontal">
-            <Button
-                android:id="@+id/add_call_1_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/add_call_1"/>
 
             <Button
-                android:id="@+id/disconnect_call_1_button"
+                android:id="@+id/startOutgoingCall"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/disconnect_call_1"/>
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal">
+                android:text="@string/start_outgoing"
+            />
 
             <Button
-                android:id="@+id/add_call_2_button"
+                android:id="@+id/startIncomingCall"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/add_call_2"/>
-
-            <Button
-                android:id="@+id/set_call_2_active_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/set_call_active"/>
-
-            <Button
-                android:id="@+id/disconnect_call_2_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/disconnect_call_2"/>
+                android:text="@string/start_incoming"
+            />
         </LinearLayout>
     </LinearLayout>
 </LinearLayout>
diff --git a/testapps/transactionalVoipApp/res/raw/sample_audio.ogg b/testapps/transactionalVoipApp/res/raw/sample_audio.ogg
new file mode 100644
index 0000000..0129b46
--- /dev/null
+++ b/testapps/transactionalVoipApp/res/raw/sample_audio.ogg
Binary files differ
diff --git a/testapps/transactionalVoipApp/res/raw/sample_audio2.ogg b/testapps/transactionalVoipApp/res/raw/sample_audio2.ogg
new file mode 100644
index 0000000..a0b39b4
--- /dev/null
+++ b/testapps/transactionalVoipApp/res/raw/sample_audio2.ogg
Binary files differ
diff --git a/testapps/transactionalVoipApp/res/values-af/strings.xml b/testapps/transactionalVoipApp/res/values-af/strings.xml
index 3e12b43..78abd1b 100644
--- a/testapps/transactionalVoipApp/res/values-af/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-af/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API-toetsaktiwiteit"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaksionele inoproepaktiwiteit"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registreer foonrekening"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"uitgaande"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"inkomend"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"voeg oproep 1 by"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"beëindig oproep 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"voeg oproep 2 by"</string>
-    <string name="set_call_active" msgid="248748409907478011">"stel oproep 2 as aktief"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"beëindig oproep 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Begin voorgronddiens (simuleer masjienvertaling + app op agtergrond)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Begin uitgaande oproep"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Begin inkomende oproep"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"oproep-id is nie gestel nie"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"antwoord"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ontkoppel"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Oorstuk"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Luidspreker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"begin stroom"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-am/strings.xml b/testapps/transactionalVoipApp/res/values-am/strings.xml
index 9aba40a..2766bf8 100644
--- a/testapps/transactionalVoipApp/res/values-am/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-am/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"የግብይት ኤፒአይ ሙከራ እንቅስቃሴ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"በጥሪ እንቅስቃሴ ውስጥ ግብይታዊ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"የስልክ መለያ መዝግብ"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ወጪ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"መጪ"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ጥሪ 1ን አክል"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"የጥሪ 1ን ግንኙነት አቋርጥ"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ጥሪ 2ን አክል"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ጥሪ 2ን ወደ ንቁ አቀናብር"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"የጥሪ 2ን ግንኙነት አቋርጥ"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS ይጀምሩ (በዳራው ውስጥ MT + መተግበሪያን ያስመስላል)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ወጪ ጥሪን ይጀምሩ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ገቢ ጥሪን ይጀምሩ"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"የደዋይ መታወቂያ አልተቀናበረም"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"ወደ ገቢር ተቀናብሯል"</string>
+    <string name="answer" msgid="5423590397665409939">"መልስ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"ወደ ገቢር ያልሆነ ተቀናብሯል"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ግንኙነትን ያቋርጡ"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ማዳመጫ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ድምጽ ማውጫ"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ብሉቱዝ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ዥረት ይጀምሩ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ar/strings.xml b/testapps/transactionalVoipApp/res/values-ar/strings.xml
index c76746b..8a42e30 100644
--- a/testapps/transactionalVoipApp/res/values-ar/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ar/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"نشاط اختبار واجهة برمجة التطبيقات من خلال المعاملات"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"نشاط المعاملات أثناء المكالمة"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"تسجيل حساب الهاتف"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"الصادرة"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"الواردة"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"إضافة المكالمة 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"قطع المكالمة 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"إضافة المكالمة 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ضبط حالة المكالمة 2 على نشطة"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"قطع المكالمة 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"‏بدء FGS (محاكاة الترجمة الآلية + التطبيق في الخلفية)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"بدء مكالمة صادرة"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"بدء مكالمة واردة"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"لم يتم ضبط رقم تعريف المكالمة"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"الإجابة"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"إلغاء الربط"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"سماعة الأذن"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"مكبّر الصوت"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"البلوتوث"</string>
+    <string name="start_stream" msgid="3567634786280097431">"بدء البث"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-as/strings.xml b/testapps/transactionalVoipApp/res/values-as/strings.xml
index 66fae5f..56014c4 100644
--- a/testapps/transactionalVoipApp/res/values-as/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-as/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"লেনদেন সম্বন্ধীয় API পৰীক্ষণৰ কাৰ্যকলাপ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"কলত হোৱা লেনদেন সম্বন্ধীয় কাৰ্যকলাপ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ফ\'নৰ একাউণ্ট পঞ্জীয়ন কৰক"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"বহিৰ্গামী"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"অন্তৰ্গামী"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"কল ১ যোগ কৰক"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"কল ১ৰ সংযোগ বিচ্ছিন্ন কৰক"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"কল ২ যোগ কৰক"</string>
-    <string name="set_call_active" msgid="248748409907478011">"কল ২ক সক্ৰিয় হিচাপে ছেট কৰক"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"কল ২ৰ সংযোগ বিচ্ছিন্ন কৰক"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS আৰম্ভ কৰক (নেপথ্যত MT + এপ্ ছিমুলে’ট কৰক)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"বহিৰ্গামী কল আৰম্ভ কৰক"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"অন্তৰ্গামী কল আৰম্ভ কৰক"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"কলৰ আইডিটো ছেট কৰা হোৱা নাই"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"সক্ৰিয় হিচাপে ছেট কৰক"</string>
+    <string name="answer" msgid="5423590397665409939">"উত্তৰ দিয়ক"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"নিষ্ক্ৰিয় হিচাপে ছেট কৰক"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"সংযোগ বিচ্ছিন্ন কৰক"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ইয়েৰপিচ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"স্পীকাৰ"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ব্লুটুথ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ষ্ট্ৰীম কৰিবলৈ আৰম্ভ কৰক"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-az/strings.xml b/testapps/transactionalVoipApp/res/values-az/strings.xml
index 1bdb545..14af0ab 100644
--- a/testapps/transactionalVoipApp/res/values-az/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-az/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Tranzaksiya ilə bağlı API test Fəaliyyəti"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Tranzaksiya üzrə Zəngdaxili Fəaliyyət"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefon Hesabını Qeydiyyatdan Keçirin"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"gedən"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"gələn"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"1-ci zəngi əlavə edin"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"1-ci zəngi bitirin"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"2-ci zəngi əlavə edin"</string>
-    <string name="set_call_active" msgid="248748409907478011">"2-ci zəngi aktivləşdirin"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"2-ci zəngi bitirin"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS-ni başladın (arxa fonda MT + tətbiqini simulyasiya edin)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Gedən zəng başladın"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Gələn zəng başladın"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"zəng ID-si təyin olunmayıb"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Aktiv kimi təyin edin"</string>
+    <string name="answer" msgid="5423590397665409939">"cavab"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Qeyri-aktiv kimi təyin edin"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"əlaqəni kəsin"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Qulaqlıq"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Dinamik"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"yayıma başlayın"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml b/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml
index ad4605d..3c4019c 100644
--- a/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-b+sr+Latn/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktivnost testiranja transakcionog API-ja"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Aktivnost poziva u vezi sa transakcijama"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registruj nalog telefona"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"odlazni"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"dolazni"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"dodaj 1. poziv"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"prekini 1. poziv"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"dodaj 2. poziv"</string>
-    <string name="set_call_active" msgid="248748409907478011">"podesi 2. poziv kao aktivan"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"prekini 2. poziv"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Pokreni FGS (simulirajte MT + aplikaciju u pozadini)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Započnite odlazni poziv"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Započnite dolazni poziv"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID poziva nije podešen"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"odgovori"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"prekini vezu"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Slušalica"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"počnite da strimujete"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-be/strings.xml b/testapps/transactionalVoipApp/res/values-be/strings.xml
index ecb1464..9decf62 100644
--- a/testapps/transactionalVoipApp/res/values-be/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-be/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Праверачныя дзеянні API трансакцый"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Дзеянні падчас выклікаў"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Зарэгістраваць уліковы запіс тэлефона"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"выходны"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"уваходны"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"дадаць выклік 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"завяршыць выклік 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"дадаць выклік 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"зрабіць выклік 2 актыўным"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"завяршыць выклік 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Запусціць FGS (сімуляцыя MT + праграма ў фонавым рэжыме)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Пачаць выходны выклік"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Пачаць уваходны выклік"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ідэнтыфікатар выкліку не зададзены"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"адказаць"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"завяршыць выклік"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Навушнік"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Дынамік"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"пачаць перадачу плынню"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-bg/strings.xml b/testapps/transactionalVoipApp/res/values-bg/strings.xml
index c822ad5..63b55f9 100644
--- a/testapps/transactionalVoipApp/res/values-bg/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bg/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Активност за тестване на API за транзакции"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Транзакционална активност в обаждане"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Регистриране на профила на телефона"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"изходящо"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"входящо"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"добавяне на обаждане 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"прекратяване на обаждане 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"добавяне на обаждане 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"задаване на обаждане 2 като активно"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"прекратяване на обаждане 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Стартиране на FGS (симулиране на MT + приложението на заден план)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Започване на изходящо обаждане"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Започване на входящо обаждане"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"идентификаторът на обаждането не е зададен"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"отговаряне"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"прекратяване на връзката"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Слушалка"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Високоговорител"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"започване на поточно предаване"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-bn/strings.xml b/testapps/transactionalVoipApp/res/values-bn/strings.xml
index af4ecbc..b03123a 100644
--- a/testapps/transactionalVoipApp/res/values-bn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bn/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API টেস্ট সংক্রান্ত অ্যাক্টিভিটি"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"কল অ্যাক্টিভিটিতে হওয়া ট্রানজ্যাকশন"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ফোনের অ্যাকাউন্ট রেজিস্টার করুন"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"আউটগোয়িং"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ইনকামিং"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"কল ১ যোগ করুন"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"কল ১ ডিসকানেক্ট করুন"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"কল ২ যোগ করুন"</string>
-    <string name="set_call_active" msgid="248748409907478011">"কল ২ চালু আছে হিসেবে সেট করুন"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"কল ২ ডিসকানেক্ট করুন"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS শুরু করুন (সিমুলেট MT + ব্যাকগ্রাউন্ডে থাকা অ্যাপ)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"আউটগোয়িং কল শুরু করুন"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ইনকামিং কল শুরু করুন"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"কলার আইডি সেট করা নেই"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"উত্তর দিন"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ডিসকানেক্ট করুন"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ইয়ারপিস"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"স্পিকার"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ব্লুটুথ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"স্ট্রিমিং শুরু করুন"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-bs/strings.xml b/testapps/transactionalVoipApp/res/values-bs/strings.xml
index 96cedc5..e4cbb08 100644
--- a/testapps/transactionalVoipApp/res/values-bs/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-bs/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktivnost testa transakcijskog API-ja"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transakcijska aktivnost u pozivu"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrirajte račun telefona"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"odlazno"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"dolazno"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"dodaj poziv 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"prekini poziv 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"dodaj poziv 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"postavi poziv 2 kao aktivan"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"prekini poziv 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Pokreni FGS (simuliraj MT i aplikaciju u pozadini)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Pokreni odlazni poziv"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Pokreni dolazni poziv"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID poziva nije postavljen"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"postavi na Aktivno"</string>
+    <string name="answer" msgid="5423590397665409939">"odgovori"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"postavi na Neaktivno"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"prekini vezu"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Slušalica"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"pokreni prijenos"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ca/strings.xml b/testapps/transactionalVoipApp/res/values-ca/strings.xml
index ab16064..6780882 100644
--- a/testapps/transactionalVoipApp/res/values-ca/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ca/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Activitat de prova de l\'API transaccional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Activitat de transaccions durant la trucada"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registra el compte del telèfon"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"sortint"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"entrant"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"afegeix la trucada 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"desconnecta la trucada 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"afegeix la trucada 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"defineix la trucada 2 com a activa"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"desconnecta la trucada 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Inicia FGS (simula MT + aplicació en segon pla)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Inicia una trucada sortint"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Inicia una trucada entrant"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"identificador de trucada no definit"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"defineix com a activa"</string>
+    <string name="answer" msgid="5423590397665409939">"respon"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"defineix com a inactiva"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"desconnecta"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricular"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altaveu"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"inicia la reproducció en continu"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-cs/strings.xml b/testapps/transactionalVoipApp/res/values-cs/strings.xml
index b7be57f..46a938b 100644
--- a/testapps/transactionalVoipApp/res/values-cs/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-cs/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktivita testování v transakčním rozhraní API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transakční aktivita během hovoru"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrovat telefonní účet"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"odchozí"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"příchozí"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"přidat hovor 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"odpojit hovor 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"přidat hovor 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"nastavit hovor 2 jako aktivní"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"odpojit hovor 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Spustit službu v popředí (simulovat MT a aplikaci v pozadí)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Zahájit odchozí hovor"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Zahájit příchozí hovor"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID hovoru není nastaveno"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"odpověď"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"odpojit"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Sluchátko"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Reproduktor"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"zahájit streamování"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-da/strings.xml b/testapps/transactionalVoipApp/res/values-da/strings.xml
index 46e6a33..e857f3e 100644
--- a/testapps/transactionalVoipApp/res/values-da/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-da/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Testaktivitet for transaktions-API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaktionsrelateret aktivitet i opkald"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrer telefonkonto"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"udgående"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"indgående"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"tilføj opkald 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"afslut opkald 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"tilføj opkald 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"konfigurer opkald 2 som aktivt"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"afslut opkald 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simuler maskinoversættelse + app i baggrunden)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Start udgående opkald"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Start indgående opkald"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"opkalds-id ikke konfigureret"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Indstil som aktiv"</string>
+    <string name="answer" msgid="5423590397665409939">"svar"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Indstil som inaktiv"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"afslut opkald"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Højttaler"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Højttaler"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"start med at streame"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-de/strings.xml b/testapps/transactionalVoipApp/res/values-de/strings.xml
index b7d04e0..cf3116c 100644
--- a/testapps/transactionalVoipApp/res/values-de/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-de/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Testaktivität zur transaktionalen API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaktionsaktivität bei aktiven Anruf"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefonkonto registrieren"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"Ausgehend"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"Eingehend"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"Anruf 1 hinzufügen"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"Anruf 1 trennen"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"Anruf 2 hinzufügen"</string>
-    <string name="set_call_active" msgid="248748409907478011">"Anruf 2 aktivieren"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"Anruf 2 trennen"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS starten (MT und App im Hintergrund simulieren)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Ausgehenden Anruf starten"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Eingehenden Anruf starten"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"Anrufer-ID nicht festgelegt"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"aktiv"</string>
+    <string name="answer" msgid="5423590397665409939">"annehmen"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"inaktiv"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"beenden"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Kopfhörer"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Lautsprecher"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Streaming starten"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-el/strings.xml b/testapps/transactionalVoipApp/res/values-el/strings.xml
index bb5b0d9..d838d2e 100644
--- a/testapps/transactionalVoipApp/res/values-el/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-el/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Δοκιμαστική δραστηριότητα API συναλλαγών"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Δραστηριότητα συναλλαγής στην κλήση"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Εγγραφή λογαριασμού τηλεφώνου"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"εξερχόμενη"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"εισερχόμενη"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"προσθήκη κλήσης 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"αποσύνδεση κλήσης 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"προσθήκη κλήσης 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ορισμός κλήσης 2 ως ενεργής"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"αποσύνδεση κλήσης 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Έναρξη FGS (προσομοίωση MT + εφαρμογή στο παρασκήνιο)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Έναρξη εξερχόμενης κλήσης"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Έναρξη εισερχόμενης κλήσης"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"δεν έχει οριστεί αναγνωριστικό κλήσης"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"απάντηση"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"αποσύνδεση"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Ακουστικό τηλεφώνου"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Ηχείο"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"έναρξη ροής"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml b/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml
index 95c71e4..5bfa1a1 100644
--- a/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rAU/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API test activity"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional in-call activity"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Register phone account"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"outgoing"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"incoming"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"add call 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"disconnect call 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"add call 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"set call 2 active"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"disconnect call 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simulate MT + app in background)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Start outgoing call"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Start incoming call"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"call ID not set"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"answer"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"disconnect"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml b/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
index 0c705e3..1014001 100644
--- a/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rCA/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API test Activity"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional In Call Activity"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Register Phone Account"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"outgoing"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"incoming"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"add call 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"disconnect call 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"add call 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"set call 2 active"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"disconnect call 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simulate MT + app in background)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Start Outgoing Call"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Start Incoming Call"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"call id not set"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"answer"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"disconnect"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml b/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml
index 95c71e4..5bfa1a1 100644
--- a/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rGB/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API test activity"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional in-call activity"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Register phone account"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"outgoing"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"incoming"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"add call 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"disconnect call 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"add call 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"set call 2 active"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"disconnect call 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simulate MT + app in background)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Start outgoing call"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Start incoming call"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"call ID not set"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"answer"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"disconnect"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml b/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml
index 95c71e4..5bfa1a1 100644
--- a/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rIN/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API test activity"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional in-call activity"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Register phone account"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"outgoing"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"incoming"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"add call 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"disconnect call 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"add call 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"set call 2 active"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"disconnect call 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simulate MT + app in background)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Start outgoing call"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Start incoming call"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"call ID not set"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"answer"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"disconnect"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"start streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml b/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
index 2e69f39..40b0016 100644
--- a/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-en-rXC/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‏‎Transactional API test Activity‎‏‎‎‏‎"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎Transactional In Call Activity‎‏‎‎‏‎"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎Register Phone Account‎‏‎‎‏‎"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎outgoing‎‏‎‎‏‎"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎incoming‎‏‎‎‏‎"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎add call 1‎‏‎‎‏‎"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎disconnect call 1‎‏‎‎‏‎"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎add call 2‎‏‎‎‏‎"</string>
-    <string name="set_call_active" msgid="248748409907478011">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎set call 2 active‎‏‎‎‏‎"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎disconnect call 2‎‏‎‎‏‎"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎Start FGS (simulate MT + app in background)‎‏‎‎‏‎"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎Start Outgoing Call‎‏‎‎‏‎"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎Start Incoming Call‎‏‎‎‏‎"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎call id not set‎‏‎‎‏‎"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎setActive‎‏‎‎‏‎"</string>
+    <string name="answer" msgid="5423590397665409939">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎answer‎‏‎‎‏‎"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‎setInactive‎‏‎‎‏‎"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎disconnect‎‏‎‎‏‎"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎Earpiece‎‏‎‎‏‎"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎Speaker‎‏‎‎‏‎"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎Bluetooth‎‏‎‎‏‎"</string>
+    <string name="start_stream" msgid="3567634786280097431">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎start streaming‎‏‎‎‏‎"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml b/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml
index 06098c4..3410a16 100644
--- a/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-es-rUS/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Actividad de prueba de la API transaccional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Actividad transaccional en las llamadas"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrar cuenta telefónica"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"saliente"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"entrante"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"agregar llamada 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"desconectar la llamada 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"agregar llamada 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"establecer llamada 2 activa"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"desconectar la llamada 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Iniciar FGS (simulación de TA y app en segundo plano)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Iniciar llamada saliente"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Iniciar llamada entrante"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"no se estableció el identificador de llamadas"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"responder"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"desconectar"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricular"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Bocina"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Iniciar transmisión"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-es/strings.xml b/testapps/transactionalVoipApp/res/values-es/strings.xml
index 1391d3c..2ce1e81 100644
--- a/testapps/transactionalVoipApp/res/values-es/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-es/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Actividad de prueba de API transaccional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Actividad transaccional durante la llamada"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrar cuenta de teléfono"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"saliente"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"entrante"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"añadir llamada 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"desconectar llamada 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"añadir llamada 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"activar llamada 2"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"desconectar llamada 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Iniciar FGS (simular MT + aplicación en segundo plano)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Iniciar llamada saliente"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Iniciar llamada entrante"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"identificador de llamada no definido"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Activar"</string>
+    <string name="answer" msgid="5423590397665409939">"responder"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Desactivar"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"desconectar"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricular"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altavoz"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"iniciar emisión"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-et/strings.xml b/testapps/transactionalVoipApp/res/values-et/strings.xml
index c2dbe2e..477dec5 100644
--- a/testapps/transactionalVoipApp/res/values-et/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-et/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Tehingupõhise API testimise tegevus"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Kõnesisene toimingutegevus"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefonikonto registreerimine"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"väljaminevad"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"sissetulevad"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"lisa kõne 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"lõpeta kõne 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"lisa kõne 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"kõne 2 aktiivseks seadmine"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"lõpeta kõne 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Käivita FGS (simuleeri taustal MT-d ja rakendust)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Alusta väljuvat kõnet"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Alusta sissetulevat kõnet"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"helistaja ID pole seadistatud"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"vastus"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"katkesta ühendus"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Kuular"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Kõlar"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"käivita voogesitus"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-eu/strings.xml b/testapps/transactionalVoipApp/res/values-eu/strings.xml
index f3b0406..962346f 100644
--- a/testapps/transactionalVoipApp/res/values-eu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-eu/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transakzio bidezko APIen proba-jarduerak"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Deiko transakzio-jarduerak"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Erregistratu telefonoaren kontua"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"irteerakoa"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"sarrerakoa"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"gehitu 1. deia"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"deskonektatu 1. deia"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"gehitu 2. deia"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ezarri 2. deia aktibo gisa"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"deskonektatu 2. deia"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Hasi FGS (simulatu itzulpen automatikoa + aplikazioa atzeko planoan)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Hasi irteerako dei bat simulatzen"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Hasi sarrerako dei bat simulatzen"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ez da ezarri deiaren identifikatzailea"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"erantzun"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"deskonektatu"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Aurikularrak"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Bozgorailua"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetootha"</string>
+    <string name="start_stream" msgid="3567634786280097431">"hasi zuzenean igortzen"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fa/strings.xml b/testapps/transactionalVoipApp/res/values-fa/strings.xml
index 634f55e..bd9cddf 100644
--- a/testapps/transactionalVoipApp/res/values-fa/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fa/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"‏فعالیت آزمایشی Transactional API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"تبادلی در فعالیت تماس"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ثبت حساب تلفن"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"خروجی"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ورودی"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"افزودن تماس ۱"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"قطع تماس ۱"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"افزودن تماس ۲"</string>
-    <string name="set_call_active" msgid="248748409907478011">"تنظیم تماس ۲ روی حالت فعال"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"قطع تماس ۲"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"‏شروع FGS (شبیه‌سازی ترجمه ماشینی + برنامه در پس‌زمینه)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"شروع تماس خروجی"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"شروع تماس ورودی"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"شناسه تماس تنظیم نشده است"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"تنظیم به‌عنوان فعال"</string>
+    <string name="answer" msgid="5423590397665409939">"پاسخ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"تنظیم به‌عنوان غیرفعال"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"قطع ارتباط"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"گوشی"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"بلندگو"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"بلوتوث"</string>
+    <string name="start_stream" msgid="3567634786280097431">"شروع جاری‌سازی"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fi/strings.xml b/testapps/transactionalVoipApp/res/values-fi/strings.xml
index 6c9c5f7..c95efcb 100644
--- a/testapps/transactionalVoipApp/res/values-fi/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fi/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Tapahtuman API-testitoiminta"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Tapahtuman puhelunaikainen toiminta"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Rekisteröi puhelintili"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"lähtevä"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"saapuva"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"lisää puhelu 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"katkaise puhelu 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"lisää puhelu 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"aseta puhelu 2 aktiiviseksi"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"katkaise puhelu 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Käynnistä FGS (simuloi MT + sovellus taustalla)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Aloita lähtevä puhelu"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Aloita saapuva puhelu"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"soittajan tunnusta ei asetettu"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"aseta aktiiviseksi"</string>
+    <string name="answer" msgid="5423590397665409939">"vastaa"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"aseta ei-aktiiviseksi"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"katkaise yhteys"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Kaiutin"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Kaiutin"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"aloita suoratoisto"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml b/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml
index 5c9a397..64df91c 100644
--- a/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fr-rCA/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Activité de test de l\'API transactionnelle"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Activité transactionnelle durant l\'appel"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Inscrire un compte téléphonique"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"sortant"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"entrant"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ajouter l\'appel 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"déconnecter l\'appel 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ajouter l\'appel 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"définir l\'appel 2 comme actif"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"déconnecter l\'appel 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Démarrer FGS (simuler TA + application en arrière-plan)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Démarrer un appel sortant"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Démarrer un appel entrant"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"identifiant de l\'appel non défini"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"répondre"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"déconnecter"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Écouteur"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Haut-parleur"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"démarrer une diffusion"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-fr/strings.xml b/testapps/transactionalVoipApp/res/values-fr/strings.xml
index 48d8062..f1d1bd7 100644
--- a/testapps/transactionalVoipApp/res/values-fr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-fr/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Activité de test de l\'API transactionnelle"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Activité transactionnelle en cours d\'appel"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Enregistrer un compte de téléphonie"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"sortant"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"entrant"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ajouter un appel 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"mettre fin à l\'appel 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ajouter un appel 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"définir l\'appel 2 comme actif"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"mettre fin à l\'appel 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Démarrer les services de premier plan (simuler la MT + l\'application en arrière-plan)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Démarrer un appel sortant"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Démarrer un appel entrant"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"affichage du numéro de l\'appelant non défini"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Définir comme actif"</string>
+    <string name="answer" msgid="5423590397665409939">"réponse"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Définir comme inactif"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"raccrocher"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Écouteur"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Haut-parleur"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"démarrer la diffusion"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-gl/strings.xml b/testapps/transactionalVoipApp/res/values-gl/strings.xml
index 70cc9f6..76fbb34 100644
--- a/testapps/transactionalVoipApp/res/values-gl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-gl/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Actividade de proba da API transaccional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Actividade transaccional nas chamadas"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Rexistrar conta do teléfono"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"saínte"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"entrante"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"engadir chamada 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"desconectar chamada 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"engadir chamada 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"definir chamada 2 como activa"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"desconectar chamada 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Iniciar FGS (simular MT + aplicación en segundo plano)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Iniciar chamada saínte"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Iniciar chamada entrante"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"identificador de chamada non definido"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"responder"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"desconectar"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricular"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altofalante"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"iniciar reprodución en tempo real"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-gu/strings.xml b/testapps/transactionalVoipApp/res/values-gu/strings.xml
index d9935ae..b0066da 100644
--- a/testapps/transactionalVoipApp/res/values-gu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-gu/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional APIના પરીક્ષણની પ્રવૃત્તિ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"કૉલમાંની વ્યવહારિક પ્રવૃત્તિ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ફોન એકાઉન્ટ રજિસ્ટર કરો"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"આઉટગોઇંગ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ઇનકમિંગ"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"કૉલ 1 ઉમેરો"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"કૉલ 1 ડિસ્કનેક્ટ કરો"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"કૉલ 2 ઉમેરો"</string>
-    <string name="set_call_active" msgid="248748409907478011">"કૉલ 2ને સક્રિય પર સેટ કરો"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"કૉલ 2 ડિસ્કનેક્ટ કરો"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS (MT સિમ્યુલેટ કરવું + બૅકગ્રાઉન્ડમાં ઍપ) શરૂ કરો"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"આઉટગોઇંગ કૉલ શરૂ કરો"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ઇનકમિંગ કૉલ શરૂ કરો"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"કૉલર ID સેટ કરેલું નથી"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"સક્રિય તરીકે સેટ કરો"</string>
+    <string name="answer" msgid="5423590397665409939">"જવાબ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"નિષ્ક્રિય તરીકે સેટ કરો"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ડિસ્કનેક્ટ કરો"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ઇયરપીસ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"સ્પીકર"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"બ્લૂટૂથ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"સ્ટ્રીમિંગ શરૂ કરો"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hi/strings.xml b/testapps/transactionalVoipApp/res/values-hi/strings.xml
index 9121a37..a6e4a10 100644
--- a/testapps/transactionalVoipApp/res/values-hi/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hi/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API से जुड़ी टेस्ट गतिविधि"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"कॉल में क्लाइंट और सर्वर के बीच हुई बातचीत से जुड़ी गतिविधि"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Phone Account में रजिस्टर करें"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"किए जाने वाले (आउटगोइंग) कॉल"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"आने वाले (इनकमिंग) कॉल"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"कॉल 1 जोड़ें"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"कॉल 1 को डिसकनेक्ट करें"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"कॉल 2 जोड़ें"</string>
-    <string name="set_call_active" msgid="248748409907478011">"कॉल 2 को \'चालू है\' के तौर पर सेट करें"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"कॉल 2 को डिसकनेक्ट करें"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS शुरू करें (बैकग्राउंड में MT + ऐप्लिकेशन को सिम्युलेट करें)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"आउटगोइंग कॉल शुरू करें"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"इनकमिंग कॉल शुरू करें"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"कॉल आईडी सेट नहीं है"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"जवाब"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"डिसकनेक्ट करें"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ईयरपीस"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"स्पीकर"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ब्लूटूथ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"स्ट्रीमिंग शुरू करें"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hr/strings.xml b/testapps/transactionalVoipApp/res/values-hr/strings.xml
index 68291d6..768d378 100644
--- a/testapps/transactionalVoipApp/res/values-hr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hr/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Testna aktivnost API-ja za transakcije"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"transakcijska aktivnost u pozivu"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registracija telefonskog računa"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"odlazni"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"dolazni"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"dodavanje 1. poziva"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"prekid 1. poziva"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"dodavanje 2. poziva"</string>
-    <string name="set_call_active" msgid="248748409907478011">"postavljane 2. poziva kao aktivnog"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"prekid 2. poziva"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Pokretanje FGS-a (simulacija: MT i aplikacija u pozadini)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Pokretanje odlaznog poziva"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Pokretanje dolaznog poziva"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"id poziva nije postavljen"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Postavljanje kao aktivno"</string>
+    <string name="answer" msgid="5423590397665409939">"odgovor"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Postavljanje kao neaktivno"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"prekid veze"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Slušalica"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvučnik"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"pokretanje streaminga"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hu/strings.xml b/testapps/transactionalVoipApp/res/values-hu/strings.xml
index fc903f8..cda3b7e 100644
--- a/testapps/transactionalVoipApp/res/values-hu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hu/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Tranzakciós API-teszttevékenység"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Hívás közbeni tranzakciós tevékenység"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefonáláshoz használt fiók regisztrálása"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"kimenő"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"bejövő"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"1. hívás hozzáadása"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"1. hívás megszakítása"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"2. hívás hozzáadása"</string>
-    <string name="set_call_active" msgid="248748409907478011">"2. hívás aktívra állítása"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"2. hívás megszakítása"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Előtérben futó szolgáltatás indítása (gépi fordítás + alkalmazás szimulálása a háttérben)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Kimenő hívás indítása"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Bejövő hívás indítása"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"nincs beállítva hívásazonosító"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"válasz"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"leválasztás"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Fülhallgató"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Hangszóró"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"streamelés indítása"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-hy/strings.xml b/testapps/transactionalVoipApp/res/values-hy/strings.xml
index a781b20..b56941f 100644
--- a/testapps/transactionalVoipApp/res/values-hy/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-hy/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Գործարքային API-ների փորձարկման գործողություն"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Գործարքներ զանգի ժամանակ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Հեռախոսի հաշվի գրանցում"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ելքային"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"մուտքային"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ավելացնել զանգ 1-ը"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ընդհատել զանգ 1-ը"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ավելացնել զանգ 2-ը"</string>
-    <string name="set_call_active" msgid="248748409907478011">"զանգ 2-ը դարձնել ակտիվ"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ընդհատել զանգ 2-ը"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Գործարկել FGS-ը (ՄԹ-ի սիմուլացիա + հավելված ֆոնային ռեժիմում)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Սկսել ելքային զանգ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Սկսել մուտքային զանգ"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"զանգի նույնացուցիչ սահմանված չէ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"ակտիվացնել"</string>
+    <string name="answer" msgid="5423590397665409939">"պատասխանել"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"ապակտիվացնել"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"անջատել"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Լսափող"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Բարձրախոս"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"սկսել հեռարձակում"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-in/strings.xml b/testapps/transactionalVoipApp/res/values-in/strings.xml
index 0b1f12f..e29fea7 100644
--- a/testapps/transactionalVoipApp/res/values-in/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-in/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktivitas pengujian API Transaksional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Aktivitas Transaksi Dalam Panggilan"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Daftarkan Akun Ponsel"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"keluar"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"masuk"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"tambahkan panggilan 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"akhiri panggilan 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"tambahkan panggilan 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"setel panggilan 2 ke aktif"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"akhiri panggilan 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Mulai FGS (simulasikan MT + aplikasi di latar belakang)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Mulai Panggilan Keluar"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Mulai Panggilan Masuk"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"id panggilan tidak ditetapkan"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setelAktif"</string>
+    <string name="answer" msgid="5423590397665409939">"jawab"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setelNonaktif"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"putuskan koneksi"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"mulai streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-is/strings.xml b/testapps/transactionalVoipApp/res/values-is/strings.xml
index c067c74..4ecb2ca 100644
--- a/testapps/transactionalVoipApp/res/values-is/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-is/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Prófun á virkni forritaskila færslna"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Virkni í símtali"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Skrá símareikning"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"hringt"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"móttekið"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"bæta við símtali 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"slíta símtali 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"bæta við símtali 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"virkja símtal 2"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"slíta símtali 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Ræsa FGS (líkja eftir MT + forriti í bakgrunni)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Hefja hringt símtal"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Hefja símtal sem berst"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"númerabirting ekki stillt"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"svara"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"aftengja"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Eyrnatól"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Hátalari"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"hefja streymi"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-it/strings.xml b/testapps/transactionalVoipApp/res/values-it/strings.xml
index 35da8ae..bb83aa1 100644
--- a/testapps/transactionalVoipApp/res/values-it/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-it/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Attività di test dell\'API transazionale"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Attività di transazione durante la chiamata"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registra account telefono"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"in uscita"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"in arrivo"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"aggiungi chiamata 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"termina chiamata 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"aggiungi chiamata 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"imposta chiamata 2 attiva"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"termina chiamata 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Avvia FGS (simulazione di MT + app in background)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Avvia chiamata in uscita"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Avvia chiamata in arrivo"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"id chiamata non impostato"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"risposta"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"disconnetti"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricolare"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altoparlante"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"avvia streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-iw/strings.xml b/testapps/transactionalVoipApp/res/values-iw/strings.xml
index 95557df..4de997e 100644
--- a/testapps/transactionalVoipApp/res/values-iw/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-iw/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API test Activity"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"בר ביצוע בפעילות השיחה"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"רישום חשבון הטלפון"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"שיחה יוצאת"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"שיחה נכנסת"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"הוספת שיחה 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ניתוק שיחה 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"הוספת שיחה 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"הגדרת שיחה 2 פעילה"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ניתוק שיחה 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"‏הפעלת FGS (סימולציה של MT + אפליקציה ברקע)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"התחלת שיחה יוצאת"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"התחלת שיחה נכנסת"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"מזהה השיחה לא הוגדר"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"הגדרה כפעיל"</string>
+    <string name="answer" msgid="5423590397665409939">"תשובה"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"הגדרה כלא פעיל"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ניתוק"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"אוזניה"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"רמקול"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"התחלת השידור"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ja/strings.xml b/testapps/transactionalVoipApp/res/values-ja/strings.xml
index 86d6189..a5e8251 100644
--- a/testapps/transactionalVoipApp/res/values-ja/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ja/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API テスト アクティビティ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transactional 通話アクティビティ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"スマートフォン アカウントを登録"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"発信"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"着信"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"通話 1 を追加"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"通話 1 を切る"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"通話 2 を追加"</string>
-    <string name="set_call_active" msgid="248748409907478011">"通話 2 をアクティブに設定"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"通話 2 を切る"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS を開始(MT + アプリをバックグラウンドでシミュレート)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"発信を開始"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"着信を開始"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"通話 ID が設定されていません"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"応答"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"切断"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"受話口"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"スピーカー"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ストリーミングを開始"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ka/strings.xml b/testapps/transactionalVoipApp/res/values-ka/strings.xml
index 1f3e69e..671cffb 100644
--- a/testapps/transactionalVoipApp/res/values-ka/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ka/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ტრანზაქციული API ტესტის აქტივობა"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ტრანზაქციის ზარის აქტივობა"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ტელეფონის ანგარიშის რეგისტრაცია"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"გამავალი"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"შემომავალი"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ზარი 1-ის დამატება"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ზარი 1-ის გათიშვა"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ზარი 2-ის დამატება"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ზარი 2-ის აქტივაცია"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ზარის 2-ის გათიშვა"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS-ის დაწყება (MT + აპის სიმულაცია ფონზე)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"დაიწყეთ გამავალი ზარი"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"დაიწყეთ შემომავალი ზარი"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"აბონენტის ID არ არის დაყენებული"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"პასუხი"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"კავშირის გაწყვეტა"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ყურმილი"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"დინამიკი"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"სტრიმინგის დაწყება"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-kk/strings.xml b/testapps/transactionalVoipApp/res/values-kk/strings.xml
index a82c02a..2713491 100644
--- a/testapps/transactionalVoipApp/res/values-kk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-kk/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Транзакциялық API сынағына қатысты әрекет"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Қоңыраулар тарихындағы транзакциялық қолданба"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Телефон аккаунтын тіркеу"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"шығыс"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"кіріс"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"1-қоңырауды қосу"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"1-қоңырауды ажырату"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"2-қоңырауды қосу"</string>
-    <string name="set_call_active" msgid="248748409907478011">"2-қоңырауды белсенді қылу"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"2-қоңырауды ажырату"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS-ті бастау (MT мен қолданбаны фонда симуляциялау)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Шығыс қоңырауын бастау"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Кіріс қоңырауын бастау"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"қоңырау идентификаторы орнатылмады"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"жауап беру"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ажырату"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Динамик"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Динамик"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"трансляцияны бастау"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-km/strings.xml b/testapps/transactionalVoipApp/res/values-km/strings.xml
index 518fda6..13f4983 100644
--- a/testapps/transactionalVoipApp/res/values-km/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-km/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"សកម្មភាព​ធ្វើតេស្ត API ប្រតិបត្តិការ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"សកម្មភាពប្រតិបត្តិការ​នៅក្នុងការហៅទូរសព្ទ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ចុះឈ្មោះ​គណនី​ទូរសព្ទ"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ចេញ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ចូល"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"បញ្ចូល​ការហៅ​ទូរសព្ទទី 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ផ្ដាច់​ការហៅទូរសព្ទទី 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"បញ្ចូល​ការហៅ​ទូរសព្ទទី 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"កំណត់​ការហៅ​ទូរសព្ទទី 2 ឱ្យសកម្ម"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ផ្ដាច់​ការហៅទូរសព្ទទី 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"ចាប់ផ្ដើម FGS (ត្រាប់តាម MT + កម្មវិធី​នៅផ្ទៃខាងក្រោយ)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ចាប់ផ្ដើម​ការហៅចេញ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ចាប់ផ្ដើម​ការហៅ​ចូល"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"មិនបានកំណត់​លេខសម្គាល់​ការហៅទូរសព្ទទេ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"ឆ្លើយ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ផ្ដាច់"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ឧបករណ៍ស្ដាប់សំឡេង"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ឧបករណ៍​បំពង​សំឡេង"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ប៊្លូធូស"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ចាប់ផ្ដើម​ការផ្សាយ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-kn/strings.xml b/testapps/transactionalVoipApp/res/values-kn/strings.xml
index 094bf01..b994f92 100644
--- a/testapps/transactionalVoipApp/res/values-kn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-kn/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ಟ್ರಾನ್ಸಾಕ್ಷನಲ್ API ಪರೀಕ್ಷಾ ಚಟುವಟಿಕೆ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ಕರೆ ಚಟುವಟಿಕೆಯಲ್ಲಿ ಟ್ರಾನ್ಸಾಕ್ಷನಲ್"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ಫೋನ್ ಖಾತೆಯನ್ನು ನೋಂದಾಯಿಸಿ"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ಹೊರಹೋಗುವುದು"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ಒಳಬರುವುದು"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ಕರೆ 1 ಅನ್ನು ಸೇರಿಸಿ"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ಕರೆ 1 ಅನ್ನು ಕಡಿತಗೊಳಿಸಿ"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ಕರೆ 2 ಅನ್ನು ಸೇರಿಸಿ"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ಕರೆ 2 ಅನ್ನು ಸಕ್ರಿಯ ಎಂದು ಸೆಟ್ ಮಾಡಿ"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ಕರೆ 2 ಅನ್ನು ಕಡಿತಗೊಳಿಸಿ"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS ಅನ್ನು ಪ್ರಾರಂಭಿಸಿ (MT + ಆ್ಯಪ್ ಅನ್ನು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಅನುಕರಿಸಿ)."</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ಹೊರಹೋಗುವ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ಒಳಬರುವ ಕರೆಯನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ಕರೆಮಾಡುವವರ ID ಅನ್ನು ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"ಉತ್ತರ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ಡಿಸ್‌ಕನೆಕ್ಟ್"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ಇಯರ್‌ಪೀಸ್‌"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ಸ್ಪೀಕರ್"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ಬ್ಲೂಟೂತ್"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ಸ್ಟ್ರೀಮ್ ಮಾಡುವುದನ್ನು ಪ್ರಾರಂಭಿಸಿ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ko/strings.xml b/testapps/transactionalVoipApp/res/values-ko/strings.xml
index 10c9f62..9eb4556 100644
--- a/testapps/transactionalVoipApp/res/values-ko/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ko/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"트랜잭션 API 테스트 활동"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"통화 중 거래 활동"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"전화 계정 등록"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"발신"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"수신"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"통화 1 추가"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"통화 1 끊기"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"통화 2 추가"</string>
-    <string name="set_call_active" msgid="248748409907478011">"통화 2 활성으로 설정"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"통화 2 끊기"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS 시작(MT 및 백그라운드 앱 시뮬레이션)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"발신 전화 시작"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"수신 전화 시작"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"통화 ID가 설정되지 않음"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"활성으로 설정"</string>
+    <string name="answer" msgid="5423590397665409939">"답변"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"비활성으로 설정"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"연결 해제"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"스피커"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"스피커"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"블루투스"</string>
+    <string name="start_stream" msgid="3567634786280097431">"스트리밍 시작"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ky/strings.xml b/testapps/transactionalVoipApp/res/values-ky/strings.xml
index 06bff74..577dcda 100644
--- a/testapps/transactionalVoipApp/res/values-ky/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ky/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Транзакциялык API сыноосунун активдүүлүгү"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Чалуу учурундагы транзакциялар"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Телефон аккаунтун каттоо"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"чыгуучу"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"келүүчү"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"1-чалууну кошуу"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"1-чалууну үзүү"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"2-чалууну кошуу"</string>
-    <string name="set_call_active" msgid="248748409907478011">"2-чалууну активдүү кылуу"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"2-чалууну үзүү"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS\'ти иштетүү (фондо MT + колдонмону симуляциялоо)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Чыгуучу чалууну баштоо"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Кирүүчү чалууну баштоо"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"чалуунун идентификатору коюлган жок"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"жооп берүү"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ажыратуу"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Кулакчын"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Динамик"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"агымды баштоо"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-lo/strings.xml b/testapps/transactionalVoipApp/res/values-lo/strings.xml
index f5f6dd5..69126d9 100644
--- a/testapps/transactionalVoipApp/res/values-lo/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-lo/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ກິດຈະກໍາການທົດສອບ API ທຸລະກໍາ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ການເຄື່ອນໄຫວຂອງທຸລະກຳລະຫວ່າງການໂທ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ລົງທະບຽນບັນຊີໂທລະສັບ"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ສາຍໂທອອກ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ສາຍໂທເຂົ້າ"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ເພີ່ມການໂທ 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ຕັດເຊື່ອມຕໍ່ການໂທ 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ເພີ່ມການໂທ 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ຕັ້ງການໂທ 2 ເປັນນຳໃຊ້"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ຕັດເຊື່ອມຕໍ່ການໂທ 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"ເລີ່ມ FGS (ຈຳລອງ MT + ແອັບໃນພື້ນຫຼັງ)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ເລີ່ມສາຍໂທອອກ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ເລີ່ມສາຍໂທເຂົ້າ"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ບໍ່ໄດ້ຕັ້ງໝາຍເລກຜູ້ໂທ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"ຕັ້ງຄ່າເປັນນຳໃຊ້ຢູ່"</string>
+    <string name="answer" msgid="5423590397665409939">"ຄຳຕອບ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"ຕັ້ງຄ່າເປັນບໍ່ໄດ້ນຳໃຊ້"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ຕັດການເຊື່ອມຕໍ່"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ຫູຟັງ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ລຳໂພງ"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ເລີ່ມການສະຕຣີມ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-lt/strings.xml b/testapps/transactionalVoipApp/res/values-lt/strings.xml
index 5e66c15..91e51fe 100644
--- a/testapps/transactionalVoipApp/res/values-lt/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-lt/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Operacijų API testavimo veikla"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Operacijų skambutyje veikla"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Užregistruoti telefono paskyrą"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"siunčiamieji"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"gaunamieji"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"pridėti 1 skambutį"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"atjungti 1 skambutį"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"pridėti 2 skambutį"</string>
-    <string name="set_call_active" msgid="248748409907478011">"nustatyti 2 skambutį kaip aktyvų"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"atjungti 2 skambutį"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Pradėti FGS (modeliuoti MT ir programą fone)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Pradėti siunčiamąjį skambutį"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Pradėti gaunamąjį skambutį"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"skambučio ID nenustatytas"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"atsakyti"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"atsijungti"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Garsiakalbis prie ausies"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Garsiakalbis"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"pradėti srautinį perdavimą"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-lv/strings.xml b/testapps/transactionalVoipApp/res/values-lv/strings.xml
index 6678d6e..ae6896f 100644
--- a/testapps/transactionalVoipApp/res/values-lv/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-lv/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transakciju API testa darbība"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Ar darījumiem saistītas darbības zvana laikā"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Reģistrēt tālruņa kontu"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"izejošs"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ienākošs"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"pievienot 1. zvanu"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"pārtraukt 1. zvanu"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"pievienot 2. zvanu"</string>
-    <string name="set_call_active" msgid="248748409907478011">"iestatīt 2. zvanu kā aktīvu"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"pārtraukt 2. zvanu"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Sākt FGS (simulēt mašīntulkojumu un lietotni fonā)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Sākt izejoša zvana simulāciju"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Sākt ienākoša zvana simulāciju"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"zvana ID nav iestatīts"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"atbildēt"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"pārtraukt"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auss skaļrunis"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Skaļrunis"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"sākt straumēšanu"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-mk/strings.xml b/testapps/transactionalVoipApp/res/values-mk/strings.xml
index 01cd42e..8501eaf 100644
--- a/testapps/transactionalVoipApp/res/values-mk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-mk/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Активност на тестирање на API за трансакции"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Трансакциска активност во повикот"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Регистрирај телефонска сметка"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"појдовен"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"дојдовен"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"додај повик 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"прекини го повикот 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"додај повик 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"постави го повик 2 како активен"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"прекини го повикот 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Започни FGS (симулирај MT + апликација во заднина)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Започни појдовен повик"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Започни дојдовен повик"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"не е поставен ID на повикувач"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"одговори"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"прекини врска"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Слушалка"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Звучник"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"започни стриминг"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ml/strings.xml b/testapps/transactionalVoipApp/res/values-ml/strings.xml
index 66ec4ef..67e4e34 100644
--- a/testapps/transactionalVoipApp/res/values-ml/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ml/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ട്രാൻസാക്ഷണൽ API ടെസ്റ്റ് ആക്റ്റിവിറ്റി"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ട്രാൻസാക്ഷണൽ ഇൻ കോൾ ആക്റ്റിവിറ്റി"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ഫോൺ അക്കൗണ്ട് രജിസ്റ്റർ ചെയ്യുക"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ഔട്ട്‌ഗോയിംഗ്"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ഇൻകമിംഗ്"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"കോൾ 1 ചേർക്കുക"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"കോൾ 1 വിച്ഛേദിക്കുക"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"കോൾ 2 ചേർക്കുക"</string>
-    <string name="set_call_active" msgid="248748409907478011">"കോൾ 2 സജീവമായി സജ്ജീകരിക്കുക"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"കോൾ 2 വിച്ഛേദിക്കുക"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS ആരംഭിക്കുക (പശ്ചാത്തലത്തിൽ മെഷീൻ ട്രാൻസ്‌ലേഷൻ + ആപ്പ് സിമുലേറ്റ് ചെയ്യുക)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ഔട്ട്‌ഗോയിംഗ് കോൾ ആരംഭിക്കുക"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ഇൻകമിംഗ് കോൾ ആരംഭിക്കുക"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"കോൾ ഐഡി സജ്ജീകരിച്ചിട്ടില്ല"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"സജീവമെന്ന് സജ്ജീകരിക്കുക"</string>
+    <string name="answer" msgid="5423590397665409939">"ഉത്തരം നൽകുക"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"സജീവമല്ലെന്ന് സജ്ജീകരിക്കുക"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"വിച്ഛേദിക്കുക"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ഇയർഫോൺ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"സ്പീക്കർ"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"സ്‌ട്രീമിംഗ് ആരംഭിക്കുക"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-mn/strings.xml b/testapps/transactionalVoipApp/res/values-mn/strings.xml
index f056b2b..e4b6f36 100644
--- a/testapps/transactionalVoipApp/res/values-mn/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-mn/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Гүйлгээний API-н туршилтын үйл ажиллагаа"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Дуудлагын үйл ажиллагааны гүйлгээ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Утасны бүртгэл бүртгүүлэх"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"залгаж буй"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ирж буй"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"1-р дуудлага нэмэх"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"1-р дуудлагыг салгах"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"2-р дуудлага нэмэх"</string>
-    <string name="set_call_active" msgid="248748409907478011">"2-р дуудлагыг идэвхтэй болгож тохируулах"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"2-р дуудлагыг салгах"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS-г эхлүүлэх (дэвсгэрт MT + аппыг загварчлах)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Залгасан дуудлагыг эхлүүлэх"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Ирсэн дуудлагыг эхлүүлэх"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"дуудлагын ID-г тохируулаагүй"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"хариулах"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"салгах"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Чихний спикер"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Чанга яригч"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"дамжуулалтыг эхлүүлэх"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-mr/strings.xml b/testapps/transactionalVoipApp/res/values-mr/strings.xml
index 4ca3b8e..dfb3184 100644
--- a/testapps/transactionalVoipApp/res/values-mr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-mr/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"व्यावहारिक API चाचणी अ‍ॅक्टिव्हिटी"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"कॉल अ‍ॅक्टिव्हिटी यामधील व्यवहार"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"फोन खात्याची नोंदणी करा"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"आउटगोइंग"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"इनकमिंग"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"कॉल १ जोडा"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"कॉल १ डिस्कनेक्ट करा"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"कॉल २ जोडा"</string>
-    <string name="set_call_active" msgid="248748409907478011">"कॉल २ अ‍ॅक्टिव्ह यावर सेट करा"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"कॉल २ डिस्कनेक्ट करा"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS सुरू करा (बॅकग्राउंडमध्ये MT + अ‍ॅप सिम्युलेट करा)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"आउटगोइंग कॉल सुरू करा"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"इनकमिंग कॉल सुरू करा"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"कॉल आयडी सेट केलेला नाही"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"उत्तर"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"‍डिस्कनेक्ट करा"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"इअरपिस"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"स्पीकर"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ब्लूटूथ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"स्ट्रीम करणे सुरू करा"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ms/strings.xml b/testapps/transactionalVoipApp/res/values-ms/strings.xml
index d888672..3005391 100644
--- a/testapps/transactionalVoipApp/res/values-ms/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ms/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktiviti ujian API transaksi"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaksi Aktiviti Dalam Panggilan"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Daftar Akaun Telefon"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"keluar"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"masuk"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"tambahkan panggilan 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"putuskan panggilan 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"tambahkan panggilan 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"tetapkan panggilan 2 sebagai aktif"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"putuskan panggilan 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Mulakan FGS (simulasi MT + apl pada latar)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Mulakan Panggilan Keluar"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Mulakan Panggilan Masuk"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID panggilan tidak ditetapkan"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"jawab"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"putuskan sambungan"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Alat dengar"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Pembesar suara"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"mulakan penstriman"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-my/strings.xml b/testapps/transactionalVoipApp/res/values-my/strings.xml
index 7422922..818a3f7 100644
--- a/testapps/transactionalVoipApp/res/values-my/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-my/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"အသိအမှတ်ပြုမှုဆိုင်ရာ API စမ်းသပ်လုပ်ဆောင်ချက်"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ခေါ်ဆိုမှုလုပ်ဆောင်ချက်ရှိ မှတ်တမ်းဆိုင်ရာ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ဖုန်းအကောင့် မှတ်ပုံတင်ရန်"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"အထွက်"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"အဝင်"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ခေါ်ဆိုမှု ၁ ထည့်ရန်"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ခေါ်ဆိုမှု ၁ ဖြတ်တောက်ရန်"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ခေါ်ဆိုမှု ၂ ထည့်ရန်"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ခေါ်ဆိုမှု ၂ ကို လက်ရှိပြောနေကြောင်း သတ်မှတ်ရန်"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ခေါ်ဆိုမှု ၂ ဖြတ်တောက်ရန်"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS (အသွင်တူ MT + နောက်ခံရှိ အက်ပ်) စတင်ရန်"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"အထွက် ခေါ်ဆိုမှု စတင်ရန်"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"အဝင်ခေါ်ဆိုမှု စတင်ရန်"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ခေါ်ဆိုမှု id သတ်မှတ်မထားပါ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"ပြောနေသည်ဟု သတ်မှတ်ရန်"</string>
+    <string name="answer" msgid="5423590397665409939">"ဖြေကြားရန်"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"ပြောမနေပါဟု သတ်မှတ်ရန်"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"တယ်လီဖုန်းနားခွက်"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"စပီကာ"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ဘလူးတုသ်"</string>
+    <string name="start_stream" msgid="3567634786280097431">"တိုက်ရိုက်လွှင့်ခြင်း စတင်ရန်"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-nb/strings.xml b/testapps/transactionalVoipApp/res/values-nb/strings.xml
index 124e0a0..ab0353d 100644
--- a/testapps/transactionalVoipApp/res/values-nb/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-nb/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Testaktivitet for Transactional API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaksjonell i samtale-aktivitet"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrer telefonkonto"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"utgående"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"innkommende"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"legg til anrop 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"avslutt anrop 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"legg til anrop 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"angi anrop 2 som aktivt"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"avslutt anrop 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Start FGS (simuler MT + app i bakgrunnen)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Start utgående anrop"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Start innkommende anrop"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"anrops-ID er ikke angitt"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"svar"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"koble fra"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Ørehøyttaler"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Høyttaler"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"start strømming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ne/strings.xml b/testapps/transactionalVoipApp/res/values-ne/strings.xml
index 84481ec..3a12a70 100644
--- a/testapps/transactionalVoipApp/res/values-ne/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ne/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API को परीक्षणसम्बन्धी गतिविधि"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"कलमा क्लाइन्ट र सर्भरबिच गरिएको कुराकानीसम्बन्धी क्रियाकलाप"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"फोन खाता दर्ता गर्नुहोस्"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"बहिर्गमन"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"आगमन"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"कल १ कनेक्ट गर्नुहोस्"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"कल १ डिस्कनेक्ट गर्नुहोस्"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"कल २ कनेक्ट गर्नुहोस्"</string>
-    <string name="set_call_active" msgid="248748409907478011">"कल २ लाई सक्रिय कलका रूपमा सेट गर्नुहोस्"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"कल २ डिस्कनेक्ट गर्नुहोस्"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS सुरु गर्नुहोस् (ब्याकग्राउन्डमा MT + एप सिमुलेट गर्नुहोस्)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"बहिर्गमन कल सुरु गर्नुहोस्"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"आगमन कल सुरु गर्नुहोस्"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"कल ID सेट गरिएको छैन"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"कल उठाउनुहोस्"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"डिस्कनेक्ट गर्नुहोस्"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"इयरपिस"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"स्पिकर"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ब्लुटुथ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"स्ट्रिम गर्न थाल्नुहोस्"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-nl/strings.xml b/testapps/transactionalVoipApp/res/values-nl/strings.xml
index 949d71e..7c9ce32 100644
--- a/testapps/transactionalVoipApp/res/values-nl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-nl/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Testactiviteit Transactional API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Beveiligd gesprek"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefoonaccount registreren"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"uitgaand"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"binnenkomend"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"gesprek 1 toevoegen"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"gesprek 1 beëindigen"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"gesprek 2 toevoegen"</string>
-    <string name="set_call_active" msgid="248748409907478011">"gesprek 2 instellen als actief"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"gesprek 2 beëindigen"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Service op de voorgrond (FGS) starten (MT + app op de achtergrond simuleren)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Uitgaand gesprek starten"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Inkomend gesprek starten"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"Beller-ID niet ingesteld"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"antwoord"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"loskoppelen"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Oortelefoon"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"streamen starten"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-or/strings.xml b/testapps/transactionalVoipApp/res/values-or/strings.xml
index 7cda1c8..7a805f4 100644
--- a/testapps/transactionalVoipApp/res/values-or/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-or/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ଟ୍ରାଞ୍ଜେକସନାଲ API ପରୀକ୍ଷଣର କାର୍ଯ୍ୟକଳାପ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ଟ୍ରାଞ୍ଜେକସନାଲ ଇନ କଲ କାର୍ଯ୍ୟକଳାପ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ଫୋନ ଆକାଉଣ୍ଟର ପଞ୍ଜିକରଣ କରନ୍ତୁ"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ଆଉଟଗୋଇଂ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ଇନକମିଂ"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"କଲ 1 ଯୋଗ କରନ୍ତୁ"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"କଲ 1 ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"କଲ 2 ଯୋଗ କରନ୍ତୁ"</string>
-    <string name="set_call_active" msgid="248748409907478011">"କଲ 2କୁ ସକ୍ରିୟ ଭାବେ ସେଟ କରନ୍ତୁ"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"କଲ 2 ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS ଆରମ୍ଭ କରନ୍ତୁ (ପୃଷ୍ଠପଟରେ MT + ଆପକୁ ସିମୁଲେଟ କରନ୍ତୁ)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ଆଉଟଗୋଇଂ କଲ ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ଇନକମିଂ କଲ ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"କଲ ID ସେଟ କରାଯାଇନାହିଁ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"ଉତ୍ତର ଦିଅନ୍ତୁ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ଇୟରପିସ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ସ୍ପିକର"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ବ୍ଲୁଟୁଥ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ଷ୍ଟ୍ରିମିଂ ଆରମ୍ଭ କରନ୍ତୁ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pa/strings.xml b/testapps/transactionalVoipApp/res/values-pa/strings.xml
index c01d6a3..8293899 100644
--- a/testapps/transactionalVoipApp/res/values-pa/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pa/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ਲੈਣ-ਦੇਣ API ਜਾਂਚ ਸਰਗਰਮੀ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ਲੈਣ-ਦੇਣ ਸੰਬੰਧੀ ਇਨ-ਕਾਲ ਸਰਗਰਮੀ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ਫ਼ੋਨ ਖਾਤਾ ਰਜਿਸਟਰ ਕਰੋ"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"ਆਊਟਗੋਇੰਗ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ਇਨਕਮਿੰਗ"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ਕਾਲ 1 ਸ਼ਾਮਲ ਕਰੋ"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ਕਾਲ 1 ਕੱਟੋ"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ਕਾਲ 2 ਸ਼ਾਮਲ ਕਰੋ"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ਕਾਲ 2 ਨੂੰ ਕਿਰਿਆਸ਼ੀਲ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ਕਾਲ 2 ਕੱਟੋ"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS ਸ਼ੁਰੂ ਕਰੋ (ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ MT + ਐਪ ਨੂੰ ਸਿਮੂਲੇਟ ਕਰੋ)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"ਆਊਟਗੋਇੰਗ ਕਾਲ ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ਇਨਕਮਿੰਗ ਕਾਲ ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ਕਾਲਰ ਆਈਡੀ ਸੈੱਟ ਨਹੀਂ ਹੈ"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"ਜਵਾਬ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ਈਯਰਪੀਸ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ਸਪੀਕਰ"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"ਬਲੂਟੁੱਥ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ਸਟ੍ਰੀਮਿੰਗ ਸ਼ੁਰੂ ਕਰੋ"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pl/strings.xml b/testapps/transactionalVoipApp/res/values-pl/strings.xml
index 60f370b..3cb8ac4 100644
--- a/testapps/transactionalVoipApp/res/values-pl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pl/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Czynność testowa dotycząca transakcji związanej z interfejsem API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Aktywność transakcyjna w trakcie rozmowy"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Zarejestruj konto telefonu"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"wychodzące"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"przychodzące"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"dodaj połączenie 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"rozłącz połączenie 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"dodaj połączenie 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ustaw połączenie 2 jako aktywne"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"rozłącz połączenie 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Uruchom FGS (symulacja MT + aplikacja w tle)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Rozpocznij połączenie wychodzące"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Rozpocznij połączenie przychodzące"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"nie ustawiono ID rozmówcy"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"ustawAktywny"</string>
+    <string name="answer" msgid="5423590397665409939">"odpowiedź"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"ustawNieaktywny"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"rozłącz"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Słuchawka"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Głośnik"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"rozpocznij transmisję"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml b/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml
index ebeb482..6c4f149 100644
--- a/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pt-rPT/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Atividade de teste da API transacional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transacional na atividade da chamada"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registar conta do telemóvel"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"feita"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"recebida"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"adicionar chamada 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"desligar chamada 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"adicionar chamada 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"definir chamada 2 como ativa"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"desligar chamada 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Iniciar FGS (simular TA + app em segundo plano)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Iniciar chamada feita"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Iniciar chamada recebida"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID da chamada não definido"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"atender"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"desligar"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricular"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altifalante"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Iniciar stream"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-pt/strings.xml b/testapps/transactionalVoipApp/res/values-pt/strings.xml
index ea46628..97bba50 100644
--- a/testapps/transactionalVoipApp/res/values-pt/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-pt/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Atividade de teste da API transacional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Atividade em chamadas transacionais"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrar conta telefônica"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"realizada"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"recebida"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"adicionar ligação 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"encerrar ligação 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"adicionar ligação 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"definir a ligação 2 como ativa"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"encerrar ligação 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Iniciar FGS (simular MT + app em segundo plano)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Iniciar ligação efetuada"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Iniciar ligação recebida"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"identificador de chamadas não definido"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"resposta"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"desconectar"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Minifone de ouvido"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Alto-falante"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Iniciar transmissão"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ro/strings.xml b/testapps/transactionalVoipApp/res/values-ro/strings.xml
index ee6bfa2..bb630a8 100644
--- a/testapps/transactionalVoipApp/res/values-ro/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ro/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Activitate de testare a API-ului tranzacțional"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Activitate tranzacțională în timpul apelului"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Înregistrează contul de telefon"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"efectuat"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"primit"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"adaugă apelul 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"deconectează apelul 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"adaugă apelul 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"setează apelul 2 ca activ"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"deconectează apelul 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Pornește FGS (simulează MT + aplicația în fundal)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Inițiază un apel efectuat"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Inițiază un apel primit"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID-ul apelului nu este setat"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"răspuns"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"deconectează"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Cască"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Difuzor"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"începe streamingul"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ru/strings.xml b/testapps/transactionalVoipApp/res/values-ru/strings.xml
index 5345e7b..87c06f1 100644
--- a/testapps/transactionalVoipApp/res/values-ru/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ru/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Активность тестирования API транзакций"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Транзакции во время вызовов"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Зарегистрировать аккаунт телефона"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"исходящий"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"входящий"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"добавить звонок 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"отключить звонок 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"добавить звонок 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"сделать звонок 2 активным"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"отключить звонок 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Запустить активную службу (симуляция МП + приложение в фоновом режиме)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Начать исходящий вызов"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Начать входящий вызов"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"идентификатор вызова не задан"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Активировать"</string>
+    <string name="answer" msgid="5423590397665409939">"ответить"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Деактивировать"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"разъединить"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Динамик телефона"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Колонка"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Начать трансляцию"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-si/strings.xml b/testapps/transactionalVoipApp/res/values-si/strings.xml
index 87d421c..c28e166 100644
--- a/testapps/transactionalVoipApp/res/values-si/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-si/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"ගනුදෙනු API පරීක්ෂණ ක්‍රියාකාරකම්"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"ඇමතුම් ක්‍රියාකාරකම්වල ගනුදෙනු"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"දුරකථන ගිණුම ලියාපදිංචි කරන්න"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"පිටතට යන"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ඇතුළට එන"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"ඇමතුම 1 එක් කරන්න"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ඇමතුම 1 විසන්ධි කරන්න"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"ඇමතුම 2 එක් කරන්න"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ඇමතුම 2 සක්‍රිය ලෙස සකසන්න"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ඇමතුම 2 විසන්ධි කරන්න"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS අරඹන්න (පසුබිමේ MT + යෙදුම අනුකරණය කරන්න)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"පිටතට යන ඇමතුම අරඹන්න"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"එන ඇමතුම අරඹන්න"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"අමතුම්කරුගේ id සකසා නැත"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"පිළිතුරු දෙන්න"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"විසන්ධි කරන්න"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"සවන් කඩ"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ස්පීකරය"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"බ්ලූටූත්"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ප්‍රවාහය අරඹන්න"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sk/strings.xml b/testapps/transactionalVoipApp/res/values-sk/strings.xml
index 1d08d78..5e76289 100644
--- a/testapps/transactionalVoipApp/res/values-sk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sk/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Testovacia aktivita transakčného rozhrania API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transakčná aktivita počas hovoru"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrovať telefónny účet"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"odchádzajúci"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"prichádzajúci"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"pridať hovor 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"odpojiť hovor 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"pridať hovor 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"nastaviť hovor 2 ako aktívny"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"odpojiť hovor 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Spustiť FGS (simulácia MT a aplikácie na pozadí)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Začať odchádzajúci hovor"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Začať prichádzajúci hovor"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"identifikátor hovoru nie je nastavený"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"prijať"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"odpojiť"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Slúchadlo"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Reproduktor"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"spustiť streamovanie"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sl/strings.xml b/testapps/transactionalVoipApp/res/values-sl/strings.xml
index c82c86d..435eac9 100644
--- a/testapps/transactionalVoipApp/res/values-sl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sl/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Preizkusna dejavnost transakcijskega API-ja"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transakcijska dejavnost v klicu"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registracija telefonskega računa"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"odhodni"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"dohodni"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"dodaj klic 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"prekinitev klica 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"dodaj klic 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"nastavi klic 2 kot aktiven"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"prekinitev klica 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Zaženi FGS (simuliraj strojni prevod + aplikacijo v ozadju)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Začni odhodni klic"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Začni dohodni klic"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"id klica ni nastavljen"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Nastavi kot aktivno"</string>
+    <string name="answer" msgid="5423590397665409939">"sprejmi"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Nastavi kot neaktivno"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"prekini klic"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Slušalka"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Zvočnik"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"začni pretočno predvajanje"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sq/strings.xml b/testapps/transactionalVoipApp/res/values-sq/strings.xml
index 8a44cdd..3d18edf 100644
--- a/testapps/transactionalVoipApp/res/values-sq/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sq/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktiviteti i testimit të API-së së transaksioneve"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Aktivitet transaksioni brenda telefonatës"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Regjistro llogarinë e telefonit"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"dalëse"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"hyrëse"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"shto telefonatën 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"shkëput telefonatën 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"shto telefonatën 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"caktoje telefonatën 2 si aktive"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"shkëput telefonatën 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Nis shërbimin FGS (simulo përkthimin kompjuterik dhe aplikacionin në sfond)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Nis një telefonatë dalëse"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Nis një telefonatë hyrëse"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ID-ja e telefonatës nuk është caktuar"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"përgjigju"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"shkëput"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Receptori"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altoparlanti"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"nis transmetimin"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sr/strings.xml b/testapps/transactionalVoipApp/res/values-sr/strings.xml
index 8e66da7..df6a08b 100644
--- a/testapps/transactionalVoipApp/res/values-sr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sr/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Активност тестирања трансакционог API-ја"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Активност позива у вези са трансакцијама"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Региструј налог телефона"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"одлазни"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"долазни"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"додај 1. позив"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"прекини 1. позив"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"додај 2. позив"</string>
-    <string name="set_call_active" msgid="248748409907478011">"подеси 2. позив као активан"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"прекини 2. позив"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Покрени FGS (симулирајте MT + апликацију у позадини)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Започните одлазни позив"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Започните долазни позив"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ИД позива није подешен"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"одговори"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"прекини везу"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Слушалица"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Звучник"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"почните да стримујете"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sv/strings.xml b/testapps/transactionalVoipApp/res/values-sv/strings.xml
index 586d1a4..51d300a 100644
--- a/testapps/transactionalVoipApp/res/values-sv/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sv/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktiviteten Test av transaktions-API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaktioner i samtalsaktivitet"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Registrera telefonkonto"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"utgående"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"inkommande"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"lägg till samtal 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"lägg på samtal 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"lägg till samtal 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ställ in samtal 2 som aktivt"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"lägg på samtal 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Starta FGS (simulera MT + app i bakgrunden)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Starta utgående samtal"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Starta inkommande samtal"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"nummerpresentatör inte inställd"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"svara"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"koppla från"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Lur"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Högtalare"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"starta streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-sw/strings.xml b/testapps/transactionalVoipApp/res/values-sw/strings.xml
index 11a5a77..3ad2501 100644
--- a/testapps/transactionalVoipApp/res/values-sw/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-sw/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Shughuli za jaribio la API ya Uthibitishaji"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Shughuli ya Muamala Kwenye Simu"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Sajili Akaunti ya Simu"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"simu unazopiga"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"simu zinazoingia"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"weka simu ya 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"kata simu ya 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"weka simu ya 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"piga simu ya 2"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"kata simu ya 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Anzisha FGS (kuiga Tafsiri ya Mashine na programu katika hali ya chinichini)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Anzisha Uigaji wa Simu Unayopiga"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Anzisha Uigaji wa Simu Uliyopigiwa"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"kitambulisho cha anayepiga hakijawekwa"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"jibu"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ondoa"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Spika ya sikioni"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Spika"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"anzisha kutiririsha"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ta/strings.xml b/testapps/transactionalVoipApp/res/values-ta/strings.xml
index 73d9944..884291d 100644
--- a/testapps/transactionalVoipApp/res/values-ta/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ta/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API சோதனை செயல்பாடு"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"டிரான்சாக்‌ஷனல் இன் கால் ஆக்டிவிட்டி"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"மொபைல் கணக்கைப் பதிவுசெய்தல்"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"வெளிச்செல்லும் அழைப்பு"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"உள்வரும் அழைப்பு"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"அழைப்பு 1ஐச் சேர்த்தல்"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"அழைப்பு 1ஐத் துண்டித்தல்"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"அழைப்பு 2ஐச் சேர்த்தல்"</string>
-    <string name="set_call_active" msgid="248748409907478011">"அழைப்பு 2 செயலில் உள்ளதாக அமைத்தல்"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"அழைப்பு 2ஐத் துண்டித்தல்"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGSஸைத் தொடங்கு (MT + ஆப்ஸைப் பின்னணியில் சிமுலேட் செய்)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"வெளிச்செல்லும் அழைப்பைத் தொடங்கு"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"உள்வரும் அழைப்பைத் தொடங்கு"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"அழைப்பு ஐடி அமைக்கப்படவில்லை"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"செயலில் அமை"</string>
+    <string name="answer" msgid="5423590397665409939">"பதில்"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"செயலற்ற நிலையில் அமை"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"துண்டி"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ஒலி கேட்கும் பகுதி"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ஸ்பீக்கர்"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"புளூடூத்"</string>
+    <string name="start_stream" msgid="3567634786280097431">"ஸ்ட்ரீமிங்கைத் தொடங்கு"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-te/strings.xml b/testapps/transactionalVoipApp/res/values-te/strings.xml
index 708122a..b926d1a 100644
--- a/testapps/transactionalVoipApp/res/values-te/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-te/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"లావాదేవీల API టెస్ట్ యాక్టివిటీ"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"కాల్ యాక్టివిటీలో లావాదేవీ"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ఫోన్ ఖాతాను రిజిస్టర్ చేయండి"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"అవుట్‌గోయింగ్"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"ఇన్‌కమింగ్"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"కాల్ 1ని జోడించండి"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"కాల్ 1ని డిస్కనెక్ట్ చేయండి"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"కాల్ 2ను జోడించండి"</string>
-    <string name="set_call_active" msgid="248748409907478011">"కాల్ 2ను యాక్టివ్‌గా సెట్ చేయండి"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"కాల్ 2ను డిస్కనెక్ట్ చేయండి"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS (అనుకరణ MT + బ్యాక్‌గ్రౌండ్‌లో యాప్)ను ప్రారంభించండి"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"అవుట్‌గోయింగ్ కాల్‌ను ప్రారంభించండి"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"ఇన్‌కమింగ్ కాల్‌ను ప్రారంభించండి"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"కాల్ id సెట్ చేయబడలేదు"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"సమాధానం"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"డిస్‌కనెక్ట్ చేయండి"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ఇయర్‌పీస్"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"స్పీకర్"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"బ్లూటూత్"</string>
+    <string name="start_stream" msgid="3567634786280097431">"స్ట్రీమింగ్‌ను ప్రారంభించండి"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-th/strings.xml b/testapps/transactionalVoipApp/res/values-th/strings.xml
index 207f291..a1a9803 100644
--- a/testapps/transactionalVoipApp/res/values-th/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-th/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"กิจกรรมการทดสอบ API ธุรกรรม"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"กิจกรรมธุรกรรมระหว่างการโทร"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"ลงทะเบียนบัญชีของโทรศัพท์"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"สายโทรออก"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"สายเรียกเข้า"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"เพิ่มการโทร 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ตัดสาย 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"เพิ่มการโทร 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"ตั้งค่าการโทร 2 เป็นใช้งานอยู่"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ตัดสาย 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"เริ่ม FGS (จําลอง MT + แอปในพื้นหลัง)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"เริ่มสายโทรออก"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"เริ่มสายเรียกเข้า"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ไม่ได้ตั้งค่าหมายเลขผู้โทร"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"ตั้งค่าเป็นใช้งานอยู่"</string>
+    <string name="answer" msgid="5423590397665409939">"คำตอบ"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"ตั้งค่าเป็นไม่ใช้งาน"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ยกเลิกการเชื่อมต่อ"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"หูฟังโทรศัพท์"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"ลำโพง"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"บลูทูธ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"เริ่มสตรีมมิง"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-tl/strings.xml b/testapps/transactionalVoipApp/res/values-tl/strings.xml
index 77f963b..d3399ff 100644
--- a/testapps/transactionalVoipApp/res/values-tl/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-tl/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Aktibidad ng pansubok na Transactional API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Transaksyonal na In Call na Aktibidad"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Irehistro ang Phone Account"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"papalabas"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"incoming"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"magdagdag ng tawag 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"idiskonekta ang tawag 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"magdagdag ng tawag 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"itakdang aktibo ang tawag 2"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"idiskonekta ang tawag 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Simulan ang FGS (i-simulate ang MT + app sa background)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Magsimula ng Papalabas na Tawag"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Magsimula ng Papasok na Tawag"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"hindi naitakda ang call id"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"sagutin"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"idiskonekta"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Earpiece"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Speaker"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"simulan ang streaming"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-tr/strings.xml b/testapps/transactionalVoipApp/res/values-tr/strings.xml
index 4d31191..d9a94ab 100644
--- a/testapps/transactionalVoipApp/res/values-tr/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-tr/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API test etkinliği"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Görüşme İçin İşlem Etkinliği"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefon Hesabını Kaydet"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"giden"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"gelen"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"1. aramayı ekle"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"1. aramayı sonlandır"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"2. aramayı ekle"</string>
-    <string name="set_call_active" msgid="248748409907478011">"2. aramayı etkinleştir"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"2. aramayı sonlandır"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Ön plan hizmetlerini (FGS) başlat (makine çevirisi + arka plandaki uygulamayı simüle et)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Giden Arama Başlat"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Gelen Arama Başlat"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"arama kimliği ayarlanmadı"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"yanıtla"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"bağlantıyı kes"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Kulaklık"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Hoparlör"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"yayın başlat"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-uk/strings.xml b/testapps/transactionalVoipApp/res/values-uk/strings.xml
index d1f5f1d..e08728c 100644
--- a/testapps/transactionalVoipApp/res/values-uk/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-uk/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Тестування API підтвердження"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Трансакції під час викликів"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Зареєструвати обліковий запис телефона"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"вихідні дзвінки"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"вхідні дзвінки"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"додати дзвінок 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"завершити дзвінок 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"додати дзвінок 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"активувати дзвінок 2"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"завершити дзвінок 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Запустити активний сервіс (симуляція МП + додаток у фоновому режимі)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Почати вихідний виклик"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Почати вхідний виклик"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"ідентифікатор виклику не налаштовано"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"позначити як активний"</string>
+    <string name="answer" msgid="5423590397665409939">"відповідь"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"позначити як неактивний"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"від’єднати"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Динамік"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Колонка"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Почати трансляцію"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-ur/strings.xml b/testapps/transactionalVoipApp/res/values-ur/strings.xml
index 8c14f04..e0e0c6e 100644
--- a/testapps/transactionalVoipApp/res/values-ur/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ur/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"‏ٹرانزیکشنل API ٹیسٹ کی سرگرمی"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"کال کی سرگرمی میں ٹرانزیکشنل"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"فون کے اکاؤنٹ کو رجسٹر کریں"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"آؤٹ گوئنگ"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"اِن کمنگ"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"کال 1 کو شامل کریں"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"کال 1 کو منقطع کریں"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"کال 2 کو شامل کریں"</string>
-    <string name="set_call_active" msgid="248748409907478011">"کال 2 کو فعال کریں"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"کال 2 کو منقطع کریں"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"‏FGS شروع کریں ( بیک گراؤنڈ میں MT +‎ ایپ کی نقل کریں)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"آؤٹ گوئنگ کال شروع کریں"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"اِن کمنگ کال شروع کریں"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"‏کال ID سیٹ نہیں ہے"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"فعال پر سیٹ کریں"</string>
+    <string name="answer" msgid="5423590397665409939">"جواب"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"غیر فعال پر سیٹ کریں"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"غیر منسلک کریں"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"ایئر پیس"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"اسپیکر"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"بلوٹوتھ"</string>
+    <string name="start_stream" msgid="3567634786280097431">"سلسلہ بندی شروع کریں"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-uz/strings.xml b/testapps/transactionalVoipApp/res/values-uz/strings.xml
index 4e212a1..5421322 100644
--- a/testapps/transactionalVoipApp/res/values-uz/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-uz/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Tranzaksiyaviy API sinovi faoliyati"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Chaqiruvda tranzaksiya faoliyati"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Telefon hisobini ro‘yxatdan o‘tkazish"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"chiquvchi"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"kiruvchi"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"chaqiruv qo‘shish 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"chaqiruvni uzish 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"chaqiruv qo‘shish 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"chaqiruv 2-ni faol qilish"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"chaqiruvni uzish 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"FGS boshlash (MT + fonda ilova simulyatsiyasi)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Chiquvchi chaqiruvni boshlash"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Kiruvchi chaqiruvni boshlash"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"chaqiruv id belgilanmagan"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"javob berish"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"uzish"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Quloq karnaychasi"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Karnay"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"strimingni boshlash"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-vi/strings.xml b/testapps/transactionalVoipApp/res/values-vi/strings.xml
index 2bcd65f..88362e4 100644
--- a/testapps/transactionalVoipApp/res/values-vi/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-vi/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Hoạt động kiểm tra cho API Xác nhận trao đổi"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Hoạt động giao dịch trong cuộc gọi"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Đăng ký tài khoản điện thoại"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"cuộc gọi đi"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"cuộc gọi đến"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"thêm cuộc gọi 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"ngắt cuộc gọi 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"thêm cuộc gọi 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"đặt cuộc gọi 2 ở trạng thái đang diễn ra"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"ngắt cuộc gọi 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Khởi động FGS (mô phỏng MT + ứng dụng trong nền)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Bắt đầu cuộc gọi đi"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Bắt đầu cuộc gọi đến"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"chưa đặt mã cuộc gọi"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"Đặt thành đang hoạt động"</string>
+    <string name="answer" msgid="5423590397665409939">"trả lời"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"Đặt thành không hoạt động"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"ngắt kết nối"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Loa tai nghe"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Loa"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"bắt đầu phát trực tuyến"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml b/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml
index 512fdea..4b816ba 100644
--- a/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zh-rCN/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"事务性 API 测试活动"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"通话活动中的事务"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"注册电话帐号"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"外拨电话"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"来电"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"添加通话 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"中断通话 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"添加通话 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"将通话 2 设置为通话中"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"中断通话 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"启动 FGS(在后台模拟 MT + 应用)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"开始去电"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"开始来电"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"未设置来电显示/本机号码"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"回复"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"断开连接"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"手机听筒"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"扬声器"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"蓝牙"</string>
+    <string name="start_stream" msgid="3567634786280097431">"开始直播"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml b/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml
index 7729a28..5b80831 100644
--- a/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zh-rHK/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Transactional API 測試活動"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"交易來電活動"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"註冊電話帳戶"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"撥出"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"來電"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"新增通話 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"中斷通話 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"新增通話 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"將通話 2 設為進行中"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"中斷通話 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"開始 FGS (模擬 MT + 背景應用程式)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"開始撥出電話"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"開始來電"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"未設定來電顯示"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"設為使用中"</string>
+    <string name="answer" msgid="5423590397665409939">"接聽"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"設為停用"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"解除連結"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"聽筒"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"喇叭"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"藍牙"</string>
+    <string name="start_stream" msgid="3567634786280097431">"開始串流播放"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml b/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml
index 90f8299..b8a2045 100644
--- a/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zh-rTW/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"交易 API 測試活動"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"通話活動交易資訊"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"註冊電話帳戶"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"撥出通話"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"來電"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"新增通話 1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"中斷通話 1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"新增通話 2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"將通話 2 設為啟用"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"中斷通話 2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"啟動 FGS (在背景模擬機器翻譯和應用程式)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"開始模擬撥出電話"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"開始模擬來電"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"未設定通話 ID"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"設為使用中"</string>
+    <string name="answer" msgid="5423590397665409939">"接聽"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"設為閒置"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"掛斷"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"耳機"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"喇叭"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"藍牙"</string>
+    <string name="start_stream" msgid="3567634786280097431">"開始串流播放"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values-zu/strings.xml b/testapps/transactionalVoipApp/res/values-zu/strings.xml
index 7689224..8e14895 100644
--- a/testapps/transactionalVoipApp/res/values-zu/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-zu/strings.xml
@@ -18,12 +18,18 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2907804426411305091">"Umsebenzi wokuhlolwa kwe-Transactional API"</string>
+    <string name="in_call_activity_name" msgid="7545884666442897585">"Okwenziwayo Emsebenzini Wekholi"</string>
     <string name="register_phone_account" msgid="1920315963082350332">"Bhalisa I-akhawunti Yefoni"</string>
-    <string name="direction_outgoing" msgid="2535369086068422248">"okuphumayo"</string>
-    <string name="direction_incoming" msgid="3279919900558410227">"okungenayo"</string>
-    <string name="add_call_1" msgid="5825706540046010457">"engeza ikholi e-1"</string>
-    <string name="disconnect_call_1" msgid="2687960802565131403">"Nqamula ikholi e-1"</string>
-    <string name="add_call_2" msgid="6706005258041717434">"engeza ikholi yesi-2"</string>
-    <string name="set_call_active" msgid="248748409907478011">"setha ikholi yesi-2 ukuthi isebenze"</string>
-    <string name="disconnect_call_2" msgid="133113412102219516">"Nqamula ikholi yesi-2"</string>
+    <string name="start_foreground_service" msgid="8968755699895128574">"Qala ama-FGS (lingisa i-app ye-MT + ngemuva)"</string>
+    <string name="start_outgoing" msgid="1441644037370361864">"Qala ikholi ephumela ngaphandle"</string>
+    <string name="start_incoming" msgid="6444983300186361271">"Qala Ikholi Engenayo"</string>
+    <string name="get_call_id" msgid="5513943242738347108">"I-ID yekholi ayisethiwe"</string>
+    <string name="set_call_active" msgid="3365404393507589899">"I-setActive"</string>
+    <string name="answer" msgid="5423590397665409939">"impendulo"</string>
+    <string name="set_call_inactive" msgid="7106775211368705195">"I-setInactive"</string>
+    <string name="disconnect_call" msgid="1349412380315371385">"nqamula"</string>
+    <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Isipikha sendlebe"</string>
+    <string name="request_speaker_endpoint" msgid="1033259535289845405">"Isipikha"</string>
+    <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"I-Bluetooth"</string>
+    <string name="start_stream" msgid="3567634786280097431">"Qala ukusakaza-bukhoma"</string>
 </resources>
diff --git a/testapps/transactionalVoipApp/res/values/strings.xml b/testapps/transactionalVoipApp/res/values/strings.xml
index 038adc1..23a5118 100644
--- a/testapps/transactionalVoipApp/res/values/strings.xml
+++ b/testapps/transactionalVoipApp/res/values/strings.xml
@@ -17,13 +17,26 @@
 
 <resources>
     <string name="app_name">Transactional API test Activity</string>
+    <string name="in_call_activity_name">Transactional In Call Activity</string>
+
+    <!-- Main Activity -->
     <string name="register_phone_account">Register Phone Account</string>
-    <string name="direction_outgoing">outgoing</string>
-    <string name="direction_incoming">incoming</string>
-    <string name="add_call_1">add call 1</string>
-    <string name="disconnect_call_1">disconnect call 1</string>
-    <string name="add_call_2">add call 2</string>
-    <string name="set_call_active">set call 2 active</string>
-    <string name="disconnect_call_2">disconnect call 2</string>
+    <string name="start_foreground_service">Start FGS (simulate MT + app in background)</string>
+    <string name="start_outgoing">Start Outgoing Call</string>
+    <string name="start_incoming">Start Incoming Call</string>
+
+    <!-- InCall Activity -->
+    <string name="get_call_id">call id not set</string>
+    <!--  control the call state -->
+    <string name="set_call_active">setActive</string>
+    <string name="answer">answer</string>
+    <string name="set_call_inactive">setInactive</string>
+    <string name="disconnect_call">disconnect</string>
+    <!-- control the call audio -->
+    <string name="request_earpiece_endpoint">Earpiece</string>
+    <string name="request_speaker_endpoint">Speaker</string>
+    <string name="request_bluetooth_endpoint">Bluetooth</string>
+    <!-- extra functionality -->
+    <string name="start_stream">start streaming</string>
 
 </resources>
\ No newline at end of file
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/BackgroundIncomingCallService.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/BackgroundIncomingCallService.java
new file mode 100644
index 0000000..b503e94
--- /dev/null
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/BackgroundIncomingCallService.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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.transactionalVoipApp;
+
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+public class BackgroundIncomingCallService extends Service {
+    // finals
+    private static final String TAG = "BackgroundIncomingCallService";
+    // instance vars
+    private NotificationManager mNotificationManager;
+    private final IBinder mBinder = new LocalBinder();
+
+    @Override
+    public void onCreate() {
+        Log.i(TAG, "onCreate");
+        mNotificationManager = getSystemService(NotificationManager.class);
+    }
+
+    @Override
+    @StartResult
+    public int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
+        Log.i(TAG, String.format("onStartCommand: intent=[%s]", intent));
+
+        // create the notification channel
+        if (mNotificationManager != null) {
+            mNotificationManager.createNotificationChannel(new NotificationChannel(
+                    Utils.CHANNEL_ID, "incoming calls", NotificationManager.IMPORTANCE_DEFAULT));
+        }
+
+        // start the foreground service and post a notification
+        startForeground(98765, Utils.createCallStyleNotification(this),
+                FOREGROUND_SERVICE_TYPE_PHONE_CALL);
+
+        return Service.START_STICKY_COMPATIBILITY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, String.format("onBind: intent=[%s]", intent));
+        return mBinder;
+    }
+
+    /**
+     * Class used for the client Binder.  Because we know this service always
+     * runs in the same process as its clients, we don't need to deal with IPC.
+     */
+    public class LocalBinder extends Binder {
+        BackgroundIncomingCallService getService() {
+            // Return this instance of LocalService so clients can call public methods
+            return BackgroundIncomingCallService.this;
+        }
+    }
+}
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
new file mode 100644
index 0000000..4c9f52d
--- /dev/null
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/InCallActivity.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2023 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.transactionalVoipApp;
+
+import static android.telecom.CallAttributes.AUDIO_CALL;
+import static android.telecom.CallAttributes.DIRECTION_INCOMING;
+import static android.telecom.CallAttributes.DIRECTION_OUTGOING;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.MediaPlayer;
+import android.net.StringNetworkSpecifier;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.OutcomeReceiver;
+import android.telecom.CallAttributes;
+import android.telecom.CallControl;
+import android.telecom.CallEndpoint;
+import android.telecom.CallException;
+import android.telecom.DisconnectCause;
+import android.telecom.TelecomManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+public class InCallActivity extends Activity {
+    private static final String TAG = "InCallActivity";
+    private final AudioManager.AudioRecordingCallback mAudioRecordingCallback =
+            Utils.getAudioRecordingCallback();
+    private static TelecomManager mTelecomManager;
+    private MyVoipCall mVoipCall;
+    private MediaPlayer mMediaPlayer;
+    private AudioRecord mAudioRecord;
+    private int mCallDirection = DIRECTION_INCOMING;
+    private TextView mCurrentEndpointTextView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "#onCreate: in function");
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.in_call_activity);
+
+        Bundle extras = getIntent().getExtras();
+        if (extras != null) {
+            mCallDirection = extras.getInt(Utils.sCALL_DIRECTION_KEY, DIRECTION_INCOMING);
+        }
+        mCurrentEndpointTextView = findViewById(R.id.current_endpoint);
+        mCurrentEndpointTextView.setText("Endpoint/Audio Route NOT ESTABLISHED");
+        updateCallId();
+        mTelecomManager = getSystemService(TelecomManager.class);
+        mMediaPlayer = Utils.createMediaPlayer(getApplicationContext());
+        mAudioRecord = Utils.createAudioRecord();
+        mAudioRecord.registerAudioRecordingCallback(Runnable::run, mAudioRecordingCallback);
+
+        if (mVoipCall == null) {
+            addCall();
+        }
+
+        findViewById(R.id.set_call_active_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                updateCurrentEndpoint();
+                if (canUseCallControl()) {
+                    mVoipCall.mCallControl.setActive(Runnable::run,
+                            Utils.getLoggableOutcomeReceiver("setActive"));
+                }
+                mAudioRecord.startRecording();
+                mMediaPlayer.start();
+            }
+        });
+
+
+        findViewById(R.id.answer_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                updateCurrentEndpoint();
+                if (canUseCallControl() && mCallDirection != DIRECTION_OUTGOING) {
+                    mVoipCall.mCallControl.answer(AUDIO_CALL, Runnable::run,
+                            Utils.getLoggableOutcomeReceiver("answer"));
+                    mAudioRecord.startRecording();
+                    mMediaPlayer.start();
+                }
+            }
+        });
+
+
+        findViewById(R.id.set_call_inactive_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (canUseCallControl()) {
+                    mVoipCall.mCallControl.setInactive(Runnable::run,
+                            Utils.getLoggableOutcomeReceiver("setInactive"));
+                }
+                mAudioRecord.stop();
+                mMediaPlayer.pause();
+            }
+        });
+
+        findViewById(R.id.disconnect_call_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                disconnectAndStopAudio();
+                finish();
+            }
+        });
+
+        findViewById(R.id.start_stream_button).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (canUseCallControl()) {
+                    mVoipCall.mCallControl.startCallStreaming(Runnable::run,
+                            Utils.getLoggableOutcomeReceiver("startCallStream"));
+                }
+            }
+        });
+
+        findViewById(R.id.request_earpiece).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (canUseCallControl() && mVoipCall.mEarpieceEndpoint != null) {
+                    requestEndpointChange(mVoipCall.mEarpieceEndpoint,
+                            "Request EARPIECE Endpoint:");
+                }
+            }
+        });
+
+        findViewById(R.id.request_speaker).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (canUseCallControl() && mVoipCall.mSpeakerEndpoint != null) {
+                    requestEndpointChange(mVoipCall.mSpeakerEndpoint,
+                            "Request SPEAKER Endpoint:");
+                }
+            }
+        });
+
+        findViewById(R.id.request_bluetooth).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (canUseCallControl() && mVoipCall.mBluetoothEndpoint != null) {
+                    requestEndpointChange(mVoipCall.mBluetoothEndpoint,
+                            "Request BLUETOOTH Endpoint:");
+                }
+            }
+        });
+    }
+
+    @Override
+    protected void onDestroy() {
+        disconnectAndStopAudio();
+        super.onDestroy();
+    }
+
+    private boolean canUseCallControl() {
+        return mVoipCall != null && mVoipCall.mCallControl != null;
+    }
+
+    private void updateCurrentEndpoint() {
+        if (mCurrentEndpointTextView != null) {
+            if (mVoipCall != null && mVoipCall.mCurrentEndpoint != null) {
+                mCurrentEndpointTextView.setText("CallEndpoint=[" +
+                        mVoipCall.mCurrentEndpoint.getEndpointName() + "]");
+            }
+        }
+    }
+
+    private void updateCurrentEndpointWithOnResult(CallEndpoint endpoint) {
+        if (mCurrentEndpointTextView != null) {
+            if (mVoipCall != null && mVoipCall.mCurrentEndpoint != null) {
+                mCurrentEndpointTextView.setText("CallEndpoint=[" +
+                        endpoint.getEndpointName() + "]");
+            }
+        }
+    }
+
+    private void updateCallId() {
+        TextView view = findViewById(R.id.getCallIdTextView);
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        if (canUseCallControl()) {
+            String id = mVoipCall.mCallControl.getCallId().toString();
+            sb.append(id);
+        } else {
+            sb.append("Error Getting Id");
+        }
+        sb.append("]");
+        view.setText(sb.toString());
+    }
+
+    private void addCall() {
+        mVoipCall = new MyVoipCall("123");
+
+        CallAttributes callAttributes =
+                new CallAttributes.Builder(
+                        Utils.PHONE_ACCOUNT_HANDLE,
+                        mCallDirection,
+                        "Alan Turing",
+                        Uri.parse("tel:6506959001")).build();
+
+        mTelecomManager.addCall(callAttributes, Runnable::run,
+                new OutcomeReceiver<CallControl, CallException>() {
+                    @Override
+                    public void onResult(CallControl callControl) {
+                        Log.i(TAG, "addCall: onResult: callback fired");
+                        mVoipCall.onAddCallControl(callControl);
+                        updateCallId();
+                        updateCurrentEndpoint();
+                    }
+
+                    @Override
+                    public void onError(CallException exception) {
+
+                    }
+                },
+                mVoipCall, mVoipCall);
+    }
+
+    private void disconnectAndStopAudio() {
+        if (mVoipCall != null) {
+            mVoipCall.mCallControl.disconnect(
+                    new DisconnectCause(DisconnectCause.LOCAL),
+                    Runnable::run,
+                    Utils.getLoggableOutcomeReceiver("disconnect"));
+        }
+        mMediaPlayer.stop();
+        mAudioRecord.stop();
+        try {
+            mAudioRecord.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+        } catch (IllegalArgumentException e) {
+            // pass through
+        }
+    }
+
+    private void requestEndpointChange(CallEndpoint endpoint, String tag) {
+        mVoipCall.mCallControl.requestCallEndpointChange(
+                endpoint,
+                Runnable::run,
+                new OutcomeReceiver<Void, CallException>() {
+                    @Override
+                    public void onResult(Void result) {
+                        Log.i(TAG, String.format("success w/ %s", tag));
+                        updateCurrentEndpointWithOnResult(endpoint);
+                    }
+
+                    @Override
+                    public void onError(CallException e) {
+                        Log.i(TAG, String.format("%s :failed to switch to endpoint=[%s],"
+                                + " due to exception=[%s]", tag, endpoint, e.toString()));
+                    }
+                });
+    }
+}
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
index 690311e..e2b5b14 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/MyVoipCall.java
@@ -24,6 +24,7 @@
 import android.telecom.DisconnectCause;
 import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import androidx.annotation.NonNull;
@@ -34,7 +35,12 @@
 
     private static final String TAG = "MyVoipCall";
     private final String mCallId;
-    CallControl mCallControl;
+    public CallControl mCallControl;
+    public CallEndpoint mCurrentEndpoint;
+    public CallEndpoint mEarpieceEndpoint;
+    public CallEndpoint mSpeakerEndpoint;
+    public CallEndpoint mBluetoothEndpoint;
+    List<CallEndpoint> mAvailableEndpoint = new ArrayList<>();
 
     MyVoipCall(String id) {
         mCallId = id;
@@ -89,6 +95,7 @@
     @Override
     public void onCallEndpointChanged(@NonNull CallEndpoint newCallEndpoint) {
         Log.i(TAG, String.format("onCallEndpointChanged: endpoint=[%s]", newCallEndpoint));
+        mCurrentEndpoint = newCallEndpoint;
     }
 
     @Override
@@ -97,7 +104,17 @@
         Log.i(TAG, String.format("onAvailableCallEndpointsChanged: callId=[%s]", mCallId));
         for (CallEndpoint endpoint : availableEndpoints) {
             Log.i(TAG, String.format("endpoint=[%s]", endpoint));
+            if (endpoint != null && endpoint.getEndpointType() == CallEndpoint.TYPE_EARPIECE) {
+                mEarpieceEndpoint = endpoint;
+            }
+            if (endpoint != null && endpoint.getEndpointType() == CallEndpoint.TYPE_SPEAKER) {
+                mSpeakerEndpoint = endpoint;
+            }
+            if (endpoint != null && endpoint.getEndpointType() == CallEndpoint.TYPE_BLUETOOTH) {
+                mBluetoothEndpoint = endpoint;
+            }
         }
+        mAvailableEndpoint = availableEndpoints;
     }
 
     @Override
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
new file mode 100644
index 0000000..98de790
--- /dev/null
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/Utils.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2023 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.transactionalVoipApp;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioRecordingConfiguration;
+import android.media.MediaPlayer;
+import android.media.MediaRecorder;
+import android.os.OutcomeReceiver;
+import android.telecom.CallException;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.util.Log;
+
+import java.util.List;
+
+public class Utils {
+    public static final String TAG = "TransactionalAppUtils";
+    public static final String sEXTRAS_KEY = "ExtrasKey";
+    public static final String sCALL_DIRECTION_KEY = "CallDirectionKey";
+    public static final String CHANNEL_ID = "TelecomVoipAppChannelId";
+    private static final int SAMPLING_RATE_HZ = 44100;
+
+    public static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE = new PhoneAccountHandle(
+            new ComponentName("com.android.server.telecom.transactionalVoipApp",
+                    "com.android.server.telecom.transactionalVoipApp.VoipAppMainActivity"), "123");
+
+    public static final PhoneAccount PHONE_ACCOUNT =
+            PhoneAccount.builder(PHONE_ACCOUNT_HANDLE, "test label")
+                    .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
+                            PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS).build();
+
+
+    public static Notification createCallStyleNotification(Context context) {
+        Intent answerIntent = new Intent(context, InCallActivity.class);
+        Intent rejectIntent = new Intent(context, InCallActivity.class);
+
+        // Creating a pending intent and wrapping our intent
+        PendingIntent pendingAnswer = PendingIntent.getActivity(context, 0,
+                answerIntent, PendingIntent.FLAG_IMMUTABLE);
+        PendingIntent pendingReject = PendingIntent.getActivity(context, 0,
+                rejectIntent, PendingIntent.FLAG_IMMUTABLE);
+
+
+        Notification callStyleNotification = new Notification.Builder(context,
+                CHANNEL_ID)
+                .setContentText("Answer/Reject call")
+                .setContentTitle("Incoming call")
+                .setSmallIcon(R.drawable.ic_android_black_24dp)
+                .setStyle(Notification.CallStyle.forIncomingCall(
+                        new Person.Builder().setName("Tom Stu").setImportant(true).build(),
+                        pendingAnswer, pendingReject)
+                )
+                .setFullScreenIntent(pendingAnswer, true)
+                .build();
+
+        return callStyleNotification;
+    }
+
+    public static MediaPlayer createMediaPlayer(Context context) {
+        int audioToPlay = (Math.random() > 0.5f) ?
+                com.android.server.telecom.transactionalVoipApp.R.raw.sample_audio :
+                com.android.server.telecom.transactionalVoipApp.R.raw.sample_audio2;
+        MediaPlayer mediaPlayer = MediaPlayer.create(context, audioToPlay);
+        mediaPlayer.setLooping(true);
+        return mediaPlayer;
+    }
+
+    public static AudioRecord createAudioRecord() {
+        return new AudioRecord.Builder()
+                .setAudioFormat(new AudioFormat.Builder()
+                        .setSampleRate(SAMPLING_RATE_HZ)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build())
+                .setAudioSource(MediaRecorder.AudioSource.DEFAULT)
+                .setBufferSizeInBytes(
+                        AudioRecord.getMinBufferSize(SAMPLING_RATE_HZ,
+                                AudioFormat.CHANNEL_IN_MONO,
+                                AudioFormat.ENCODING_PCM_16BIT) * 10)
+                .build();
+    }
+
+
+    public static AudioManager.AudioRecordingCallback getAudioRecordingCallback() {
+        return new AudioManager.AudioRecordingCallback() {
+            @Override
+            public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
+                super.onRecordingConfigChanged(configs);
+
+                for (AudioRecordingConfiguration config : configs) {
+                    if (config != null) {
+                        Log.i(TAG, String.format("onRecordingConfigChanged: random: "
+                                        + "isClientSilenced=[%b], config=[%s]",
+                                config.isClientSilenced(), config));
+                    }
+                }
+            }
+        };
+    }
+
+    public static OutcomeReceiver<Void, CallException> getLoggableOutcomeReceiver(String tag) {
+        return new OutcomeReceiver<Void, CallException>() {
+            @Override
+            public void onResult(Void result) {
+                Log.i(TAG, tag + " : onResult");
+            }
+
+            @Override
+            public void onError(CallException exception) {
+                Log.i(TAG, tag + " : onError");
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
index 4e1ec4c..ae7d9d0 100644
--- a/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
+++ b/testapps/transactionalVoipApp/src/com/android/server/telecom/transactionalVoipApp/VoipAppMainActivity.java
@@ -20,7 +20,12 @@
 import static android.telecom.CallAttributes.DIRECTION_OUTGOING;
 
 import android.app.Activity;
-import android.content.ComponentName;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.OutcomeReceiver;
@@ -28,142 +33,115 @@
 import android.telecom.CallControl;
 import android.telecom.CallException;
 import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.util.Log;
 import android.view.View;
 import android.widget.ToggleButton;
 
 public class VoipAppMainActivity extends Activity {
-
     private static final String TAG = "VoipAppMainActivity";
+    private static final String ACT_STATE_TAG = "VoipActivityState";
     private static TelecomManager mTelecomManager;
-    private MyVoipCall mCall1;
-    private MyVoipCall mCall2;
-    private ToggleButton mCallDirectionButton;
-
-    PhoneAccountHandle handle = new PhoneAccountHandle(
-            new ComponentName("com.android.server.telecom.transactionalVoipApp",
-                    "com.android.server.telecom.transactionalVoipApp.VoipAppMainActivity"), "123");
-
-    PhoneAccount mPhoneAccount = PhoneAccount.builder(handle, "test label")
-            .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED |
-                    PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS).build();
+    NotificationManager mNotificationManager;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, ACT_STATE_TAG + "onCreate");
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main_activity);
 
         mTelecomManager = getSystemService(TelecomManager.class);
-        mCallDirectionButton = findViewById(R.id.callDirectionButton);
+        mNotificationManager = getSystemService(NotificationManager.class);
+        // create a notification channel
+        if (mNotificationManager != null) {
+            mNotificationManager.createNotificationChannel(new NotificationChannel(
+                    Utils.CHANNEL_ID, "new call channel",
+                    NotificationManager.IMPORTANCE_DEFAULT));
+        }
 
         // register account
         findViewById(R.id.registerButton).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                mTelecomManager.registerPhoneAccount(mPhoneAccount);
+                mTelecomManager.registerPhoneAccount(Utils.PHONE_ACCOUNT);
             }
         });
 
-        // call 1 buttons
-        findViewById(R.id.add_call_1_button).setOnClickListener(new View.OnClickListener() {
+        // Start a foreground service that will post a notification within 10 seconds.
+        // This is helpful for debugging scenarios where the app is in the background and posting
+        // an incoming call notification.
+        findViewById(R.id.startForegroundService).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                Bundle extras = new Bundle();
-                extras.putString("testKey", "testValue");
-                mCall1 = new MyVoipCall("1");
-                addCall(mCall1, true);
+                Intent startForegroundService = new Intent(getApplicationContext(),
+                        BackgroundIncomingCallService.class);
+                getApplicationContext().startForegroundService(startForegroundService);
             }
         });
 
-        findViewById(R.id.disconnect_call_1_button).setOnClickListener(new View.OnClickListener() {
+
+        // post a new call notification and start an InCall activity
+        findViewById(R.id.startOutgoingCall).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                disconnectCall(mCall1);
+                startInCallActivity(DIRECTION_OUTGOING);
             }
         });
 
-
-        //call 2 buttons
-        findViewById(R.id.add_call_2_button).setOnClickListener(new View.OnClickListener() {
+        // post a new call notification and start an InCall activity
+        findViewById(R.id.startIncomingCall).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                Bundle extras = new Bundle();
-                extras.putString("call2extraKey", "call2Value");
-                mCall2 = new MyVoipCall("2");
-                addCall(mCall2, false);
+                startInCallActivity(DIRECTION_INCOMING);
             }
         });
 
-        findViewById(R.id.set_call_2_active_button).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                setCallActive(mCall2);
-            }
-        });
-
-        findViewById(R.id.disconnect_call_2_button).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                disconnectCall(mCall2);
-            }
-        });
     }
 
-    private void addCall(MyVoipCall call, boolean setActive) {
-        int direction = (mCallDirectionButton.isChecked() ? DIRECTION_INCOMING
-                : DIRECTION_OUTGOING);
-
-        CallAttributes callAttributes = new CallAttributes.Builder(handle, direction, "Alan Turing",
-                Uri.fromParts("tel", "abc", "123")).build();
-
-        mTelecomManager.addCall(callAttributes, Runnable::run,
-                new OutcomeReceiver<CallControl, CallException>() {
-                    @Override
-                    public void onResult(CallControl callControl) {
-                        Log.i(TAG, "addCall: onResult: callback fired");
-                        call.onAddCallControl(callControl);
-                        if (setActive) {
-                            setCallActive(call);
-                        }
-                    }
-
-                    @Override
-                    public void onError(CallException exception) {
-
-                    }
-                },
-                call, call);
+    private void startInCallActivity(int direction) {
+        mNotificationManager.notify(123456,
+                Utils.createCallStyleNotification(getApplicationContext()));
+        Bundle extras = new Bundle();
+        extras.putInt(Utils.sCALL_DIRECTION_KEY, direction);
+        Intent intent = new Intent(getApplicationContext(), InCallActivity.class);
+        intent.putExtra(Utils.sEXTRAS_KEY, extras);
+        startActivity(intent);
     }
 
-    private void setCallActive(MyVoipCall call) {
-        call.mCallControl.setActive(Runnable::run, new OutcomeReceiver<Void, CallException>() {
-            @Override
-            public void onResult(Void result) {
-                Log.i(TAG, "setCallActive: onResult");
-            }
-
-            @Override
-            public void onError(CallException exception) {
-                Log.i(TAG, "setCallActive: onError");
-            }
-        });
+    @Override
+    protected void onResume() {
+        Log.i(TAG, ACT_STATE_TAG + " onResume: When the activity enters the Resumed state,"
+                + " it comes to the foreground");
+        super.onResume();
     }
 
-    private void disconnectCall(MyVoipCall call) {
-        call.mCallControl.disconnect(new DisconnectCause(DisconnectCause.LOCAL), Runnable::run,
-                new OutcomeReceiver<Void, CallException>() {
-                    @Override
-                    public void onResult(Void result) {
-                        Log.i(TAG, "disconnectCall: onResult");
-                    }
+    @Override
+    protected void onPause() {
+        Log.i(TAG, ACT_STATE_TAG + " onPause: The system calls this method as the first"
+                + " indication that the user is leaving your activity.  It indicates that the"
+                + " activity is no longer in the foreground, but it is still visible if the user"
+                + " is in multi-window mode");
+        super.onPause();
+    }
 
-                    @Override
-                    public void onError(CallException exception) {
-                        Log.i(TAG, "disconnectCall: onError");
-                    }
-                });
+    @Override
+    protected void onStop() {
+        Log.i(TAG, ACT_STATE_TAG + "onStop: When your activity is no longer visible to"
+                + " the user, it enters the Stopped state,");
+        super.onStop();
+    }
+
+    @Override
+    protected void onRestart() {
+        Log.i(TAG, ACT_STATE_TAG + " onRestart: onStop has called onRestart and the "
+                + "activity comes back to interact with the user");
+        super.onRestart();
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.i(TAG, ACT_STATE_TAG + " onDestroy: is called before the activity is"
+                + " destroyed. ");
+        super.onDestroy();
     }
 }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index c84db3b..4ca6030 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -44,6 +44,9 @@
     <!-- Used to access PlatformCompat APIs -->
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
+    
+    <!-- Used to register NotificationListenerService -->
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
 
     <application android:label="@string/app_name"
                  android:debuggable="true">
diff --git a/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java b/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
index 93d1314..7e197fe 100644
--- a/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAnomalyWatchdogTest.java
@@ -21,11 +21,13 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
 import android.net.Uri;
+import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 
@@ -37,6 +39,7 @@
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceWrapper;
+import com.android.server.telecom.EmergencyCallDiagnosticLogger;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.TelecomSystem;
@@ -85,6 +88,8 @@
     @Mock private ConnectionServiceWrapper mMockConnectionService;
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
 
+    @Mock private EmergencyCallDiagnosticLogger mMockEmergencyCallDiagnosticLogger;
+
     @Override
     @Before
     public void setUp() throws Exception {
@@ -116,7 +121,7 @@
         doReturn(new ComponentName(mContext, CallTest.class))
                 .when(mMockConnectionService).getComponentName();
         mCallAnomalyWatchdog = new CallAnomalyWatchdog(mTestScheduledExecutorService, mLock,
-                mTimeouts, mMockClockProxy);
+                mTimeouts, mMockClockProxy, mMockEmergencyCallDiagnosticLogger);
         mCallAnomalyWatchdog.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
     }
 
@@ -755,6 +760,90 @@
     }
 
     /**
+     * Emulate the case where a new incoming call is created but the connection fails for a known
+     * reason before being added to CallsManager. In this case, the watchdog should stop tracking
+     * the call and not trigger an anomaly report.
+     */
+    @Test
+    public void testIncomingCallCreatedButNotAddedNoAnomalyReport() {
+        //The call is created:
+        Call call = getCall();
+        call.setState(CallState.NEW, "foo");
+        call.setIsCreateConnectionComplete(false);
+        mCallAnomalyWatchdog.onStartCreateConnection(call);
+
+        //The connection fails before being added to CallsManager for a known reason:
+        call.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
+
+        // Move the clock forward:
+        when(mMockClockProxy.elapsedRealtime()).
+                thenReturn(TEST_NON_VOIP_INTERMEDIATE_MILLIS + 1);
+        mTestScheduledExecutorService.advanceTime(TEST_NON_VOIP_INTERMEDIATE_MILLIS + 1);
+
+        //Ensure an anomaly report is not generated:
+        verify(mAnomalyReporterAdapter, never()).reportAnomaly(
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+    }
+
+    /**
+     * Emulate the case where a new outgoing call is created but the connection fails for a known
+     * reason before being added to CallsManager. In this case, the watchdog should stop tracking
+     * the call and not trigger an anomaly report.
+     */
+    @Test
+    public void testOutgoingCallCreatedButNotAddedNoAnomalyReport() {
+        //The call is created:
+        Call call = getCall();
+        call.setCallDirection(Call.CALL_DIRECTION_OUTGOING);
+        call.setState(CallState.NEW, "foo");
+        call.setIsCreateConnectionComplete(false);
+        mCallAnomalyWatchdog.onStartCreateConnection(call);
+
+        //The connection fails before being added to CallsManager for a known reason.
+        call.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
+
+        // Move the clock forward:
+        when(mMockClockProxy.elapsedRealtime()).
+                thenReturn(TEST_NON_VOIP_INTERMEDIATE_MILLIS + 1);
+        mTestScheduledExecutorService.advanceTime(TEST_NON_VOIP_INTERMEDIATE_MILLIS + 1);
+
+        //Ensure an anomaly report is not generated:
+        verify(mAnomalyReporterAdapter, never()).reportAnomaly(
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+    }
+
+    /**
+     * Emulate the case where a new incoming call is created but the connection fails for a known
+     * reason before being added to CallsManager and CallsManager notifies the watchdog by invoking
+     * {@link CallsManager.CallsManagerListener#onCreateConnectionFailed(Call)}.
+     * In this case, the watchdog should stop tracking the call and not trigger an anomaly report.
+     */
+    @Test
+    public void testCallCreatedButNotAddedPreventsAnomalyReport() {
+        //The call is created:
+        Call call = getCall();
+        call.setState(CallState.NEW, "foo");
+        call.setIsCreateConnectionComplete(false);
+        mCallAnomalyWatchdog.onStartCreateConnection(call);
+
+        //Telecom cancels the connection before adding it to CallsManager:
+        mCallAnomalyWatchdog.onCreateConnectionFailed(call);
+
+        // Move the clock forward:
+        when(mMockClockProxy.elapsedRealtime()).
+                thenReturn(TEST_NON_VOIP_INTERMEDIATE_MILLIS + 1);
+        mTestScheduledExecutorService.advanceTime(TEST_NON_VOIP_INTERMEDIATE_MILLIS + 1);
+
+        //Ensure an anomaly report is not generated:
+        verify(mAnomalyReporterAdapter, never()).reportAnomaly(
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_UUID,
+                CallAnomalyWatchdog.WATCHDOG_DISCONNECTED_STUCK_CALL_MSG);
+    }
+
+
+    /**
      * @return an instance of {@link Call} for testing purposes.
      */
     private Call getCall() {
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index f53c953..569c487 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -153,7 +153,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
 
         // Since we don't know if we're on a platform with an earpiece or not, all we can do
         // is ensure the stateMachine construction didn't fail.  But at least we exercised the
@@ -172,7 +173,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         Set<Call> trackedCalls = new HashSet<>(Arrays.asList(fakeCall, fakeSelfManagedCall));
@@ -217,7 +219,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);
@@ -260,7 +263,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
@@ -305,7 +309,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -349,7 +354,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
         Collection<BluetoothDevice> availableDevices = Collections.singleton(bluetoothDevice1);
 
@@ -427,7 +433,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -463,7 +470,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
         setInBandRing(false);
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -518,7 +526,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
         List<BluetoothDevice> availableDevices =
                 Arrays.asList(bluetoothDevice1, bluetoothDevice2, bluetoothDevice3);
@@ -568,7 +577,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_SPEAKER,
@@ -599,7 +609,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
@@ -633,7 +644,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
         List<BluetoothDevice> availableDevices =
                 Arrays.asList(bluetoothDevice1, bluetoothDevice2);
@@ -748,7 +760,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.initialize();
         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
     }
@@ -764,7 +777,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
@@ -801,7 +815,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 earpieceControl,
-                mThreadHandler.getLooper());
+                mThreadHandler.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.initialize();
         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 5caa432..cf684de 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -269,7 +269,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 mParams.earpieceControl,
-                mHandlerThread.getLooper());
+                mHandlerThread.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         setupMocksForParams(stateMachine, mParams);
@@ -365,7 +366,8 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 mParams.earpieceControl,
-                mHandlerThread.getLooper());
+                mHandlerThread.getLooper(),
+                Runnable::run /** do async stuff sync for test purposes */);
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         // Set up bluetooth and speakerphone state
diff --git a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
index 51d5960..01446d1 100644
--- a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
@@ -354,4 +354,28 @@
         assertEquals(REDIRECTED_GATEWAY_NUMBER_WITH_POST_DIAL,
                 gatewayInfoArgumentCaptor.getValue().getGatewayAddress());
     }
+
+    @Test
+    public void testUnbindOnNullBind() throws Exception {
+        startProcessWithNoGateWayInfo();
+        // To make sure tests are not flaky, clean all the previous handler messages
+        waitForHandlerAction(mProcessor.getHandler(), HANDLER_TIMEOUT_DELAY);
+        enableUserDefinedCallRedirectionService();
+        disableCarrierCallRedirectionService();
+
+        mProcessor.performCallRedirection(UserHandle.CURRENT);
+
+        // Capture the binder
+        ArgumentCaptor<ServiceConnection> serviceConnectionCaptor = ArgumentCaptor.forClass(
+                ServiceConnection.class);
+        // Verify binding occurred
+        verify(mContext, times(1)).bindServiceAsUser(any(Intent.class),
+                serviceConnectionCaptor.capture(), anyInt(), eq(UserHandle.CURRENT));
+        // Simulate null return from onBind
+        serviceConnectionCaptor.getValue().onNullBinding(USER_DEFINED_SERVICE_TEST_COMPONENT_NAME);
+
+        // Verify service was unbound
+        verify(mContext, times(1)).
+                unbindService(any(ServiceConnection.class));
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/CallTest.java b/tests/src/com/android/server/telecom/tests/CallTest.java
index 6b817d8..997e7dd 100644
--- a/tests/src/com/android/server/telecom/tests/CallTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallTest.java
@@ -26,16 +26,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.ComponentName;
 import android.content.Intent;
@@ -43,7 +38,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Parcel;
+import android.telecom.CallAttributes;
 import android.telecom.CallerInfo;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
@@ -56,12 +51,10 @@
 import android.telecom.VideoProfile;
 import android.telephony.CallQuality;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
 import android.widget.Toast;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.internal.telecom.IVideoProvider;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallIdMapper;
 import com.android.server.telecom.CallState;
@@ -69,8 +62,6 @@
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceWrapper;
-import com.android.server.telecom.InCallController;
-import com.android.server.telecom.InCallController.InCallServiceInfo;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.TelecomSystem;
@@ -640,8 +631,40 @@
         verify(listener).onFailedUnknownCall(unknownCall);
     }
 
+    /**
+     * ensure a Call object does not throw an NPE when the CallingPackageIdentity is not set and
+     * the correct values are returned when set
+     */
     @Test
     @SmallTest
+    public void testCallingPackageIdentity() {
+        final int packageUid = 123;
+        final int packagePid = 1;
+
+        Call call = createCall("1");
+
+        // assert default values for a Calls CallingPackageIdentity are -1 unless set via the setter
+        assertEquals(-1, call.getCallingPackageIdentity().mCallingPackageUid);
+        assertEquals(-1, call.getCallingPackageIdentity().mCallingPackagePid);
+
+        // set the Call objects CallingPackageIdentity via the setter and a bundle
+        Bundle extras = new Bundle();
+        extras.putInt(CallAttributes.CALLER_UID_KEY, packageUid);
+        extras.putInt(CallAttributes.CALLER_PID_KEY, packagePid);
+        // assert that the setter removed the extras
+        assertEquals(packageUid, extras.getInt(CallAttributes.CALLER_UID_KEY));
+        assertEquals(packagePid, extras.getInt(CallAttributes.CALLER_PID_KEY));
+        call.setCallingPackageIdentity(extras);
+        // assert that the setter removed the extras
+        assertEquals(0, extras.getInt(CallAttributes.CALLER_UID_KEY));
+        assertEquals(0, extras.getInt(CallAttributes.CALLER_PID_KEY));
+        // assert the properties are fetched correctly
+        assertEquals(packageUid, call.getCallingPackageIdentity().mCallingPackageUid);
+        assertEquals(packagePid, call.getCallingPackageIdentity().mCallingPackagePid);
+    }
+
+        @Test
+    @SmallTest
     public void testOnConnectionEventNotifiesListener() {
         Call.Listener listener = mock(Call.Listener.class);
         Call call = createCall("1");
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 7544dc9..fc4e935 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -58,16 +58,19 @@
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.telecom.CallerInfo;
+import android.provider.BlockedNumberContract;
+import android.telecom.CallException;
 import android.telecom.CallScreeningService;
+import android.telecom.CallerInfo;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.GatewayInfo;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
-import android.telecom.CallException;
 import android.telecom.VideoProfile;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneCapability;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -81,9 +84,9 @@
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioModeStateMachine;
 import com.android.server.telecom.CallAudioRouteStateMachine;
+import com.android.server.telecom.CallDiagnosticServiceController;
 import com.android.server.telecom.CallEndpointController;
 import com.android.server.telecom.CallEndpointControllerFactory;
-import com.android.server.telecom.CallDiagnosticServiceController;
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.CallsManager;
@@ -92,6 +95,7 @@
 import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory;
 import com.android.server.telecom.ConnectionServiceWrapper;
 import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.EmergencyCallDiagnosticLogger;
 import com.android.server.telecom.EmergencyCallHelper;
 import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.HeadsetMediaButton;
@@ -114,10 +118,12 @@
 import com.android.server.telecom.WiredHeadsetManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+import com.android.server.telecom.callfiltering.BlockedNumbersAdapter;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 import com.android.server.telecom.ui.AudioProcessingNotification;
 import com.android.server.telecom.ui.DisconnectedCallNotifier;
 import com.android.server.telecom.ui.ToastFactory;
+import com.android.server.telecom.voip.TransactionManager;
 
 import org.junit.After;
 import org.junit.Before;
@@ -135,16 +141,15 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(JUnit4.class)
 public class CallsManagerTest extends TelecomTestCase {
     private static final int TEST_TIMEOUT = 5000;  // milliseconds
+    private static final long STATE_TIMEOUT = 5000L;
     private static final int SECONDARY_USER_ID = 12;
     private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
             ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
@@ -235,8 +240,12 @@
     @Mock private ToastFactory mToastFactory;
     @Mock private Toast mToast;
     @Mock private CallAnomalyWatchdog mCallAnomalyWatchdog;
+
+    @Mock private EmergencyCallDiagnosticLogger mEmergencyCallDiagnosticLogger;
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
     @Mock private Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
+    @Mock private BlockedNumbersAdapter mBlockedNumbersAdapter;
+    @Mock private PhoneCapability mPhoneCapability;
 
     private CallsManager mCallsManager;
 
@@ -256,7 +265,7 @@
         when(mCallEndpointControllerFactory.create(any(), any(), any())).thenReturn(
                 mCallEndpointController);
         when(mCallAudioRouteStateMachineFactory.create(any(), any(), any(), any(), any(), any(),
-                anyInt())).thenReturn(mCallAudioRouteStateMachine);
+                anyInt(), any())).thenReturn(mCallAudioRouteStateMachine);
         when(mCallAudioModeStateMachineFactory.create(any(), any()))
                 .thenReturn(mCallAudioModeStateMachine);
         when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
@@ -267,6 +276,9 @@
                 .thenReturn(mDisconnectedCallNotifier);
         when(mTimeoutsAdapter.getCallDiagnosticServiceTimeoutMillis(any(ContentResolver.class)))
                 .thenReturn(2000L);
+        when(mTimeoutsAdapter.getNonVoipCallTransitoryStateTimeoutMillis())
+                .thenReturn(STATE_TIMEOUT);
+        when(mClockProxy.elapsedRealtime()).thenReturn(0L);
         mCallsManager = new CallsManager(
                 mComponentContextFixture.getTestDouble().getApplicationContext(),
                 mLock,
@@ -299,9 +311,12 @@
                 mToastFactory,
                 mCallEndpointControllerFactory,
                 mCallAnomalyWatchdog,
+                mAccessibilityManagerAdapter,
                 // Just do async tasks synchronously to support testing.
                 command -> command.run(),
-                mAccessibilityManagerAdapter);
+                mBlockedNumbersAdapter,
+                TransactionManager.getTestInstance(),
+                mEmergencyCallDiagnosticLogger);
 
         when(mPhoneAccountRegistrar.getPhoneAccount(
                 eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
@@ -326,6 +341,26 @@
         assertEquals(0, mCallsManager.constructPossiblePhoneAccounts(null, null, false, false).size());
     }
 
+    private Call constructOngoingCall(String callId, PhoneAccountHandle phoneAccountHandle) {
+        Call ongoingCall = new Call(
+                callId,
+                mContext,
+                mCallsManager,
+                mLock,
+                null /* ConnectionServiceRepository */,
+                mPhoneNumberUtilsAdapter,
+                TEST_ADDRESS,
+                null /* GatewayInfo */,
+                null /* connectionManagerPhoneAccountHandle */,
+                phoneAccountHandle,
+                Call.CALL_DIRECTION_INCOMING,
+                false /* shouldAttachToExistingConnection*/,
+                false /* isConference */,
+                mClockProxy,
+                mToastFactory);
+        ongoingCall.setState(CallState.ACTIVE, "just cuz");
+        return ongoingCall;
+    }
     /**
      * Verify behavior for multisim devices where we want to ensure that the active sim is used for
      * placing a new call.
@@ -336,23 +371,7 @@
     public void testConstructPossiblePhoneAccountsMultiSimActive() throws Exception {
         setupMsimAccounts();
 
-        Call ongoingCall = new Call(
-                "1", /* callId */
-                mContext,
-                mCallsManager,
-                mLock,
-                null /* ConnectionServiceRepository */,
-                mPhoneNumberUtilsAdapter,
-                TEST_ADDRESS,
-                null /* GatewayInfo */,
-                null /* connectionManagerPhoneAccountHandle */,
-                SIM_2_HANDLE,
-                Call.CALL_DIRECTION_INCOMING,
-                false /* shouldAttachToExistingConnection*/,
-                false /* isConference */,
-                mClockProxy,
-                mToastFactory);
-        ongoingCall.setState(CallState.ACTIVE, "just cuz");
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
         mCallsManager.addCall(ongoingCall);
 
         List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
@@ -375,6 +394,47 @@
         assertEquals(2, phoneAccountHandles.size());
     }
 
+    /**
+     * For DSDA-enabled multisim devices with an ongoing call, verify that both SIMs'
+     * PhoneAccountHandles are constructed while placing a new call.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_dsdaCallingPossible() throws
+            Exception {
+        setupMsimAccounts();
+        setMaxActiveVoiceSubscriptions(2);
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, false);
+        assertEquals(2, phoneAccountHandles.size());
+    }
+
+    /**
+     * For DSDA-enabled multisim devices with an ongoing call, verify that only the active SIMs'
+     * PhoneAccountHandle is constructed while placing an emergency call.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testConstructPossiblePhoneAccountsMultiSimActive_dsdaCallingPossible_emergencyCall()
+            throws Exception {
+        setupMsimAccounts();
+        setMaxActiveVoiceSubscriptions(2);
+
+        Call ongoingCall = constructOngoingCall("1", SIM_2_HANDLE);
+        mCallsManager.addCall(ongoingCall);
+
+        List<PhoneAccountHandle> phoneAccountHandles = mCallsManager.constructPossiblePhoneAccounts(
+                TEST_ADDRESS, null, false, true /* isEmergency */);
+        assertEquals(1, phoneAccountHandles.size());
+        assertEquals(SIM_2_HANDLE, phoneAccountHandles.get(0));
+    }
+
     private void setupCallerInfoLookupHelper() {
         doAnswer(invocation -> {
             Uri handle = invocation.getArgument(0);
@@ -632,6 +692,54 @@
         verify(heldCall).unhold(any());
     }
 
+    /**
+     * Ensures we don't auto-unhold a call from a different app when we locally disconnect a call.
+     */
+    @SmallTest
+    @Test
+    public void testDontUnholdCallsBetweenConnectionServices() {
+        // GIVEN a CallsManager with ongoing call
+        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
+        when(ongoingCall.isDisconnectHandledViaFuture()).thenReturn(false);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a held call which has different ConnectionService
+        Call heldCall = addSpyCall(VOIP_1_HANDLE, CallState.ON_HOLD);
+
+        // Disconnect and cleanup the active ongoing call.
+        mCallsManager.disconnectCall(ongoingCall);
+        mCallsManager.markCallAsRemoved(ongoingCall);
+
+        // Should not unhold the held call since its in another app.
+        verify(heldCall, never()).unhold();
+    }
+
+    /**
+     * Ensures we do auto-unhold a call from the same app when we locally disconnect a call.
+     */
+    @SmallTest
+    @Test
+    public void testUnholdCallWhenDisconnectingInSameApp() {
+        // GIVEN a CallsManager with ongoing call
+        Call ongoingCall = addSpyCall(SIM_1_HANDLE, CallState.ACTIVE);
+        when(ongoingCall.isDisconnectHandledViaFuture()).thenReturn(false);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a held call which has same ConnectionService
+        Call heldCall = addSpyCall(SIM_1_HANDLE, CallState.ON_HOLD);
+
+        // Disconnect and cleanup the active ongoing call.
+        mCallsManager.disconnectCall(ongoingCall);
+        mCallsManager.markCallAsRemoved(ongoingCall);
+
+        // Should auto-unhold the held call since its in the same app.
+        verify(heldCall).unhold();
+    }
+
     @SmallTest
     @Test
     public void testUnholdCallWhenOngoingEmergCallCanNotBeHeldAndFromDifferentConnectionService() {
@@ -1459,6 +1567,8 @@
                 .thenReturn(false);
         newCall.setHandle(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
+        // Make sure enough time has passed that we'd drop the connecting call.
+        when(mClockProxy.elapsedRealtime()).thenReturn(STATE_TIMEOUT + 10L);
         assertTrue(mCallsManager.makeRoomForOutgoingCall(newCall));
         verify(mAnomalyReporterAdapter).reportAnomaly(
                 CallsManager.LIVE_CALL_STUCK_CONNECTING_ERROR_UUID,
@@ -1466,6 +1576,26 @@
         verify(ongoingCall).disconnect(anyLong(), anyString());
     }
 
+    /**
+     * Verifies that we won't auto-disconnect an outgoing CONNECTING call unless it has timed out.
+     */
+    @SmallTest
+    @Test
+    public void testDontDisconnectConnectingCallWhenNotTimedOut() {
+        mCallsManager.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
+        Call ongoingCall = addSpyCall(SIM_2_HANDLE, CallState.CONNECTING);
+
+        Call newCall = createCall(SIM_1_HANDLE, CallState.NEW);
+        when(mComponentContextFixture.getTelephonyManager().isEmergencyNumber(any()))
+                .thenReturn(false);
+        newCall.setHandle(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
+
+        // Make sure it has been a short time so we don't try to disconnect the call
+        when(mClockProxy.elapsedRealtime()).thenReturn(STATE_TIMEOUT / 2);
+        assertFalse(mCallsManager.makeRoomForOutgoingCall(newCall));
+        verify(ongoingCall, never()).disconnect(anyLong(), anyString());
+    }
+
     @SmallTest
     @Test
     public void testMakeRoomForEmergencyCallHasOutgoingCall() {
@@ -1539,6 +1669,7 @@
         Call ongoingCall = addSpyCall(SIM_2_HANDLE, CallState.CONNECTING);
         Call newCall = createCall(SIM_1_HANDLE, CallState.NEW);
 
+        when(mClockProxy.elapsedRealtime()).thenReturn(STATE_TIMEOUT + 10L);
         assertTrue(mCallsManager.makeRoomForOutgoingCall(newCall));
         verify(ongoingCall).disconnect(anyLong(), anyString());
     }
@@ -1548,7 +1679,7 @@
     public void testMakeRoomForOutgoingCallForSameCall() {
         addSpyCall(SIM_2_HANDLE, CallState.CONNECTING);
         Call ongoingCall2 = addSpyCall();
-
+        when(mClockProxy.elapsedRealtime()).thenReturn(STATE_TIMEOUT + 10L);
         assertTrue(mCallsManager.makeRoomForOutgoingCall(ongoingCall2));
     }
 
@@ -2000,6 +2131,79 @@
                 SELF_MANAGED_HANDLE.getUserHandle()));
     }
 
+    /**
+     * Emulate the case where a new incoming call is created but the connection fails for a known
+     * reason before being added to CallsManager. In this case, the listeners should be notified
+     * properly.
+     */
+    @Test
+    public void testIncomingCallCreatedButNotAddedNotifyListener() {
+        //The call is created and a listener is added:
+        Call incomingCall = createCall(SIM_2_HANDLE, null, CallState.NEW);
+        CallsManager.CallsManagerListener listener = mock(CallsManager.CallsManagerListener.class);
+        mCallsManager.addListener(listener);
+
+        //The connection fails before being added to CallsManager for a known reason:
+        incomingCall.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
+
+        //Ensure the listener is notified properly:
+        verify(listener).onCreateConnectionFailed(incomingCall);
+    }
+
+    /**
+     * Emulate the case where a new incoming call is created but the connection fails for a known
+     * reason after being added to CallsManager. Since the call was added to CallsManager, the
+     * listeners should not be notified via onCreateConnectionFailed().
+     */
+    @Test
+    public void testIncomingCallCreatedAndAddedDoNotNotifyListener() {
+        //The call is created and a listener is added:
+        Call incomingCall = createCall(SIM_2_HANDLE, null, CallState.NEW);
+        CallsManager.CallsManagerListener listener = mock(CallsManager.CallsManagerListener.class);
+        mCallsManager.addListener(listener);
+
+        //The call is added to CallsManager:
+        mCallsManager.addCall(incomingCall);
+
+        //The connection fails after being added to CallsManager for a known reason:
+        incomingCall.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
+
+        //Since the call was added to CallsManager, onCreateConnectionFailed shouldn't be invoked:
+        verify(listener, never()).onCreateConnectionFailed(incomingCall);
+    }
+
+    /**
+     * Emulate the case where a new outgoing call is created but is aborted before being added to
+     * CallsManager since there are no available phone accounts. In this case, the listeners
+     * should be notified properly.
+     */
+    @Test
+    public void testAbortOutgoingCallNoPhoneAccountsNotifyListeners() throws Exception {
+        // Setup a new outgoing call and add a listener
+        Call newCall = addSpyCall(CallState.NEW);
+        CallsManager.CallsManagerListener listener = mock(CallsManager.CallsManagerListener.class);
+        mCallsManager.addListener(listener);
+
+        // Ensure contact info lookup succeeds but do not set the phone account info
+        doAnswer(invocation -> {
+            Uri handle = invocation.getArgument(0);
+            CallerInfo info = new CallerInfo();
+            CompletableFuture<Pair<Uri, CallerInfo>> callerInfoFuture = new CompletableFuture<>();
+            callerInfoFuture.complete(new Pair<>(handle, info));
+            return callerInfoFuture;
+        }).when(mCallerInfoLookupHelper).startLookup(any(Uri.class));
+
+        // Start the outgoing call
+        CompletableFuture<Call> callFuture = mCallsManager.startOutgoingCall(
+                newCall.getHandle(), newCall.getTargetPhoneAccount(), new Bundle(),
+                UserHandle.CURRENT, new Intent(), "com.test.stuff");
+        Call result = callFuture.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+
+        //Ensure the listener is notified properly:
+        verify(listener).onCreateConnectionFailed(any());
+        assertNull(result);
+    }
+
     @Test
     public void testIsInSelfManagedCallOnlySelfManaged() {
         Call selfManagedCall = createCall(SELF_MANAGED_HANDLE, CallState.ACTIVE);
@@ -2637,6 +2841,61 @@
         assertTrue(mCallsManager.hasSelfManagedCalls());
     }
 
+    /**
+     * Verifies when {@link CallsManager} receives a carrier config change it will trigger an
+     * update of the emergency call notification.
+     * Note: this test mocks out {@link BlockedNumbersAdapter} so does not actually test posting of
+     * the notification.  Notification posting in the actual implementation is covered by
+     * {@link BlockedNumbersUtilTests}.
+     */
+    @SmallTest
+    @Test
+    public void testUpdateEmergencyCallNotificationOnCarrierConfigChange() {
+        when(mBlockedNumbersAdapter.shouldShowEmergencyCallNotification(any(Context.class)))
+                .thenReturn(true);
+        mComponentContextFixture.getBroadcastReceivers().forEach(c -> c.onReceive(mContext,
+                new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)));
+        verify(mBlockedNumbersAdapter).updateEmergencyCallNotification(any(Context.class),
+                eq(true));
+
+        when(mBlockedNumbersAdapter.shouldShowEmergencyCallNotification(any(Context.class)))
+                .thenReturn(false);
+        mComponentContextFixture.getBroadcastReceivers().forEach(c -> c.onReceive(mContext,
+                new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)));
+        verify(mBlockedNumbersAdapter).updateEmergencyCallNotification(any(Context.class),
+                eq(false));
+    }
+
+    /**
+     * Verifies when {@link CallsManager} receives a signal from the blocked number provider that
+     * the call blocking enabled state changes, it will trigger an update of the emergency call
+     * notification.
+     * Note: this test mocks out {@link BlockedNumbersAdapter} so does not actually test posting of
+     * the notification.  Notification posting in the actual implementation is covered by
+     * {@link BlockedNumbersUtilTests}.
+     */
+    @SmallTest
+    @Test
+    public void testUpdateEmergencyCallNotificationOnNotificationVisibilityChange() {
+        when(mBlockedNumbersAdapter.shouldShowEmergencyCallNotification(any(Context.class)))
+                .thenReturn(true);
+        mComponentContextFixture.getBroadcastReceivers().forEach(c -> c.onReceive(mContext,
+                new Intent(
+                        BlockedNumberContract.SystemContract
+                                .ACTION_BLOCK_SUPPRESSION_STATE_CHANGED)));
+        verify(mBlockedNumbersAdapter).updateEmergencyCallNotification(any(Context.class),
+                eq(true));
+
+        when(mBlockedNumbersAdapter.shouldShowEmergencyCallNotification(any(Context.class)))
+                .thenReturn(false);
+        mComponentContextFixture.getBroadcastReceivers().forEach(c -> c.onReceive(mContext,
+                new Intent(
+                        BlockedNumberContract.SystemContract
+                                .ACTION_BLOCK_SUPPRESSION_STATE_CHANGED)));
+        verify(mBlockedNumbersAdapter).updateEmergencyCallNotification(any(Context.class),
+                eq(false));
+    }
+
     private Call addSpyCall() {
         return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
     }
@@ -2729,4 +2988,10 @@
         when(mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser()).thenReturn(
                 new ArrayList<>(Arrays.asList(SIM_1_HANDLE, SIM_2_HANDLE)));
     }
+
+    private void setMaxActiveVoiceSubscriptions(int num) {
+        TelephonyManager mockTelephonyManager = mComponentContextFixture.getTelephonyManager();
+        when(mockTelephonyManager.getPhoneCapability()).thenReturn(mPhoneCapability);
+        when(mPhoneCapability.getMaxActiveVoiceSubscriptions()).thenReturn(num);
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index e3a1471..cc22de2 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -55,9 +55,13 @@
 import android.location.LocationManager;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
+import android.os.BugreportManager;
 import android.os.Bundle;
+import android.os.DropBoxManager;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
@@ -110,6 +114,7 @@
  * property points to an application context implementing all the nontrivial functionality.
  */
 public class ComponentContextFixture implements TestFixture<Context> {
+    private HandlerThread mHandlerThread;
 
     public class FakeApplicationContext extends MockContext {
         @Override
@@ -275,8 +280,12 @@
                 return Context.NOTIFICATION_SERVICE;
             } else if (svcClass == AccessibilityManager.class) {
                 return Context.ACCESSIBILITY_SERVICE;
+            } else if (svcClass == DropBoxManager.class) {
+                return Context.DROPBOX_SERVICE;
+            } else if (svcClass == BugreportManager.class) {
+                return Context.BUGREPORT_SERVICE;
             }
-            throw new UnsupportedOperationException();
+            throw new UnsupportedOperationException(svcClass.getName());
         }
 
         @Override
@@ -305,6 +314,15 @@
         }
 
         @Override
+        public Looper getMainLooper() {
+            if (mHandlerThread == null) {
+                mHandlerThread = new HandlerThread(this.getClass().getSimpleName());
+                mHandlerThread.start();
+            }
+            return mHandlerThread.getLooper();
+        }
+
+        @Override
         public ContentResolver getContentResolver() {
             return new ContentResolver(mApplicationContextSpy) {
                 @Override
@@ -344,30 +362,34 @@
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
-            // TODO -- this is called by WiredHeadsetManager!!!
+            mBroadcastReceivers.add(receiver);
             return null;
         }
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+            mBroadcastReceivers.add(receiver);
             return null;
         }
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                 String broadcastPermission, Handler scheduler) {
+            mBroadcastReceivers.add(receiver);
             return null;
         }
 
         @Override
         public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                 String broadcastPermission, Handler scheduler, int flags) {
+            mBroadcastReceivers.add(receiver);
             return null;
         }
 
         @Override
         public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle handle,
                 IntentFilter filter, String broadcastPermission, Handler scheduler) {
+            mBroadcastReceivers.add(receiver);
             return null;
         }
 
@@ -591,6 +613,7 @@
             mock(PermissionCheckerManager.class);
     private final PermissionInfo mPermissionInfo = mock(PermissionInfo.class);
     private final SensorPrivacyManager mSensorPrivacyManager = mock(SensorPrivacyManager.class);
+    private final List<BroadcastReceiver> mBroadcastReceivers = new ArrayList<>();
 
     private TelecomManager mTelecomManager = mock(TelecomManager.class);
 
@@ -795,6 +818,10 @@
         return mNotificationManager;
     }
 
+    public List<BroadcastReceiver> getBroadcastReceivers() {
+        return mBroadcastReceivers;
+    }
+
     private void addService(String action, ComponentName name, IInterface service) {
         mComponentNamesByAction.put(action, name);
         mServiceByComponentName.put(name, service);
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index f977ddb..dbcab66 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -517,6 +517,43 @@
     }
 
     /**
+     * Ensure that the call goes out on the PhoneAccount for the incoming call and not the
+     * Telephony preferred emergency account.
+     */
+    @SmallTest
+    @Test
+    public void testMTEmergencyCallMultiSimUserPreferred() throws Exception {
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
+        when(mMockCall.isIncoming()).thenReturn(true);
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        PhoneAccount emergencyPhoneAccount1 = makeEmergencyPhoneAccount("tel_emer1", 0, null);
+        mapToSubSlot(emergencyPhoneAccount1, 1 /*subId*/, 0 /*slotId*/);
+        setTargetPhoneAccount(mMockCall, emergencyPhoneAccount1.getAccountHandle());
+        phoneAccounts.add(emergencyPhoneAccount1);
+        // Make this the user preferred phone account
+        setTargetPhoneAccount(mMockCall, emergencyPhoneAccount1.getAccountHandle());
+        PhoneAccount emergencyPhoneAccount2 = makeEmergencyPhoneAccount("tel_emer2",
+                PhoneAccount.CAPABILITY_EMERGENCY_PREFERRED, null);
+        mapToSubSlot(emergencyPhoneAccount2, 2 /*subId*/, 1 /*slotId*/);
+        phoneAccounts.add(emergencyPhoneAccount2);
+        PhoneAccountHandle emergencyPhoneAccountHandle2 = emergencyPhoneAccount2.getAccountHandle();
+
+        mTestCreateConnectionProcessor.process();
+
+        verify(mMockCall).setConnectionManagerPhoneAccount(
+                eq(emergencyPhoneAccount1.getAccountHandle()));
+        // The account we're using to place the call should be the user preferred account
+        verify(mMockCall).setTargetPhoneAccount(eq(emergencyPhoneAccount1.getAccountHandle()));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        // Notify successful connection to call
+        CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
+        mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
+        verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
+    }
+
+    /**
      * If the user preferred PhoneAccount is associated with an invalid slot, place on the other,
      * valid slot.
      */
diff --git a/tests/src/com/android/server/telecom/tests/EmergencyCallDiagnosticLoggerTest.java b/tests/src/com/android/server/telecom/tests/EmergencyCallDiagnosticLoggerTest.java
new file mode 100644
index 0000000..3cb8196
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/EmergencyCallDiagnosticLoggerTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+
+import static android.telephony.TelephonyManager.EmergencyCallDiagnosticParams;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.os.BugreportManager;
+import android.os.DropBoxManager;
+import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallState;
+import com.android.server.telecom.CallerInfoLookupHelper;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.ClockProxy;
+import com.android.server.telecom.EmergencyCallDiagnosticLogger;
+import com.android.server.telecom.PhoneAccountRegistrar;
+import com.android.server.telecom.PhoneNumberUtilsAdapter;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.ui.ToastFactory;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class EmergencyCallDiagnosticLoggerTest extends TelecomTestCase {
+
+    private static final ComponentName COMPONENT_NAME_1 = ComponentName
+            .unflattenFromString("com.foo/.Blah");
+    private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
+            COMPONENT_NAME_1, "Sim1");
+    private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.
+            Builder(SIM_1_HANDLE, "Sim1")
+            .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION
+                    | PhoneAccount.CAPABILITY_CALL_PROVIDER)
+            .setIsEnabled(true)
+            .build();
+    private static final String DROP_BOX_TAG = "ecall_diagnostic_data";
+
+    private static final long EMERGENCY_CALL_ACTIVE_TIME_THRESHOLD_MILLIS = 100L;
+
+    private static final long EMERGENCY_CALL_TIME_BEFORE_USER_DISCONNECT_THRESHOLD_MILLIS = 120L;
+
+    private static final int DAYS_BACK_TO_SEARCH_EMERGENCY_DIAGNOSTIC_ENTRIES = 1;
+    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {
+    };
+    EmergencyCallDiagnosticLogger mEmergencyCallDiagnosticLogger;
+    @Mock
+    private Timeouts.Adapter mTimeouts;
+    @Mock
+    private CallsManager mMockCallsManager;
+    @Mock
+    private CallerInfoLookupHelper mMockCallerInfoLookupHelper;
+    @Mock
+    private PhoneAccountRegistrar mMockPhoneAccountRegistrar;
+    @Mock
+    private ClockProxy mMockClockProxy;
+    @Mock
+    private ToastFactory mMockToastProxy;
+    @Mock
+    private PhoneNumberUtilsAdapter mMockPhoneNumberUtilsAdapter;
+
+    @Mock
+    private TelephonyManager mTm;
+    @Mock
+    private BugreportManager mBrm;
+    @Mock
+    private DropBoxManager mDbm;
+
+    @Mock
+    private ClockProxy mClockProxy;
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        doReturn(mMockCallerInfoLookupHelper).when(mMockCallsManager).getCallerInfoLookupHelper();
+        doReturn(mMockPhoneAccountRegistrar).when(mMockCallsManager).getPhoneAccountRegistrar();
+        doReturn(SIM_1_ACCOUNT).when(mMockPhoneAccountRegistrar).getPhoneAccountUnchecked(
+                eq(SIM_1_HANDLE));
+        when(mTimeouts.getEmergencyCallActiveTimeThresholdMillis()).
+                thenReturn(EMERGENCY_CALL_ACTIVE_TIME_THRESHOLD_MILLIS);
+        when(mTimeouts.getEmergencyCallTimeBeforeUserDisconnectThresholdMillis()).
+                thenReturn(EMERGENCY_CALL_TIME_BEFORE_USER_DISCONNECT_THRESHOLD_MILLIS);
+        when(mTimeouts.getDaysBackToSearchEmergencyDiagnosticEntries()).
+                thenReturn(DAYS_BACK_TO_SEARCH_EMERGENCY_DIAGNOSTIC_ENTRIES);
+        when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
+
+        mEmergencyCallDiagnosticLogger = new EmergencyCallDiagnosticLogger(mTm, mBrm,
+                mTimeouts, mDbm, Runnable::run, mClockProxy);
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+        //reset(mTm);
+    }
+
+    /**
+     * Helper function that creates the call being tested.
+     * Also invokes onStartCreateConnection
+     */
+    private Call createCall(boolean isEmergencyCall, int direction) {
+        Call call = getCall();
+        call.setCallDirection(direction);
+        call.setIsEmergencyCall(isEmergencyCall);
+        mEmergencyCallDiagnosticLogger.onStartCreateConnection(call);
+        return call;
+    }
+
+    /**
+     * @return an instance of {@link Call} for testing purposes.
+     */
+    private Call getCall() {
+        return new Call(
+                "1", /* callId */
+                mContext,
+                mMockCallsManager,
+                mLock,
+                null /* ConnectionServiceRepository */,
+                mMockPhoneNumberUtilsAdapter,
+                Uri.parse("tel:6505551212"),
+                null /* GatewayInfo */,
+                null /* connectionManagerPhoneAccountHandle */,
+                SIM_1_HANDLE,
+                Call.CALL_DIRECTION_OUTGOING,
+                false /* shouldAttachToExistingConnection*/,
+                false /* isConference */,
+                mMockClockProxy,
+                mMockToastProxy);
+    }
+
+    /**
+     * Test that only outgoing emergency calls are tracked
+     */
+    @Test
+    public void testNonEmergencyCallNotTracked() {
+        //should not be tracked
+        createCall(false, Call.CALL_DIRECTION_OUTGOING);
+        assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+
+        //should not be tracked (not in scope)
+        createCall(false, Call.CALL_DIRECTION_INCOMING);
+        assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+    }
+
+    /**
+     * Test that incoming emergency calls are not tracked (not in scope right now)
+     */
+    @Test
+    public void testIncomingEmergencyCallsNotTracked() {
+        //should not be tracked
+        createCall(true, Call.CALL_DIRECTION_INCOMING);
+        assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+    }
+
+
+    /**
+     * Test getDataCollectionTypes(reason)
+     */
+    @Test
+    public void testCollectionTypeForReasonDoesNotReturnUnreasonableValues() {
+        int reason = EmergencyCallDiagnosticLogger.REPORT_REASON_RANGE_START + 1;
+        while (reason < EmergencyCallDiagnosticLogger.REPORT_REASON_RANGE_END) {
+            List<Integer> ctypes = EmergencyCallDiagnosticLogger.getDataCollectionTypes(reason);
+            assertNotNull(ctypes);
+            Set<Integer> ctypesSet = new HashSet<>(ctypes);
+
+            //assert that list is not empty
+            assertNotEquals(0, ctypes.size());
+
+            //assert no repeated values
+            assertEquals(ctypes.size(), ctypesSet.size());
+
+            //if bugreport type is present, that should be the only collection type
+            if (ctypesSet.contains(EmergencyCallDiagnosticLogger.COLLECTION_TYPE_BUGREPORT)) {
+                assertEquals(1, ctypes.size());
+            }
+            reason++;
+        }
+    }
+
+
+    /**
+     * Test emergency call reported stuck
+     */
+    @Test
+    public void testStuckEmergencyCall() {
+        Call call = createCall(true, Call.CALL_DIRECTION_OUTGOING);
+        mEmergencyCallDiagnosticLogger.onCallAdded(call);
+        mEmergencyCallDiagnosticLogger.reportStuckCall(call);
+
+        //for stuck calls, we should always be persisting some data
+        ArgumentCaptor<EmergencyCallDiagnosticParams> captor =
+                ArgumentCaptor.forClass(EmergencyCallDiagnosticParams.class);
+        verify(mTm, times(1)).persistEmergencyCallDiagnosticData(eq(DROP_BOX_TAG),
+                captor.capture());
+        EmergencyCallDiagnosticParams dp = captor.getValue();
+
+        assertNotNull(dp);
+        assertTrue(
+                dp.isLogcatCollectionEnabled() || dp.isTelecomDumpSysCollectionEnabled()
+                        || dp.isTelephonyDumpSysCollectionEnabled());
+
+        //tracking should end
+        assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+    }
+
+    @Test
+    public void testEmergencyCallNeverWentActiveWithNonLocalDisconnectCause() {
+        Call call = createCall(true, Call.CALL_DIRECTION_OUTGOING);
+        mEmergencyCallDiagnosticLogger.onCallAdded(call);
+
+        //call is tracked
+        assertEquals(1, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+
+        call.setDisconnectCause(new DisconnectCause(DisconnectCause.REJECTED));
+        mEmergencyCallDiagnosticLogger.onCallRemoved(call);
+
+        //for non-local disconnect of non-active call,  we should always be persisting some data
+        ArgumentCaptor<TelephonyManager.EmergencyCallDiagnosticParams> captor =
+                ArgumentCaptor.forClass(
+                        TelephonyManager.EmergencyCallDiagnosticParams.class);
+        verify(mTm, times(1)).persistEmergencyCallDiagnosticData(eq(DROP_BOX_TAG),
+                captor.capture());
+        TelephonyManager.EmergencyCallDiagnosticParams dp = captor.getValue();
+
+        assertNotNull(dp);
+        assertTrue(
+                dp.isLogcatCollectionEnabled() || dp.isTelecomDumpSysCollectionEnabled()
+                        || dp.isTelephonyDumpSysCollectionEnabled());
+
+        //tracking should end
+        assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+    }
+
+    @Test
+    public void testEmergencyCallWentActiveForLongDuration_shouldNotCollectDiagnostics()
+            throws Exception {
+        Call call = createCall(true, Call.CALL_DIRECTION_OUTGOING);
+        mEmergencyCallDiagnosticLogger.onCallAdded(call);
+
+        //call went active
+        mEmergencyCallDiagnosticLogger.onCallStateChanged(call, CallState.DIALING,
+                CallState.ACTIVE);
+
+        //return large value for time when call is disconnected
+        when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis() + 10000L);
+
+        call.setDisconnectCause(new DisconnectCause(DisconnectCause.ERROR));
+        mEmergencyCallDiagnosticLogger.onCallRemoved(call);
+
+        //no diagnostic data should be persisted
+        verify(mTm, never()).persistEmergencyCallDiagnosticData(eq(DROP_BOX_TAG),
+                any());
+
+        //tracking should end
+        assertEquals(0, mEmergencyCallDiagnosticLogger.getEmergencyCallsMap().size());
+    }
+
+}
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index f50753d..e61bc2a 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -35,7 +35,6 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -53,10 +52,12 @@
 import android.app.UiModeManager;
 import android.content.AttributionSource;
 import android.content.AttributionSourceState;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.PermissionChecker;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
@@ -64,9 +65,10 @@
 import android.content.pm.PermissionInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.compat.testing.PlatformCompatChangeRule;
-import android.os.Binder;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -74,6 +76,7 @@
 import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.permission.PermissionCheckerManager;
 import android.telecom.CallAudioState;
 import android.telecom.InCallService;
@@ -122,14 +125,13 @@
 import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 
-import libcore.junit.util.compat.CoreCompatChangeRule;
-
 @RunWith(JUnit4.class)
 public class InCallControllerTests extends TelecomTestCase {
     @Mock CallsManager mMockCallsManager;
@@ -150,29 +152,31 @@
     @Mock PermissionInfo mMockPermissionInfo;
     @Mock InCallController.InCallServiceInfo mInCallServiceInfo;
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
+    @Mock UserManager mMockUserManager;
+    @Mock UserInfo mMockUserInfo;
 
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
-    private static final int CURRENT_USER_ID = 900973;
+    private static final int CURRENT_USER_ID = 9;
     private static final String DEF_PKG = "defpkg";
     private static final String DEF_CLASS = "defcls";
-    private static final int DEF_UID = 1;
+    private static final int DEF_UID = 900972;
     private static final String SYS_PKG = "syspkg";
     private static final String SYS_CLASS = "syscls";
-    private static final int SYS_UID = 2;
+    private static final int SYS_UID = 900971;
     private static final String COMPANION_PKG = "cpnpkg";
     private static final String COMPANION_CLASS = "cpncls";
-    private static final int COMPANION_UID = 3;
+    private static final int COMPANION_UID = 900970;
     private static final String CAR_PKG = "carpkg";
     private static final String CAR2_PKG = "carpkg2";
     private static final String CAR_CLASS = "carcls";
     private static final String CAR2_CLASS = "carcls";
-    private static final int CAR_UID = 4;
-    private static final int CAR2_UID = 5;
+    private static final int CAR_UID = 900969;
+    private static final int CAR2_UID = 900968;
     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 int NONUI_UID = 900973;
     private static final String APPOP_NONUI_PKG = "appop_nonui_pkg";
     private static final String APPOP_NONUI_CLASS = "appop_nonui_cls";
     private static final int APPOP_NONUI_UID = 7;
@@ -180,6 +184,7 @@
     private static final PhoneAccountHandle PA_HANDLE =
             new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"),
                     "pa_id_0", UserHandle.of(CURRENT_USER_ID));
+    private static final UserHandle DUMMY_USER_HANDLE = UserHandle.of(10);
 
     private UserHandle mUserHandle = UserHandle.of(CURRENT_USER_ID);
     private InCallController mInCallController;
@@ -187,6 +192,7 @@
     private EmergencyCallHelper mEmergencyCallHelper;
     private SystemStateHelper.SystemStateListener mSystemStateListener;
     private CarModeTracker mCarModeTracker = spy(new CarModeTracker());
+    private BroadcastReceiver mRegisteredReceiver;
 
     private final int serviceBindingFlags = Context.BIND_AUTO_CREATE
         | Context.BIND_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
@@ -199,6 +205,7 @@
         MockitoAnnotations.initMocks(this);
         when(mMockCall.getAnalytics()).thenReturn(new Analytics.CallInfo());
         when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(mUserHandle);
+        when(mMockCall.getId()).thenReturn("TC@1");
         doReturn(mMockResources).when(mMockContext).getResources();
         doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
         doReturn(SYS_PKG).when(mMockResources).getString(
@@ -222,6 +229,12 @@
         mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
                 mMockSystemStateHelper, mDefaultDialerCache, mTimeoutsAdapter,
                 mEmergencyCallHelper, mCarModeTracker, mClockProxy);
+        // Capture the broadcast receiver registered.
+        doAnswer(invocation -> {
+            mRegisteredReceiver = invocation.getArgument(0);
+            return null;
+        }).when(mMockContext).registerReceiver(any(BroadcastReceiver.class),
+                any(IntentFilter.class));
 
         ArgumentCaptor<SystemStateHelper.SystemStateListener> systemStateListenerArgumentCaptor
                 = ArgumentCaptor.forClass(SystemStateHelper.SystemStateListener.class);
@@ -281,6 +294,11 @@
                 .thenReturn(PackageManager.PERMISSION_DENIED);
 
         when(mMockCallsManager.getAudioState()).thenReturn(new CallAudioState(false, 0, 0));
+
+        when(mMockContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mMockUserManager);
+        // Mock user info to allow binding on user stored in the phone account (mUserHandle).
+        when(mMockUserManager.getUserInfo(anyInt())).thenReturn(mMockUserInfo);
+        when(mMockUserInfo.isManagedProfile()).thenReturn(true);
     }
 
     @Override
@@ -425,60 +443,6 @@
 
     @MediumTest
     @Test
-    public void testBindToService_OutgoingCall_FailToBind_AnomalyReported() throws Exception {
-        mInCallController.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
-        when(mMockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
-                anyInt(), any(UserHandle.class))).thenReturn(false);
-        Bundle callExtras = new Bundle();
-        callExtras.putBoolean("whatever", true);
-
-        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
-        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
-        when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
-        when(mMockCall.isIncoming()).thenReturn(false);
-        when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
-        when(mMockCall.getIntentExtras()).thenReturn(callExtras);
-        when(mMockCall.isExternalCall()).thenReturn(false);
-        when(mTimeoutsAdapter.getEmergencyCallbackWindowMillis(any(ContentResolver.class)))
-                .thenReturn(300_000L);
-
-        setupMockPackageManager(false /* default */, true /* system */, false /* external calls */);
-        mInCallController.bindToServices(mMockCall);
-
-        verify(mAnomalyReporterAdapter).reportAnomaly(InCallController.BIND_TO_IN_CALL_ERROR_UUID,
-                InCallController.BIND_TO_IN_CALL_ERROR_MSG);
-    }
-
-    @MediumTest
-    @Test
-    public void testBindToService_OutgoingEmergCall_FailToBind_AnomalyReported() throws Exception {
-        mInCallController.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
-        when(mMockContext.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class),
-                anyInt(), any(UserHandle.class))).thenReturn(false);
-        Bundle callExtras = new Bundle();
-        callExtras.putBoolean("whatever", true);
-
-        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
-        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
-        when(mMockCallsManager.isInEmergencyCall()).thenReturn(false);
-        when(mMockCall.isEmergencyCall()).thenReturn(true);
-        when(mMockCall.isIncoming()).thenReturn(false);
-        when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
-        when(mMockCall.getIntentExtras()).thenReturn(callExtras);
-        when(mMockCall.isExternalCall()).thenReturn(false);
-        when(mTimeoutsAdapter.getEmergencyCallbackWindowMillis(any(ContentResolver.class)))
-                .thenReturn(300_000L);
-
-        setupMockPackageManager(false /* default */, true /* system */, false /* external calls */);
-        mInCallController.bindToServices(mMockCall);
-
-        verify(mAnomalyReporterAdapter).reportAnomaly(
-                InCallController.BIND_TO_IN_CALL_EMERGENCY_ERROR_UUID,
-                InCallController.BIND_TO_IN_CALL_EMERGENCY_ERROR_MSG);
-    }
-
-    @MediumTest
-    @Test
     public void testBindToService_DefaultDialer_NoEmergency() throws Exception {
         Bundle callExtras = new Bundle();
         callExtras.putBoolean("whatever", true);
@@ -539,6 +503,9 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(true);
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+            .thenReturn(mMockUserManager);
+        when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
@@ -598,6 +565,60 @@
                 eq(Manifest.permission.ACCESS_FINE_LOCATION), eq(mUserHandle));
     }
 
+    @MediumTest
+    @Test
+    public void
+    testBindToService_UserAssociatedWithCallIsInQuietMode_EmergCallInCallUi_BindsToPrimaryUser()
+        throws Exception {
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(DUMMY_USER_HANDLE);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+            .thenReturn(mMockUserManager);
+        when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(true);
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        setupMockPackageManagerLocationPermission(SYS_PKG, false /* granted */);
+
+        mInCallController.bindToServices(mMockCall);
+
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+            bindIntentCaptor.capture(),
+            any(ServiceConnection.class),
+            eq(serviceBindingFlags),
+            eq(mUserHandle));
+        Intent bindIntent = bindIntentCaptor.getValue();
+        assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+    }
+
+    @MediumTest
+    @Test
+    public void
+    testBindToService_UserAssociatedWithCallNotInQuietMode_EmergCallInCallUi_BindsToAssociatedUser()
+        throws Exception {
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.getUserHandleFromTargetPhoneAccount()).thenReturn(DUMMY_USER_HANDLE);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+            .thenReturn(mMockUserManager);
+        when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        setupMockPackageManagerLocationPermission(SYS_PKG, false /* granted */);
+
+        mInCallController.bindToServices(mMockCall);
+
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+            bindIntentCaptor.capture(),
+            any(ServiceConnection.class),
+            eq(serviceBindingFlags),
+            eq(DUMMY_USER_HANDLE));
+        Intent bindIntent = bindIntentCaptor.getValue();
+        assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+    }
+
     /**
      * This test verifies the behavior of Telecom when the system dialer crashes on binding and must
      * be restarted.  Specifically, it ensures when the system dialer crashes we revoke the runtime
@@ -614,6 +635,9 @@
         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
         when(mMockCallsManager.isInEmergencyCall()).thenReturn(true);
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+            .thenReturn(mMockUserManager);
+        when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
         when(mMockCall.isIncoming()).thenReturn(false);
         when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
         when(mMockCall.getIntentExtras()).thenReturn(callExtras);
@@ -870,6 +894,114 @@
     }
 
     /**
+     * This test verifies the behavior of Telecom when the system dialer crashes on binding and must
+     * be restarted.  Specifically, it ensures when the system dialer crashes we revoke the runtime
+     * location permission, and when it restarts we re-grant the permission.
+     * @throws Exception
+     */
+    @MediumTest
+    @Test
+    public void testBindToLateConnectionNonUiIcs() throws Exception {
+        Bundle callExtras = new Bundle();
+        callExtras.putBoolean("whatever", true);
+
+        // Make a basic call and bind to the default dialer.
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mUserHandle);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockCallsManager.isInEmergencyCall()).thenReturn(true);
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+                .thenReturn(mMockUserManager);
+        when(mMockUserManager.isQuietModeEnabled(any(UserHandle.class))).thenReturn(false);
+        when(mMockCall.isIncoming()).thenReturn(false);
+        when(mMockCall.getTargetPhoneAccount()).thenReturn(PA_HANDLE);
+        when(mMockCall.getIntentExtras()).thenReturn(callExtras);
+        when(mMockCall.isExternalCall()).thenReturn(false);
+        when(mDefaultDialerCache.getDefaultDialerApplication(CURRENT_USER_ID))
+                .thenReturn(DEF_PKG);
+        ArgumentCaptor<ServiceConnection> serviceConnectionCaptor =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        when(mMockContext.bindServiceAsUser(any(Intent.class), serviceConnectionCaptor.capture(),
+                eq(serviceBindingFlags),
+                eq(mUserHandle))).thenReturn(true);
+        when(mTimeoutsAdapter.getEmergencyCallbackWindowMillis(any(ContentResolver.class)))
+                .thenReturn(300_000L);
+
+        // Setup package manager; there is a dialer and disable non-ui ICS
+        when(mMockPackageManager.queryIntentServicesAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(
+                Arrays.asList(
+                        getDefResolveInfo(false /* externalCalls */, false /* selfMgd */),
+                        getNonUiResolveinfo(false /* selfManaged */,
+                                false /* isEnabled */)
+                )
+        );
+        when(mMockPackageManager
+                .getComponentEnabledSetting(new ComponentName(DEF_PKG, DEF_CLASS)))
+                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+        when(mMockPackageManager
+                .getComponentEnabledSetting(new ComponentName(NONUI_PKG, NONUI_CLASS)))
+                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
+
+        mInCallController.addCall(mMockCall);
+        mInCallController.bindToServices(mMockCall);
+
+        // There will be 4 calls for the various types of ICS.
+        verify(mMockPackageManager, times(4)).queryIntentServicesAsUser(
+                any(Intent.class),
+                eq(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS),
+                eq(CURRENT_USER_ID));
+
+        // Verify bind to the dialer
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                any(ServiceConnection.class),
+                eq(serviceBindingFlags),
+                eq(mUserHandle));
+
+        Intent bindIntent = bindIntentCaptor.getValue();
+        assertEquals(InCallService.SERVICE_INTERFACE, bindIntent.getAction());
+        assertEquals(SYS_PKG, bindIntent.getComponent().getPackageName());
+        assertEquals(SYS_CLASS, bindIntent.getComponent().getClassName());
+
+        // Setup mocks to enable nonui ICS
+        when(mMockPackageManager.queryIntentServicesAsUser(
+                any(Intent.class), anyInt(), anyInt())).thenReturn(
+                        Arrays.asList(
+                                getDefResolveInfo(false /* externalCalls */, false /* selfMgd */),
+                                getNonUiResolveinfo(false /* selfManaged */,
+                                        true /* isEnabled */)
+                        )
+        );
+        when(mMockPackageManager
+                .getComponentEnabledSetting(new ComponentName(NONUI_PKG, NONUI_CLASS)))
+                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+        // Emulate a late enable of the non-ui ICS
+        Intent packageUpdated = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+        packageUpdated.setData(Uri.fromParts("package", NONUI_PKG, null));
+        packageUpdated.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
+                new String[] {NONUI_CLASS});
+        packageUpdated.putExtra(Intent.EXTRA_UID, NONUI_UID);
+        mRegisteredReceiver.onReceive(mMockContext, packageUpdated);
+
+        // Now, we expect to auto-rebind to the system dialer (verify 2 times since this is the
+        // second binding).
+        verify(mMockContext, times(2)).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                any(ServiceConnection.class),
+                eq(serviceBindingFlags),
+                eq(mUserHandle));
+
+        // Unbind!
+        mInCallController.unbindFromServices(UserHandle.of(CURRENT_USER_ID));
+
+        // Make sure we unbound 2 times
+        verify(mMockContext, times(2)).unbindService(any(ServiceConnection.class));
+    }
+
+    /**
      * Ensures that the {@link InCallController} will bind to an {@link InCallService} which
      * supports external calls.
      */
@@ -1455,6 +1587,26 @@
                 android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB));
     }
 
+    @Test
+    public void testSecondaryUserCallBindToCurrentUser() throws Exception {
+        setupMocks(true /* isExternalCall */);
+        setupMockPackageManager(true /* default */, true /* system */, false /* external calls */);
+        // Force the difference between the phone account user and current user. This is supposed to
+        // simulate a secondary user placing a call over an unassociated sim.
+        assertFalse(mUserHandle.equals(UserHandle.USER_CURRENT));
+        when(mMockUserInfo.isManagedProfile()).thenReturn(false);
+
+        mInCallController.bindToServices(mMockCall);
+
+        // Bind InCallService on UserHandle.CURRENT and not the user from the call (mUserHandle)
+        ArgumentCaptor<Intent> bindIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext, times(1)).bindServiceAsUser(
+                bindIntentCaptor.capture(),
+                any(ServiceConnection.class),
+                eq(serviceBindingFlags),
+                eq(UserHandle.CURRENT));
+    }
+
     private void setupMocks(boolean isExternalCall) {
         setupMocks(isExternalCall, false /* isSelfManagedCall */);
     }
@@ -1549,14 +1701,14 @@
         }};
     }
 
-    private ResolveInfo getNonUiResolveinfo(boolean supportsSelfManaged) {
+    private ResolveInfo getNonUiResolveinfo(boolean supportsSelfManaged, boolean isEnabled) {
         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.enabled = isEnabled;
             serviceInfo.permission = Manifest.permission.BIND_INCALL_SERVICE;
             serviceInfo.metaData = new Bundle();
             if (supportsSelfManaged) {
@@ -1647,7 +1799,7 @@
                 } else {
                     // InCallController uses a blank package name when querying for non-ui incalls
                     if (useNonUiInCalls) {
-                        resolveInfo.add(getNonUiResolveinfo(includeSelfManagedCallsInNonUi));
+                        resolveInfo.add(getNonUiResolveinfo(includeSelfManagedCallsInNonUi, true));
                     }
                     // InCallController uses a blank package name when querying for App Op non-ui incalls
                     if (useAppOpNonUiInCalls) {
diff --git a/tests/src/com/android/server/telecom/tests/MissedInformationTest.java b/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
index f28a781..8ea2739 100644
--- a/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedInformationTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom.tests;
 
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
 import static android.provider.CallLog.Calls.AUTO_MISSED_EMERGENCY_CALL;
 import static android.provider.CallLog.Calls.AUTO_MISSED_MAXIMUM_DIALING;
 import static android.provider.CallLog.Calls.AUTO_MISSED_MAXIMUM_RINGING;
@@ -117,6 +118,8 @@
         mPackageManager = mContext.getPackageManager();
         when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
         mCountDownLatch  = new CountDownLatch(1);
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
     }
 
     @Override
@@ -356,6 +359,7 @@
         doReturn(mNotificationManager).when(mSpyContext)
                 .getSystemService(Context.NOTIFICATION_SERVICE);
         doReturn(false).when(mNotificationManager).matchesCallFilter(any(Bundle.class));
+        doReturn(false).when(mIncomingCall).wasDndCheckComputedForCall();
         mCallsManager.getRinger().setNotificationManager(mNotificationManager);
 
         CallFilteringResult result = new CallFilteringResult.Builder()
diff --git a/tests/src/com/android/server/telecom/tests/MmiUtilsTest.java b/tests/src/com/android/server/telecom/tests/MmiUtilsTest.java
new file mode 100644
index 0000000..ed74637
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/MmiUtilsTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.MmiUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MmiUtilsTest extends TelecomTestCase {
+
+    private static final String[] sDangerousDialStrings = {
+        "*21*1234567#", // fwd unconditionally to 1234567,
+        "*67*1234567#", // fwd to 1234567 when line is busy
+        "*61*1234567#", // fwd to 1234567 when no one picks up
+        "*62*1234567#", // fwd to 1234567 when out of range
+        "*004*1234567#", // fwd to 1234567 when busy, not pickup up, out of range
+        "*004*1234567#", // fwd to 1234567 conditionally
+        "**21*1234567#", // fwd unconditionally to 1234567
+
+        // north american vertical service codes
+
+        "*094565678", // Selective Call Blocking/Reporting
+        "*4278889", // Change Forward-To Number for Customer Programmable Call Forwarding Don't
+                    // Answer
+        "*5644456", // Change Forward-To Number for ISDN Call Forwarding
+        "*6045677", // Selective Call Rejection Activation
+        "*635678", // Selective Call Forwarding Activation
+        "*64678899", // Selective Call Acceptance Activation
+        "*683456", // Call Forwarding Busy Line/Don't Answer Activation
+        "*721234", // Call Forwarding Activation
+        "*77", // Anonymous Call Rejection Activation
+        "*78", // Do Not Disturb Activation
+    };
+
+    private MmiUtils mMmiUtils = new MmiUtils();
+    private static final String[] sNonDangerousDialStrings = {"*6712345678", "*272", "*272911"};
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @SmallTest
+    @Test
+    public void testDangerousDialStringsDetected() throws Exception {
+        for (String s : sDangerousDialStrings) {
+            Uri.Builder b = new Uri.Builder();
+            b.scheme("tel").opaquePart(s);
+            assertTrue(mMmiUtils.isDangerousMmiOrVerticalCode(b.build()));
+        }
+    }
+
+    @SmallTest
+    @Test
+    public void testNonDangerousDialStringsNotDetected() throws Exception {
+        for (String s : sNonDangerousDialStrings) {
+            Uri.Builder b = new Uri.Builder();
+            b.scheme("tel").opaquePart(s);
+            assertFalse(mMmiUtils.isDangerousMmiOrVerticalCode(b.build()));
+        }
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index 169aeb2..f2bcf18 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -56,6 +56,7 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.MmiUtils;
 import com.android.server.telecom.NewOutgoingCallIntentBroadcaster;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
@@ -93,6 +94,7 @@
     @Mock private RoleManagerAdapter mRoleManagerAdapter;
     @Mock private DefaultDialerCache mDefaultDialerCache;
 
+    @Mock private MmiUtils mMmiUtils;
     private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new PhoneNumberUtilsAdapterImpl();
 
     @Override
@@ -261,6 +263,58 @@
         assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK, dialerIntent.getFlags());
     }
 
+    @Test
+    public void testDangerousMmiCodeWithNonDefaultDialer() {
+        Uri handle = Uri.parse("tel:*21*1234567#");
+        doReturn(true).when(mMmiUtils).isDangerousMmiOrVerticalCode(handle);
+        Intent intent = new Intent(Intent.ACTION_CALL, handle);
+
+        String ui_package_string = "sample_string_1";
+        String dialer_default_class_string = "sample_string_2";
+        mComponentContextFixture.putResource(com.android.internal.R.string.config_defaultDialer,
+                ui_package_string);
+        mComponentContextFixture.putResource(R.string.dialer_default_class,
+                dialer_default_class_string);
+        when(mDefaultDialerCache.getSystemDialerApplication()).thenReturn(ui_package_string);
+        when(mDefaultDialerCache.getDialtactsSystemDialerComponent()).thenReturn(
+                new ComponentName(ui_package_string, dialer_default_class_string));
+
+        int result = processIntent(intent, false).disconnectCause;
+
+        assertEquals(DisconnectCause.OUTGOING_CANCELED, result);
+        verifyNoBroadcastSent();
+        verifyNoCallPlaced();
+
+        ArgumentCaptor<Intent> dialerIntentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext).startActivityAsUser(dialerIntentCaptor.capture(), any(UserHandle.class));
+        Intent dialerIntent = dialerIntentCaptor.getValue();
+        assertEquals(new ComponentName(ui_package_string, dialer_default_class_string),
+                dialerIntent.getComponent());
+        assertEquals(Intent.ACTION_DIAL, dialerIntent.getAction());
+        assertEquals(handle, dialerIntent.getData());
+        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK, dialerIntent.getFlags());
+    }
+
+    @Test
+    public void testNonDangerousMmiCodeWithNonDefaultDialer() {
+        Uri handle = Uri.parse("tel:*12*1234567#");
+        doReturn(false).when(mMmiUtils).isDangerousMmiOrVerticalCode(handle);
+        Intent intent = new Intent(Intent.ACTION_CALL, handle);
+
+        String ui_package_string = "sample_string_1";
+        String dialer_default_class_string = "sample_string_2";
+        mComponentContextFixture.putResource(com.android.internal.R.string.config_defaultDialer,
+                ui_package_string);
+        mComponentContextFixture.putResource(R.string.dialer_default_class,
+                dialer_default_class_string);
+        when(mDefaultDialerCache.getSystemDialerApplication()).thenReturn(ui_package_string);
+        when(mDefaultDialerCache.getDialtactsSystemDialerComponent()).thenReturn(
+                new ComponentName(ui_package_string, dialer_default_class_string));
+
+        int result = processIntent(intent, false).disconnectCause;
+        assertEquals(DisconnectCause.NOT_DISCONNECTED, result);
+    }
+
     @SmallTest
     @Test
     public void testActionCallEmergencyCall() {
@@ -488,7 +542,7 @@
             boolean isDefaultPhoneApp) {
         NewOutgoingCallIntentBroadcaster b = new NewOutgoingCallIntentBroadcaster(
                 mContext, mCallsManager, intent, mPhoneNumberUtilsAdapter,
-                isDefaultPhoneApp, mDefaultDialerCache);
+                isDefaultPhoneApp, mDefaultDialerCache, mMmiUtils);
         NewOutgoingCallIntentBroadcaster.CallDisposition cd = b.evaluateCall();
         if (cd.disconnectCause == DisconnectCause.NOT_DISCONNECTED) {
             b.processCall(mCall, cd);
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index c6cbbbb..e573bb8 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -23,8 +23,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyObject;
-import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -707,7 +705,7 @@
                 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)
                 .build();
 
-        assertFalse(mRegistrar.hasTransactionalCallCapabilites(accountWithoutCapability));
+        assertFalse(mRegistrar.hasTransactionalCallCapabilities(accountWithoutCapability));
 
         try {
             mRegistrar.registerPhoneAccount(accountWithoutCapability);
@@ -730,7 +728,7 @@
                         PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS)
                 .build();
 
-        assertTrue(mRegistrar.hasTransactionalCallCapabilites(accountWithCapability));
+        assertTrue(mRegistrar.hasTransactionalCallCapabilities(accountWithCapability));
 
         try {
             mRegistrar.registerPhoneAccount(accountWithCapability);
@@ -752,7 +750,7 @@
                         PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS)
                 .build();
 
-        assertTrue(mRegistrar.hasTransactionalCallCapabilites(accountWithCapability));
+        assertTrue(mRegistrar.hasTransactionalCallCapabilities(accountWithCapability));
 
         try {
             // WHEN
@@ -1667,6 +1665,41 @@
         }
     }
 
+    /**
+     * Ensure an IllegalArgumentException is thrown when providing a SubscriptionAddress that
+     * exceeds the PhoneAccountRegistrar limit.
+     */
+    @Test
+    public void testLimitOnSubscriptionAddress() throws Exception {
+        String text = "a".repeat(100);
+        PhoneAccount.Builder builder =  new PhoneAccount.Builder(makeQuickAccountHandle(TEST_ID),
+                TEST_LABEL).setSubscriptionAddress(Uri.fromParts(text, text, text));
+        try {
+            mRegistrar.enforceCharacterLimit(builder.build());
+            fail("failed to throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // pass test
+        }
+    }
+
+    /**
+     * PhoneAccounts with CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS do not require a
+     * ConnectionService. Ensure that such an account can be registered and fetched.
+     */
+    @Test
+    public void testFetchingTransactionalAccounts() {
+        PhoneAccount account = makeBuilderWithBindCapabilities(
+                makeQuickAccountHandle(TEST_ID)).build();
+
+        try {
+            assertEquals(0, mRegistrar.getAllPhoneAccounts(null, true).size());
+            registerAndEnableAccount(account);
+            assertEquals(1, mRegistrar.getAllPhoneAccounts(null, true).size());
+        } finally {
+            mRegistrar.unregisterPhoneAccount(account.getAccountHandle());
+        }
+    }
+
     private static PhoneAccount.Builder makeBuilderWithBindCapabilities(PhoneAccountHandle handle) {
         return new PhoneAccount.Builder(handle, TEST_LABEL)
                 .setCapabilities(PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS);
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index d397910..abbfe34 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -24,13 +24,16 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.NotificationManager;
@@ -42,15 +45,14 @@
 import android.media.VolumeShaper;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Parcel;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.view.accessibility.AccessibilityManager;
 
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.Call;
@@ -69,83 +71,19 @@
 import org.mockito.Mock;
 import org.mockito.Spy;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 
 @RunWith(JUnit4.class)
 public class RingerTest extends TelecomTestCase {
     private static final Uri FAKE_RINGTONE_URI = Uri.parse("content://media/fake/audio/1729");
-    private static class UriVibrationEffect extends VibrationEffect {
-        final Uri mUri;
-
-        private UriVibrationEffect(Uri uri) {
-            mUri = uri;
-        }
-
-        @Override
-        public VibrationEffect resolve(int defaultAmplitude) {
-            return this;
-        }
-
-        @Override
-        public VibrationEffect scale(float scaleFactor) {
-            return this;
-        }
-
-        @Override
-        public long[] computeCreateWaveformOffOnTimingsOrNull() {
-            return null; // not needed
-        }
-
-        @Override
-        public boolean areVibrationFeaturesSupported(Vibrator vibrator) {
-            return true; // not needed
-        }
-
-        @Override
-        public void validate() {
-            // not needed
-        }
-
-        @Override
-        public long getDuration() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            // not needed
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            UriVibrationEffect that = (UriVibrationEffect) o;
-            return Objects.equals(mUri, that.mUri);
-        }
-    }
+    // Returned when the a URI-based VibrationEffect is attempted, to avoid depending on actual
+    // device configuration for ringtone URIs. The actual Uri can be verified via the
+    // VibrationEffectProxy mock invocation.
+    private static final VibrationEffect URI_VIBRATION_EFFECT =
+            VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
 
     @Mock InCallTonePlayer.Factory mockPlayerFactory;
     @Mock SystemSettingsUtil mockSystemSettingsUtil;
-    @Mock AsyncRingtonePlayer mockRingtonePlayer;
     @Mock RingtoneFactory mockRingtoneFactory;
     @Mock Vibrator mockVibrator;
     @Mock InCallController mockInCallController;
@@ -162,6 +100,8 @@
             new PhoneAccountHandle(new ComponentName("pa_pkg", "pa_cls"),
                     "pa_id");
 
+    boolean mIsHapticPlaybackSupported = true;  // Note: initializeRinger() after changes.
+    AsyncRingtonePlayer asyncRingtonePlayer = new AsyncRingtonePlayer();
     Ringer mRingerUnderTest;
     AudioManager mockAudioManager;
     CompletableFuture<Void> mRingCompletionFuture = new CompletableFuture<>();
@@ -171,25 +111,33 @@
     public void setUp() throws Exception {
         super.setUp();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-        doAnswer(invocation -> {
-            Uri ringtoneUriForEffect = invocation.getArgument(0);
-            return new UriVibrationEffect(ringtoneUriForEffect);
-        }).when(spyVibrationEffectProxy).get(any(), any());
+        doReturn(URI_VIBRATION_EFFECT).when(spyVibrationEffectProxy).get(any(), any());
         when(mockPlayerFactory.createPlayer(anyInt())).thenReturn(mockTonePlayer);
         mockAudioManager = mContext.getSystemService(AudioManager.class);
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class))).thenReturn(true);
+        when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class)))
+                .thenAnswer((invocation) -> mIsHapticPlaybackSupported);
         mockNotificationManager =mContext.getSystemService(NotificationManager.class);
         when(mockTonePlayer.startTone()).thenReturn(true);
         when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
         when(mockRingtoneFactory.hasHapticChannels(any(Ringtone.class))).thenReturn(false);
-        mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
-                mockRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
-                mockInCallController, mockNotificationManager, mockAccessibilityManagerAdapter);
         when(mockCall1.getState()).thenReturn(CallState.RINGING);
         when(mockCall2.getState()).thenReturn(CallState.RINGING);
         when(mockCall1.getUserHandleFromTargetPhoneAccount()).thenReturn(PA_HANDLE.getUserHandle());
         when(mockCall2.getUserHandleFromTargetPhoneAccount()).thenReturn(PA_HANDLE.getUserHandle());
+
+        createRingerUnderTest();
+    }
+
+    /**
+     * (Re-)Creates the Ringer for the test. This needs to be called if changing final properties,
+     * like mIsHapticPlaybackSupported.
+     */
+    private void createRingerUnderTest() {
+        mRingerUnderTest = new Ringer(mockPlayerFactory, mContext, mockSystemSettingsUtil,
+                asyncRingtonePlayer, mockRingtoneFactory, mockVibrator, spyVibrationEffectProxy,
+                mockInCallController, mockNotificationManager, mockAccessibilityManagerAdapter);
+        // This future is used to wait for AsyncRingtonePlayer to finish its part.
         mRingerUnderTest.setBlockOnRingingFuture(mRingCompletionFuture);
     }
 
@@ -201,33 +149,30 @@
 
     @SmallTest
     @Test
-    public void testNoActionInTheaterMode() {
+    public void testNoActionInTheaterMode() throws Exception {
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockSystemSettingsUtil.isTheaterModeOn(any(Context.class))).thenReturn(true);
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
-        verify(mockRingtoneFactory, never())
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockTonePlayer, never()).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
 
     @SmallTest
     @Test
-    public void testNoActionWithExternalRinger() {
+    public void testNoActionWithExternalRinger() throws Exception {
         Bundle externalRingerExtra = new Bundle();
         externalRingerExtra.putBoolean(TelecomManager.EXTRA_CALL_HAS_IN_BAND_RINGTONE, true);
         when(mockCall1.getIntentExtras()).thenReturn(externalRingerExtra);
         when(mockCall2.getIntentExtras()).thenReturn(externalRingerExtra);
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
-        verify(mockRingtoneFactory, never())
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
+
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockTonePlayer, never()).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
@@ -235,19 +180,15 @@
     @SmallTest
     @Test
     public void testNoActionWhenDialerRings() throws Exception {
-        ensureRingtoneMocked();
-
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockInCallController.doesConnectedDialerSupportRinging(
                 any(UserHandle.class))).thenReturn(true);
         ensureRingerIsNotAudible();
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
-        verify(mockRingtoneFactory, never())
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
-        mRingCompletionFuture.get();
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
+
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockTonePlayer, never()).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
     }
@@ -255,48 +196,46 @@
     @SmallTest
     @Test
     public void testAudioFocusStillAcquiredWhenDialerRings() throws Exception {
-        ensureRingtoneMocked();
 
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockInCallController.doesConnectedDialerSupportRinging(
                 any(UserHandle.class))).thenReturn(true);
         ensureRingerIsAudible();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
-        verify(mockRingtoneFactory, never())
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
-        mRingCompletionFuture.get();
+        assertTrue(startRingingAndWaitForAsync(mockCall2, false));
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockTonePlayer, never()).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
 
     @SmallTest
     @Test
-    public void testNoActionWhenCallIsSelfManaged() {
+    public void testNoActionWhenCallIsSelfManaged() throws Exception {
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockCall2.isSelfManaged()).thenReturn(true);
         // We do want to acquire audio focus when self-managed
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
+        assertTrue(startRingingAndWaitForAsync(mockCall2, true));
+
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockTonePlayer, never()).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
 
     @SmallTest
     @Test
-    public void testCallWaitingButNoRingForSpecificContacts() {
+    public void testCallWaitingButNoRingForSpecificContacts() throws Exception {
         when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
         // Start call waiting to make sure that it does stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
         verify(mockTonePlayer).startTone();
 
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
+
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
@@ -311,12 +250,12 @@
         enableVibrationWhenRinging();
         // Pretend we're using audio coupled haptics.
         setIsUsingHaptics(mockRingtone, true);
-        assertTrue(mRingerUnderTest.startRinging(mockCall1, false));
-        mRingCompletionFuture.get();
+        assertTrue(startRingingAndWaitForAsync(mockCall1, false));
         verify(mockRingtoneFactory, times(1))
             .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
+        verify(mockRingtone).play();
         verify(mockVibrator, never()).vibrate(any(VibrationEffect.class),
                 any(VibrationAttributes.class));
     }
@@ -331,11 +270,16 @@
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         enableVibrationWhenRinging();
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
-        mRingCompletionFuture.get();
+        // The ringtone isn't known to be null until the async portion after the call completes,
+        // so startRinging still returns true here as there should nominally be a ringtone.
+        // Notably, vibration still happens in this scenario.
+        assertTrue(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockTonePlayer).stopTone();
-        // Ringtone does not exist, make sure it does not try to play it
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
+
+        // Just the one call to mockRingtoneFactory, which returned null.
+        verify(mockRingtoneFactory).getRingtone(
+                any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
 
         // Play default vibration when future completes with no audio coupled haptics
         verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
@@ -353,14 +297,30 @@
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
         when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
         enableVibrationWhenRinging();
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
-        mRingCompletionFuture.get();
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         // Try to play a silent haptics ringtone
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
         verify(mockRingtoneFactory, times(1)).getHapticOnlyRingtone();
-        verify(mockRingtoneFactory, never())
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
+        verify(mockRingtone).play();
+
+        // Play default vibration when future completes with no audio coupled haptics
+        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
+                any(VibrationAttributes.class));
+    }
+
+    @SmallTest
+    @Test
+    public void testVibrateButNoRingForSilentRingtoneWithoutAudioHapticSupport() throws Exception {
+        mIsHapticPlaybackSupported = false;
+        createRingerUnderTest();  // Needed after changing haptic playback support.
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
+        enableVibrationWhenRinging();
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
+        verify(mockTonePlayer).stopTone();
+        verifyZeroInteractions(mockRingtoneFactory);
 
         // Play default vibration when future completes with no audio coupled haptics
         verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
@@ -377,14 +337,13 @@
         when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
         setIsUsingHaptics(mockRingtone, true);
         enableVibrationWhenRinging();
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
+
         verify(mockRingtoneFactory, times(1)).getHapticOnlyRingtone();
-        verify(mockRingtoneFactory, never())
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
-        mRingCompletionFuture.get();
+        verifyNoMoreInteractions(mockRingtoneFactory);
         verify(mockTonePlayer).stopTone();
         // Try to play a silent haptics ringtone
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
+        verify(mockRingtone).play();
         // Skip vibration for audio coupled haptics
         verify(mockVibrator, never()).vibrate(any(VibrationEffect.class),
                 any(VibrationAttributes.class));
@@ -394,36 +353,34 @@
     @Test
     public void testCustomVibrationForRingtone() throws Exception {
         mRingerUnderTest.startCallWaiting(mockCall1);
-        Ringtone mockRingtone = mock(Ringtone.class);
+        Ringtone mockRingtone = ensureRingtoneMocked();
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        when(mockRingtoneFactory.getRingtone(any(Call.class), eq(null), anyBoolean()))
-            .thenReturn(mockRingtone);
         when(mockRingtone.getUri()).thenReturn(FAKE_RINGTONE_URI);
         enableVibrationWhenRinging();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
-        mRingCompletionFuture.get();
+        assertTrue(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
         verify(mockRingtoneFactory, times(1))
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
-        verify(mockVibrator).vibrate(eq(spyVibrationEffectProxy.get(FAKE_RINGTONE_URI, mContext)),
-                any(VibrationAttributes.class));
+            .getRingtone(any(Call.class), isNull(), anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
+        verify(mockRingtone).play();
+        verify(spyVibrationEffectProxy).get(eq(FAKE_RINGTONE_URI), any(Context.class));
+        verify(mockVibrator).vibrate(eq(URI_VIBRATION_EFFECT), any(VibrationAttributes.class));
     }
 
     @SmallTest
     @Test
     public void testRingAndNoVibrate() throws Exception {
-        ensureRingtoneMocked();
+        Ringtone mockRingtone = ensureRingtoneMocked();
 
         mRingerUnderTest.startCallWaiting(mockCall1);
         ensureRingerIsAudible();
         enableVibrationOnlyWhenNotRinging();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
-        mRingCompletionFuture.get();
+        assertTrue(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockRingtoneFactory, times(1))
             .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
+        verify(mockRingtone).play();
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
@@ -431,50 +388,31 @@
     @SmallTest
     @Test
     public void testRingWithRampingRinger() throws Exception {
-        ensureRingtoneMocked();
+        Ringtone mockRingtone = ensureRingtoneMocked();
 
         mRingerUnderTest.startCallWaiting(mockCall1);
         ensureRingerIsAudible();
         enableRampingRinger();
         enableVibrationWhenRinging();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+        assertTrue(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockRingtoneFactory, times(1))
             .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
-        mRingCompletionFuture.get();
+        verifyNoMoreInteractions(mockRingtoneFactory);
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
+        verify(mockRingtone).play();
     }
 
     @SmallTest
     @Test
-    public void testSilentRingWithHfpStillAcquiresFocus1() throws Exception {
+    public void testSilentRingWithHfpStillAcquiresFocus() throws Exception {
         mRingerUnderTest.startCallWaiting(mockCall1);
-        ensureRingtoneMocked();
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
         enableVibrationOnlyWhenNotRinging();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
-        mRingCompletionFuture.get();
+        assertTrue(startRingingAndWaitForAsync(mockCall2, true));
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
-        verify(mockVibrator, never())
-                .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
-    }
-
-    @SmallTest
-    @Test
-    public void testSilentRingWithHfpStillAcquiresFocus2() throws Exception {
-        mRingerUnderTest.startCallWaiting(mockCall1);
-        when(mockRingtoneFactory.getRingtone(
-                 any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()))
-            .thenReturn(null);
-        when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(0);
-        enableVibrationOnlyWhenNotRinging();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
-        mRingCompletionFuture.get();
-        verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer, never()).play(any(Ringtone.class));
+        // Ringer not audible, so never tries to create a ringtone.
+        verifyZeroInteractions(mockRingtoneFactory);
         verify(mockVibrator, never())
                 .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
     }
@@ -483,28 +421,17 @@
     @Test
     public void testRingAndVibrateForAllowedCallInDndMode() throws Exception {
         mRingerUnderTest.startCallWaiting(mockCall1);
-        Ringtone mockRingtone = mock(Ringtone.class);
+        Ringtone mockRingtone = ensureRingtoneMocked();
         when(mockNotificationManager.getZenMode()).thenReturn(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        when(mockRingtoneFactory.getRingtone(any(Call.class), eq(null), anyBoolean()))
-                .thenReturn(mockRingtone);
         when(mockAudioManager.getRingerMode()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         when(mockAudioManager.getStreamVolume(AudioManager.STREAM_RING)).thenReturn(100);
         enableVibrationWhenRinging();
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, true));
+        assertTrue(startRingingAndWaitForAsync(mockCall2, true));
         verify(mockRingtoneFactory, times(1))
-            .getRingtone(any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean());
-        mRingCompletionFuture.get();
+            .getRingtone(any(Call.class), isNull(), anyBoolean());
+        verifyNoMoreInteractions(mockRingtoneFactory);
         verify(mockTonePlayer).stopTone();
-        verify(mockRingtonePlayer).play(any(Ringtone.class));
-    }
-
-    private Ringtone ensureRingtoneMocked() {
-        Ringtone mockRingtone = mock(Ringtone.class);
-        when(mockRingtoneFactory.getRingtone(
-                 any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()))
-            .thenReturn(mockRingtone);
-        when(mockRingtoneFactory.getHapticOnlyRingtone()).thenReturn(mockRingtone);
-        return mockRingtone;
+        verify(mockRingtone).play();
     }
 
     /**
@@ -547,7 +474,7 @@
     }
 
     @Test
-    public void testNoFlashNotificationWhenCallSuppressed() {
+    public void testNoFlashNotificationWhenCallSuppressed() throws Exception {
         ensureRingtoneMocked();
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
@@ -556,13 +483,13 @@
         when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(false);
 
         assertFalse(mRingerUnderTest.shouldRingForContact(mockCall2));
-        assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
+        assertFalse(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockAccessibilityManagerAdapter, never())
                 .startFlashNotificationSequence(any(Context.class), anyInt());
     }
 
     @Test
-    public void testStartFlashNotificationWhenRingStarts()  {
+    public void testStartFlashNotificationWhenRingStarts() throws Exception {
         ensureRingtoneMocked();
         // Start call waiting to make sure that it doesn't stop when we start ringing
         mRingerUnderTest.startCallWaiting(mockCall1);
@@ -571,15 +498,27 @@
         when(mockNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
 
         assertTrue(mRingerUnderTest.shouldRingForContact(mockCall2));
-        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+        assertTrue(startRingingAndWaitForAsync(mockCall2, false));
         verify(mockAccessibilityManagerAdapter, atLeastOnce())
                 .startFlashNotificationSequence(any(Context.class), anyInt());
     }
 
     @Test
-    public void testStopFlashNotificationWhenRingStops() {
-        ensureRingtoneMocked();
+    public void testStopFlashNotificationWhenRingStops() throws Exception {
+        Ringtone mockRingtone = mock(Ringtone.class);
+        when(mockRingtoneFactory.getRingtone(
+                any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()))
+                .thenAnswer(x -> {
+                    // Be slow to create ringtone.
+                    try {
+                        Thread.sleep(300);
+                    } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                    }
+                    return mockRingtone;
+                });
         // Start call waiting to make sure that it doesn't stop when we start ringing
+        enableVibrationWhenRinging();
         mRingerUnderTest.startCallWaiting(mockCall1);
         when(mockCall2.wasDndCheckComputedForCall()).thenReturn(false);
         when(mockCall2.getHandle()).thenReturn(Uri.parse(""));
@@ -590,7 +529,36 @@
         mRingerUnderTest.stopRinging();
         verify(mockAccessibilityManagerAdapter, atLeastOnce())
                 .stopFlashNotificationSequence(any(Context.class));
+        mRingCompletionFuture.get();  // Don't leak async work.
+        verify(mockVibrator, never())  // cancelled before it started.
+                .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
+    }
 
+    @SmallTest
+    @Test
+    public void testNoRingingForQuietProfile() throws Exception {
+        UserManager um = mContext.getSystemService(UserManager.class);
+        when(um.isManagedProfile(PA_HANDLE.getUserHandle().getIdentifier())).thenReturn(true);
+        when(um.isQuietModeEnabled(PA_HANDLE.getUserHandle())).thenReturn(true);
+        // We don't want to acquire audio focus when self-managed
+        assertFalse(startRingingAndWaitForAsync(mockCall2, true));
+
+        verify(mockTonePlayer, never()).stopTone();
+        verifyZeroInteractions(mockRingtoneFactory);
+        verify(mockVibrator, never())
+                .vibrate(any(VibrationEffect.class), any(VibrationAttributes.class));
+    }
+
+    /**
+     * Call startRinging and wait for its effects to have played out, to allow reliable assertions
+     * after it. The effects are generally "start playing ringtone" and "start vibration" - not
+     * waiting for anything open-ended.
+     */
+    private boolean startRingingAndWaitForAsync(Call mockCall2, boolean isHfpDeviceAttached)
+            throws Exception {
+        boolean result = mRingerUnderTest.startRinging(mockCall2, isHfpDeviceAttached);
+        mRingCompletionFuture.get();
+        return result;
     }
 
     private void ensureRingerIsAudible() {
@@ -618,10 +586,19 @@
     }
 
     private void setIsUsingHaptics(Ringtone mockRingtone, boolean useHaptics) {
-        when(mockSystemSettingsUtil.isHapticPlaybackSupported(any(Context.class)))
-            .thenReturn(useHaptics);
+        // Note: using haptics can also depend on mIsHapticPlaybackSupported. If changing
+        // that, the ringerUnderTest needs to be re-created.
         when(mockSystemSettingsUtil.isAudioCoupledVibrationForRampingRingerEnabled())
             .thenReturn(useHaptics);
         when(mockRingtone.hasHapticChannels()).thenReturn(useHaptics);
     }
+
+    private Ringtone ensureRingtoneMocked() {
+        Ringtone mockRingtone = mock(Ringtone.class);
+        when(mockRingtoneFactory.getRingtone(
+                any(Call.class), nullable(VolumeShaper.Configuration.class), anyBoolean()))
+                .thenReturn(mockRingtone);
+        when(mockRingtoneFactory.getHapticOnlyRingtone()).thenReturn(mockRingtone);
+        return mockRingtone;
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 569d6b4..7b5afe6 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -33,6 +33,7 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -292,6 +293,51 @@
         assertEquals(SIP_PA_HANDLE_17, returnedHandleSip);
     }
 
+    /**
+     * Clear the groupId from the PhoneAccount if a package does NOT have MODIFY_PHONE_STATE
+     */
+    @SmallTest
+    @Test
+    public void testGroupIdIsClearedWhenPermissionIsMissing() throws RemoteException {
+        // GIVEN
+        PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT)
+                .setGroupId("testId")
+                .build();
+        // WHEN
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+                eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class), anyBoolean());
+        doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
+        // THEN
+        PhoneAccount account =
+                mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_CURRENT, PACKAGE_NAME);
+        assertEquals("***", account.getGroupId());
+    }
+
+    /**
+     * Ensure groupId is not cleared if a package has MODIFY_PHONE_STATE
+     */
+    @SmallTest
+    @Test
+    public void testGroupIdIsNotCleared() throws RemoteException {
+        // GIVEN
+        final String groupId = "testId";
+        PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_CURRENT)
+                .setGroupId(groupId)
+                .build();
+        // WHEN
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+                eq(TEL_PA_HANDLE_CURRENT), any(UserHandle.class), anyBoolean());
+        doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
+        // THEN
+        PhoneAccount account =
+                mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_CURRENT, DEFAULT_DIALER_PACKAGE);
+        assertEquals(groupId, account.getGroupId());
+    }
+
     @SmallTest
     @Test
     public void testGetDefaultOutgoingPhoneAccountSucceedsIfCallerIsSimCallManager()
@@ -608,6 +654,8 @@
     @SmallTest
     @Test
     public void testGetPhoneAccount() throws Exception {
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingPermission(MODIFY_PHONE_STATE);
         makeAccountsVisibleToAllUsers(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
         assertEquals(TEL_PA_HANDLE_16, mTSIBinder.getPhoneAccount(TEL_PA_HANDLE_16,
                 mContext.getPackageName()).getAccountHandle());
@@ -903,6 +951,26 @@
 
     @SmallTest
     @Test
+    public void testRegisterPhoneAccountImageIconCrossUser() throws RemoteException {
+        String packageNameToUse = "com.android.officialpackage";
+        PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
+                packageNameToUse, "cs"), "test", Binder.getCallingUserHandle());
+        Icon icon = Icon.createWithContentUri("content://10@media/external/images/media/");
+        PhoneAccount phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingOrSelfPermission(MODIFY_PHONE_STATE);
+
+        // This should fail; security exception will be thrown.
+        registerPhoneAccountTestHelper(phoneAccount, false);
+
+        icon = Icon.createWithContentUri("content://0@media/external/images/media/");
+        phoneAccount = makePhoneAccount(phHandle).setIcon(icon).build();
+        // This should succeed.
+        registerPhoneAccountTestHelper(phoneAccount, true);
+    }
+
+    @SmallTest
+    @Test
     public void testUnregisterPhoneAccount() throws RemoteException {
         String packageNameToUse = "com.android.officialpackage";
         PhoneAccountHandle phHandle = new PhoneAccountHandle(new ComponentName(
@@ -1067,12 +1135,18 @@
         }
     }
 
+    /**
+     * Place a managed call with no PhoneAccount specified and ensure no security exception is
+     * thrown.
+     */
     @SmallTest
     @Test
     public void testPlaceCallWithNonEmergencyPermission() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
 
+        // We have passed in the DEFAULT_DIALER_PACKAGE for this test, so canCallPhone is always
+        // true.
         when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
                 nullable(String.class), nullable(String.class)))
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
@@ -1082,15 +1156,339 @@
                 .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
 
         mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
-        placeCallTestHelper(handle, extras, true);
+        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+                /*shouldNonEmergencyBeAllowed*/ true);
     }
 
+    /**
+     * Ensure that we get a SecurityException if the UID of the caller doesn't match the UID of the
+     * UID of the package name passed in.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_enforceCallingPackageFailure() throws Exception {
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+        // ConnectionService.
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // Return a non-matching UID for testing purposes.
+        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(-1);
+        try {
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+            fail("Expected SecurityException because calling package doesn't match");
+        } catch(SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * In the case that there is a self-managed call request and MANAGE_OWN_CALLS is granted, ensure
+     * that placeCall does not generate a SecurityException.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_selfManaged_permissionGranted() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+        // ConnectionService.
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // pass MANAGE_OWN_CALLS check, but do not have CALL_PHONE
+        doNothing().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+
+        try {
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+            placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
+                    /*shouldNonEmergencyBeAllowed*/ false);
+        } catch(SecurityException e) {
+            fail("Unexpected SecurityException - MANAGE_OWN_CALLS is set");
+        }
+    }
+
+    /**
+     * In the case that the placeCall API is being used place a self-managed call
+     * (phone account is marked self-managed and the calling application owns that PhoneAccount),
+     * ensure that the call gets placed as not self-managed as to not disclose PA info.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_selfManaged_noPermission() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+        // ConnectionService.
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+
+        try {
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+            fail("Expected SecurityException because MANAGE_OWN_CALLS is not set");
+        } catch(SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * In the case that there is a self-managed call request and the app doesn't own that
+     * PhoneAccount, we will need to check CALL_PHONE. If they do not have CALL_PHONE permission,
+     * we need to throw a security exception.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_selfManaged_permissionFail() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage doesn't match the PhoneAccountHandle package
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
+        doNothing().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+        doThrow(new SecurityException())
+                .when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+
+        try {
+            // Calling package is received and is not the same as PACKAGE_NAME
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+            fail("Expected a SecurityException - CALL_PHONE was not granted");
+        } catch(SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * In the case that there is a self-managed call request and the app doesn't own that
+     * PhoneAccount, we will need to check CALL_PHONE. If they have the CALL_PHONE permission, but
+     * the app op has been denied, this should throw a security exception.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_selfManaged_appOpPermissionFail() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage doesn't match the PhoneAccountHandle package.
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
+        doNothing().when(mContext).enforceCallingOrSelfPermission(
+                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+        doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+                nullable(String.class), nullable(String.class)))
+                .thenReturn(AppOpsManager.MODE_ERRORED);
+        try {
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+            fail("Expected a SecurityException - CALL_PHONE app op is denied");
+        } catch(SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * In the case that there is a self-managed call request and the app doesn't own that
+     * PhoneAccount, we will need to check CALL_PHONE. If they have the correct permissions, the
+     * call will go through, however we will have removed the self-managed PhoneAccountHandle. The
+     * call will go through as a normal managed call request with no PhoneAccountHandle.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_selfManaged_differentCallingPackage() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage doesn't match the PhoneAccountHandle package
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // simulate default dialer so CALL_PHONE is granted.
+        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+                nullable(String.class), nullable(String.class)))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+
+        // We expect the call to go through with no PhoneAccount specified, since the request
+        // contained a self-managed PhoneAccountHandle that didn't belong to this app.
+        Bundle expectedExtras = extras.deepCopy();
+        expectedExtras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+        try {
+            mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
+        } catch (SecurityException e) {
+            fail("Unexpected SecurityException - CTS is default dialer and MANAGE_OWN_CALLS is not"
+                    + " required. Exception: " + e);
+        }
+        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+                /*shouldNonEmergencyBeAllowed*/ true);
+    }
+
+    /**
+     * In the case that there is a managed call request and the app owns that
+     * PhoneAccount (but is not a self-managed), we will still need to check CALL_PHONE.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_samePackage_managedPhoneAccount_permissionFail() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makePhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage doesn't match the PhoneAccountHandle package
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // CALL_PHONE is not granted to the device.
+        doThrow(new SecurityException())
+                .when(mContext).enforceCallingOrSelfPermission(
+                        eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+        doThrow(new SecurityException())
+                .when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+
+        try {
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+            fail("Expected a SecurityException - CALL_PHONE is not granted");
+        } catch(SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * In the case that there is a managed call request and the app owns that
+     * PhoneAccount (but is not a self-managed), we will still need to check CALL_PHONE.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_samePackage_managedPhoneAccount_AppOpFail() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makePhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage matches the PhoneAccountHandle, but this is not a self managed phone
+        // account.
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // CALL_PHONE is granted, but the app op is not
+        doThrow(new SecurityException())
+                .when(mContext).enforceCallingOrSelfPermission(
+                        eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
+        doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+                nullable(String.class), nullable(String.class)))
+                .thenReturn(AppOpsManager.MODE_ERRORED);
+
+        try {
+            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
+            fail("Expected a SecurityException - CALL_PHONE app op is denied");
+        } catch(SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Since this is a self-managed call being requested, so ensure we report the call as
+     * self-managed and without non-emergency permissions.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_selfManaged_nonEmergencyPermission() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        when(mFakePhoneAccountRegistrar.getPhoneAccountUnchecked(TEL_PA_HANDLE_CURRENT)).thenReturn(
+                makeSelfManagedPhoneAccount(TEL_PA_HANDLE_CURRENT).build());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage matches the PhoneAccountHandle, so this is an app with a self-managed
+        // ConnectionService.
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // enforceCallingOrSelfPermission is implicitly granted for MANAGE_OWN_CALLS here and
+        // CALL_PHONE is not required.
+        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+                nullable(String.class), nullable(String.class)))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+
+        mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
+        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
+                /*shouldNonEmergencyBeAllowed*/ false);
+    }
+
+    /**
+     * Default dialer is calling placeCall and has CALL_PHONE granted, so non-emergency calls
+     * are allowed.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCall_managed_nonEmergencyGranted() throws Exception {
+        doReturn(false).when(mDefaultDialerCache).isDefaultOrSystemDialer(
+                eq(DEFAULT_DIALER_PACKAGE), anyInt());
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+        // callingPackage doesn't match the PhoneAccountHandle, so this app does not have a
+        // self-managed ConnectionService
+        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
+
+        // CALL_PHONE granted
+        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+                nullable(String.class), nullable(String.class)))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+        doReturn(PackageManager.PERMISSION_GRANTED)
+                .when(mContext).checkCallingPermission(CALL_PHONE);
+        doReturn(PackageManager.PERMISSION_DENIED)
+                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
+
+        mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
+        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+                /*shouldNonEmergencyBeAllowed*/ true);
+    }
+
+    /**
+     * The default dialer is requesting to place a call and CALL_PHONE is granted, however
+     * OP_CALL_PHONE app op is denied to that app, so non-emergency calls will be denied.
+     */
     @SmallTest
     @Test
     public void testPlaceCallWithAppOpsOff() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
 
+        // We have passed in the DEFAULT_DIALER_PACKAGE for this test, so canCallPhone is always
+        // true.
         when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
                 nullable(String.class), nullable(String.class)))
                 .thenReturn(AppOpsManager.MODE_IGNORED);
@@ -1100,15 +1498,21 @@
                 .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
 
         mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
-        placeCallTestHelper(handle, extras, false);
+        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+                /*shouldNonEmergencyBeAllowed*/ false);
     }
 
+    /**
+     * The default dialer is requesting to place a call, however CALL_PHONE is denied to that app,
+     * so non-emergency calls will be denied.
+     */
     @SmallTest
     @Test
     public void testPlaceCallWithNoCallingPermission() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
 
+        // We are assumed to be default dialer in this test, so canCallPhone is always true.
         when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
                 nullable(String.class), nullable(String.class)))
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
@@ -1118,37 +1522,82 @@
                 .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
 
         mTSIBinder.placeCall(handle, extras, DEFAULT_DIALER_PACKAGE, null);
-        placeCallTestHelper(handle, extras, false);
+        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
+                /*shouldNonEmergencyBeAllowed*/ false);
     }
 
+    /**
+     * Ensure the expected handle, extras, and non-emergency call permission checks have been
+     * correctly included in the ACTION_CALL intent as part of the
+     * {@link UserCallIntentProcessor#processIntent} method called during the placeCall procedure.
+     * @param expectedHandle Expected outgoing number handle
+     * @param expectedExtras Expected extras in the ACTION_CALL intent.
+     * @param shouldNonEmergencyBeAllowed true if non-emergency calls should be allowed, false if
+     *                                    permission checks failed for non-emergency.
+     */
     private void placeCallTestHelper(Uri expectedHandle, Bundle expectedExtras,
-            boolean shouldNonEmergencyBeAllowed) {
+            boolean isSelfManagedExpected, boolean shouldNonEmergencyBeAllowed) {
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mUserCallIntentProcessor).processIntent(intentCaptor.capture(), anyString(),
-                eq(shouldNonEmergencyBeAllowed), eq(true));
+                eq(isSelfManagedExpected), eq(shouldNonEmergencyBeAllowed), eq(true));
         Intent capturedIntent = intentCaptor.getValue();
         assertEquals(Intent.ACTION_CALL, capturedIntent.getAction());
         assertEquals(expectedHandle, capturedIntent.getData());
         assertTrue(areBundlesEqual(expectedExtras, capturedIntent.getExtras()));
     }
 
+    /**
+     * Ensure that if the caller was never granted CALL_PHONE (and is not the default dialer), a
+     * SecurityException is thrown.
+     */
     @SmallTest
     @Test
     public void testPlaceCallFailure() throws Exception {
         Uri handle = Uri.parse("tel:6505551234");
         Bundle extras = createSampleExtras();
 
+        // The app is not considered a privileged dialer and does not have the CALL_PHONE
+        // permission.
         doThrow(new SecurityException())
                 .when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
 
         try {
             mTSIBinder.placeCall(handle, extras, "arbitrary_package_name", null);
+            fail("Expected SecurityException because CALL_PHONE was not granted to caller");
         } catch (SecurityException e) {
             // expected
         }
 
         verify(mUserCallIntentProcessor, never())
-                .processIntent(any(Intent.class), anyString(), anyBoolean(), eq(true));
+                .processIntent(any(Intent.class), anyString(), eq(false), anyBoolean(), eq(true));
+    }
+
+    /**
+     * Ensure that if the caller was granted CALL_PHONE, but did not get the OP_CALL_PHONE app op
+     * (and is not the default dialer), a SecurityException is thrown.
+     */
+    @SmallTest
+    @Test
+    public void testPlaceCallAppOpFailure() throws Exception {
+        Uri handle = Uri.parse("tel:6505551234");
+        Bundle extras = createSampleExtras();
+
+        // The app is not considered a privileged dialer and does not have the OP_CALL_PHONE
+        // app op.
+        doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
+        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
+                nullable(String.class), nullable(String.class)))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
+
+        try {
+            mTSIBinder.placeCall(handle, extras, "arbitrary_package_name", null);
+            fail("Expected SecurityException because CALL_PHONE was not granted to caller");
+        } catch (SecurityException e) {
+            // expected
+        }
+
+        verify(mUserCallIntentProcessor, never())
+                .processIntent(any(Intent.class), anyString(), eq(false), anyBoolean(), eq(true));
     }
 
     @SmallTest
@@ -1654,6 +2103,8 @@
     }
 
     private static boolean areBundlesEqual(Bundle b1, Bundle b2) {
+        if (b1.keySet().size() != b2.keySet().size()) return false;
+
         for (String key1 : b1.keySet()) {
             if (!b1.get(key1).equals(b2.get(key1))) {
                 return false;
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 7e7235d..d013fae 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -66,7 +66,6 @@
 import android.telecom.VideoProfile;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyRegistryManager;
-import android.text.TextUtils;
 
 import com.android.internal.telecom.IInCallAdapter;
 import com.android.server.telecom.AsyncRingtonePlayer;
@@ -97,6 +96,7 @@
 import com.android.server.telecom.Timeouts;
 import com.android.server.telecom.WiredHeadsetManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.callfiltering.BlockedNumbersAdapter;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 
@@ -113,6 +113,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -211,6 +212,8 @@
     @Mock DeviceIdleControllerAdapter mDeviceIdleControllerAdapter;
 
     @Mock Ringer.AccessibilityManagerAdapter mAccessibilityManagerAdapter;
+    @Mock
+    BlockedNumbersAdapter mBlockedNumbersAdapter;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -391,6 +394,7 @@
                 handlerThread.quitSafely();
             }
             handlerThreads.clear();
+            mTelecomSystem.getCallsManager().getVoipCallMonitor().stopMonitor();
         }
         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
         waitForHandlerAction(mHandlerThread.getThreadHandler(), TEST_TIMEOUT);
@@ -512,7 +516,8 @@
                             WiredHeadsetManager wiredHeadsetManager,
                             StatusBarNotifier statusBarNotifier,
                             CallAudioManager.AudioServiceFactory audioServiceFactory,
-                            int earpieceControl) {
+                            int earpieceControl,
+                            Executor asyncTaskExecutor) {
                         return new CallAudioRouteStateMachine(context,
                                 callsManager,
                                 bluetoothManager,
@@ -521,7 +526,8 @@
                                 audioServiceFactory,
                                 // Force enable an earpiece for the end-to-end tests
                                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
-                                mHandlerThread.getLooper());
+                                mHandlerThread.getLooper(),
+                                Runnable::run /* async tasks as now sync for testing! */);
                     }
                 },
                 new CallAudioModeStateMachine.Factory() {
@@ -540,7 +546,9 @@
                             ContactsAsyncHelper.ContentResolverAdapter adapter) {
                         return new ContactsAsyncHelper(adapter, mHandlerThread.getLooper());
                     }
-                }, mDeviceIdleControllerAdapter, mAccessibilityManagerAdapter);
+                }, mDeviceIdleControllerAdapter, mAccessibilityManagerAdapter,
+                Runnable::run,
+                mBlockedNumbersAdapter);
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),
@@ -757,7 +765,7 @@
         final UserHandle userHandle = initiatingUser;
         Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
         new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
-                actionCallIntent, null, true /* hasCallAppOp*/, false /* isLocal */);
+                actionCallIntent, null, false, true /* hasCallAppOp*/, false /* isLocal */);
         // Wait for handler to start CallerInfo lookup.
         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
         // Send the CallerInfo lookup reply.
diff --git a/tests/src/com/android/server/telecom/tests/TransactionTests.java b/tests/src/com/android/server/telecom/tests/TransactionTests.java
index 1e6734b..3fc87a9 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionTests.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionTests.java
@@ -47,13 +47,12 @@
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.ui.ToastFactory;
-import com.android.server.telecom.voip.AnswerCallTransaction;
 import com.android.server.telecom.voip.EndCallTransaction;
 import com.android.server.telecom.voip.HoldCallTransaction;
 import com.android.server.telecom.voip.IncomingCallTransaction;
 import com.android.server.telecom.voip.OutgoingCallTransaction;
-import com.android.server.telecom.voip.HoldActiveCallForNewCallTransaction;
-import com.android.server.telecom.voip.RequestFocusTransaction;
+import com.android.server.telecom.voip.MaybeHoldCallForNewCallTransaction;
+import com.android.server.telecom.voip.RequestNewActiveCallTransaction;
 
 import org.junit.After;
 import org.junit.Before;
@@ -143,40 +142,56 @@
     }
 
     @Test
-    public void testTransactionalRequestFocus() throws Exception {
+    public void testRequestNewCallFocusWithDialingCall() throws Exception {
         // GIVEN
-        RequestFocusTransaction transaction =
-                new RequestFocusTransaction(mCallsManager, mMockCall1);
+        RequestNewActiveCallTransaction transaction =
+                new RequestNewActiveCallTransaction(mCallsManager, mMockCall1);
 
         // WHEN
+        when(mMockCall1.getState()).thenReturn(CallState.DIALING);
         transaction.processTransaction(null);
 
         // THEN
         verify(mCallsManager, times(1))
-                .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ACTIVE),
-                        isA(OutcomeReceiver.class));
+                .requestNewCallFocusAndVerify(eq(mMockCall1), isA(OutcomeReceiver.class));
     }
 
     @Test
-    public void testAnswerCallTransaction() throws Exception {
+    public void testRequestNewCallFocusWithRingingCall() throws Exception {
         // GIVEN
-        AnswerCallTransaction transaction =
-                new AnswerCallTransaction(mCallsManager, mMockCall1, 0);
+        RequestNewActiveCallTransaction transaction =
+                new RequestNewActiveCallTransaction(mCallsManager, mMockCall1);
 
         // WHEN
+        when(mMockCall1.getState()).thenReturn(CallState.RINGING);
         transaction.processTransaction(null);
 
         // THEN
         verify(mCallsManager, times(1))
-                .transactionRequestNewFocusCall(eq(mMockCall1), eq(CallState.ANSWERED),
-                        isA(OutcomeReceiver.class));
+                .requestNewCallFocusAndVerify(eq(mMockCall1), isA(OutcomeReceiver.class));
+    }
+
+    @Test
+    public void testRequestNewCallFocusFailure() throws Exception {
+        // GIVEN
+        RequestNewActiveCallTransaction transaction =
+                new RequestNewActiveCallTransaction(mCallsManager, mMockCall1);
+
+        // WHEN
+        when(mMockCall1.getState()).thenReturn(CallState.DISCONNECTING);
+        when(mCallsManager.getActiveCall()).thenReturn(null);
+        transaction.processTransaction(null);
+
+        // THEN
+        verify(mCallsManager, times(0))
+                .requestNewCallFocusAndVerify( eq(mMockCall1), isA(OutcomeReceiver.class));
     }
 
     @Test
     public void testTransactionalHoldActiveCallForNewCall() throws Exception {
         // GIVEN
-        HoldActiveCallForNewCallTransaction transaction =
-                new HoldActiveCallForNewCallTransaction(mCallsManager, mMockCall1);
+        MaybeHoldCallForNewCallTransaction transaction =
+                new MaybeHoldCallForNewCallTransaction(mCallsManager, mMockCall1);
 
         // WHEN
         transaction.processTransaction(null);
diff --git a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
index ae6e15f..98624d4 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
@@ -35,6 +35,7 @@
 import com.android.internal.telecom.ICallEventCallback;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TransactionalServiceRepository;
 import com.android.server.telecom.TransactionalServiceWrapper;
 import com.android.server.telecom.voip.EndCallTransaction;
@@ -68,6 +69,7 @@
     @Mock private TransactionManager mTransactionManager;
     @Mock private ICallEventCallback mCallEventCallback;
     @Mock private TransactionalServiceRepository mRepository;
+    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
 
     @Override
     @Before
@@ -76,6 +78,7 @@
         MockitoAnnotations.initMocks(this);
         Mockito.when(mMockCall1.getId()).thenReturn(CALL_ID_1);
         Mockito.when(mMockCall2.getId()).thenReturn(CALL_ID_2);
+        Mockito.when(mCallsManager.getLock()).thenReturn(mLock);
 
         mTransactionalServiceWrapper = new TransactionalServiceWrapper(mCallEventCallback,
                 mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository);
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
new file mode 100644
index 0000000..346b3d8
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/VoipCallMonitorTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.telecom.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.app.ForegroundServiceDelegationOptions;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.telecom.PhoneAccountHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.telecom.Call;
+import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.voip.VoipCallMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+@RunWith(JUnit4.class)
+public class VoipCallMonitorTest extends TelecomTestCase {
+    private VoipCallMonitor mMonitor;
+    private static final String PKG_NAME_1 = "telecom.voip.test1";
+    private static final String PKG_NAME_2 = "telecom.voip.test2";
+    private static final String CLS_NAME = "VoipActivity";
+    private static final String ID_1 = "id1";
+    private static final UserHandle USER_HANDLE_1 = new UserHandle(1);
+    private static final long TIMEOUT = 5000L;
+
+    @Mock private TelecomSystem.SyncRoot mLock;
+    @Mock private ActivityManagerInternal mActivityManagerInternal;
+    @Mock private NotificationListenerService mListenerService;
+
+    private final PhoneAccountHandle mHandle1User1 = new PhoneAccountHandle(
+            new ComponentName(PKG_NAME_1, CLS_NAME), ID_1, USER_HANDLE_1);
+    private final PhoneAccountHandle mHandle2User1 = new PhoneAccountHandle(
+            new ComponentName(PKG_NAME_2, CLS_NAME), ID_1, USER_HANDLE_1);
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mMonitor = new VoipCallMonitor(mContext, mLock);
+        mActivityManagerInternal = mock(ActivityManagerInternal.class);
+        mListenerService = mock(NotificationListenerService.class);
+        mMonitor.setActivityManagerInternal(mActivityManagerInternal);
+        mMonitor.setNotificationListenerService(mListenerService);
+        doNothing().when(mListenerService).registerAsSystemService(eq(mContext),
+                any(ComponentName.class), anyInt());
+        mMonitor.startMonitor();
+    }
+
+    @SmallTest
+    @Test
+    public void testStartMonitorForOneCall() {
+        Call call = createTestCall("testCall", mHandle1User1);
+        IBinder service = mock(IBinder.class);
+
+        ArgumentCaptor<ServiceConnection> captor = ArgumentCaptor.forClass(ServiceConnection.class);
+        mMonitor.onCallAdded(call);
+        verify(mActivityManagerInternal, timeout(TIMEOUT)).startForegroundServiceDelegate(any(
+                ForegroundServiceDelegationOptions.class), captor.capture());
+        ServiceConnection conn = captor.getValue();
+        conn.onServiceConnected(mHandle1User1.getComponentName(), service);
+
+        mMonitor.onCallRemoved(call);
+        verify(mActivityManagerInternal, timeout(TIMEOUT)).stopForegroundServiceDelegate(eq(conn));
+    }
+
+    @SmallTest
+    @Test
+    public void testMonitorForTwoCallsOnSameHandle() {
+        Call call1 = createTestCall("testCall1", mHandle1User1);
+        Call call2 = createTestCall("testCall2", mHandle1User1);
+        IBinder service = mock(IBinder.class);
+
+        ArgumentCaptor<ServiceConnection> captor1 =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        mMonitor.onCallAdded(call1);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(1))
+                .startForegroundServiceDelegate(any(ForegroundServiceDelegationOptions.class),
+                        captor1.capture());
+        ServiceConnection conn1 = captor1.getValue();
+        conn1.onServiceConnected(mHandle1User1.getComponentName(), service);
+
+        ArgumentCaptor<ServiceConnection> captor2 =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        mMonitor.onCallAdded(call2);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(2))
+                .startForegroundServiceDelegate(any(ForegroundServiceDelegationOptions.class),
+                        captor2.capture());
+        ServiceConnection conn2 = captor2.getValue();
+        conn2.onServiceConnected(mHandle1User1.getComponentName(), service);
+
+        mMonitor.onCallRemoved(call1);
+        verify(mActivityManagerInternal, never()).stopForegroundServiceDelegate(
+                any(ServiceConnection.class));
+        mMonitor.onCallRemoved(call2);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(1))
+                .stopForegroundServiceDelegate(eq(conn2));
+    }
+
+    @SmallTest
+    @Test
+    public void testMonitorForTwoCallsOnDifferentHandle() {
+        Call call1 = createTestCall("testCall1", mHandle1User1);
+        Call call2 = createTestCall("testCall2", mHandle2User1);
+        IBinder service = mock(IBinder.class);
+
+        ArgumentCaptor<ServiceConnection> connCaptor1 = ArgumentCaptor.forClass(
+                ServiceConnection.class);
+        ArgumentCaptor<ForegroundServiceDelegationOptions> optionsCaptor1 =
+                ArgumentCaptor.forClass(ForegroundServiceDelegationOptions.class);
+        mMonitor.onCallAdded(call1);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(1))
+                .startForegroundServiceDelegate(optionsCaptor1.capture(), connCaptor1.capture());
+        ForegroundServiceDelegationOptions options1 = optionsCaptor1.getValue();
+        ServiceConnection conn1 = connCaptor1.getValue();
+        conn1.onServiceConnected(mHandle1User1.getComponentName(), service);
+        assertEquals(PKG_NAME_1, options1.getComponentName().getPackageName());
+
+        ArgumentCaptor<ServiceConnection> connCaptor2 = ArgumentCaptor.forClass(
+                ServiceConnection.class);
+        ArgumentCaptor<ForegroundServiceDelegationOptions> optionsCaptor2 =
+                ArgumentCaptor.forClass(ForegroundServiceDelegationOptions.class);
+        mMonitor.onCallAdded(call2);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(2))
+                .startForegroundServiceDelegate(optionsCaptor2.capture(), connCaptor2.capture());
+        ForegroundServiceDelegationOptions options2 = optionsCaptor2.getValue();
+        ServiceConnection conn2 = connCaptor2.getValue();
+        conn2.onServiceConnected(mHandle2User1.getComponentName(), service);
+        assertEquals(PKG_NAME_2, options2.getComponentName().getPackageName());
+
+        mMonitor.onCallRemoved(call2);
+        verify(mActivityManagerInternal).stopForegroundServiceDelegate(eq(conn2));
+        mMonitor.onCallRemoved(call1);
+        verify(mActivityManagerInternal).stopForegroundServiceDelegate(eq(conn1));
+    }
+
+    @SmallTest
+    @Test
+    public void testStopDelegation() {
+        Call call1 = createTestCall("testCall1", mHandle1User1);
+        Call call2 = createTestCall("testCall2", mHandle1User1);
+        IBinder service = mock(IBinder.class);
+
+        ArgumentCaptor<ServiceConnection> captor1 =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        mMonitor.onCallAdded(call1);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(1))
+                .startForegroundServiceDelegate(any(ForegroundServiceDelegationOptions.class),
+                        captor1.capture());
+        ServiceConnection conn1 = captor1.getValue();
+        conn1.onServiceConnected(mHandle1User1.getComponentName(), service);
+
+        ArgumentCaptor<ServiceConnection> captor2 =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        mMonitor.onCallAdded(call2);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(2))
+                .startForegroundServiceDelegate(any(ForegroundServiceDelegationOptions.class),
+                        captor2.capture());
+        ServiceConnection conn2 = captor2.getValue();
+        conn2.onServiceConnected(mHandle1User1.getComponentName(), service);
+
+        mMonitor.stopFGSDelegation(mHandle1User1);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(1))
+                .stopForegroundServiceDelegate(eq(conn2));
+        conn2.onServiceDisconnected(mHandle1User1.getComponentName());
+        mMonitor.onCallRemoved(call1);
+        verify(mActivityManagerInternal, timeout(TIMEOUT).times(1))
+                .stopForegroundServiceDelegate(any(ServiceConnection.class));
+    }
+
+    private Call createTestCall(String id, PhoneAccountHandle handle) {
+        Call call = mock(Call.class);
+        when(call.getTargetPhoneAccount()).thenReturn(handle);
+        when(call.isTransactionalCall()).thenReturn(true);
+        when(call.getExtras()).thenReturn(new Bundle());
+        when(call.getId()).thenReturn(id);
+        when(call.getCallingPackageIdentity()).thenReturn( new Call.CallingPackageIdentity() );
+        return call;
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
index 58e4a77..e2c7b7b 100644
--- a/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
+++ b/tests/src/com/android/server/telecom/tests/VoipCallTransactionTest.java
@@ -25,6 +25,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.voip.ParallelTransaction;
 import com.android.server.telecom.voip.SerialTransaction;
 import com.android.server.telecom.voip.TransactionManager;
@@ -49,6 +50,7 @@
 public class VoipCallTransactionTest extends TelecomTestCase {
     private StringBuilder mLog;
     private TransactionManager mTransactionManager;
+    private static final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
 
     private class TestVoipCallTransaction extends VoipCallTransaction {
         public static final int SUCCESS = 0;
@@ -60,7 +62,7 @@
         private int mType;
 
         public TestVoipCallTransaction(String name, long sleepTime, int type) {
-            super();
+            super(mLock);
             mName = name;
             mSleepTime = sleepTime;
             mType = type;
@@ -125,7 +127,8 @@
         OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
                 resultFuture::complete;
         String expectedLog = "t1 success;\nt2 success;\nt3 success;\n";
-        mTransactionManager.addTransaction(new SerialTransaction(subTransactions), outcomeReceiver);
+        mTransactionManager.addTransaction(new SerialTransaction(subTransactions, mLock),
+                outcomeReceiver);
         assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
                 resultFuture.get(5000L, TimeUnit.MILLISECONDS).getResult());
         assertEquals(expectedLog, mLog.toString());
@@ -158,7 +161,7 @@
                         exceptionFuture.complete(e.getMessage());
                     }
                 };
-        mTransactionManager.addTransaction(new SerialTransaction(subTransactions),
+        mTransactionManager.addTransaction(new SerialTransaction(subTransactions, mLock),
                 outcomeReceiver);
         exceptionFuture.get(5000L, TimeUnit.MILLISECONDS);
         String expectedLog = "t1 success;\nt2 failed;\n";
@@ -182,7 +185,7 @@
         CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
         OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
                 resultFuture::complete;
-        mTransactionManager.addTransaction(new ParallelTransaction(subTransactions),
+        mTransactionManager.addTransaction(new ParallelTransaction(subTransactions, mLock),
                 outcomeReceiver);
         assertEquals(VoipCallTransactionResult.RESULT_SUCCEED,
                 resultFuture.get(5000L, TimeUnit.MILLISECONDS).getResult());
@@ -208,7 +211,7 @@
         subTransactions.add(t3);
         CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
         OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
-                new OutcomeReceiver<VoipCallTransactionResult, CallException>() {
+                new OutcomeReceiver<>() {
             @Override
             public void onResult(VoipCallTransactionResult result) {
 
@@ -219,7 +222,7 @@
                 exceptionFuture.complete(e.getMessage());
             }
         };
-        mTransactionManager.addTransaction(new ParallelTransaction(subTransactions),
+        mTransactionManager.addTransaction(new ParallelTransaction(subTransactions, mLock),
                 outcomeReceiver);
         exceptionFuture.get(5000L, TimeUnit.MILLISECONDS);
         assertTrue(mLog.toString().contains("t2 failed;\n"));
@@ -231,12 +234,20 @@
             throws ExecutionException, InterruptedException, TimeoutException {
         VoipCallTransaction t = new TestVoipCallTransaction("t", 10000L,
                 TestVoipCallTransaction.SUCCESS);
-        CompletableFuture<VoipCallTransactionResult> resultFuture = new CompletableFuture<>();
+        CompletableFuture<String> exceptionFuture = new CompletableFuture<>();
         OutcomeReceiver<VoipCallTransactionResult, CallException> outcomeReceiver =
-                resultFuture::complete;
-        mTransactionManager.addTransaction(t, outcomeReceiver);
-        VoipCallTransactionResult result = resultFuture.get(7000L, TimeUnit.MILLISECONDS);
-        assertEquals(VoipCallTransactionResult.RESULT_FAILED, result.getResult());
-        assertTrue(result.getMessage().contains("timeout"));
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(VoipCallTransactionResult result) {
+
+                    }
+
+                    @Override
+                    public void onError(CallException e) {
+                        exceptionFuture.complete(e.getMessage());
+                    }
+                };        mTransactionManager.addTransaction(t, outcomeReceiver);
+        String message = exceptionFuture.get(7000L, TimeUnit.MILLISECONDS);
+        assertTrue(message.contains("timeout"));
     }
 }