[automerger skipped] Merge 24Q4 (ab/12406339) into aosp-main-future am: bb3d978113 -s ours

am skip reason: Merged-In Ic8c973caac761882481a6b2be319f41497012095 with SHA-1 be3fa44813 is already in history

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

Change-Id: Id03ae03d6b6686dd70c6d5845c83e8f255c658fa
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/assets/CarrierRestrictionOperatorDetails.json b/assets/CarrierRestrictionOperatorDetails.json
index a088f94..b3c8976 100644
--- a/assets/CarrierRestrictionOperatorDetails.json
+++ b/assets/CarrierRestrictionOperatorDetails.json
@@ -2,9 +2,13 @@
   "_comment": "Operator should register with its application package name, carrierId and all the corresponding  SHA256IDs",
   "_comment": "Example format :: << \"packageName\" : {\"carrierId\":[<int>], \"callerSHA256Ids\":[<SHAID1>, <SHAID2>]} >>",
   "com.vzw.hss.myverizon":{"carrierIds":[1839],"callerSHA256Ids":["AE23A03436DF07B0CD70FE881CDA2EC1D21215D7B7B0CC68E67B67F5DF89526A"]},
+  "com.verizon.mips.services":{"carrierIds":[1839],"callerSHA256Ids":["FF82050BF6BED1F152AC1A12DC83CACBAD401775161882872C6665FC5E15C8F2"]},
   "com.google.android.apps.tycho":{"carrierIds":[1989],"callerSHA256Ids":["B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350","4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"]},
   "com.comcast.mobile.mxs":{"carrierIds": [2032,2532,2556],"callerSHA256Ids":["914C26403B57D2D482359FC235CC825AD00D52B0121C18EF2B2B9D4DDA4B8996"]},
   "com.xfinity.digitalhome": {"carrierIds": [2032,2532,2556],"callerSHA256Ids":["31b4c17315c2269040d535f7b6a79cf4d11517c664d9de8f1ddf4f8a785aad47"]},
   "com.xfinity.digitalhome.debug":{"carrierIds": [2032,2532,2556],"callerSHA256Ids":["c9133e8168f97573c8c567f46777dff74ade0c015ecf2c5e91be3e4e76ddcae2"]},
-  "com.xfinity.dh.xm.app": {"carrierIds": [2032,2532,2556],"callerSHA256Ids":["c9133e8168f97573c8c567f46777dff74ade0c015ecf2c5e91be3e4e76ddcae2"]}
+  "com.xfinity.dh.xm.app": {"carrierIds": [2032,2532,2556],"callerSHA256Ids":["c9133e8168f97573c8c567f46777dff74ade0c015ecf2c5e91be3e4e76ddcae2"]},
+  "com.tmobile.tmte": {"carrierIds": [1],"callerSHA256Ids":["3D:1A:4B:EF:6E:E7:AF:7D:34:D1:20:E7:B1:AA:C0:DD:24:55:85:DE:62:37:CF:10:0F:68:33:3A:FA:CF:F5:62"]},
+  "com.tmobile.tuesdays": {"carrierIds": [1],"callerSHA256Ids":["3D:1A:4B:EF:6E:E7:AF:7D:34:D1:20:E7:B1:AA:C0:DD:24:55:85:DE:62:37:CF:10:0F:68:33:3A:FA:CF:F5:62","92:B5:F8:11:7F:BD:9B:D5:73:8F:F1:68:A4:FA:12:CB:E2:84:BE:83:4E:DE:1A:7B:B4:4D:D8:45:5B:A1:59:20"]},
+  "com.tmobile.pr.mytmobile": {"carrierIds": [1],"callerSHA256Ids":["92:B5:F8:11:7F:BD:9B:D5:73:8F:F1:68:A4:FA:12:CB:E2:84:BE:83:4E:DE:1A:7B:B4:4D:D8:45:5B:A1:59:20"]}
 }
\ No newline at end of file
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 226ab18..f553698 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -284,33 +284,33 @@
     <string name="data_enable_summary" msgid="696860063456536557">"Dozvoli korištenje podataka"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"Pažnja"</string>
     <string name="roaming" msgid="1576180772877858949">"Roming"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"Povezivanje na usluge prijenosa podataka u romingu"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"Povezivanje na usluge prijenosa podataka u romingu"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"Povezivanje na usluge prenosa podataka u romingu"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"Povezivanje na usluge prenosa podataka u romingu"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"Roming podataka je isključen. Dodirnite da ga uključite."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"Mogu nastati troškovi za roming. Dodirnite da izmijenite."</string>
-    <string name="roaming_notification_title" msgid="3590348480688047320">"Veza za prijenos podataka na mobilnoj mreži je izgubljena"</string>
+    <string name="roaming_notification_title" msgid="3590348480688047320">"Veza za prenos podataka na mobilnoj mreži je izgubljena"</string>
     <string name="roaming_on_notification_title" msgid="7451473196411559173">"Roming podataka je uključen"</string>
     <string name="roaming_warning" msgid="7855681468067171971">"Može dovesti do značajnih troškova."</string>
     <string name="roaming_check_price_warning" msgid="8212484083990570215">"Raspitajte se kod svog mobilnog operatera za cijene."</string>
     <string name="roaming_alert_title" msgid="5689615818220960940">"Dozvoliti roming podataka?"</string>
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ograničena funkcionalnost SIM-a"</string>
-    <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Pozivi i usluge prijenosa podataka operatera <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blokirane kada koristite broj <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
-    <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Pozivi i usluge prijenosa pod. op. <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blok. kada koristite drugi SIM."</string>
+    <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Pozivi i usluge prenosa podataka operatera <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blokirane kada koristite broj <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
+    <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Pozivi i usluge prenosa pod. op. <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blok. kada koristite drugi SIM."</string>
     <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Zastarjeli SIP računi su pronađeni i uklonjeni"</string>
     <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android platforma više ne podržva SIP pozivanje.\nVaši postojeći SIP računi <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> su uklonjeni.\nPotvrdite zadanu postavku računa za pozivanje."</string>
     <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Idi u postavke"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Prijenos podataka u aplikaciji"</string>
-    <string name="data_usage_template" msgid="6287906680674061783">"Iskorišteno je <xliff:g id="ID_1">%1$s</xliff:g> prijenosa podataka u periodu <xliff:g id="ID_2">%2$s</xliff:g>"</string>
+    <string name="data_usage_template" msgid="6287906680674061783">"Iskorišteno je <xliff:g id="ID_1">%1$s</xliff:g> prenosa podataka u periodu <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Napredno"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"Operater"</string>
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"mobilni operater, esim, sim, euicc, promijeni mobilnog operatera, dodaj mobilnog operatera"</string>
     <string name="carrier_settings_euicc_summary" msgid="2027941166597330117">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> — <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>"</string>
     <string name="mobile_data_settings_title" msgid="7228249980933944101">"Prijenos podataka na mobilnoj mreži"</string>
-    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Pristup prijenosu podataka mobilnom mrežom"</string>
+    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Pristup prenosu podataka mobilnom mrežom"</string>
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Isključiti prijenos podataka na mobilnoj mreži?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Potreban izbor"</string>
-    <string name="sim_change_data_title" msgid="9142726786345906606">"Promijeniti SIM za prijenos podataka?"</string>
-    <string name="sim_change_data_message" msgid="3567358694255933280">"Koristiti SIM karticu <xliff:g id="NEW_SIM">%1$s</xliff:g> umjesto SIM kartice <xliff:g id="OLD_SIM">%2$s</xliff:g> za prijenos podataka na mobilnoj mreži?"</string>
+    <string name="sim_change_data_title" msgid="9142726786345906606">"Promijeniti SIM za prenos podataka?"</string>
+    <string name="sim_change_data_message" msgid="3567358694255933280">"Koristiti SIM karticu <xliff:g id="NEW_SIM">%1$s</xliff:g> umjesto SIM kartice <xliff:g id="OLD_SIM">%2$s</xliff:g> za prenos podataka na mobilnoj mreži?"</string>
     <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Pozivanje putem WiFi-ja"</string>
     <string name="video_calling_settings_title" msgid="342829454913266078">"Video pozivi putem operatera"</string>
     <string name="gsm_umts_options" msgid="4968446771519376808">"GSM/UMTS opcije"</string>
@@ -318,13 +318,13 @@
     <string name="throttle_data_usage" msgid="1944145350660420711">"Korištenje podataka"</string>
     <string name="throttle_current_usage" msgid="7483859109708658613">"Iskorišteni podaci u trenutnom periodu"</string>
     <string name="throttle_time_frame" msgid="1813452485948918791">"Period korištenja podataka"</string>
-    <string name="throttle_rate" msgid="7641913901133634905">"Pravila o brzini prijenosa podataka"</string>
+    <string name="throttle_rate" msgid="7641913901133634905">"Pravila o brzini prenosa podataka"</string>
     <string name="throttle_help" msgid="2624535757028809735">"Saznajte više"</string>
     <string name="throttle_status_subtext" msgid="1110276415078236687">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g> ٪) od <xliff:g id="USED_2">%3$s</xliff:g> maksimuma perioda\nSljedeći period počinje za <xliff:g id="USED_3">%4$d</xliff:g> dan(a) (<xliff:g id="USED_4">%5$s</xliff:g>)"</string>
     <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g> ٪) od <xliff:g id="USED_2">%3$s</xliff:g> maksimuma perioda"</string>
-    <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> ograničenje je prekoračeno \nBrzina prijenosa podataka je smanjena na <xliff:g id="USED_1">%2$d</xliff:g> Kb/s"</string>
+    <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> ograničenje je prekoračeno \nBrzina prenosa podataka je smanjena na <xliff:g id="USED_1">%2$d</xliff:g> Kb/s"</string>
     <string name="throttle_time_frame_subtext" msgid="6462089615392402127">"Proteklo je <xliff:g id="USED_0">%1$d</xliff:g> ٪ ciklusa\nSljedeći period počinje za <xliff:g id="USED_1">%2$d</xliff:g> dan(a) (<xliff:g id="USED_2">%3$s</xliff:g>)"</string>
-    <string name="throttle_rate_subtext" msgid="7221971817325779535">"Brzina prijenosa podataka se smanjuje na <xliff:g id="USED">%1$d</xliff:g> Kb/s ako se prekorači ograničenje korištenja podataka"</string>
+    <string name="throttle_rate_subtext" msgid="7221971817325779535">"Brzina prenosa podataka se smanjuje na <xliff:g id="USED">%1$d</xliff:g> Kb/s ako se prekorači ograničenje korištenja podataka"</string>
     <string name="throttle_help_subtext" msgid="2817114897095534807">"Više informacija o pravilima korištenja podataka mobilne mreže vašeg operatera"</string>
     <string name="cell_broadcast_sms" msgid="4053449797289031063">"SMS info servisa"</string>
     <string name="enable_disable_cell_bc_sms" msgid="4759958924031721350">"SMS info servisa"</string>
@@ -553,7 +553,7 @@
     <string name="incall_error_supp_service_switch" msgid="5272822448189448479">"Nije moguće prebacivati pozive."</string>
     <string name="incall_error_supp_service_resume" msgid="1276861499306817035">"Nije moguće nastaviti poziv."</string>
     <string name="incall_error_supp_service_separate" msgid="8932660028965274353">"Nije moguće odvojiti poziv."</string>
-    <string name="incall_error_supp_service_transfer" msgid="8211925891867334323">"Prijenos nije moguć."</string>
+    <string name="incall_error_supp_service_transfer" msgid="8211925891867334323">"Prenos nije moguć."</string>
     <string name="incall_error_supp_service_conference" msgid="27578082433544702">"Nije moguće spajati pozive."</string>
     <string name="incall_error_supp_service_reject" msgid="3044363092441655912">"Nije moguće odbiti poziv."</string>
     <string name="incall_error_supp_service_hangup" msgid="836524952243836735">"Nije moguće uputiti poziv(e)."</string>
@@ -621,7 +621,7 @@
     <string name="ota_title_activate" msgid="4049645324841263423">"Aktivirajte svoj telefon"</string>
     <string name="ota_touch_activate" msgid="838764494319694754">"Za aktiviranje telefonske usluge potrebno je uputiti poseban poziv. \n\nNakon što pritisnete „Aktiviraj“, poslušajte uputstva za aktiviranje telefona."</string>
     <string name="ota_hfa_activation_title" msgid="3300556778212729671">"Aktivacija u toku..."</string>
-    <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"Telefon aktivira uslugu prijenosa mobilnih podataka.\n\nTo može potrajati do 5 minuta."</string>
+    <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"Telefon aktivira uslugu prenosa mobilnih podataka.\n\nTo može potrajati do 5 minuta."</string>
     <string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"Preskočiti aktivaciju?"</string>
     <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"Ako preskočite aktivaciju, nećete moći upućivati pozive niti se povezati na mobilne podatkovne mreže (iako se možete povezati s WiFi mrežama). Dok ne aktivirate telefon, prikazivat će se upit za aktivaciju svaki put kada upalite telefon."</string>
     <string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"Preskoči"</string>
@@ -642,7 +642,7 @@
     <string name="phone_entered_ecm_text" msgid="8431238297843035842">"Aktiviran način rada za hitni povratni poziv"</string>
     <string name="phone_in_ecm_notification_title" msgid="6825016389926367946">"Način rada za hitni povratni poziv"</string>
     <string name="phone_in_ecm_call_notification_text" msgid="653972232922670335">"Podatkovna veza je onemogućena"</string>
-    <string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"Nema veze za prijenos podataka do <xliff:g id="COMPLETETIME">%s</xliff:g>"</string>
+    <string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"Nema veze za prenos podataka do <xliff:g id="COMPLETETIME">%s</xliff:g>"</string>
     <!-- format error in translation for alert_dialog_exit_ecm (7661603870224398025) -->
     <!-- format error in translation for alert_dialog_not_avaialble_in_ecm (8717711120099503279) -->
     <string name="alert_dialog_in_ecm_call" msgid="1207545603149771978">"Odabrana radnja nije dostupna tokom hitnog poziva."</string>
@@ -707,10 +707,10 @@
     <string name="mobile_data_status_roaming_with_plan_subtext" msgid="2576177169108123095">"Trenutno u romingu, plan za podatke je aktivan"</string>
     <string name="mobile_data_status_no_plan_subtext" msgid="170331026419263657">"Nema preostalih mobilnih podataka"</string>
     <string name="mobile_data_activate_prepaid" msgid="4276738964416795596">"Nema preostalih mobilnih podataka"</string>
-    <string name="mobile_data_activate_prepaid_summary" msgid="6846085278531605925">"Dodajte podatke za prijenos na mobilnoj mreži putem operatera <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
+    <string name="mobile_data_activate_prepaid_summary" msgid="6846085278531605925">"Dodajte podatke za prenos na mobilnoj mreži putem operatera <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
     <string name="mobile_data_activate_roaming_plan" msgid="922290995866269366">"Nema plana za roming"</string>
     <string name="mobile_data_activate_roaming_plan_summary" msgid="5379228493306235969">"Dodajte plan za roming pomoću pružaoca usluga <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
-    <string name="mobile_data_activate_footer" msgid="7895874069807204548">"Možete dodati podatke za prijenos na mobilnoj mreži ili plan za roming pomoću operatera, <xliff:g id="PROVIDER_NAME">%s</xliff:g>."</string>
+    <string name="mobile_data_activate_footer" msgid="7895874069807204548">"Možete dodati podatke za prenos na mobilnoj mreži ili plan za roming pomoću operatera, <xliff:g id="PROVIDER_NAME">%s</xliff:g>."</string>
     <string name="mobile_data_activate_diag_title" msgid="5401741936224757312">"Dodati podatke?"</string>
     <string name="mobile_data_activate_diag_message" msgid="3527260988020415441">"Možda ćete morati dodati podatke preko pružaoca usluga <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
     <string name="mobile_data_activate_button" msgid="1139792516354374612">"DODAJTE PODATKE"</string>
@@ -830,8 +830,8 @@
     <string name="supp_service_over_ut_precautions_dual_sim" msgid="5166866975550910474">"Za korištenje usluge <xliff:g id="SUPP_SERVICE">%1$s</xliff:g>, provjerite je li za SIM <xliff:g id="SIM_NUMBER">%2$d</xliff:g> uključen prijenos podataka na mobilnoj mreži. Ovo možete promijeniti u postavkama mobilne mreže."</string>
     <string name="supp_service_over_ut_precautions_roaming_dual_sim" msgid="6627654855191817965">"Za korištenje usluge <xliff:g id="SUPP_SERVICE">%1$s</xliff:g>, provjerite jesu li za SIM <xliff:g id="SIM_NUMBER">%2$d</xliff:g> uključeni prijenos podataka na mobilnoj mreži i roming podataka. Ovo možete promijeniti u postavkama mobilne mreže."</string>
     <string name="supp_service_over_ut_precautions_dialog_dismiss" msgid="5934541487903081652">"Odbaci"</string>
-    <string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Omogućite vezu za prijenos podataka"</string>
-    <string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Onemogući vezu za prijenos podataka"</string>
+    <string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Omogućite vezu za prenos podataka"</string>
+    <string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Onemogući vezu za prenos podataka"</string>
     <string name="volte_provisioned_switch_string" msgid="4812874990480336178">"VoLTE omogućen"</string>
     <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Video pozivi su omogućeni"</string>
     <string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"WiFi poziv obezbijeđen"</string>
@@ -882,7 +882,7 @@
     <string name="radioInfo_lac" msgid="3892986460272607013">"LAC"</string>
     <string name="radioInfo_cid" msgid="1423185536264406705">"CID"</string>
     <string name="radio_info_subid" msgid="6839966868621703203">"Trenutni pomoćni ID:"</string>
-    <string name="radio_info_dds" msgid="1122593144425697126">"Pomoćni ID za zadani SIM za prijenos podataka:"</string>
+    <string name="radio_info_dds" msgid="1122593144425697126">"Pomoćni ID za zadani SIM za prenos podataka:"</string>
     <string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL propusnost (kbps):"</string>
     <string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL propusnost (kbps):"</string>
     <string name="radio_info_phy_chan_config" msgid="608045501232211303">"Konfiguracije fizičkih kanala:"</string>
@@ -902,7 +902,7 @@
     <string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Poruka na čekanju:"</string>
     <string name="radio_info_phone_number_label" msgid="2533852539562512203">"Broj telefona:"</string>
     <string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Vrsta glasovne mreže:"</string>
-    <string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Vrsta mreže za prijenos podataka:"</string>
+    <string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Vrsta mreže za prenos podataka:"</string>
     <string name="radio_info_override_network_type_label" msgid="4176280017221092005">"Zaobilaženje vrste mreže:"</string>
     <string name="radio_info_voice_raw_registration_state_label" msgid="2822988327145825128">"Stanje registracije sirovog glasa:"</string>
     <string name="radio_info_data_raw_registration_state_label" msgid="2895895513822604539">"Stanje registracije sirovih podataka:"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index f4c5593..c6e4796 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -680,7 +680,7 @@
     <string name="accessibility_settings_activity_title" msgid="7883415189273700298">"Accessibilitat"</string>
     <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"Trucada per Wi-Fi de"</string>
     <string name="status_hint_label_wifi_call" msgid="942993035689809853">"Trucada per Wi-Fi"</string>
-    <string name="message_decode_error" msgid="1061856591500290887">"S\'ha produït un error en descodificar el missatge."</string>
+    <string name="message_decode_error" msgid="1061856591500290887">"Hi ha hagut un error en descodificar el missatge."</string>
     <string name="callFailed_cdma_activation" msgid="5392057031552253550">"Una targeta SIM ha activat el servei, i s\'ha actualitzat la funció d\'itinerància del telèfon."</string>
     <string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"Hi ha massa trucades actives. Finalitza\'n alguna o combina-les abans de fer-ne una de nova."</string>
     <string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"No es pot establir la connexió. Insereix una targeta SIM vàlida."</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 452df67..1bd3ed4 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -297,7 +297,7 @@
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Baliteke <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bidezko deiak eta datu-zerbitzuak blokeatuta egotea <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> telefono-zenbakia erabiltzean."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Baliteke <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bidezko deiak eta datu-zerbitzuak blokeatuta egotea beste SIM txartel bat erabiltzean."</string>
     <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"SIP-eko kontu zaharkituak aurkitu eta kendu dira"</string>
-    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP bidezko deiak jadanik ez dira bateragarriak Android-en plataformarekin.\nZeneuzkan SIP-eko kontuak (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) kendu egin dira.\nBerretsi deietarako ezarri duzun kontu lehenetsia."</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP bidezko deiak jadanik ez dira onartzen Android-en plataforman.\nZeneuzkan SIP-eko kontuak (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) kendu egin dira.\nBerretsi deietarako ezarri duzun kontu lehenetsia."</string>
     <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Joan ezarpenetara"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Aplikazioak erabilitako datuak"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Datuen <xliff:g id="ID_1">%1$s</xliff:g> erabili dira data hauen artean: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index b62728f..ff32473 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -582,7 +582,7 @@
     <string name="description_concat_format" msgid="2014471565101724088">"%1$s, %2$s"</string>
     <string name="dialerKeyboardHintText" msgid="1115266533703764049">"Utilisez le clavier pour composer un numéro."</string>
     <string name="onscreenHoldText" msgid="4025348842151665191">"Attente"</string>
-    <string name="onscreenEndCallText" msgid="6138725377654842757">"Terminé"</string>
+    <string name="onscreenEndCallText" msgid="6138725377654842757">"Terminer"</string>
     <string name="onscreenShowDialpadText" msgid="658465753816164079">"Clavier numérique"</string>
     <string name="onscreenMuteText" msgid="5470306116733843621">"Couper le son"</string>
     <string name="onscreenAddCallText" msgid="9075675082903611677">"Autre appel"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f20960c..22a69bb 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -582,7 +582,7 @@
     <string name="description_concat_format" msgid="2014471565101724088">"%1$s, %2$s"</string>
     <string name="dialerKeyboardHintText" msgid="1115266533703764049">"Utilisez le clavier pour composer un numéro."</string>
     <string name="onscreenHoldText" msgid="4025348842151665191">"En attente"</string>
-    <string name="onscreenEndCallText" msgid="6138725377654842757">"Raccrocher"</string>
+    <string name="onscreenEndCallText" msgid="6138725377654842757">"Terminer"</string>
     <string name="onscreenShowDialpadText" msgid="658465753816164079">"Clavier"</string>
     <string name="onscreenMuteText" msgid="5470306116733843621">"Silencieux"</string>
     <string name="onscreenAddCallText" msgid="9075675082903611677">"Autre appel"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index efb22b1..af0b7f1 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -193,7 +193,7 @@
     <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"Preferensi jenis jaringan"</string>
     <string name="forbidden_network" msgid="5081729819561333023">"(terlarang)"</string>
     <string name="choose_network_title" msgid="5335832663422653082">"Pilih jaringan"</string>
-    <string name="network_disconnected" msgid="8844141106841160825">"Terputus"</string>
+    <string name="network_disconnected" msgid="8844141106841160825">"Tidak terhubung"</string>
     <string name="network_connected" msgid="2760235679963580224">"Terhubung"</string>
     <string name="network_connecting" msgid="160901383582774987">"Menghubungkan..."</string>
     <string name="network_could_not_connect" msgid="6547460848093727998">"Tidak dapat terhubung"</string>
@@ -869,7 +869,7 @@
     <string name="radioInfo_phone_idle" msgid="2191653783170757819">"Tidak ada aktivitas"</string>
     <string name="radioInfo_phone_ringing" msgid="8100354169567413370">"Berdering"</string>
     <string name="radioInfo_phone_offhook" msgid="7564601639749936170">"Panggilan sedang Berlangsung"</string>
-    <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Terputus"</string>
+    <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Tidak terhubung"</string>
     <string name="radioInfo_data_connecting" msgid="925092271092152472">"Menghubungkan"</string>
     <string name="radioInfo_data_connected" msgid="7637335645634239508">"Terhubung"</string>
     <string name="radioInfo_data_suspended" msgid="8695262782642002785">"Ditangguhkan"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ddbec3a..8cb4d1d 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -184,7 +184,7 @@
     <string name="connect_later" msgid="1950138106010005425">"現在このネットワークに接続できません。しばらくしてからもう一度お試しください。"</string>
     <string name="registration_done" msgid="5337407023566953292">"ネットワークに登録されました。"</string>
     <string name="already_auto" msgid="8607068290733079336">"すでに自動選択が適用されています。"</string>
-    <string name="select_automatically" msgid="779750291257872651">"ネットワークを自動的に選択"</string>
+    <string name="select_automatically" msgid="779750291257872651">"ネットワークを自動的に選択する"</string>
     <string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"%1$s に接続中はご利用いただけません"</string>
     <string name="network_select_title" msgid="4117305053881611988">"ネットワーク"</string>
     <string name="register_automatically" msgid="3907580547590554834">"自動登録..."</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 4e74022..a7054f9 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -539,7 +539,7 @@
     <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Мобилдик тармакты күйгүзүңүз, чалуу үчүн \"Учакта\" режимин же \"Батареяны үнөмдөө\" режимин өчүрүңүз."</string>
     <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Чалуу үчүн учак режимин өчүрүңүз."</string>
     <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Чалуу үчүн учак режимин өчүрүңүз же зымсыз тармакка туташыңыз."</string>
-    <string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"Телефон ысып кетти"</b>\n\n"Бул чалуу аяктабай жатат. Телефон муздагандан кийин кайра аракет кылыңыз.\n\nШашылыш чалууларды аткара берсеңиз болот."</string>
+    <string name="incall_error_power_off_thermal" product="default" msgid="8695809601655300168"><b>"Телефон ысып кетти"</b>\n\n"Бул чалуу аяктабай жатат. Телефон муздагандан кийин кайталап көрүңүз.\n\nШашылыш чалууларды аткара берсеңиз болот."</string>
     <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"Кадимки шартта чалуу үчүн шашылыш кайра чалуу режиминен чыгыңыз."</string>
     <string name="incall_error_emergency_only" msgid="8786127461027964653">"Тармакта катталган эмес."</string>
     <string name="incall_error_out_of_service" msgid="1927265196942672791">"Мобилдик тармак жок."</string>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index c6c26b0..3a908d2 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.compat.CompatChanges;
 import android.content.BroadcastReceiver;
@@ -899,8 +900,11 @@
             mServiceConnection[phoneId] = serviceConnection;
         }
         try {
-            if (mContext.bindService(carrierService, serviceConnection,
-                    Context.BIND_AUTO_CREATE)) {
+            if (mFeatureFlags.supportCarrierServicesForHsum()
+                    ? mContext.bindServiceAsUser(carrierService, serviceConnection,
+                    Context.BIND_AUTO_CREATE, UserHandle.of(ActivityManager.getCurrentUser()))
+                    : mContext.bindService(carrierService, serviceConnection,
+                            Context.BIND_AUTO_CREATE)) {
                 if (eventId == EVENT_CONNECTED_TO_DEFAULT_FOR_NO_SIM_CONFIG) {
                     mServiceBoundForNoSimConfig[phoneId] = true;
                 } else {
@@ -1261,7 +1265,10 @@
     @Nullable
     private String getPackageVersion(@NonNull String packageName) {
         try {
-            PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
+            PackageInfo info = mFeatureFlags.supportCarrierServicesForHsum()
+                    ? mContext.getPackageManager().getPackageInfoAsUser(packageName, 0,
+                    ActivityManager.getCurrentUser())
+                    : mContext.getPackageManager().getPackageInfo(packageName, 0);
             return Long.toString(info.getLongVersionCode());
         } catch (PackageManager.NameNotFoundException e) {
             return null;
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 6901789..6918d48 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -252,7 +252,8 @@
                                     if (DomainSelectionResolver.getInstance()
                                             .isDomainSelectionSupported()) {
                                         EmergencyStateTracker.getInstance()
-                                                .exitEmergencyCallbackMode();
+                                                .exitEmergencyCallbackMode(
+                                                        TelephonyManager.STOP_REASON_USER_ACTION);
                                     } else {
                                         mPhone.exitEmergencyCallbackMode();
                                     }
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 3c7b321..4fb96a2 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -878,7 +878,9 @@
                             + (isManualSelection ? selectedNetworkOperatorName : ""));
                 }
 
-                if (isManualSelection) {
+                if (isManualSelection
+                        && isSubscriptionVisibleToUser(
+                              mSubscriptionManager.getActiveSubscriptionInfo(subId))) {
                     mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
                     shouldShowNotification(serviceState, subId);
                 } else {
@@ -934,7 +936,9 @@
                             + (isManualSelection ? selectedNetworkOperatorName : ""));
                 }
 
-                if (isManualSelection) {
+                if (isManualSelection
+                        && isSubscriptionVisibleToUser(
+                              mSubscriptionManager.getActiveSubscriptionInfo(subId))) {
                     mSelectedNetworkOperatorName.put(subId, selectedNetworkOperatorName);
                     shouldShowNotification(serviceState, subId);
                 } else {
@@ -949,6 +953,12 @@
         }
     }
 
+    // TODO(b/261916533) This should be handled by SubscriptionManager#isSubscriptionVisible(),
+    // but that method doesn't support system callers, so here we are.
+    private boolean isSubscriptionVisibleToUser(SubscriptionInfo subInfo) {
+        return subInfo != null && (!subInfo.isOpportunistic() || subInfo.getGroupUuid() == null);
+    }
+
     private void dismissNetworkSelectionNotification(int subId) {
         if (mSelectedUnavailableNotify.get(subId, false)) {
             cancelNetworkSelection(subId);
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 0433a33..dcddf2b 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -44,6 +44,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -373,12 +374,16 @@
                     break;
 
                 case EVENT_DATA_ROAMING_DISCONNECTED:
+                    Log.d(LOG_TAG, "EVENT_DATA_ROAMING_DISCONNECTED");
                     if (SubscriptionManagerService.getInstance()
                             .isEsimBootStrapProvisioningActiveForSubId(msg.arg1)) {
                         Log.i(LOG_TAG,
                                 "skip notification/warnings during esim bootstrap activation");
+                    } else if (skipDataRoamingDisconnectedNotificationInSatelliteMode((msg.arg1))) {
+                        Log.i(LOG_TAG, "skip data roaming disconnected notification when device is "
+                                + "connected to satellite network that does not support data.");
                     } else {
-                        notificationMgr.showDataRoamingNotification(msg.arg1, false);
+                        notificationMgr.showDataRoamingNotification((msg.arg1), false);
                     }
                     break;
 
@@ -566,7 +571,8 @@
                 // Initialize EmergencyStateTracker if domain selection is supported
                 boolean isSuplDdsSwitchRequiredForEmergencyCall = getResources()
                         .getBoolean(R.bool.config_gnss_supl_requires_default_data_for_emergency);
-                EmergencyStateTracker.make(this, isSuplDdsSwitchRequiredForEmergencyCall);
+                EmergencyStateTracker.make(this, isSuplDdsSwitchRequiredForEmergencyCall,
+                        mFeatureFlags);
                 DynamicRoutingController.getInstance().initialize(this);
             }
 
@@ -1495,4 +1501,32 @@
         }
         pw.println("------- End PhoneGlobals -------");
     }
+
+    private boolean skipDataRoamingDisconnectedNotificationInSatelliteMode(int subId) {
+        SatelliteController satelliteController = SatelliteController.getInstance();
+        if (satelliteController.isSatelliteEnabledOrBeingEnabled()) {
+            Log.d(LOG_TAG, "skipDataRoamingDisconnected - skip notification as "
+                    + "satellite is enabled or being enabled");
+            return true;
+        }
+
+        int phoneId = SubscriptionManager.getPhoneId(subId);
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        ServiceState serviceState = phone.getServiceState();
+        if (serviceState != null && serviceState.isUsingNonTerrestrialNetwork()) {
+            Log.d(LOG_TAG, "skipDataRoamingDisconnected - isUsingNtn");
+            List<Integer> capabilities =
+                    satelliteController.getCapabilitiesForCarrierRoamingSatelliteMode(phone);
+            if (!capabilities.contains(NetworkRegistrationInfo.SERVICE_TYPE_DATA)) {
+                // Skip data roaming disconnected notification as device is connected to
+                // non-terrestrial network that does not support data.
+                Log.d(LOG_TAG, "skipDataRoamingDisconnected - skip notification as "
+                        + "NTN does not support data");
+                return true;
+            }
+        }
+
+        Log.d(LOG_TAG, "skipDataRoamingDisconnected - do not skip notification.");
+        return false;
+    }
 }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index a997f01..440c39d 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -158,6 +158,7 @@
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
 import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.ISatelliteDisallowedReasonsCallback;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteSupportedStateCallback;
@@ -442,6 +443,9 @@
     private PackageManager mPackageManager;
     private final int mVendorApiLevel;
 
+    @Nullable
+    private ComponentName mTestEuiccUiComponent;
+
     /** User Activity */
     private final AtomicBoolean mNotifyUserActivity;
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
@@ -475,7 +479,7 @@
     public static final String RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED =
             "reset_network_erase_modem_config_enabled";
 
-    private static final int SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS = 2000; // 2 seconds
+    private static final int BLOCKING_REQUEST_DEFAULT_TIMEOUT_MS = 2000; // 2 seconds
 
     private static final int MODEM_ACTIVITY_TIME_OFFSET_CORRECTION_MS = 50;
 
@@ -3676,9 +3680,6 @@
             return null;
         }
 
-        enforceTelephonyFeatureWithException(callingPackage,
-                PackageManager.FEATURE_TELEPHONY_GSM, "getImeiForSlot");
-
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getImei();
@@ -3695,9 +3696,6 @@
             throw new SecurityException("Caller does not have permission");
         }
 
-        enforceTelephonyFeatureWithException(callingPackage,
-                PackageManager.FEATURE_TELEPHONY_GSM, "getPrimaryImei");
-
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone : PhoneFactory.getPhones()) {
@@ -3713,9 +3711,6 @@
 
     @Override
     public String getTypeAllocationCodeForSlot(int slotIndex) {
-        enforceTelephonyFeatureWithException(getCurrentPackageName(),
-                PackageManager.FEATURE_TELEPHONY_GSM, "getTypeAllocationCodeForSlot");
-
         Phone phone = PhoneFactory.getPhone(slotIndex);
         String tac = null;
         if (phone != null) {
@@ -4233,8 +4228,12 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            Boolean success = (Boolean) sendRequest(CMD_SET_VOICEMAIL_NUMBER,
-                    new Pair<String, String>(alphaTag, number), new Integer(subId));
+            Boolean success = (Boolean) sendRequest(
+                    CMD_SET_VOICEMAIL_NUMBER,
+                    new Pair<String, String>(alphaTag, number),
+                    new Integer(subId),
+                    BLOCKING_REQUEST_DEFAULT_TIMEOUT_MS);
+            if (success == null) return false; // most likely due to a timeout
             return success;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -6642,6 +6641,8 @@
      * Sets the ImsService Package Name that Telephony will bind to.
      *
      * @param slotIndex the slot ID that the ImsService should bind for.
+     * @param userId the user ID that the ImsService should bind for or {@link UserHandle#USER_NULL}
+     *               if there is no preference.
      * @param isCarrierService true if the ImsService is the carrier override, false if the
      *         ImsService is the device default ImsService.
      * @param featureTypes An integer array of feature types associated with a packageName.
@@ -6649,7 +6650,7 @@
      *                    with.
      * @return true if setting the ImsService to bind to succeeded, false if it did not.
      */
-    public boolean setBoundImsServiceOverride(int slotIndex, boolean isCarrierService,
+    public boolean setBoundImsServiceOverride(int slotIndex, int userId, boolean isCarrierService,
             int[] featureTypes, String packageName) {
         TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setBoundImsServiceOverride");
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
@@ -6661,12 +6662,8 @@
                 // may happen if the device does not support IMS.
                 return false;
             }
-            Map<Integer, String> featureConfig = new HashMap<>();
-            for (int featureType : featureTypes) {
-                featureConfig.put(featureType, packageName);
-            }
-            return mImsResolver.overrideImsServiceConfiguration(slotIndex, isCarrierService,
-                    featureConfig);
+            return mImsResolver.overrideImsServiceConfiguration(packageName, slotIndex, userId,
+                    isCarrierService, featureTypes);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6804,7 +6801,7 @@
             }
             if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
             sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId,
-                    SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS);
+                    BLOCKING_REQUEST_DEFAULT_TIMEOUT_MS);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -11702,10 +11699,10 @@
     @Override
     public boolean setBoundGbaServiceOverride(int subId, String packageName) {
         enforceModifyPermission();
-
+        int userId = ActivityManager.getCurrentUser();
         final long identity = Binder.clearCallingIdentity();
         try {
-            return getGbaManager(subId).overrideServicePackage(packageName);
+            return getGbaManager(subId).overrideServicePackage(packageName, userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -13202,40 +13199,58 @@
     public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
             boolean isEmergency, @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("requestSatelliteEnabled");
-        if (enableSatellite) {
-            ResultReceiver resultReceiver = new ResultReceiver(mMainThreadHandler) {
-                @Override
-                protected void onReceiveResult(int resultCode, Bundle resultData) {
-                    Log.d(LOG_TAG, "Satellite access restriction resultCode=" + resultCode
-                            + ", resultData=" + resultData);
-                    boolean isAllowed = false;
-                    Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(
-                            callback::accept);
-                    if (resultCode == SATELLITE_RESULT_SUCCESS) {
-                        if (resultData != null
-                                && resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
-                            isAllowed = resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (enableSatellite) {
+                String caller = "PIM:requestSatelliteEnabled";
+                ResultReceiver resultReceiver = new ResultReceiver(mMainThreadHandler) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        Log.d(LOG_TAG, "Satellite access restriction resultCode=" + resultCode
+                                + ", resultData=" + resultData);
+                        mSatelliteController.decrementResultReceiverCount(caller);
+
+                        boolean isAllowed = false;
+                        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(
+                                callback::accept);
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData != null
+                                    && resultData.containsKey(
+                                    KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                                isAllowed = resultData.getBoolean(
+                                        KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                            } else {
+                                loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                            }
                         } else {
-                            loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                            result.accept(resultCode);
+                            return;
                         }
-                    } else {
-                        result.accept(resultCode);
-                        return;
+                        if (isAllowed) {
+                            if (mFeatureFlags.carrierRoamingNbIotNtn()
+                                    && !mSatelliteAccessController.getSatelliteDisallowedReasons()
+                                    .isEmpty()) {
+                                result.accept(SATELLITE_RESULT_ACCESS_BARRED);
+                            } else {
+                                mSatelliteController.requestSatelliteEnabled(
+                                        enableSatellite, enableDemoMode, isEmergency, callback);
+                            }
+                        } else {
+                            result.accept(SATELLITE_RESULT_ACCESS_BARRED);
+                        }
                     }
-                    if (isAllowed) {
-                        mSatelliteController.requestSatelliteEnabled(
-                                enableSatellite, enableDemoMode, isEmergency, callback);
-                    } else {
-                        result.accept(SATELLITE_RESULT_ACCESS_BARRED);
-                    }
-                }
-            };
-            mSatelliteAccessController.requestIsCommunicationAllowedForCurrentLocation(
-                    resultReceiver, true);
-        } else {
-            // No need to check if satellite is allowed at current location when disabling satellite
-            mSatelliteController.requestSatelliteEnabled(
-                    enableSatellite, enableDemoMode, isEmergency, callback);
+                };
+                mSatelliteAccessController.requestIsCommunicationAllowedForCurrentLocation(
+                        resultReceiver, true);
+                mSatelliteController.incrementResultReceiverCount(caller);
+            } else {
+                // No need to check if satellite is allowed at current location when disabling
+                // satellite
+                mSatelliteController.requestSatelliteEnabled(
+                        enableSatellite, enableDemoMode, isEmergency, callback);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -13250,7 +13265,12 @@
     @Override
     public void requestIsSatelliteEnabled(@NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestIsSatelliteEnabled");
-        mSatelliteController.requestIsSatelliteEnabled(result);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestIsSatelliteEnabled(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13264,7 +13284,12 @@
     @Override
     public void requestIsDemoModeEnabled(@NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestIsDemoModeEnabled");
-        mSatelliteController.requestIsDemoModeEnabled(result);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestIsDemoModeEnabled(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13278,7 +13303,12 @@
     @Override
     public void requestIsEmergencyModeEnabled(@NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestIsEmergencyModeEnabled");
-        mSatelliteController.requestIsEmergencyModeEnabled(result);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestIsEmergencyModeEnabled(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13286,10 +13316,18 @@
      *
      * @param result The result receiver that returns whether the satellite service is supported on
      *               the device if the request is successful or an error code if the request failed.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
     public void requestIsSatelliteSupported(@NonNull ResultReceiver result) {
-        mSatelliteController.requestIsSatelliteSupported(result);
+        enforceSatelliteCommunicationPermission("requestIsSatelliteSupported");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestIsSatelliteSupported(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13303,7 +13341,12 @@
     @Override
     public void requestSatelliteCapabilities(@NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestSatelliteCapabilities");
-        mSatelliteController.requestSatelliteCapabilities(result);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestSatelliteCapabilities(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13321,7 +13364,12 @@
             @NonNull IIntegerConsumer resultCallback,
             @NonNull ISatelliteTransmissionUpdateCallback callback) {
         enforceSatelliteCommunicationPermission("startSatelliteTransmissionUpdates");
-        mSatelliteController.startSatelliteTransmissionUpdates(resultCallback, callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.startSatelliteTransmissionUpdates(resultCallback, callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13339,7 +13387,12 @@
             @NonNull IIntegerConsumer resultCallback,
             @NonNull ISatelliteTransmissionUpdateCallback callback) {
         enforceSatelliteCommunicationPermission("stopSatelliteTransmissionUpdates");
-        mSatelliteController.stopSatelliteTransmissionUpdates(resultCallback, callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.stopSatelliteTransmissionUpdates(resultCallback, callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13361,8 +13414,13 @@
             @NonNull String token, @NonNull byte[] provisionData,
             @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("provisionSatelliteService");
-        return mSatelliteController.provisionSatelliteService(token, provisionData,
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.provisionSatelliteService(token, provisionData,
                 callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13380,7 +13438,12 @@
     public void deprovisionSatelliteService(
             @NonNull String token, @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("deprovisionSatelliteService");
-        mSatelliteController.deprovisionSatelliteService(token, callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.deprovisionSatelliteService(token, callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13396,7 +13459,12 @@
     @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged(
             @NonNull ISatelliteProvisionStateCallback callback) {
         enforceSatelliteCommunicationPermission("registerForSatelliteProvisionStateChanged");
-        return mSatelliteController.registerForSatelliteProvisionStateChanged(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.registerForSatelliteProvisionStateChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13412,7 +13480,12 @@
     public void unregisterForSatelliteProvisionStateChanged(
             @NonNull ISatelliteProvisionStateCallback callback) {
         enforceSatelliteCommunicationPermission("unregisterForSatelliteProvisionStateChanged");
-        mSatelliteController.unregisterForSatelliteProvisionStateChanged(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.unregisterForSatelliteProvisionStateChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13427,7 +13500,12 @@
     @Override
     public void requestIsSatelliteProvisioned(@NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestIsSatelliteProvisioned");
-        mSatelliteController.requestIsSatelliteProvisioned(result);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestIsSatelliteProvisioned(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13443,7 +13521,12 @@
     @SatelliteManager.SatelliteResult public int registerForSatelliteModemStateChanged(
             @NonNull ISatelliteModemStateCallback callback) {
         enforceSatelliteCommunicationPermission("registerForSatelliteModemStateChanged");
-        return mSatelliteController.registerForSatelliteModemStateChanged(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.registerForSatelliteModemStateChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13458,7 +13541,12 @@
     @Override
     public void unregisterForModemStateChanged(@NonNull ISatelliteModemStateCallback callback) {
         enforceSatelliteCommunicationPermission("unregisterForModemStateChanged");
-        mSatelliteController.unregisterForModemStateChanged(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.unregisterForModemStateChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13474,7 +13562,12 @@
     @SatelliteManager.SatelliteResult public int registerForIncomingDatagram(
             @NonNull ISatelliteDatagramCallback callback) {
         enforceSatelliteCommunicationPermission("registerForIncomingDatagram");
-        return mSatelliteController.registerForIncomingDatagram(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.registerForIncomingDatagram(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13489,7 +13582,12 @@
     @Override
     public void unregisterForIncomingDatagram(@NonNull ISatelliteDatagramCallback callback) {
         enforceSatelliteCommunicationPermission("unregisterForIncomingDatagram");
-        mSatelliteController.unregisterForIncomingDatagram(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.unregisterForIncomingDatagram(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13505,7 +13603,12 @@
      */
     public void pollPendingDatagrams(IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("pollPendingDatagrams");
-        mSatelliteController.pollPendingDatagrams(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.pollPendingDatagrams(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13530,8 +13633,71 @@
             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("sendDatagram");
-        mSatelliteController.sendDatagram(datagramType, datagram, needFullScreenPointingUI,
-                callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.sendDatagram(datagramType, datagram, needFullScreenPointingUI,
+                    callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Returns integer array of disallowed reasons of satellite.
+     *
+     * @return Integer array of disallowed reasons of satellite.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @NonNull public int[] getSatelliteDisallowedReasons() {
+        enforceSatelliteCommunicationPermission("getSatelliteDisallowedReasons");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteAccessController.getSatelliteDisallowedReasons()
+                    .stream().mapToInt(Integer::intValue).toArray();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Registers for disallowed reasons change event from satellite service.
+     *
+     * @param callback The callback to handle disallowed reasons changed event.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void registerForSatelliteDisallowedReasonsChanged(
+            @NonNull ISatelliteDisallowedReasonsCallback callback) {
+        enforceSatelliteCommunicationPermission("registerForSatelliteDisallowedReasonsChanged");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteAccessController.registerForSatelliteDisallowedReasonsChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Unregisters for disallowed reasons change event from satellite service.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback to handle disallowed reasons changed event.
+     *                 {@link #registerForSatelliteDisallowedReasonsChanged(
+     *                 ISatelliteDisallowedReasonsCallback)}.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void unregisterForSatelliteDisallowedReasonsChanged(
+            @NonNull ISatelliteDisallowedReasonsCallback callback) {
+        enforceSatelliteCommunicationPermission("unregisterForSatelliteDisallowedReasonsChanged");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteAccessController.unregisterForSatelliteDisallowedReasonsChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13549,7 +13715,36 @@
     public void requestIsCommunicationAllowedForCurrentLocation(int subId,
             @NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestIsCommunicationAllowedForCurrentLocation");
-        mSatelliteAccessController.requestIsCommunicationAllowedForCurrentLocation(result, false);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteAccessController.requestIsCommunicationAllowedForCurrentLocation(result,
+                    false);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Request to get satellite access configuration for the current location.
+     *
+     * @param result The result receiver that returns the satellite access configuration
+     *               for the current location if the request is successful or an error code
+     *               if the request failed.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void requestSatelliteAccessConfigurationForCurrentLocation(
+            @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission(
+                "requestSatelliteAccessConfigurationForCurrentLocation");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteAccessController
+                    .requestSatelliteAccessConfigurationForCurrentLocation(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13563,22 +13758,32 @@
     @Override
     public void requestTimeForNextSatelliteVisibility(@NonNull ResultReceiver result) {
         enforceSatelliteCommunicationPermission("requestTimeForNextSatelliteVisibility");
-        mSatelliteController.requestTimeForNextSatelliteVisibility(result);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.requestTimeForNextSatelliteVisibility(result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
-     * Inform that Device is aligned to satellite for demo mode.
+     * Inform whether the device is aligned with the satellite in both real and demo mode.
      *
-     * @param isAligned {@code true} Device is aligned with the satellite for demo mode
-     *                  {@code false} Device fails to align with the satellite for demo mode.
+     * @param isAligned {@code true} Device is aligned with the satellite.
+     *                  {@code false} Device fails to align with the satellite.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
 
     public void setDeviceAlignedWithSatellite(@NonNull boolean isAligned) {
-        enforceSatelliteCommunicationPermission("informDeviceAlignedToSatellite");
-        mSatelliteController.setDeviceAlignedWithSatellite(isAligned);
+        enforceSatelliteCommunicationPermission("setDeviceAlignedWithSatellite");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.setDeviceAlignedWithSatellite(isAligned);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13772,7 +13977,12 @@
     @SatelliteManager.SatelliteResult public int registerForSatelliteSupportedStateChanged(
             @NonNull ISatelliteSupportedStateCallback callback) {
         enforceSatelliteCommunicationPermission("registerForSatelliteSupportedStateChanged");
-        return mSatelliteController.registerForSatelliteSupportedStateChanged(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.registerForSatelliteSupportedStateChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13788,7 +13998,12 @@
     public void unregisterForSatelliteSupportedStateChanged(
             @NonNull ISatelliteSupportedStateCallback callback) {
         enforceSatelliteCommunicationPermission("unregisterForSatelliteSupportedStateChanged");
-        mSatelliteController.unregisterForSatelliteSupportedStateChanged(callback);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.unregisterForSatelliteSupportedStateChanged(callback);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13809,8 +14024,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setSatelliteServicePackageName");
-        return mSatelliteController.setSatelliteServicePackageName(servicePackageName,
-                provisioned);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatelliteServicePackageName(servicePackageName,
+                    provisioned);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13827,7 +14047,12 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setSatelliteGatewayServicePackageName");
-        return mSatelliteController.setSatelliteGatewayServicePackageName(servicePackageName);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatelliteGatewayServicePackageName(servicePackageName);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13846,8 +14071,13 @@
                 Binder.getCallingUid(), "setSatellitePointingUiClassName");
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                "setSatelliteGatewayServicePackageName");
-        return mSatelliteController.setSatellitePointingUiClassName(packageName, className);
+                "setSatellitePointingUiClassName");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatellitePointingUiClassName(packageName, className);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13865,7 +14095,33 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setSatelliteListeningTimeoutDuration");
-        return mSatelliteController.setSatelliteListeningTimeoutDuration(timeoutMillis);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatelliteListeningTimeoutDuration(timeoutMillis);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * This API can be used by only CTS to control ingoring cellular service state event.
+     *
+     * @param enabled Whether to enable boolean config.
+     * @return {@code true} if the value is set successfully, {@code false} otherwise.
+     */
+    public boolean setSatelliteIgnoreCellularServiceState(boolean enabled) {
+        Log.d(LOG_TAG, "setSatelliteIgnoreCellularServiceState - " + enabled);
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setSatelliteIgnoreCellularServiceState");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setSatelliteIgnoreCellularServiceState");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatelliteIgnoreCellularServiceState(enabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13884,8 +14140,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setDatagramControllerTimeoutDuration");
-        return mSatelliteController.setDatagramControllerTimeoutDuration(
-                reset, timeoutType, timeoutMillis);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setDatagramControllerTimeoutDuration(
+                    reset, timeoutType, timeoutMillis);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13904,7 +14165,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "ssetDatagramControllerBooleanConfig");
-        return mSatelliteController.setDatagramControllerBooleanConfig(reset, booleanType, enable);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setDatagramControllerBooleanConfig(reset, booleanType,
+                    enable);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
 
@@ -13924,8 +14191,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setSatelliteControllerTimeoutDuration");
-        return mSatelliteController.setSatelliteControllerTimeoutDuration(
-                reset, timeoutType, timeoutMillis);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatelliteControllerTimeoutDuration(
+                    reset, timeoutType, timeoutMillis);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13948,8 +14220,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setEmergencyCallToSatelliteHandoverType");
-        return mSatelliteController.setEmergencyCallToSatelliteHandoverType(
-                handoverType, delaySeconds);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setEmergencyCallToSatelliteHandoverType(
+                    handoverType, delaySeconds);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13968,7 +14245,12 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setOemEnabledSatelliteProvisionStatus");
-        return mSatelliteController.setOemEnabledSatelliteProvisionStatus(reset, isProvisioned);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setOemEnabledSatelliteProvisionStatus(reset, isProvisioned);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -13985,14 +14267,18 @@
                 + ", locationCountryCodeTimestampNanos" + locationCountryCodeTimestampNanos
                 + ", reset=" + reset + ", cachedNetworkCountryCodes="
                 + String.join(", ", cachedNetworkCountryCodes.keySet()));
-        TelephonyPermissions.enforceShellOnly(
-                Binder.getCallingUid(), "setCachedLocationCountryCode");
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setCountryCodes");
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                "setCachedLocationCountryCode");
-        return TelephonyCountryDetector.getInstance(getDefaultPhone().getContext(), mFeatureFlags)
-                .setCountryCodes(reset, currentNetworkCountryCodes, cachedNetworkCountryCodes,
-                        locationCountryCode, locationCountryCodeTimestampNanos);
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, "setCountryCodes");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return TelephonyCountryDetector.getInstance(getDefaultPhone().getContext(),
+                    mFeatureFlags).setCountryCodes(reset, currentNetworkCountryCodes,
+                    cachedNetworkCountryCodes, locationCountryCode,
+                    locationCountryCodeTimestampNanos);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -14016,8 +14302,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setSatelliteAccessControlOverlayConfigs");
-        return mSatelliteAccessController.setSatelliteAccessControlOverlayConfigs(reset, isAllowed,
-                s2CellFile, locationFreshDurationNanos, satelliteCountryCodes);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteAccessController.setSatelliteAccessControlOverlayConfigs(reset,
+                    isAllowed, s2CellFile, locationFreshDurationNanos, satelliteCountryCodes);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -14042,8 +14333,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setShouldSendDatagramToModemInDemoMode");
-        return mSatelliteController.setShouldSendDatagramToModemInDemoMode(
-                shouldSendToModemInDemoMode);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setShouldSendDatagramToModemInDemoMode(
+                    shouldSendToModemInDemoMode);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -14068,8 +14364,13 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                 "setIsSatelliteCommunicationAllowedForCurrentLocationCache");
-        return mSatelliteAccessController.setIsSatelliteCommunicationAllowedForCurrentLocationCache(
-                state);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteAccessController
+                    .setIsSatelliteCommunicationAllowedForCurrentLocationCache(state);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -14430,6 +14731,44 @@
     }
 
     /**
+     * Deliver the list of deprovisioned satellite subscriber ids.
+     *
+     * @param list List of deprovisioned satellite subscriber ids.
+     * @param result The result receiver that returns whether deliver success or fail.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
+            @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("deprovisionSatellite");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mSatelliteController.deprovisionSatellite(list, result);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+
+    /**
+     * Inform whether application supports NTN SMS in satellite mode.
+     *
+     * This method is used by default messaging application to inform framework whether it supports
+     * NTN SMS or not.
+     *
+     * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     */
+    @Override
+    public void setNtnSmsSupported(boolean ntnSmsSupported) {
+        enforceSatelliteCommunicationPermission("setNtnSmsSupported");
+        enforceSendSmsPermission();
+        mSatelliteController.setNtnSmsSupportedByMessagesApp(ntnSmsSupported);
+    }
+
+    /**
      * This API can be used by only CTS to override the cached value for the device overlay config
      * value :
      * config_satellite_gateway_service_package and
@@ -14461,4 +14800,49 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    /**
+     * This API can be used by only CTS to override the Euicc UI component.
+     *
+     * @param componentName ui component to be launched for testing. {@code null} to reset.
+     *
+     * @hide
+     */
+    @Override
+    public void setTestEuiccUiComponent(@Nullable ComponentName componentName) {
+        enforceModifyPermission();
+        log("setTestEuiccUiComponent: " + componentName);
+        mTestEuiccUiComponent = componentName;
+    }
+
+    /**
+     * This API can be used by only CTS to retrieve the Euicc UI component.
+     *
+     * @return Euicc UI component. {@code null} if not available.
+     * @hide
+     */
+    @Override
+    @Nullable
+    public ComponentName getTestEuiccUiComponent() {
+        enforceReadPrivilegedPermission("getTestEuiccUiComponent");
+        return mTestEuiccUiComponent;
+    }
+
+    /**
+     * This API can be used only for test purpose to override the carrier roaming Ntn eligibility
+     *
+     * @param state        to update Ntn Eligibility.
+     * @param resetRequired to reset the overridden flag in satellite controller.
+     * @return {@code true} if the shell command is successful, {@code false} otherwise.
+     */
+    public boolean overrideCarrierRoamingNtnEligibilityChanged(boolean state,
+            boolean resetRequired) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteAccessController.overrideCarrierRoamingNtnEligibilityChanged(state,
+                    resetRequired);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
 }
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index bfc93e0..5ea1304 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -56,12 +56,12 @@
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.d2d.Communicator;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.phone.callcomposer.CallComposerPictureManager;
-import com.android.phone.euicc.EuiccUiDispatcherActivity;
 import com.android.phone.utils.CarrierAllowListInfo;
 
 import java.io.IOException;
@@ -131,9 +131,6 @@
     private static final String CC_SET_VALUES_FROM_XML = "set-values-from-xml";
     private static final String CC_CLEAR_VALUES = "clear-values";
 
-    private static final String EUICC_SUBCOMMAND = "euicc";
-    private static final String EUICC_SET_UI_COMPONENT = "set-euicc-uicomponent";
-
     private static final String GBA_SUBCOMMAND = "gba";
     private static final String GBA_SET_SERVICE = "set-service";
     private static final String GBA_GET_SERVICE = "get-service";
@@ -190,6 +187,8 @@
             "set-satellite-gateway-service-package-name";
     private static final String SET_SATELLITE_LISTENING_TIMEOUT_DURATION =
             "set-satellite-listening-timeout-duration";
+    private static final String SET_SATELLITE_IGNORE_CELLULAR_SERVICE_STATE =
+            "set-satellite-ignore-cellular-service-state";
     private static final String SET_SATELLITE_POINTING_UI_CLASS_NAME =
             "set-satellite-pointing-ui-class-name";
     private static final String SET_DATAGRAM_CONTROLLER_TIMEOUT_DURATION =
@@ -213,6 +212,9 @@
     private static final String SET_SATELLITE_SUBSCRIBERID_LIST_CHANGED_INTENT_COMPONENT =
             "set-satellite-subscriberid-list-changed-intent-component";
 
+    private static final String SET_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT =
+            "set-satellite-access-restriction-checking-result";
+
     private static final String DOMAIN_SELECTION_SUBCOMMAND = "domainselection";
     private static final String DOMAIN_SELECTION_SET_SERVICE_OVERRIDE = "set-dss-override";
     private static final String DOMAIN_SELECTION_CLEAR_SERVICE_OVERRIDE = "clear-dss-override";
@@ -358,8 +360,6 @@
                 return handleDataTestModeCommand();
             case END_BLOCK_SUPPRESSION:
                 return handleEndBlockSuppressionCommand();
-            case EUICC_SUBCOMMAND:
-                return handleEuiccCommand();
             case GBA_SUBCOMMAND:
                 return handleGbaCommand();
             case D2D_SUBCOMMAND:
@@ -405,6 +405,8 @@
                 return handleSetSatelliteGatewayServicePackageNameCommand();
             case SET_SATELLITE_LISTENING_TIMEOUT_DURATION:
                 return handleSetSatelliteListeningTimeoutDuration();
+            case SET_SATELLITE_IGNORE_CELLULAR_SERVICE_STATE:
+                return handleSetSatelliteIgnoreCellularServiceState();
             case SET_SATELLITE_POINTING_UI_CLASS_NAME:
                 return handleSetSatellitePointingUiClassNameCommand();
             case SET_DATAGRAM_CONTROLLER_TIMEOUT_DURATION:
@@ -427,6 +429,8 @@
                 return handleSetIsSatelliteCommunicationAllowedForCurrentLocationCache();
             case SET_SATELLITE_SUBSCRIBERID_LIST_CHANGED_INTENT_COMPONENT:
                 return handleSetSatelliteSubscriberIdListChangedIntentComponent();
+            case SET_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT:
+                return handleOverrideCarrierRoamingNtnEligibilityChanged();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -522,11 +526,14 @@
     private void onHelpIms() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("IMS Commands:");
-        pw.println("  ims set-ims-service [-s SLOT_ID] (-c | -d | -f) PACKAGE_NAME");
+        pw.println("  ims set-ims-service [-s SLOT_ID] [-u USER_ID] (-c | -d | -f) PACKAGE_NAME");
         pw.println("    Sets the ImsService defined in PACKAGE_NAME to to be the bound");
         pw.println("    ImsService. Options are:");
         pw.println("      -s: the slot ID that the ImsService should be bound for. If no option");
         pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      -u: the user ID that the ImsService should be bound on. If no option");
+        pw.println("          is specified, the SYSTEM user ID will be preferred followed by the");
+        pw.println("          current user ID if they are different");
         pw.println("      -c: Override the ImsService defined in the carrier configuration.");
         pw.println("      -d: Override the ImsService defined in the device overlay.");
         pw.println("      -f: Set the feature that this override if for, if no option is");
@@ -691,15 +698,6 @@
         pw.println("          is specified, it will choose the default voice SIM slot.");
     }
 
-    private void onHelpEuicc() {
-        PrintWriter pw = getOutPrintWriter();
-        pw.println("Euicc Commands:");
-        pw.println("  euicc set-euicc-uicomponent COMPONENT_NAME PACKAGE_NAME");
-        pw.println("  Sets the Euicc Ui-Component which handles EuiccService Actions.");
-        pw.println("  COMPONENT_NAME: The component name which handles UI Actions.");
-        pw.println("  PACKAGE_NAME: THe package name in which ui component belongs.");
-    }
-
     private void onHelpGba() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Gba Commands:");
@@ -1358,12 +1356,22 @@
     private int handleImsSetServiceCommand() {
         PrintWriter errPw = getErrPrintWriter();
         int slotId = getDefaultSlot();
+        int userId = UserHandle.USER_NULL; // By default, set no userId constraint
         Boolean isCarrierService = null;
         List<Integer> featuresList = new ArrayList<>();
 
         String opt;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
+                case "-u": {
+                    try {
+                        userId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        errPw.println("ims set-ims-service requires an integer as a USER_ID");
+                        return -1;
+                    }
+                    break;
+                }
                 case "-s": {
                     try {
                         slotId = Integer.parseInt(getNextArgRequired());
@@ -1419,17 +1427,17 @@
             for (int i = 0; i < featuresList.size(); i++) {
                 featureArray[i] = featuresList.get(i);
             }
-            boolean result = mInterface.setBoundImsServiceOverride(slotId, isCarrierService,
+            boolean result = mInterface.setBoundImsServiceOverride(slotId, userId, isCarrierService,
                     featureArray, packageName);
             if (VDBG) {
-                Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " "
+                Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " -u " + userId + " "
                         + (isCarrierService ? "-c " : "-d ")
                         + "-f " + featuresList + " "
                         + packageName + ", result=" + result);
             }
             getOutPrintWriter().println(result);
         } catch (RemoteException e) {
-            Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " "
+            Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " -u " + userId + " "
                     + (isCarrierService ? "-c " : "-d ")
                     + "-f " + featuresList + " "
                     + packageName + ", error" + e.getMessage());
@@ -1686,9 +1694,7 @@
     }
 
     private boolean checkShellUid() {
-        // adb can run as root or as shell, depending on whether the device is rooted.
-        return UserHandle.isSameApp(Binder.getCallingUid(), Process.SHELL_UID)
-                || UserHandle.isSameApp(Binder.getCallingUid(), Process.ROOT_UID);
+        return TelephonyPermissions.isRootOrShell(Binder.getCallingUid());
     }
 
     private int handleCcCommand() {
@@ -2219,35 +2225,6 @@
         return 0;
     }
 
-    private int handleEuiccCommand() {
-        String arg = getNextArg();
-        if (arg == null) {
-            onHelpEuicc();
-            return 0;
-        }
-
-        switch (arg) {
-            case EUICC_SET_UI_COMPONENT: {
-                return handleEuiccServiceCommand();
-            }
-        }
-        return -1;
-    }
-
-    private int handleEuiccServiceCommand() {
-        String uiComponent = getNextArg();
-        String packageName = getNextArg();
-        if (packageName == null || uiComponent == null) {
-            return -1;
-        }
-        EuiccUiDispatcherActivity.setTestEuiccUiComponent(packageName, uiComponent);
-        if (VDBG) {
-            Log.v(LOG_TAG, "euicc set-euicc-uicomponent " + uiComponent +" "
-                    + packageName);
-        }
-        return 0;
-    }
-
     private int handleRestartModemCommand() {
         // Verify that the user is allowed to run the command. Only allowed in rooted device in a
         // non user build.
@@ -3400,6 +3377,37 @@
         return 0;
     }
 
+    private int handleSetSatelliteIgnoreCellularServiceState() {
+        PrintWriter errPw = getErrPrintWriter();
+        boolean enabled = false;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-d": {
+                    enabled = Boolean.parseBoolean(getNextArgRequired());
+                    break;
+                }
+            }
+        }
+        Log.d(LOG_TAG, "handleSetSatelliteIgnoreCellularServiceState: enabled =" + enabled);
+
+        try {
+            boolean result = mInterface.setSatelliteIgnoreCellularServiceState(enabled);
+            if (VDBG) {
+                Log.v(LOG_TAG, "handleSetSatelliteIgnoreCellularServiceState " + enabled
+                        + ", result = " + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "handleSetSatelliteIgnoreCellularServiceState: " + enabled
+                    + ", error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     private int handleSetDatagramControllerTimeoutDuration() {
         PrintWriter errPw = getErrPrintWriter();
         boolean reset = false;
@@ -3726,7 +3734,6 @@
         PrintWriter errPw = getErrPrintWriter();
         String opt;
         String state;
-
         if ((opt = getNextArg()) == null) {
             errPw.println(
                     "adb shell cmd phone set-is-satellite-communication-allowed-for-current"
@@ -3739,6 +3746,10 @@
                     state = "cache_allowed";
                     break;
                 }
+                case "-na": {
+                    state = "cache_not_allowed";
+                    break;
+                }
                 case "-n": {
                     state = "cache_clear_and_not_allowed";
                     break;
@@ -4077,4 +4088,48 @@
         }
         return jSonString;
     }
+
+    /**
+     * This method override the check for carrier roaming Ntn eligibility.
+     * <ul>
+     * <li> `adb shell cmd phone set-satellite-access-restriction-checking-result true` will set
+     * override eligibility to true.</li>
+     * <li> `adb shell cmd phone set-satellite-access-restriction-checking-result false` will
+     * override eligibility to false.</li>
+     * <li> `adb shell cmd phone set-satellite-access-restriction-checking-result` will reset the
+     * override data set through adb command.</li>
+     * </ul>
+     *
+     * @return {@code true} is command executed successfully otherwise {@code false}.
+     */
+    private int handleOverrideCarrierRoamingNtnEligibilityChanged() {
+        PrintWriter errPw = getErrPrintWriter();
+        String opt;
+        boolean state = false;
+        boolean isRestRequired = false;
+        try {
+            if ((opt = getNextArg()) == null) {
+                isRestRequired = true;
+            } else {
+                if ("true".equalsIgnoreCase(opt)) {
+                    state = true;
+                }
+            }
+            boolean result = mInterface.overrideCarrierRoamingNtnEligibilityChanged(state,
+                    isRestRequired);
+            if (VDBG) {
+                Log.v(LOG_TAG, "handleSetSatelliteAccessRestrictionCheckingResult "
+                        + "returns: "
+                        + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "handleSetSatelliteAccessRestrictionCheckingResult("
+                    + state + "), error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        Log.d(LOG_TAG, "handleSetSatelliteAccessRestrictionCheckingResult(" + state + ")");
+        return 0;
+    }
 }
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index a75f26f..9632329 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -28,8 +28,8 @@
 import android.os.UserHandle;
 import android.permission.LegacyPermissionManager;
 import android.service.euicc.EuiccService;
+import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccManager;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -59,8 +59,6 @@
     private LegacyPermissionManager mPermissionManager;
     private boolean mGrantPermissionDone = false;
     private ThreadPoolExecutor mExecutor;
-    // Used for CTS EuiccManager action verification
-    private static ComponentName mTestEuiccUiComponentName;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -97,18 +95,6 @@
         }
     }
 
-    /**
-    * This API used to set the Test EuiccUiComponent for CTS
-    * @param packageName package which handles the intent
-    * @param componentName ui component to be launched for testing
-    */
-    public static void setTestEuiccUiComponent(String packageName, String componentName) {
-        mTestEuiccUiComponentName = null;
-        if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(componentName)) {
-            mTestEuiccUiComponentName = new ComponentName(packageName, componentName);
-        }
-    }
-
     @VisibleForTesting
     @Nullable
     Intent resolveEuiccUiIntent() {
@@ -124,10 +110,11 @@
             return null;
         }
 
-        if (mTestEuiccUiComponentName != null) {
-            Log.i(TAG, "Test mode");
-            euiccUiIntent.setComponent(mTestEuiccUiComponentName);
-            mTestEuiccUiComponentName = null;
+        ComponentName testEuiccUiComponent = ((TelephonyManager)
+                getSystemService(Context.TELEPHONY_SERVICE)).getTestEuiccUiComponent();
+        if (testEuiccUiComponent != null) {
+            Log.i(TAG, "Test mode: " + testEuiccUiComponent);
+            euiccUiIntent.setComponent(testEuiccUiComponent);
             return euiccUiIntent;
         }
 
diff --git a/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java b/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java
index 4490460..845ff18 100644
--- a/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java
@@ -16,10 +16,12 @@
 package com.android.phone.satellite.accesscontrol;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.telephony.Rlog;
 
-import com.android.storage.s2.S2LevelRange;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.sats2range.read.SatS2RangeFileReader;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 
 import com.google.common.geometry.S2CellId;
 import com.google.common.geometry.S2LatLng;
@@ -36,31 +38,42 @@
     private static final String TAG = "S2RangeSatelliteOnDeviceAccessController";
     private static final boolean DBG = false;
 
-    @NonNull private final SatS2RangeFileReader mSatS2RangeFileReader;
+    @NonNull
+    private final SatS2RangeFileReader mSatS2RangeFileReader;
 
     private final int mS2Level;
 
+    /** Feature flags to control behavior and errors. */
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     private S2RangeSatelliteOnDeviceAccessController(
-            @NonNull SatS2RangeFileReader satS2RangeFileReader, int s2Level) {
+            @NonNull SatS2RangeFileReader satS2RangeFileReader,
+            int s2Level,
+            @NonNull FeatureFlags featureFlags) {
         mSatS2RangeFileReader = Objects.requireNonNull(satS2RangeFileReader);
         mS2Level = s2Level;
+        mFeatureFlags = featureFlags;
     }
 
     /**
      * Returns a new {@link S2RangeSatelliteOnDeviceAccessController} using the specified data file.
      *
      * @param file The input file that contains the S2-range-based access restriction information.
-     * @throws IOException in the event of a problem while reading the underlying file.
+     * @throws IOException              in the event of a problem while reading the underlying file.
      * @throws IllegalArgumentException if either the S2 level defined by
-     * {@code config_oem_enabled_satellite_s2cell_level} or the satellite access allow defined by
-     * {@code config_oem_enabled_satellite_access_allow} does not match the values included in the
-     * header of the input file.
+     *                                  {@code config_oem_enabled_satellite_s2cell_level} or the
+     *                                  satellite access allow defined by
+     *                                  {@code config_oem_enabled_satellite_access_allow} does not
+     *                                  match the values included in the
+     *                                  header of the input file.
      */
     public static S2RangeSatelliteOnDeviceAccessController create(
-            @NonNull File file) throws IOException, IllegalArgumentException {
+            @NonNull File file, FeatureFlags featureFlags)
+            throws IOException, IllegalArgumentException {
         SatS2RangeFileReader reader = SatS2RangeFileReader.open(file);
         int s2Level = reader.getS2Level();
-        return new S2RangeSatelliteOnDeviceAccessController(reader, s2Level);
+        return new S2RangeSatelliteOnDeviceAccessController(reader, s2Level, featureFlags);
     }
 
     public static LocationToken createLocationTokenForLatLng(
@@ -84,7 +97,7 @@
     }
 
     private boolean isSatCommunicationAllowedAtLocation(long s2CellId) throws IOException {
-        S2LevelRange entry = mSatS2RangeFileReader.findEntryByCellId(s2CellId);
+        SuffixTableRange entry = mSatS2RangeFileReader.findEntryByCellId(s2CellId);
         if (mSatS2RangeFileReader.isAllowedList()) {
             // The file contains an allowed list of S2 cells. Thus, satellite is allowed if an
             // entry is found
@@ -158,4 +171,25 @@
             return Objects.hash(mS2CellId);
         }
     }
+
+    @Override
+    @Nullable
+    public Integer getRegionalConfigIdForLocation(@NonNull LocationToken locationToken)
+            throws IOException {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("getAccessControlConfigIdForLocation: carrierRoamingNbIotNtn is disabled");
+            return null;
+        }
+
+        if (locationToken instanceof LocationTokenImpl locationTokenImpl) {
+            return getRegionalConfigIdForLocation(locationTokenImpl.getS2CellId());
+        } else {
+            throw new IllegalArgumentException("Unknown locationToken=" + locationToken);
+        }
+    }
+
+    private Integer getRegionalConfigIdForLocation(long s2CellId) throws IOException {
+        SuffixTableRange entry = mSatS2RangeFileReader.findEntryByCellId(s2CellId);
+        return (entry == null) ? null : entry.getEntryValue();
+    }
 }
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
index 7b244a1..5f60291 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
@@ -16,9 +16,16 @@
 
 package com.android.phone.satellite.accesscontrol;
 
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_ACCESS_CONFIGURATION;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_LOCATION_DISABLED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_LOCATION_NOT_AVAILABLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
@@ -34,7 +41,11 @@
 import android.annotation.ArrayRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -56,16 +67,20 @@
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.telecom.TelecomManager;
 import android.telephony.AnomalyReporter;
 import android.telephony.DropBoxManagerLoggerBackend;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
+import android.telephony.satellite.ISatelliteDisallowedReasonsCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
 import android.telephony.satellite.ISatelliteSupportedStateCallback;
+import android.telephony.satellite.SatelliteAccessConfiguration;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
 import android.text.TextUtils;
@@ -76,6 +91,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SmsApplication;
 import com.android.internal.telephony.TelephonyCountryDetector;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.SatelliteConfig;
@@ -97,11 +113,13 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
@@ -145,6 +163,60 @@
     protected static final int EVENT_COUNTRY_CODE_CHANGED = 5;
     protected static final int EVENT_LOCATION_SETTINGS_ENABLED = 6;
 
+    public static final int DEFAULT_REGIONAL_SATELLITE_CONFIG_ID = 0;
+    public static final int UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID = -1;
+
+
+    private static final String KEY_AVAILABLE_NOTIFICATION_SHOWN = "available_notification_shown";
+    private static final String KEY_UNAVAILABLE_NOTIFICATION_SHOWN =
+            "unavailable_notification_shown";
+    private static final String NOTIFICATION_TAG = "SatelliteAccessController";
+    private static final int NOTIFICATION_ID = 1;
+    private static final String NOTIFICATION_CHANNEL = "satelliteChannel";
+    private static final String NOTIFICATION_CHANNEL_ID = "satellite";
+    private static final int SATELLITE_DISALLOWED_REASON_NONE = -1;
+    private static final List<Integer> DISALLOWED_REASONS_TO_BE_RESET =
+            Arrays.asList(SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION,
+            SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED);
+
+    private static final HashMap<Integer, Pair<Integer, Integer>>
+            SATELLITE_SOS_UNAVAILABLE_REASONS = new HashMap<>(Map.of(
+            SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED, new Pair<>(
+                    R.string.satellite_sos_not_supported_notification_title,
+                    R.string.satellite_sos_not_supported_notification_summary),
+            SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED, new Pair<>(
+                    R.string.satellite_sos_not_provisioned_notification_title,
+                    R.string.satellite_sos_not_provisioned_notification_summary),
+            SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION, new Pair<>(
+                    R.string.satellite_sos_not_in_allowed_region_notification_title,
+                    R.string.satellite_sos_not_in_allowed_region_notification_summary),
+            SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP, new Pair<>(
+                    R.string.satellite_sos_unsupported_default_sms_app_notification_title,
+                    R.string.satellite_sos_unsupported_default_sms_app_notification_summary),
+            SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED, new Pair<>(
+                    R.string.satellite_sos_location_disabled_notification_title,
+                    R.string.satellite_sos_location_disabled_notification_summary)
+    ));
+
+    private static final HashMap<Integer, Pair<Integer, Integer>>
+            SATELLITE_MESSAGING_UNAVAILABLE_REASONS = new HashMap<>(Map.of(
+            SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED, new Pair<>(
+                    R.string.satellite_messaging_not_supported_notification_title,
+                    R.string.satellite_messaging_not_supported_notification_summary),
+            SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED, new Pair<>(
+                    R.string.satellite_messaging_not_provisioned_notification_title,
+                    R.string.satellite_messaging_not_provisioned_notification_summary),
+            SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION, new Pair<>(
+                    R.string.satellite_messaging_not_in_allowed_region_notification_title,
+                    R.string.satellite_messaging_not_in_allowed_region_notification_summary),
+            SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP, new Pair<>(
+                    R.string.satellite_messaging_unsupported_default_sms_app_notification_title,
+                    R.string.satellite_messaging_unsupported_default_sms_app_notification_summary),
+            SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED, new Pair<>(
+                    R.string.satellite_messaging_location_disabled_notification_title,
+                    R.string.satellite_messaging_location_disabled_notification_summary)
+    ));
+
     private static SatelliteAccessController sInstance;
 
     /** Feature flags to control behavior and errors. */
@@ -194,11 +266,11 @@
     private long mOverriddenLocationFreshDurationNanos;
     @GuardedBy("mLock")
     @NonNull
-    private final Map<SatelliteOnDeviceAccessController.LocationToken, Boolean>
+    private final Map<SatelliteOnDeviceAccessController.LocationToken, Integer>
             mCachedAccessRestrictionMap = new LinkedHashMap<>() {
         @Override
         protected boolean removeEldestEntry(
-                Entry<SatelliteOnDeviceAccessController.LocationToken, Boolean> eldest) {
+                Entry<SatelliteOnDeviceAccessController.LocationToken, Integer> eldest) {
             return size() > MAX_CACHE_SIZE;
         }
     };
@@ -209,6 +281,17 @@
     @GuardedBy("mLock")
     @Nullable
     private Location mFreshLastKnownLocation = null;
+    @GuardedBy("mLock")
+    @Nullable
+    protected Integer mRegionalConfigId = null;
+    @GuardedBy("mLock")
+    @Nullable
+    protected Integer mNewRegionalConfigId = null;
+
+    /** Key: Config ID; Value: SatelliteAccessConfiguration */
+    @NonNull
+    private HashMap<Integer, SatelliteAccessConfiguration> mSatelliteAccessConfigMap =
+            new HashMap<>();
 
     /** These are used for CTS test */
     private Path mCtsSatS2FilePath = null;
@@ -259,9 +342,13 @@
      */
     private final ConcurrentHashMap<IBinder, ISatelliteCommunicationAllowedStateCallback>
             mSatelliteCommunicationAllowedStateChangedListeners = new ConcurrentHashMap<>();
-    private final Object mSatelliteCommunicationAllowStateLock = new Object();
+    protected final Object mSatelliteCommunicationAllowStateLock = new Object();
     @GuardedBy("mSatelliteCommunicationAllowStateLock")
-    private boolean mCurrentSatelliteAllowedState = false;
+    protected boolean mCurrentSatelliteAllowedState = false;
+
+    private final ConcurrentHashMap<IBinder, ISatelliteDisallowedReasonsCallback>
+            mSatelliteDisallowedReasonsChangedListeners = new ConcurrentHashMap<>();
+    private final Object mSatelliteDisallowedReasonsLock = new Object();
 
     protected static final long ALLOWED_STATE_CACHE_VALID_DURATION_NANOS =
             TimeUnit.HOURS.toNanos(4);
@@ -273,6 +360,13 @@
     private long mOnDeviceLookupStartTimeMillis;
     private long mTotalCheckingStartTimeMillis;
 
+    private final boolean mNotifySatelliteAvailabilityEnabled;
+    private Notification mSatelliteAvailableNotification;
+    // Key: SatelliteManager#SatelliteDisallowedReason; Value: Notification
+    private final Map<Integer, Notification> mSatelliteUnAvailableNotifications = new HashMap<>();
+    private NotificationManager mNotificationManager;
+    private final List<Integer> mSatelliteDisallowedReasons = new ArrayList<>();
+
     protected BroadcastReceiver mLocationModeChangedBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -345,6 +439,9 @@
                 handleIsSatelliteSupportedResult(resultCode, resultData);
             }
         };
+        mSatelliteController.incrementResultReceiverCount(
+                "SAC:mInternalSatelliteSupportedResultReceiver");
+
         mInternalSatelliteProvisionedResultReceiver = new ResultReceiver(this) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
@@ -353,19 +450,38 @@
         };
 
         mConfigUpdaterMetricsStats = ConfigUpdaterMetricsStats.getOrCreateInstance();
+        mNotifySatelliteAvailabilityEnabled =
+                context.getResources().getBoolean(
+                        R.bool.config_satellite_should_notify_availability);
 
         mInternalSatelliteSupportedStateCallback = new ISatelliteSupportedStateCallback.Stub() {
             @Override
             public void onSatelliteSupportedStateChanged(boolean isSupported) {
                 logd("onSatelliteSupportedStateChanged: isSupported=" + isSupported);
                 if (isSupported) {
+                    final String caller = "SAC:onSatelliteSupportedStateChanged";
                     requestIsCommunicationAllowedForCurrentLocation(
                             new ResultReceiver(null) {
                                 @Override
                                 protected void onReceiveResult(int resultCode, Bundle resultData) {
+                                    mSatelliteController.decrementResultReceiverCount(caller);
                                     // do nothing
                                 }
                             }, false);
+                    mSatelliteController.incrementResultReceiverCount(caller);
+                    if (mSatelliteDisallowedReasons.contains(
+                            Integer.valueOf(SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED))) {
+                        mSatelliteDisallowedReasons.remove(
+                                Integer.valueOf(SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED));
+                        handleEventDisallowedReasonsChanged();
+                    }
+                } else {
+                    if (!mSatelliteDisallowedReasons.contains(
+                            Integer.valueOf(SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED))) {
+                        mSatelliteDisallowedReasons.add(
+                                Integer.valueOf(SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED));
+                        handleEventDisallowedReasonsChanged();
+                    }
                 }
             }
         };
@@ -377,13 +493,29 @@
             public void onSatelliteProvisionStateChanged(boolean isProvisioned) {
                 logd("onSatelliteProvisionStateChanged: isProvisioned=" + isProvisioned);
                 if (isProvisioned) {
+                    final String caller = "SAC:onSatelliteProvisionStateChanged";
                     requestIsCommunicationAllowedForCurrentLocation(
                             new ResultReceiver(null) {
                                 @Override
                                 protected void onReceiveResult(int resultCode, Bundle resultData) {
+                                    mSatelliteController.decrementResultReceiverCount(caller);
                                     // do nothing
                                 }
                             }, false);
+                    mSatelliteController.incrementResultReceiverCount(caller);
+                    if (mSatelliteDisallowedReasons.contains(
+                            SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED)) {
+                        mSatelliteDisallowedReasons.remove(
+                                Integer.valueOf(SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED));
+                        handleEventDisallowedReasonsChanged();
+                    }
+                } else {
+                    if (!mSatelliteDisallowedReasons.contains(
+                            SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED)) {
+                        mSatelliteDisallowedReasons.add(
+                                SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED);
+                        handleEventDisallowedReasonsChanged();
+                    }
                 }
             }
 
@@ -399,7 +531,9 @@
 
         // Init the SatelliteOnDeviceAccessController so that the S2 level can be cached
         initSatelliteOnDeviceAccessController();
+        initializeSatelliteSystemNotification(context);
         registerLocationModeChangedBroadcastReceiver(context);
+        registerDefaultSmsAppChangedBroadcastReceiver(context);
     }
 
     private void updateCurrentSatelliteAllowedState(boolean isAllowed) {
@@ -411,6 +545,13 @@
                 mCurrentSatelliteAllowedState = isAllowed;
                 notifySatelliteCommunicationAllowedStateChanged(isAllowed);
                 mControllerMetricsStats.reportAllowedStateChanged();
+                if (!isAllowed) {
+                    synchronized (mLock) {
+                        plogd("updateCurrentSatelliteAllowedState : set mNewRegionalConfigId null");
+                        mNewRegionalConfigId = null;
+                    }
+                }
+                updateRegionalConfigId();
             }
         }
     }
@@ -479,7 +620,69 @@
         }
         mAccessControllerMetricsStats.setTriggeringEvent(TRIGGERING_EVENT_EXTERNAL_REQUEST);
         sendRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED,
-                new Pair<>(mSatelliteController.getSatellitePhone().getSubId(), result));
+                new Pair<>(mSatelliteController.getSelectedSatelliteSubId(), result));
+        mSatelliteController.incrementResultReceiverCount(
+                "SAC:requestIsCommunicationAllowedForCurrentLocation");
+    }
+
+    /**
+     * Request to get satellite access configuration for the current location.
+     *
+     * @param result The result receiver that returns satellite access configuration
+     *               for the current location if the request is successful or an error code
+     *               if the request failed.
+     */
+    public void requestSatelliteAccessConfigurationForCurrentLocation(
+            @NonNull ResultReceiver result) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("carrierRoamingNbIotNtnFlag is disabled");
+            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            return;
+        }
+        plogd("requestSatelliteAccessConfigurationForCurrentLocation");
+        ResultReceiver internalResultReceiver = new ResultReceiver(this) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                plogd("requestSatelliteAccessConfigurationForCurrentLocation: resultCode="
+                        + resultCode + ", resultData=" + resultData);
+                boolean isSatelliteCommunicationAllowed = false;
+                if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                    if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                        isSatelliteCommunicationAllowed =
+                                resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                    } else {
+                        loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                        result.send(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, null);
+                        return;
+                    }
+                } else {
+                    loge("resultCode is not SATELLITE_RESULT_SUCCESS.");
+                    result.send(resultCode, null);
+                    return;
+                }
+
+                SatelliteAccessConfiguration satelliteAccessConfig = null;
+                synchronized (mLock) {
+                    if (isSatelliteCommunicationAllowed && isRegionalConfigIdValid(
+                            mRegionalConfigId)) {
+                        plogd("requestSatelliteAccessConfigurationForCurrentLocation : "
+                                + "mRegionalConfigId is " + mRegionalConfigId);
+                        satelliteAccessConfig =
+                                mSatelliteAccessConfigMap.get(mRegionalConfigId);
+                    }
+                }
+                plogd("requestSatelliteAccessConfigurationForCurrentLocation : "
+                        + "satelliteAccessConfig is " + satelliteAccessConfig);
+                Bundle bundle = new Bundle();
+                bundle.putParcelable(KEY_SATELLITE_ACCESS_CONFIGURATION, satelliteAccessConfig);
+                result.send(resultCode, bundle);
+            }
+        };
+        requestIsCommunicationAllowedForCurrentLocation(internalResultReceiver, false);
+    }
+
+    private boolean isRegionalConfigIdValid(@Nullable Integer configId) {
+        return (configId != null && configId >= 0);
     }
 
     /**
@@ -611,7 +814,7 @@
     private boolean isS2CellFileValid(@NonNull File s2CellFile) {
         try {
             SatelliteOnDeviceAccessController satelliteOnDeviceAccessController =
-                    SatelliteOnDeviceAccessController.create(s2CellFile);
+                    SatelliteOnDeviceAccessController.create(s2CellFile, mFeatureFlags);
             int s2Level = satelliteOnDeviceAccessController.getS2Level();
             if (s2Level < MIN_S2_LEVEL || s2Level > MAX_S2_LEVEL) {
                 ploge("isS2CellFileValid: invalid s2 level = " + s2Level);
@@ -946,6 +1149,18 @@
         }
     }
 
+    private void registerDefaultSmsAppChangedBroadcastReceiver(Context context) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            plogd("registerDefaultSmsAppChangedBroadcastReceiver: Flag "
+                    + "oemEnabledSatellite is disabled");
+            return;
+        }
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        intentFilter.addDataScheme("package");
+        context.registerReceiver(mDefaultSmsAppChangedBroadcastReceiver, intentFilter);
+    }
+
     private void registerLocationModeChangedBroadcastReceiver(Context context) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("registerLocationModeChangedBroadcastReceiver: Flag "
@@ -1002,8 +1217,8 @@
                         if (isRegionDisallowed(networkCountryIsoList)) {
                             Bundle bundle = new Bundle();
                             bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, false);
-                            mAccessControllerMetricsStats.setAccessControlType(
-                                    SatelliteConstants.ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE)
+                            mAccessControllerMetricsStats.setAccessControlType(SatelliteConstants
+                                            .ACCESS_CONTROL_TYPE_NETWORK_COUNTRY_CODE)
                                     .setCountryCodes(networkCountryIsoList);
                             sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
                                     false);
@@ -1049,7 +1264,7 @@
     }
 
     private void sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData,
-            boolean allowed) {
+                                                     boolean allowed) {
         plogd("sendSatelliteAllowResultToReceivers : resultCode is " + resultCode);
         if (resultCode == SATELLITE_RESULT_SUCCESS) {
             updateCurrentSatelliteAllowedState(allowed);
@@ -1057,18 +1272,103 @@
         synchronized (mLock) {
             for (ResultReceiver resultReceiver : mSatelliteAllowResultReceivers) {
                 resultReceiver.send(resultCode, resultData);
+                mSatelliteController.decrementResultReceiverCount(
+                        "SAC:requestIsCommunicationAllowedForCurrentLocation");
             }
             mSatelliteAllowResultReceivers.clear();
         }
         if (!shouldRetryValidatingPossibleChangeInAllowedRegion(resultCode)) {
             setIsSatelliteAllowedRegionPossiblyChanged(false);
         }
+        Integer disallowedReason = getDisallowedReason(resultCode, allowed);
+        boolean isChanged = false;
+        if (disallowedReason != SATELLITE_DISALLOWED_REASON_NONE) {
+            if (!mSatelliteDisallowedReasons.contains(disallowedReason)) {
+                isChanged = true;
+            }
+        } else {
+            if (mSatelliteDisallowedReasons.contains(
+                    SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION)
+                    || mSatelliteDisallowedReasons.contains(
+                    SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED)) {
+                isChanged = true;
+            }
+        }
+        mSatelliteDisallowedReasons.removeAll(DISALLOWED_REASONS_TO_BE_RESET);
+        if (disallowedReason != SATELLITE_DISALLOWED_REASON_NONE) {
+            mSatelliteDisallowedReasons.add(disallowedReason);
+        }
+        if (isChanged) {
+            handleEventDisallowedReasonsChanged();
+        }
         synchronized (mIsAllowedCheckBeforeEnablingSatelliteLock) {
             mIsAllowedCheckBeforeEnablingSatellite = false;
         }
         reportMetrics(resultCode, allowed);
     }
 
+    private int getDisallowedReason(int resultCode, boolean allowed) {
+        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+            if (!allowed) {
+                return SATELLITE_DISALLOWED_REASON_NOT_IN_ALLOWED_REGION;
+            }
+        } else if (resultCode == SATELLITE_RESULT_LOCATION_DISABLED) {
+            return SATELLITE_DISALLOWED_REASON_LOCATION_DISABLED;
+        }
+        return SATELLITE_DISALLOWED_REASON_NONE;
+    }
+
+    private void handleEventDisallowedReasonsChanged() {
+        logd("mSatelliteDisallowedReasons:"
+                + String.join(", ", mSatelliteDisallowedReasons.toString()));
+        notifySatelliteDisallowedReasonsChanged();
+        if (mNotifySatelliteAvailabilityEnabled) {
+            showSatelliteSystemNotification();
+        }
+    }
+
+    private void showSatelliteSystemNotification() {
+        if (mSatelliteDisallowedReasons.isEmpty()) {
+            if (!hasAlreadyNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN, 0)) {
+                mNotificationManager.notifyAsUser(
+                        NOTIFICATION_TAG,
+                        NOTIFICATION_ID,
+                        mSatelliteAvailableNotification,
+                        UserHandle.ALL
+                );
+                markAsNotified(KEY_AVAILABLE_NOTIFICATION_SHOWN, 0);
+            }
+        } else {
+            for (Integer reason : mSatelliteDisallowedReasons) {
+                if (!hasAlreadyNotified(KEY_UNAVAILABLE_NOTIFICATION_SHOWN, reason)) {
+                    mNotificationManager.notifyAsUser(
+                            NOTIFICATION_TAG,
+                            NOTIFICATION_ID,
+                            mSatelliteUnAvailableNotifications.get(reason),
+                            UserHandle.ALL
+                    );
+                    markAsNotified(KEY_UNAVAILABLE_NOTIFICATION_SHOWN, reason);
+                    break;
+                }
+            }
+        }
+    }
+
+    private boolean hasAlreadyNotified(String key, int reason) {
+        Set<String> reasons = mSharedPreferences.getStringSet(key, new HashSet<>());
+        return reasons.contains(String.valueOf(reason));
+    }
+
+    private void markAsNotified(String key, int reason) {
+        Set<String> reasons = mSharedPreferences.getStringSet(key, new HashSet<>());
+        if (!reasons.contains(String.valueOf(reason))) {
+            reasons.add(String.valueOf(reason));
+            SharedPreferences.Editor editor = mSharedPreferences.edit();
+            editor.putStringSet(key, reasons);
+            editor.apply();
+        }
+    }
+
     /**
      * Telephony-internal logic to verify if satellite access is restricted at the current
      * location.
@@ -1132,6 +1432,122 @@
         };
     }
 
+    private void initializeSatelliteSystemNotification(@NonNull Context context) {
+        final NotificationChannel notificationChannel = new NotificationChannel(
+                NOTIFICATION_CHANNEL_ID,
+                NOTIFICATION_CHANNEL,
+                NotificationManager.IMPORTANCE_DEFAULT
+        );
+        notificationChannel.setSound(null, null);
+        mNotificationManager = context.getSystemService(NotificationManager.class);
+        mNotificationManager.createNotificationChannel(notificationChannel);
+
+        createAvailableNotifications(context);
+        createUnavailableNotifications(context);
+    }
+
+    private Notification createNotification(@NonNull Context context,
+                                            String title,
+                                            String content) {
+        Notification.Builder notificationBuilder = new Notification.Builder(context)
+                .setContentTitle(title)
+                .setContentText(content)
+                .setSmallIcon(R.drawable.ic_android_satellite_24px)
+                .setChannelId(NOTIFICATION_CHANNEL_ID)
+                .setAutoCancel(true)
+                .setColor(context.getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setVisibility(Notification.VISIBILITY_PUBLIC);
+
+        return notificationBuilder.build();
+    }
+
+    private void createAvailableNotifications(Context context) {
+        int subId = mSatelliteController.getSelectedSatelliteSubId();
+        int titleId;
+        int summaryId;
+
+        if (mSatelliteController.isSatelliteServiceSupportedByCarrier(
+                subId, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
+            titleId = R.string.satellite_messaging_available_notification_title;
+            summaryId = R.string.satellite_messaging_available_notification_summary;
+        } else {
+            titleId = R.string.satellite_sos_available_notification_title;
+            summaryId = R.string.satellite_sos_available_notification_summary;
+        }
+
+        mSatelliteAvailableNotification = createNotification(
+                context,
+                context.getResources().getString(titleId),
+                context.getResources().getString(summaryId));
+    }
+
+    private void createUnavailableNotifications(Context context) {
+        int subId = mSatelliteController.getSelectedSatelliteSubId();
+
+        HashMap<Integer, Pair<Integer, Integer>> unavailableReasons;
+        if (mSatelliteController.isSatelliteServiceSupportedByCarrier(
+                subId, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
+            unavailableReasons = SATELLITE_MESSAGING_UNAVAILABLE_REASONS;
+        } else {
+            unavailableReasons = SATELLITE_SOS_UNAVAILABLE_REASONS;
+        }
+
+        for (int reason : unavailableReasons.keySet()) {
+            Pair<Integer, Integer> notificationString =
+                    unavailableReasons.getOrDefault(reason, null);
+            if (notificationString != null) {
+                mSatelliteUnAvailableNotifications.put(reason,
+                        createNotification(
+                                context,
+                                context.getResources().getString(notificationString.first),
+                                context.getResources().getString(notificationString.second)));
+            }
+        }
+    }
+
+    private final BroadcastReceiver mDefaultSmsAppChangedBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (intent.getAction()
+                            .equals(Intent.ACTION_PACKAGE_CHANGED)) {
+                        boolean isDefaultMsgAppSupported = false;
+                        ComponentName componentName =
+                                SmsApplication.getDefaultSmsApplicationAsUser(
+                                        context, true, context.getUser());
+                        logd("Current default SMS app:" + componentName);
+                        if (componentName != null) {
+                            String packageName = componentName.getPackageName();
+                            List<String> supportedMsgApps =
+                                    mSatelliteController.getSatelliteSupportedMsgApps(
+                                            mSatelliteController.getSelectedSatelliteSubId());
+                            if (supportedMsgApps.contains(packageName)) {
+                                isDefaultMsgAppSupported = true;
+                            }
+                        } else {
+                            logd("No default SMS app");
+                        }
+
+                        if (isDefaultMsgAppSupported) {
+                            if (mSatelliteDisallowedReasons.contains(Integer.valueOf(
+                                    SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP))) {
+                                mSatelliteDisallowedReasons.remove(Integer.valueOf(
+                                        SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP));
+                                handleEventDisallowedReasonsChanged();
+                            }
+                        } else {
+                            if (!mSatelliteDisallowedReasons.contains(Integer.valueOf(
+                                    SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP))) {
+                                mSatelliteDisallowedReasons.add(Integer.valueOf(
+                                        SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP));
+                                handleEventDisallowedReasonsChanged();
+                            }
+                        }
+                    }
+                }
+            };
+
     private void handleSatelliteAllowedRegionPossiblyChanged(int handleEvent) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             ploge("handleSatelliteAllowedRegionPossiblyChanged: "
@@ -1242,14 +1658,6 @@
 
     private void executeLocationQuery() {
         plogd("executeLocationQuery");
-        synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
-            if (isSatelliteAllowedRegionPossiblyChanged()) {
-                mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos =
-                        getElapsedRealtimeNanos();
-                plogd("mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos is set "
-                        + mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
-            }
-        }
         synchronized (mLock) {
             mFreshLastKnownLocation = getFreshLastKnownLocation();
             checkSatelliteAccessRestrictionUsingOnDeviceData();
@@ -1321,6 +1729,16 @@
                         + "Request for current location was already sent to LocationManager");
                 return;
             }
+
+            synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
+                if (isSatelliteAllowedRegionPossiblyChanged()) {
+                    mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos =
+                            getElapsedRealtimeNanos();
+                    plogd("mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos is set "
+                            + mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
+                }
+            }
+
             mLocationRequestCancellationSignal = new CancellationSignal();
             mLocationQueryStartTimeMillis = System.currentTimeMillis();
             mLocationManager.getCurrentLocation(LocationManager.FUSED_PROVIDER,
@@ -1372,7 +1790,7 @@
         }
     }
 
-    private void checkSatelliteAccessRestrictionForLocation(@NonNull Location location) {
+    protected void checkSatelliteAccessRestrictionForLocation(@NonNull Location location) {
         synchronized (mLock) {
             try {
                 SatelliteOnDeviceAccessController.LocationToken locationToken =
@@ -1380,8 +1798,11 @@
                                 location.getLatitude(),
                                 location.getLongitude(), mS2Level);
                 boolean satelliteAllowed;
+
                 if (mCachedAccessRestrictionMap.containsKey(locationToken)) {
-                    satelliteAllowed = mCachedAccessRestrictionMap.get(locationToken);
+                    mNewRegionalConfigId = mCachedAccessRestrictionMap.get(locationToken);
+                    satelliteAllowed = (mNewRegionalConfigId != null);
+                    plogd("mNewRegionalConfigId is " + mNewRegionalConfigId);
                 } else {
                     if (!initSatelliteOnDeviceAccessController()) {
                         ploge("Failed to init SatelliteOnDeviceAccessController");
@@ -1391,9 +1812,23 @@
                                 false);
                         return;
                     }
-                    satelliteAllowed = mSatelliteOnDeviceAccessController
-                            .isSatCommunicationAllowedAtLocation(locationToken);
-                    updateCachedAccessRestrictionMap(locationToken, satelliteAllowed);
+
+                    if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+                        synchronized (mLock) {
+                            mNewRegionalConfigId = mSatelliteOnDeviceAccessController
+                                    .getRegionalConfigIdForLocation(locationToken);
+                            plogd("mNewRegionalConfigId is " + mNewRegionalConfigId);
+                            satelliteAllowed = (mNewRegionalConfigId != null);
+                        }
+                    } else {
+                        plogd("checkSatelliteAccessRestrictionForLocation: "
+                                + "carrierRoamingNbIotNtn is disabled");
+                        satelliteAllowed = mSatelliteOnDeviceAccessController
+                                .isSatCommunicationAllowedAtLocation(locationToken);
+                        mNewRegionalConfigId =
+                                satelliteAllowed ? UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID : null;
+                    }
+                    updateCachedAccessRestrictionMap(locationToken, mNewRegionalConfigId);
                 }
                 mAccessControllerMetricsStats.setOnDeviceLookupTime(mOnDeviceLookupStartTimeMillis);
                 Bundle bundle = new Bundle();
@@ -1422,11 +1857,23 @@
         }
     }
 
+    private void updateRegionalConfigId() {
+        synchronized (mLock) {
+            plogd("mNewRegionalConfigId: updatedValue = " + mNewRegionalConfigId
+                    + " | mRegionalConfigId: beforeValue = " + mRegionalConfigId);
+            if (!Objects.equals(mRegionalConfigId, mNewRegionalConfigId)) {
+                mRegionalConfigId = mNewRegionalConfigId;
+                notifyRegionalSatelliteConfigurationChanged(
+                        mSatelliteAccessConfigMap.get(mRegionalConfigId));
+            }
+        }
+    }
+
     private void updateCachedAccessRestrictionMap(
             @NonNull SatelliteOnDeviceAccessController.LocationToken locationToken,
-            boolean satelliteAllowed) {
+            Integer regionalConfigId) {
         synchronized (mLock) {
-            mCachedAccessRestrictionMap.put(locationToken, satelliteAllowed);
+            mCachedAccessRestrictionMap.put(locationToken, regionalConfigId);
         }
     }
 
@@ -1580,7 +2027,8 @@
 
             try {
                 mSatelliteOnDeviceAccessController =
-                        SatelliteOnDeviceAccessController.create(getSatelliteS2CellFile());
+                        SatelliteOnDeviceAccessController.create(
+                                getSatelliteS2CellFile(), mFeatureFlags);
                 restartKeepOnDeviceAccessControllerResourcesTimer();
                 mS2Level = mSatelliteOnDeviceAccessController.getS2Level();
                 plogd("mS2Level=" + mS2Level);
@@ -1864,6 +2312,14 @@
                     logd("registerForCommunicationAllowedStateChanged: "
                             + "mCurrentSatelliteAllowedState " + mCurrentSatelliteAllowedState);
                 }
+                synchronized (mLock) {
+                    SatelliteAccessConfiguration satelliteAccessConfig =
+                            mSatelliteAccessConfigMap.get(mRegionalConfigId);
+                    callback.onSatelliteAccessConfigurationChanged(satelliteAccessConfig);
+                    logd("registerForCommunicationAllowedStateChanged: satelliteAccessConfig: "
+                            + satelliteAccessConfig + " of mRegionalConfigId: "
+                            + mRegionalConfigId);
+                }
             } catch (RemoteException ex) {
                 ploge("registerForCommunicationAllowedStateChanged: RemoteException ex=" + ex);
             }
@@ -1894,6 +2350,74 @@
     }
 
     /**
+     * Returns integer array of disallowed reasons of satellite.
+     *
+     * @return Integer array of disallowed reasons of satellite.
+     *
+     */
+    @NonNull public List<Integer> getSatelliteDisallowedReasons() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("getSatelliteDisallowedReasons: carrierRoamingNbIotNtn is disabled");
+            return new ArrayList<>();
+        }
+
+        synchronized (mSatelliteDisallowedReasonsLock) {
+            return mSatelliteDisallowedReasons;
+        }
+    }
+
+    /**
+     * Registers for disallowed reasons change event from satellite service.
+     *
+     * @param callback The callback to handle disallowed reasons changed event.
+     *
+     */
+    public void registerForSatelliteDisallowedReasonsChanged(
+            @NonNull ISatelliteDisallowedReasonsCallback callback) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("registerForSatelliteDisallowedReasonsChanged: carrierRoamingNbIotNtn is "
+                    + "disabled");
+            return;
+        }
+
+        mSatelliteDisallowedReasonsChangedListeners.put(callback.asBinder(), callback);
+
+        this.post(() -> {
+            try {
+                synchronized (mSatelliteDisallowedReasonsLock) {
+                    callback.onSatelliteDisallowedReasonsChanged(
+                            mSatelliteDisallowedReasons.stream()
+                                    .mapToInt(Integer::intValue)
+                                    .toArray());
+                    logd("registerForSatelliteDisallowedReasonsChanged: "
+                            + "mSatelliteDisallowedReasons " + mSatelliteDisallowedReasons.size());
+                }
+            } catch (RemoteException ex) {
+                ploge("registerForSatelliteDisallowedReasonsChanged: RemoteException ex=" + ex);
+            }
+        });
+    }
+
+    /**
+     * Unregisters for disallowed reasons change event from satellite service.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param callback The callback that was passed to
+     *                 {@link #registerForSatelliteDisallowedReasonsChanged(
+     *                 ISatelliteDisallowedReasonsCallback)}.
+     */
+    public void unregisterForSatelliteDisallowedReasonsChanged(
+            @NonNull ISatelliteDisallowedReasonsCallback callback) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("unregisterForSatelliteDisallowedReasonsChanged: "
+                    + "carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        mSatelliteDisallowedReasonsChangedListeners.remove(callback.asBinder());
+    }
+
+    /**
      * This API can be used by only CTS to set the cache whether satellite communication is allowed.
      *
      * @param state a state indicates whether satellite access allowed state should be cached and
@@ -1920,6 +2444,10 @@
                 mLatestSatelliteCommunicationAllowedSetTime = getElapsedRealtimeNanos();
                 mLatestSatelliteCommunicationAllowed = true;
                 mCurrentSatelliteAllowedState = true;
+            } else if ("cache_not_allowed".equalsIgnoreCase(state)) {
+                mLatestSatelliteCommunicationAllowedSetTime = getElapsedRealtimeNanos();
+                mLatestSatelliteCommunicationAllowed = false;
+                mCurrentSatelliteAllowedState = false;
             } else if ("cache_clear_and_not_allowed".equalsIgnoreCase(state)) {
                 mLatestSatelliteCommunicationAllowedSetTime = 0;
                 mLatestSatelliteCommunicationAllowed = false;
@@ -1955,6 +2483,45 @@
         });
     }
 
+    private void notifySatelliteDisallowedReasonsChanged() {
+        plogd("notifySatelliteDisallowedReasonsChanged");
+
+        List<ISatelliteDisallowedReasonsCallback> deadCallersList = new ArrayList<>();
+        mSatelliteDisallowedReasonsChangedListeners.values().forEach(listener -> {
+            try {
+                listener.onSatelliteDisallowedReasonsChanged(
+                        mSatelliteDisallowedReasons.stream()
+                                .mapToInt(Integer::intValue)
+                                .toArray());
+            } catch (RemoteException e) {
+                plogd("notifySatelliteDisallowedReasonsChanged RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSatelliteDisallowedReasonsChangedListeners.remove(listener.asBinder());
+        });
+    }
+
+    protected void notifyRegionalSatelliteConfigurationChanged(
+            @Nullable SatelliteAccessConfiguration satelliteAccessConfig) {
+        plogd("notifyRegionalSatelliteConfigurationChanged : satelliteAccessConfig is "
+                + satelliteAccessConfig);
+
+        List<ISatelliteCommunicationAllowedStateCallback> deadCallersList = new ArrayList<>();
+        mSatelliteCommunicationAllowedStateChangedListeners.values().forEach(listener -> {
+            try {
+                listener.onSatelliteAccessConfigurationChanged(satelliteAccessConfig);
+            } catch (RemoteException e) {
+                plogd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSatelliteCommunicationAllowedStateChangedListeners.remove(listener.asBinder());
+        });
+    }
+
     private void reportMetrics(int resultCode, boolean allowed) {
         if (resultCode == SATELLITE_RESULT_SUCCESS) {
             mControllerMetricsStats.reportAllowedSatelliteAccessCount(allowed);
@@ -2016,6 +2583,32 @@
         }
     }
 
+    /**
+     * This API can be used only for test purpose to override the carrier roaming Ntn eligibility
+     *
+     * @param state         to update Ntn Eligibility.
+     * @param resetRequired to reset the overridden flag in satellite controller.
+     * @return {@code true} if the shell command is successful, {@code false} otherwise.
+     */
+    public boolean overrideCarrierRoamingNtnEligibilityChanged(boolean state,
+            boolean resetRequired) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("overrideCarrierRoamingNtnEligibilityChanged: "
+                    + "carrierRoamingNbIotNtn is disabled");
+            return false;
+        }
+
+        if (!isMockModemAllowed()) {
+            logd("overrideCarrierRoamingNtnEligibilityChanged: "
+                    + "mock modem not allowed.");
+            return false;
+        }
+
+        logd("calling overrideCarrierRoamingNtnEligibilityChanged");
+        return mSatelliteController.overrideCarrierRoamingNtnEligibilityChanged(state,
+                resetRequired);
+    }
+
     private void plogv(@NonNull String log) {
         Rlog.v(TAG, log);
         if (mPersistentLogger != null) {
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java
index 520699f..2d7cf96 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java
@@ -16,6 +16,9 @@
 package com.android.phone.satellite.accesscontrol;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.telephony.flags.FeatureFlags;
 
 import java.io.Closeable;
 import java.io.File;
@@ -34,13 +37,15 @@
      * but at the cost of some memory, or close it immediately after a single use.
      *
      * @param file The input file that contains the location-based access restriction information.
-     * @throws IOException in the unlikely event of errors when reading underlying file(s)
+     * @throws IOException              in the unlikely event of errors when reading underlying
+     *                                  file(s)
      * @throws IllegalArgumentException if the input file format does not match the format defined
-     * by the device overlay configs.
+     *                                  by the device overlay configs.
      */
     public static SatelliteOnDeviceAccessController create(
-            @NonNull File file) throws IOException, IllegalArgumentException {
-        return S2RangeSatelliteOnDeviceAccessController.create(file);
+            @NonNull File file, @NonNull FeatureFlags featureFlags)
+            throws IOException, IllegalArgumentException {
+        return S2RangeSatelliteOnDeviceAccessController.create(file, featureFlags);
     }
 
     /**
@@ -83,4 +88,14 @@
         /** This will print out the location information */
         public abstract String toPiiString();
     }
+
+    /**
+     * Returns an unsigned integer if a regional access control config ID is found for the current
+     * location, {@code null} otherwise.
+     *
+     * @throws IOException in the unlikely event of errors when reading the underlying file
+     */
+    @Nullable
+    public abstract Integer getRegionalConfigIdForLocation(LocationToken locationToken)
+            throws IOException;
 }
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 24d680c..4d98d92 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -85,7 +85,6 @@
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.satellite.EnableRequestAttributes;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -368,7 +367,7 @@
 
     private String mActionEsos;
     private String mActionEsosDemo;
-
+    private Intent mNonEsosIntent;
     private TelephonyDisplayInfo mDisplayInfo;
 
     private List<PhysicalChannelConfig> mPhysicalChannelConfigs = new ArrayList<>();
@@ -786,33 +785,33 @@
         mEsosDemoButton  = (Button) findViewById(R.id.demo_esos_questionnaire);
         mSatelliteEnableNonEmergencyModeButton = (Button) findViewById(
                 R.id.satellite_enable_non_emergency_mode);
-        CarrierConfigManager cm = getSystemService(CarrierConfigManager.class);
-        if (!cm.getConfigForSubId(mSubId,
-                        CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL)
-                .getBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL)) {
-            mSatelliteEnableNonEmergencyModeButton.setVisibility(View.GONE);
-        }
-        if (!Build.isDebuggable()) {
-            if (!TextUtils.isEmpty(mActionEsos)) {
-                mEsosButton.setVisibility(View.GONE);
-            }
-            if (!TextUtils.isEmpty(mActionEsosDemo)) {
-                mEsosDemoButton.setVisibility(View.GONE);
-            }
-            mSatelliteEnableNonEmergencyModeButton.setVisibility(View.GONE);
+
+        if (shouldHideButton(mActionEsos)) {
+            mEsosButton.setVisibility(View.GONE);
         } else {
             mEsosButton.setOnClickListener(v -> startActivityAsUser(
                     new Intent(mActionEsos).addFlags(
                             Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
                     UserHandle.CURRENT)
             );
+        }
+        if (shouldHideButton(mActionEsosDemo)) {
+            mEsosDemoButton.setVisibility(View.GONE);
+        } else {
             mEsosDemoButton.setOnClickListener(v -> startActivityAsUser(
                     new Intent(mActionEsosDemo).addFlags(
                             Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
                     UserHandle.CURRENT)
             );
-            mSatelliteEnableNonEmergencyModeButton.setOnClickListener(v ->
-                    enableSatelliteNonEmergencyMode());
+        }
+        if (shouldHideNonEmergencyMode()) {
+            mSatelliteEnableNonEmergencyModeButton.setVisibility(View.GONE);
+        } else {
+            mSatelliteEnableNonEmergencyModeButton.setOnClickListener(v -> {
+                if (mNonEsosIntent != null) {
+                    sendBroadcast(mNonEsosIntent);
+                }
+            });
         }
 
         mOemInfoButton = (Button) findViewById(R.id.oem_info);
@@ -836,6 +835,21 @@
         restoreFromBundle(icicle);
     }
 
+    boolean shouldHideButton(String action) {
+        if (!Build.isDebuggable()) {
+            return true;
+        }
+        if (TextUtils.isEmpty(action)) {
+            return true;
+        }
+        PackageManager pm = getPackageManager();
+        Intent intent = new Intent(action);
+        if (pm.resolveActivity(intent, 0) == null) {
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public Intent getParentActivityIntent() {
         Intent parentActivity = super.getParentActivityIntent();
@@ -1411,7 +1425,9 @@
     }
 
     private void updateNetworkType() {
-        if (SubscriptionManager.isValidPhoneId(mPhoneId)) {
+        SubscriptionManager mSm = getSystemService(SubscriptionManager.class);
+        if (SubscriptionManager.isValidPhoneId(mPhoneId)
+                && mSm.isActiveSubscriptionId(mSubId)) {
             mDataNetwork.setText(ServiceState.rilRadioTechnologyToString(
                     mTelephonyManager.getServiceStateForSlot(mPhoneId)
                             .getRilDataRadioTechnology()));
@@ -2138,27 +2154,68 @@
                 }
             };
 
-    /**
-     * Enable modem satellite for non-emergency mode.
-     */
-    private void enableSatelliteNonEmergencyMode() {
-        SatelliteManager sm = getSystemService(SatelliteManager.class);
+    private boolean shouldHideNonEmergencyMode() {
+        if (!Build.isDebuggable()) {
+            return true;
+        }
+        String action  = SatelliteManager.ACTION_SATELLITE_START_NON_EMERGENCY_SESSION;
+        if (TextUtils.isEmpty(action)) {
+            return true;
+        }
+        if (mNonEsosIntent != null) {
+            mNonEsosIntent = null;
+        }
         CarrierConfigManager cm = getSystemService(CarrierConfigManager.class);
-        if (sm == null || cm == null) {
-            loge("enableSatelliteNonEmergencyMode: sm or cm is null");
-            return;
+        if (cm == null) {
+            loge("shouldHideNonEmergencyMode: cm is null");
+            return true;
         }
-        if (!cm.getConfigForSubId(mSubId,
-                CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL)
-                .getBoolean(CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL)) {
-            loge("enableSatelliteNonEmergencyMode: KEY_SATELLITE_ATTACH_SUPPORTED_BOOL is false");
-            return;
+        PersistableBundle bundle = cm.getConfigForSubId(mSubId,
+                CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL);
+        if (!bundle.getBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false)) {
+            log("shouldHideNonEmergencyMode: esos_supported false");
+            return true;
         }
-        log("enableSatelliteNonEmergencyMode: requestEnabled");
-        sm.requestEnabled(new EnableRequestAttributes.Builder(true)
-                        .setDemoMode(false).setEmergencyMode(false).build(),
-                Runnable::run, res -> log("enableSatelliteNonEmergencyMode: " + res)
-        );
+        if (!bundle.getBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false)) {
+            log("shouldHideNonEmergencyMode: attach_supported false");
+            return true;
+        }
+
+        String packageName = getStringFromOverlayConfig(
+                com.android.internal.R.string.config_satellite_gateway_service_package);
+
+        String className = getStringFromOverlayConfig(com.android.internal.R.string
+                .config_satellite_carrier_roaming_non_emergency_session_class);
+        if (packageName == null || className == null
+                || packageName.isEmpty() || className.isEmpty()) {
+            Log.d(TAG, "shouldHideNonEmergencyMode:"
+                    + " packageName or className is null or empty.");
+            return true;
+        }
+        PackageManager pm = getPackageManager();
+        Intent intent = new Intent(action);
+        intent.setComponent(new ComponentName(packageName, className));
+        if (pm.queryBroadcastReceivers(intent, 0).isEmpty()) {
+            Log.d(TAG, "shouldHideNonEmergencyMode: Broadcast receiver not found for intent: "
+                    + intent);
+            return true;
+        }
+        mNonEsosIntent = intent;
+        return false;
+    }
+
+    private String getStringFromOverlayConfig(int resourceId) {
+        String name;
+        try {
+            name = getResources().getString(resourceId);
+        } catch (Resources.NotFoundException ex) {
+            loge("getStringFromOverlayConfig: ex=" + ex);
+            name = null;
+        }
+        return name;
     }
 
     private boolean isImsVolteProvisioned() {
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 48786dc..e753e20 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -1046,7 +1046,7 @@
     }
 
     private static Integer getSatelliteErrorString() {
-        if (SatelliteController.getInstance().isSatelliteEnabled()) {
+        if (SatelliteController.getInstance().isSatelliteEnabledOrBeingEnabled()) {
             return R.string.incall_error_satellite_enabled;
         }
         return R.string.incall_error_carrier_roaming_satellite_mode;
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 7f0c800..af1ddb6 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -430,9 +430,9 @@
 
         super(phoneAccountHandle);
 
-        mTelecomAccountRegistry = telecomAccountRegistry;
-        mFeatureFlagProxy = featureFlagProxy;
-        mCarrierConfig = carrierConfig;
+        mTelecomAccountRegistry = Objects.requireNonNull(telecomAccountRegistry);
+        mFeatureFlagProxy = Objects.requireNonNull(featureFlagProxy);
+        mCarrierConfig = Objects.requireNonNull(carrierConfig);
 
         // Specify the connection time of the conference to be the connection time of the original
         // connection.
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index fa2151b..ca3bcfe 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -142,9 +142,9 @@
     public ImsConferenceController(TelecomAccountRegistry telecomAccountRegistry,
             TelephonyConnectionServiceProxy connectionService,
             ImsConference.FeatureFlagProxy featureFlagProxy) {
-        mConnectionService = connectionService;
-        mTelecomAccountRegistry = telecomAccountRegistry;
-        mFeatureFlagProxy = featureFlagProxy;
+        mConnectionService = Objects.requireNonNull(connectionService);
+        mTelecomAccountRegistry = Objects.requireNonNull(telecomAccountRegistry);
+        mFeatureFlagProxy = Objects.requireNonNull(featureFlagProxy);
     }
 
     void addConference(ImsConference conference) {
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index c39d121..8956266 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -70,6 +70,7 @@
 import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.phone.PhoneGlobals;
+import com.android.phone.PhoneInterfaceManager;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
 import com.android.telephony.Rlog;
@@ -434,7 +435,7 @@
             boolean isVideoEnabledByPlatform = ImsManager.getInstance(mPhone.getContext(),
                     mPhone.getPhoneId()).isVtEnabledByPlatform();
 
-            if (!mIsPrimaryUser) {
+            if (!mDoesUserSupportVideoCalling) {
                 Log.i(this, "Disabling video calling for secondary user.");
                 mIsVideoCapable = false;
                 isVideoEnabledByPlatform = false;
@@ -651,8 +652,8 @@
             // Check if IMS video pause is supported.
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
+            if (b == null) return false;
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
         }
 
         /**
@@ -697,8 +698,8 @@
         private boolean isCarrierInstantLetteringSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL);
+            if (b == null) return false;
+            return b.getBoolean(CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL);
         }
 
         /**
@@ -709,8 +710,8 @@
         private boolean isCarrierAdhocConferenceCallSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL);
+            if (b == null) return false;
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL);
         }
 
 
@@ -722,8 +723,8 @@
         private boolean isCarrierMergeCallSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_CONFERENCE_CALL_BOOL);
+            if (b == null) return false;
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_CONFERENCE_CALL_BOOL);
         }
 
         /**
@@ -734,6 +735,7 @@
         private boolean isCarrierMergeImsCallSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return false;
             return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL);
         }
 
@@ -745,8 +747,8 @@
         private boolean isCarrierEmergencyVideoCallsAllowed() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
+            if (b == null) return false;
+            return b.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
         }
 
         /**
@@ -757,8 +759,8 @@
         private boolean isCarrierVideoConferencingSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL);
+            if (b == null) return false;
+            return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL);
         }
 
         /**
@@ -771,7 +773,8 @@
         private boolean isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null && b.getBoolean(
+            if (b == null) return false;
+            return b.getBoolean(
                     CarrierConfigManager.KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL);
         }
 
@@ -784,6 +787,7 @@
         private boolean isCarrierManageImsConferenceCallSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return false;
             return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL);
         }
 
@@ -796,6 +800,7 @@
         private boolean isCarrierUsingSimCallManager() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return false;
             return !TextUtils.isEmpty(
                     b.getString(CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING));
         }
@@ -810,6 +815,7 @@
         private boolean isCarrierShowPreciseFailedCause() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return false;
             return b.getBoolean(CarrierConfigManager.KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL);
         }
 
@@ -822,6 +828,7 @@
         private boolean isCarrierUseCallRecordingTone() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return false;
             return b.getBoolean(CarrierConfigManager.KEY_PLAY_CALL_RECORDING_TONE_BOOL);
         }
 
@@ -831,6 +838,7 @@
         private boolean isCarrierAllowRttWhenRoaming() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return false;
             return b.getBoolean(CarrierConfigManager.KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL);
         }
 
@@ -843,6 +851,7 @@
         private Bundle getPhoneAccountExtras() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) return new Bundle();
 
             int instantLetteringMaxLength = b.getInt(
                     CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT);
@@ -1054,8 +1063,10 @@
 
             boolean hasVoiceAvailability = isImsVoiceAvailable();
 
-            boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr
-                    .isRttEnabled(mPhone.getSubId());
+            PhoneInterfaceManager phoneMgr = PhoneGlobals.getInstance()
+                .phoneMgr;
+            boolean isRttSupported = (phoneMgr != null) ?
+                phoneMgr.isRttEnabled(mPhone.getSubId()) : false;
 
             boolean isRoaming = mTelephonyManager.isNetworkRoaming(mPhone.getSubId());
             boolean isOnWfc = mPhone.getImsRegistrationTech()
@@ -1214,7 +1225,7 @@
                 Log.i(this, "TelecomAccountRegistry: User changed, re-registering phone accounts.");
 
                 UserHandle currentUser = intent.getParcelableExtra(Intent.EXTRA_USER);
-                mIsPrimaryUser = currentUser == null ? true : currentUser.isSystem();
+                mDoesUserSupportVideoCalling = currentUser == null ? true : currentUser.isSystem();
 
                 // Any time the user changes, re-register the accounts.
                 tearDownAccounts();
@@ -1287,7 +1298,8 @@
     private int mSubscriptionListenerState = LISTENER_STATE_UNREGISTERED;
     private int mServiceState = ServiceState.STATE_POWER_OFF;
     private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-    private boolean mIsPrimaryUser = UserHandle.of(ActivityManager.getCurrentUser()).isSystem();
+    private boolean mDoesUserSupportVideoCalling =
+            UserHandle.of(ActivityManager.getCurrentUser()).isSystem();
     private ExponentialBackoff mRegisterSubscriptionListenerBackoff;
     private ExponentialBackoff mTelecomReadyBackoff;
     private final HandlerThread mHandlerThread = new HandlerThread("TelecomAccountRegistry");
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 3280c04..c69880d 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1185,8 +1185,7 @@
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
                 || (isRadioPowerDownOnBluetooth() && !isPhoneWifiCallingEnabled);
 
-        if (mSatelliteController.isSatelliteEnabled()
-                || mSatelliteController.isSatelliteBeingEnabled()) {
+        if (mSatelliteController.isSatelliteEnabledOrBeingEnabled()) {
             Log.d(this, "onCreateOutgoingConnection, "
                     + " needToTurnOnRadio=" + needToTurnOnRadio
                     + " needToTurnOffSatellite=" + needToTurnOffSatellite
@@ -1203,20 +1202,17 @@
             }
         }
 
-        boolean forNormalRoutingEmergencyCall = false;
         if (mDomainSelectionResolver.isDomainSelectionSupported()) {
-            if (isEmergencyNumber) {
-                // Normal routing emergency number shall be handled by normal call domain selector.
-                int routing = getEmergencyCallRouting(phone, number, needToTurnOnRadio);
-                if (routing != EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL) {
-                    final Connection resultConnection =
-                            placeEmergencyConnection(phone,
-                                    request, numberToDial, isTestEmergencyNumber,
-                                    handle, needToTurnOnRadio, routing);
-                    if (resultConnection != null) return resultConnection;
-                }
-                forNormalRoutingEmergencyCall = true;
-                Log.d(this, "onCreateOutgoingConnection, forNormalRoutingEmergencyCall");
+            // Normal routing emergency number shall be handled by normal call domain selector.
+            int routing = (isEmergencyNumber)
+                    ? getEmergencyCallRouting(phone, number, needToTurnOnRadio)
+                    : EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+            if (isEmergencyNumber && routing != EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL) {
+                final Connection resultConnection =
+                        placeEmergencyConnection(phone,
+                                request, numberToDial, isTestEmergencyNumber,
+                                handle, needToTurnOnRadio, routing);
+                if (resultConnection != null) return resultConnection;
             }
         }
 
@@ -1287,8 +1283,7 @@
                         // reporting the OUT_OF_SERVICE state.
                         return phone.getState() == PhoneConstants.State.OFFHOOK
                                 || (phone.getServiceStateTracker().isRadioOn()
-                                && (!mSatelliteController.isSatelliteEnabled()
-                                    && !mSatelliteController.isSatelliteBeingEnabled()));
+                                && !mSatelliteController.isSatelliteEnabledOrBeingEnabled());
                     } else {
                         SubscriptionInfoInternal subInfo = SubscriptionManagerService
                                 .getInstance().getSubscriptionInfoInternal(phone.getSubId());
@@ -1305,7 +1300,7 @@
                     }
                 }
             }, isEmergencyNumber && !isTestEmergencyNumber, phone, isTestEmergencyNumber,
-                    timeoutToOnTimeoutCallback, forNormalRoutingEmergencyCall);
+                    timeoutToOnTimeoutCallback);
             // Return the still unconnected GsmConnection and wait for the Radios to boot before
             // connecting it to the underlying Phone.
             return resultConnection;
@@ -2152,8 +2147,7 @@
     }
 
     private boolean shouldExitSatelliteModeForEmergencyCall(boolean isEmergencyNumber) {
-        if (!mSatelliteController.isSatelliteEnabled()
-                && !mSatelliteController.isSatelliteBeingEnabled()) {
+        if (!mSatelliteController.isSatelliteEnabledOrBeingEnabled()) {
             return false;
         }
 
@@ -2167,16 +2161,12 @@
                 return true;
             } else { // satellite is for emergency
                 if (mFeatureFlags.carrierRoamingNbIotNtn()) {
-                    Phone satellitePhone = mSatelliteController.getSatellitePhone();
-                    if (satellitePhone == null) {
-                        loge("satellite is/being enabled, but satellitePhone is null");
-                        return false;
-                    }
+                    int subId = mSatelliteController.getSelectedSatelliteSubId();
                     SubscriptionInfoInternal info = SubscriptionManagerService.getInstance()
-                            .getSubscriptionInfoInternal(satellitePhone.getSubId());
+                            .getSubscriptionInfoInternal(subId);
                     if (info == null) {
                         loge("satellite is/being enabled, but satellite sub "
-                                + satellitePhone.getSubId() + " is null");
+                                + subId + " is null");
                         return false;
                     }
 
@@ -4827,8 +4817,12 @@
             mSatelliteSOSMessageRecommender = new SatelliteSOSMessageRecommender(phone.getContext(),
                     phone.getContext().getMainLooper());
         }
+
+        String number = connection.getAddress().getSchemeSpecificPart();
+        final boolean isTestEmergencyNumber = isEmergencyNumberTestNumber(number);
+
         connection.addTelephonyConnectionListener(mEmergencyConnectionSatelliteListener);
-        mSatelliteSOSMessageRecommender.onEmergencyCallStarted(connection);
+        mSatelliteSOSMessageRecommender.onEmergencyCallStarted(connection, isTestEmergencyNumber);
         mSatelliteSOSMessageRecommender.onEmergencyCallConnectionStateChanged(
                 connection.getTelecomCallId(), connection.STATE_DIALING);
     }
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
index 37813e3..a7ed708 100644
--- a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -140,7 +140,10 @@
 
         if (subId == getSubId()) {
             logd("NormalCallDomainSelection triggered. Sub-id:" + subId);
-            sendEmptyMessageDelayed(MSG_WAIT_FOR_IMS_STATE_TIMEOUT, WAIT_FOR_IMS_STATE_TIMEOUT_MS);
+            if (!mReselectDomain) {
+                sendEmptyMessageDelayed(MSG_WAIT_FOR_IMS_STATE_TIMEOUT,
+                        WAIT_FOR_IMS_STATE_TIMEOUT_MS);
+            }
             post(() -> selectDomain());
         } else {
             mSelectorState = SelectorState.INACTIVE;
diff --git a/testapps/TestSatelliteApp/AndroidManifest.xml b/testapps/TestSatelliteApp/AndroidManifest.xml
index eaddf95..a1f22fa 100644
--- a/testapps/TestSatelliteApp/AndroidManifest.xml
+++ b/testapps/TestSatelliteApp/AndroidManifest.xml
@@ -20,6 +20,7 @@
     <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE"/>
     <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION"/>
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.SEND_SMS"/>
     <application android:label="SatelliteTestApp">
         <activity android:name=".SatelliteTestApp"
              android:label="SatelliteTestApp"
diff --git a/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml b/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml
index 9e53f41..ba61328 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_Datagram.xml
@@ -15,12 +15,14 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
     android:paddingLeft="4dp">
 
     <LinearLayout
@@ -125,4 +127,4 @@
             android:textColor="@android:color/holo_blue_light"
             android:textSize="15dp" />
     </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_MultipleSendReceive.xml b/testapps/TestSatelliteApp/res/layout/activity_MultipleSendReceive.xml
index 3632ecb..c81eb3b 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_MultipleSendReceive.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_MultipleSendReceive.xml
@@ -15,12 +15,14 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
     android:paddingLeft="4dp">
 
     <LinearLayout
@@ -95,4 +97,4 @@
             android:layout_centerVertical="true"
             android:textSize="15dp" />
     </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_NbIotSatellite.xml b/testapps/TestSatelliteApp/res/layout/activity_NbIotSatellite.xml
index c33522e..fef4292 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_NbIotSatellite.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_NbIotSatellite.xml
@@ -15,79 +15,86 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="center"
-    android:paddingStart="4dp"
-    android:paddingEnd="4dp">
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
+    android:paddingLeft="4dp">
 
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        android:layout_weight="0"
-        android:textColor="@android:color/holo_blue_dark"
-        android:textSize="20sp"
-        android:text="@string/NbIotSatellite"/>
-    <Button
-        android:id="@+id/testRegisterForSupportedStateChanged"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/testRegisterForSupportedStateChanged"/>
-    <Button
-        android:id="@+id/testUnregisterForSupportedStateChanged"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/testUnregisterForSupportedStateChanged"/>
-    <Button
-        android:id="@+id/testRequestIsSupported"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/testRequestIsSupported"/>
-     <Button
-        android:id="@+id/reportSatelliteSupportedFromModem"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/reportSatelliteSupportedFromModem"/>
-    <Button
-        android:id="@+id/reportSatelliteNotSupportedFromModem"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/reportSatelliteNotSupportedFromModem"/>
-    <Button
-        android:id="@+id/showCurrentSatelliteSupportedStated"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/showCurrentSatelliteSupportedStated"/>
-    <Button
-        android:id="@+id/Back"
-        android:onClick="Back"
-        android:textColor="@android:color/holo_blue_dark"
-        android:layout_marginTop="100dp"
-        android:layout_gravity="center"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:text="@string/Back"/>
-    <TextView
-        android:id="@+id/text_id"
-        android:layout_width="300dp"
-        android:layout_height="200dp"
-        android:textColor="@android:color/holo_blue_light"
-        android:textSize="15sp" />
-</LinearLayout>
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="0"
+            android:textColor="@android:color/holo_blue_dark"
+            android:textSize="20sp"
+            android:text="@string/NbIotSatellite"/>
+        <Button
+            android:id="@+id/testRegisterForSupportedStateChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/testRegisterForSupportedStateChanged"/>
+        <Button
+            android:id="@+id/testUnregisterForSupportedStateChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/testUnregisterForSupportedStateChanged"/>
+        <Button
+            android:id="@+id/testRequestIsSupported"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/testRequestIsSupported"/>
+         <Button
+            android:id="@+id/reportSatelliteSupportedFromModem"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/reportSatelliteSupportedFromModem"/>
+        <Button
+            android:id="@+id/reportSatelliteNotSupportedFromModem"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/reportSatelliteNotSupportedFromModem"/>
+        <Button
+            android:id="@+id/showCurrentSatelliteSupportedStated"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/showCurrentSatelliteSupportedStated"/>
+        <Button
+            android:id="@+id/Back"
+            android:onClick="Back"
+            android:textColor="@android:color/holo_blue_dark"
+            android:layout_marginTop="100dp"
+            android:layout_gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/Back"/>
+        <TextView
+            android:id="@+id/text_id"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:textColor="@android:color/holo_blue_light"
+            android:textSize="15sp" />
+    </LinearLayout>
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml b/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml
index da5105d..afcc706 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_Provisioning.xml
@@ -15,12 +15,14 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
     android:paddingLeft="4dp">
 
     <LinearLayout
@@ -90,4 +92,4 @@
             android:layout_centerVertical="true"
             android:textSize="15dp" />
     </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
index 151f6ca..23f6ee9 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
@@ -21,7 +21,8 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="center"
-    android:paddingTop="100dp"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
     android:paddingLeft="4dp">
 
     <LinearLayout
@@ -37,11 +38,17 @@
             android:textSize="20dp"
             android:text="Satellite Control APIs"/>
         <Button
-            android:id="@+id/enableSatellite"
+            android:id="@+id/enableSatelliteDemoMode"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingRight="4dp"
-            android:text="@string/enableSatellite"/>
+            android:text="@string/enableSatelliteDemoMode"/>
+        <Button
+            android:id="@+id/enableSatelliteRealMode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/enableSatelliteRealMode"/>
         <Button
             android:id="@+id/disableSatellite"
             android:layout_width="match_parent"
@@ -138,6 +145,12 @@
             android:layout_height="wrap_content"
             android:paddingRight="4dp"
             android:text="@string/provisionSatellite"/>
+        <Button
+            android:id="@+id/deprovisionSatellite"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/deprovisionSatellite"/>
          <Button
             android:id="@+id/Back"
             android:onClick="Back"
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
index 8fdc01f..26b45e3 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
@@ -15,12 +15,14 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:paddingTop="100dp"
+    android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
     android:paddingLeft="4dp">
 
     <LinearLayout
@@ -80,4 +82,4 @@
             android:paddingEnd="4dp"
             android:text="@string/TestSatelliteWrapper"/>
     </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SendReceive.xml b/testapps/TestSatelliteApp/res/layout/activity_SendReceive.xml
index 6490e5d..4ac3483 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_SendReceive.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_SendReceive.xml
@@ -15,12 +15,14 @@
   ~ limitations under the License
   -->
 
-<LinearLayout
+<ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
     android:paddingLeft="4dp">
 
     <LinearLayout
@@ -94,4 +96,4 @@
             android:layout_centerVertical="true"
             android:textSize="15dp" />
     </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_TestSatelliteWrapper.xml b/testapps/TestSatelliteApp/res/layout/activity_TestSatelliteWrapper.xml
index 39a4bd6..d046f03 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_TestSatelliteWrapper.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_TestSatelliteWrapper.xml
@@ -14,10 +14,16 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
+
 <ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp"
+    android:paddingLeft="4dp">
 
     <LinearLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
@@ -167,6 +173,37 @@
             android:layout_height="wrap_content"
             android:paddingRight="4dp"
             android:text="@string/unregisterForModemStateChanged"/>
+        <Button
+            android:id="@+id/requestSatelliteSubscriberProvisionStatusWrapper"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestSatelliteSubscriberProvisionStatus"/>
+        <Button
+            android:id="@+id/provisionSatelliteWrapper"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/provisionSatellite"/>
+        <Button
+            android:id="@+id/deprovisionSatelliteWrapper"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/deprovisionSatellite"/>
+        <Button
+            android:id="@+id/setNtnSmsSupportedTrue"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/setNtnSmsSupportedTrue"/>
+        <Button
+            android:id="@+id/setNtnSmsSupportedFalse"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/setNtnSmsSupportedFalse"/>
+
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -203,5 +240,4 @@
             android:layout_centerVertical="true"
             android:textSize="8dp" />
     </LinearLayout>
-
-</ScrollView>
\ No newline at end of file
+</ScrollView>
diff --git a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
index 728576a..4b5ea5b 100644
--- a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
@@ -23,7 +23,8 @@
     <string name="SendReceive">Send and Receive datagrams</string>
     <string name="NbIotSatellite">NB IoT Satellite modem interface test</string>
 
-    <string name="enableSatellite">enableSatellite</string>
+    <string name="enableSatelliteRealMode">enableSatellite Real Mode</string>
+    <string name="enableSatelliteDemoMode">enableSatellite Demo Mode</string>
     <string name="disableSatellite">disableSatellite</string>
     <string name="requestIsSatelliteEnabled">requestIsSatelliteEnabled</string>
     <string name="requestIsDemoModeEnabled">requestIsDemoModeEnabled</string>
@@ -95,6 +96,10 @@
 
     <string name="requestSatelliteSubscriberProvisionStatus">requestSatelliteSubscriberProvisionStatus</string>
     <string name="provisionSatellite">provisionSatellite</string>
+    <string name="deprovisionSatellite">deprovisionSatellite</string>
+    <string name="setNtnSmsSupportedTrue">setNtnSmsSupportedTrue</string>
+    <string name="setNtnSmsSupportedFalse">setNtnSmsSupportedFalse</string>
+
 
     <string name="Back">Back</string>
     <string name="ClearLog">Clear Log</string>
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
index 15c8fd8..08984be 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
@@ -24,12 +24,14 @@
 import android.os.OutcomeReceiver;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.SatelliteProvisionStateCallback;
+import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
 import android.telephony.satellite.stub.SatelliteResult;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.TextView;
 
+import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -82,6 +84,12 @@
             Log.d(TAG, "onSatelliteProvisionStateChanged in SatelliteTestApp: provisioned="
                     + mProvisioned);
         }
+
+        @Override
+        public void onSatelliteSubscriptionProvisionStateChanged(
+                List<SatelliteSubscriberProvisionStatus> list) {
+            Log.d(TAG, "onSatelliteSubscriptionProvisionStateChanged in SatelliteTestApp" + list);
+        }
     }
 
     private void provisionServiceApp(View view) {
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
index 379fc74..484a6d1 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
@@ -35,6 +35,7 @@
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.List;
+import android.util.Log;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -44,7 +45,7 @@
  */
 public class SatelliteControl extends Activity {
 
-    private static final long TIMEOUT = 3000;
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(3);
 
     private SatelliteManager mSatelliteManager;
     private SubscriptionManager mSubscriptionManager;
@@ -58,8 +59,10 @@
         mSubscriptionManager = getSystemService(SubscriptionManager.class);
 
         setContentView(R.layout.activity_SatelliteControl);
-        findViewById(R.id.enableSatellite)
-                .setOnClickListener(this::enableSatelliteApp);
+        findViewById(R.id.enableSatelliteDemoMode)
+                .setOnClickListener(v -> enableSatelliteApp(/* isDemoMode */ true));
+        findViewById(R.id.enableSatelliteRealMode)
+                .setOnClickListener(v -> enableSatelliteApp(/* isDemoMode */ false));
         findViewById(R.id.disableSatellite)
                 .setOnClickListener(this::disableSatelliteApp);
         findViewById(R.id.requestIsSatelliteEnabled)
@@ -92,6 +95,8 @@
                 .setOnClickListener(this::requestSatelliteSubscriberProvisionStatusApp);
         findViewById(R.id.provisionSatellite)
                 .setOnClickListener(this::provisionSatelliteApp);
+        findViewById(R.id.deprovisionSatellite)
+                .setOnClickListener(this::deprovisionSatelliteApp);
         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -100,24 +105,32 @@
         });
     }
 
-    private void enableSatelliteApp(View view) {
+    private void enableSatelliteApp(boolean isDemoMode) {
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
         mSatelliteManager.requestEnabled(
-                new EnableRequestAttributes.Builder(true).setDemoMode(true).setEmergencyMode(true)
+                new EnableRequestAttributes.Builder(true)
+                        .setDemoMode(isDemoMode)
+                        .setEmergencyMode(true)
                         .build(), Runnable::run, error::offer);
         TextView textView = findViewById(R.id.text_id);
+        Log.d("SatelliteTestApp", "enableSatelliteApp: isDemoMode=" + isDemoMode);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
                 textView.setText("Timed out to enable the satellite");
+                Log.d("SatelliteTestApp", "Timed out to enable the satellite");
             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
                 textView.setText("Failed to enable the satellite, error ="
                         + SatelliteErrorUtils.mapError(value));
+                Log.d("SatelliteTestApp", "Failed to enable the satellite, error ="
+                        + SatelliteErrorUtils.mapError(value));
             } else {
                 textView.setText("Successfully enabled the satellite");
+                Log.d("SatelliteTestApp", "Successfully enabled the satellite");
             }
         } catch (InterruptedException e) {
             textView.setText("Enable SatelliteService exception caught =" + e);
+            Log.d("SatelliteTestApp", "Enable SatelliteService exception caught =" + e);
         }
     }
 
@@ -456,4 +469,36 @@
         }
         mSatelliteManager.provisionSatellite(list, Runnable::run, receiver);
     }
+
+    private void deprovisionSatelliteApp(View view) {
+        final AtomicReference<Boolean> enabled = new AtomicReference<>();
+        final AtomicReference<Integer> errorCode = new AtomicReference<>();
+        OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        enabled.set(result);
+                        TextView textView = findViewById(R.id.text_id);
+                        if (enabled.get()) {
+                            textView.setText("deprovisionSatellite is true");
+                        } else {
+                            textView.setText("Status for deprovisionSatellite result : "
+                                    + enabled.get());
+                        }
+                    }
+
+                    @Override
+                    public void onError(SatelliteManager.SatelliteException exception) {
+                        errorCode.set(exception.getErrorCode());
+                        TextView textView = findViewById(R.id.text_id);
+                        textView.setText("Status for deprovisionSatellite error : "
+                                + SatelliteErrorUtils.mapError(errorCode.get()));
+                    }
+                };
+        List<SatelliteSubscriberInfo> list = new ArrayList<>();
+        for (SatelliteSubscriberProvisionStatus status : mSatelliteSubscriberProvisionStatuses) {
+            list.add(status.getSatelliteSubscriberInfo());
+        }
+        mSatelliteManager.deprovisionSatellite(list, Runnable::run, receiver);
+    }
 }
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
index 7c4ae00..cb56e87 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
@@ -16,11 +16,13 @@
 
 package com.android.phone.testapps.satellitetestapp;
 
+import android.Manifest;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.telephony.satellite.stub.SatelliteDatagram;
@@ -42,6 +44,7 @@
 
     private TestSatelliteServiceConnection mSatelliteServiceConn;
     private List<SatelliteDatagram> mSentSatelliteDatagrams = new ArrayList<>();
+    private static final int REQUEST_CODE_SEND_SMS = 1;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -105,6 +108,16 @@
         });
     }
 
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (checkSelfPermission(Manifest.permission.SEND_SMS)
+                != PackageManager.PERMISSION_GRANTED) {
+            requestPermissions(new String[]{Manifest.permission.SEND_SMS}, REQUEST_CODE_SEND_SMS);
+        }
+    }
+
+
     private final ILocalSatelliteListener mSatelliteListener =
             new ILocalSatelliteListener.Stub() {
                 @Override
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SendReceive.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SendReceive.java
index ede2377..bc60c9b 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SendReceive.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SendReceive.java
@@ -290,7 +290,7 @@
             satellitePositionTextView.setText("startSatelliteTransmissionUpdates exception caught ="
                         + e);
         }
-        //Device is aligned with the satellite for demo mode
+        //Device is aligned with the satellite
         mSatelliteManager.setDeviceAlignedWithSatellite(true);
     }
 }
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
index 9c75a84..225fba0 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
@@ -60,7 +60,7 @@
     private static final int SATELLITE_ALWAYS_VISIBLE = 0;
     /** SatelliteCapabilities constant indicating that the radio technology is proprietary. */
     private static final int[] SUPPORTED_RADIO_TECHNOLOGIES =
-            new int[]{NTRadioTechnology.PROPRIETARY};
+            new int[]{NTRadioTechnology.NB_IOT_NTN};
     /** SatelliteCapabilities constant indicating that pointing to satellite is required. */
     private static final boolean POINTING_TO_SATELLITE_REQUIRED = true;
     /** SatelliteCapabilities constant indicating the maximum number of characters per datagram. */
@@ -208,14 +208,14 @@
 
     private void enableSatellite(@NonNull IIntegerConsumer errorCallback) {
         mIsEnabled = true;
-        updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_IDLE);
         runWithExecutor(() -> errorCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS));
+        updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_IN_SERVICE);
     }
 
     private void disableSatellite(@NonNull IIntegerConsumer errorCallback) {
         mIsEnabled = false;
-        updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_OFF);
         runWithExecutor(() -> errorCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS));
+        updateSatelliteModemState(SatelliteModemState.SATELLITE_MODEM_STATE_OFF);
     }
 
     @Override
@@ -494,6 +494,7 @@
      * @param modemState The {@link SatelliteModemState} to update.
      */
     private void updateSatelliteModemState(int modemState) {
+        logd("updateSatelliteModemState: new modemState=" + modemState);
         if (modemState == mModemState) {
             return;
         }
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
index d8e6e7c..7d5e9af 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
@@ -30,6 +30,8 @@
 import android.telephony.satellite.wrapper.SatelliteCommunicationAllowedStateCallbackWrapper;
 import android.telephony.satellite.wrapper.SatelliteManagerWrapper;
 import android.telephony.satellite.wrapper.SatelliteModemStateCallbackWrapper2;
+import android.telephony.satellite.wrapper.SatelliteSubscriberInfoWrapper;
+import android.telephony.satellite.wrapper.SatelliteSubscriberProvisionStatusWrapper;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -62,6 +64,8 @@
     private SatelliteCapabilitiesCallbackWrapper mSatelliteCapabilitiesCallback;
     private SubscriptionManager mSubscriptionManager;
     private int mSubId;
+    private List<SatelliteSubscriberProvisionStatusWrapper> mSatelliteSubscriberProvisionStatuses =
+            new ArrayList<>();
 
     private ListView mLogListView;
 
@@ -117,6 +121,17 @@
                 .setOnClickListener(this::registerForModemStateChanged);
         findViewById(R.id.unregisterForModemStateChanged)
                 .setOnClickListener(this::unregisterForModemStateChanged);
+        findViewById(R.id.requestSatelliteSubscriberProvisionStatusWrapper)
+                .setOnClickListener(this::requestSatelliteSubscriberProvisionStatus);
+        findViewById(R.id.provisionSatelliteWrapper)
+                .setOnClickListener(this::provisionSatellite);
+        findViewById(R.id.deprovisionSatelliteWrapper)
+                .setOnClickListener(this::deprovisionSatellite);
+        findViewById(R.id.setNtnSmsSupportedTrue)
+                .setOnClickListener(this::setNtnSmsSupportedTrue);
+        findViewById(R.id.setNtnSmsSupportedFalse)
+                .setOnClickListener(this::setNtnSmsSupportedFalse);
+
 
         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
             @Override
@@ -389,7 +404,137 @@
         }
     }
 
+    private void requestSatelliteSubscriberProvisionStatus(View view) {
+        addLogMessage("requestSatelliteSubscriberProvisionStatus");
+        logd("requestSatelliteSubscriberProvisionStatus");
 
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("requestSatelliteSubscriberProvisionStatus: Subscription ID is invalid");
+            logd("requestSatelliteSubscriberProvisionStatus: Subscription ID is invalid");
+            return;
+        }
+
+        OutcomeReceiver<List<SatelliteSubscriberProvisionStatusWrapper>,
+                SatelliteManagerWrapper.SatelliteExceptionWrapper> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(List<SatelliteSubscriberProvisionStatusWrapper> result) {
+                        mSatelliteSubscriberProvisionStatuses = result;
+                        logd("requestSatelliteSubscriberProvisionStatus: onResult=" + result);
+                        addLogMessage(
+                                "requestSatelliteSubscriberProvisionStatus: onResult=" + result);
+                    }
+
+                    @Override
+                    public void onError(
+                            SatelliteManagerWrapper.SatelliteExceptionWrapper exception) {
+                        if (exception != null) {
+                            String onError = "requestSatelliteSubscriberProvisionStatus exception: "
+                                    + translateResultCodeToString(exception.getErrorCode());
+                            logd(onError);
+                            addLogMessage(onError);
+                        }
+                    }
+                };
+
+        try {
+            mSatelliteManagerWrapper.requestSatelliteSubscriberProvisionStatus(mExecutor, receiver);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "requestSatelliteSubscriberProvisionStatus: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void provisionSatellite(View view) {
+        addLogMessage("provisionSatellite");
+        logd("provisionSatellite");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("provisionSatellite: Subscription ID is invalid");
+            logd("provisionSatellite: Subscription ID is invalid");
+            return;
+        }
+
+        OutcomeReceiver<Boolean,
+                SatelliteManagerWrapper.SatelliteExceptionWrapper> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        logd("provisionSatellite: onResult=" + result);
+                        addLogMessage("provisionSatellite: onResult=" + result);
+                    }
+
+                    @Override
+                    public void onError(
+                            SatelliteManagerWrapper.SatelliteExceptionWrapper exception) {
+                        if (exception != null) {
+                            String onError = "provisionSatellite exception: "
+                                    + translateResultCodeToString(exception.getErrorCode());
+                            logd(onError);
+                            addLogMessage(onError);
+                        }
+                    }
+                };
+
+        List<SatelliteSubscriberInfoWrapper> list = new ArrayList<>();
+        for (SatelliteSubscriberProvisionStatusWrapper status :
+                mSatelliteSubscriberProvisionStatuses) {
+            list.add(status.getSatelliteSubscriberInfo());
+        }
+        try {
+            mSatelliteManagerWrapper.provisionSatellite(list, mExecutor, receiver);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "provisionSatellite: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void deprovisionSatellite(View view) {
+        addLogMessage("deprovisionSatellite");
+        logd("deprovisionSatellite");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("deprovisionSatellite: Subscription ID is invalid");
+            logd("deprovisionSatellite: Subscription ID is invalid");
+            return;
+        }
+
+        OutcomeReceiver<Boolean,
+                SatelliteManagerWrapper.SatelliteExceptionWrapper> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        logd("deprovisionSatellite: onResult=" + result);
+                        addLogMessage("deprovisionSatellite: onResult=" + result);
+                    }
+
+                    @Override
+                    public void onError(
+                            SatelliteManagerWrapper.SatelliteExceptionWrapper exception) {
+                        if (exception != null) {
+                            String onError = "deprovisionSatellite exception: "
+                                    + translateResultCodeToString(exception.getErrorCode());
+                            logd(onError);
+                            addLogMessage(onError);
+                        }
+                    }
+                };
+
+        List<SatelliteSubscriberInfoWrapper> list = new ArrayList<>();
+        for (SatelliteSubscriberProvisionStatusWrapper status :
+                mSatelliteSubscriberProvisionStatuses) {
+            list.add(status.getSatelliteSubscriberInfo());
+        }
+        try {
+            mSatelliteManagerWrapper.deprovisionSatellite(list, mExecutor, receiver);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "deprovisionSatellite: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
 
     public class NtnSignalStrengthCallback implements NtnSignalStrengthCallbackWrapper {
         @Override
@@ -659,6 +804,31 @@
         }
     }
 
+    private void setNtnSmsSupportedTrue(View view) {
+        setNtnSmsSupported(true);
+    }
+
+    private void setNtnSmsSupportedFalse(View view) {
+        setNtnSmsSupported(false);
+    }
+
+    private void setNtnSmsSupported(boolean ntnSmsSupported) {
+        String msg = "setNtnSmsSupported:" + ntnSmsSupported;
+        addLogMessage(msg);
+        logd(msg);
+
+        try {
+            mSatelliteManagerWrapper.setNtnSmsSupported(ntnSmsSupported);
+            msg = "setNtnSmsSupported=" + ntnSmsSupported + " is successful";
+            logd(msg);
+            addLogMessage(msg);
+        } catch (SecurityException | IllegalStateException ex) {
+            msg = "setNtnSmsSupported=" + ntnSmsSupported + " failed. " + ex.getMessage();
+            logd(msg);
+            addLogMessage(msg);
+        }
+    }
+
     private int getActiveSubId() {
         int subId;
         List<SubscriptionInfo> subscriptionInfoList =
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index d72d85e..3e74eb7 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -16,21 +16,38 @@
 
 package com.android;
 
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
 
+import android.content.ContextWrapper;
+import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.data.DataConfigManager;
+import com.android.internal.telephony.data.DataNetworkController;
+import com.android.phone.PhoneGlobals;
+import com.android.phone.PhoneInterfaceManager;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
+import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
 import java.lang.reflect.Field;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -39,31 +56,50 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Helper class to load Mockito Resources into a test.
+ * Helper class to load Mockito Resources into Telephony unit tests.
  */
 public class TelephonyTestBase {
     @Rule public final MockitoRule mocks = MockitoJUnit.rule();
 
     protected TestContext mContext;
+    @Mock protected PhoneGlobals mPhoneGlobals;
+    @Mock protected GsmCdmaPhone mPhone;
+    @Mock protected DataNetworkController mDataNetworkController;
 
     private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
     private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
 
     @Before
     public void setUp() throws Exception {
-        mContext = spy(new TestContext());
-        // Set up the looper if it does not exist on the test thread.
         if (Looper.myLooper() == null) {
             Looper.prepare();
-            // Wait until the looper is not null anymore
-            for(int i = 0; i < 5; i++) {
-                if (Looper.myLooper() != null) {
-                    break;
-                }
-                Looper.prepare();
-                Thread.sleep(100);
-            }
         }
+
+        doCallRealMethod().when(mPhoneGlobals).getBaseContext();
+        doCallRealMethod().when(mPhoneGlobals).getResources();
+        doCallRealMethod().when(mPhone).getServiceState();
+
+        mContext = spy(new TestContext());
+        doReturn(mContext).when(mPhone).getContext();
+        replaceInstance(ContextWrapper.class, "mBase", mPhoneGlobals, mContext);
+
+        Resources resources = InstrumentationRegistry.getTargetContext().getResources();
+        assertNotNull(resources);
+        doReturn(resources).when(mContext).getResources();
+
+        replaceInstance(Handler.class, "mLooper", mPhone, Looper.myLooper());
+        replaceInstance(PhoneFactory.class, "sMadeDefaults", null, true);
+        replaceInstance(PhoneFactory.class, "sPhone", null, mPhone);
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
+        replaceInstance(PhoneGlobals.class, "sMe", null, mPhoneGlobals);
+
+        doReturn(mDataNetworkController).when(mPhone).getDataNetworkController();
+        doReturn(Collections.emptyList()).when(mDataNetworkController)
+                .getInternetDataDisallowedReasons();
+        doReturn(Mockito.mock(DataConfigManager.class)).when(mDataNetworkController)
+                .getDataConfigManager();
+
+        mPhoneGlobals.phoneMgr = Mockito.mock(PhoneInterfaceManager.class);
     }
 
     @After
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index e464ad5..a684ef5 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -103,6 +103,11 @@
     }
 
     @Override
+    public String getOpPackageName() {
+        return getPackageName();
+    }
+
+    @Override
     public String getAttributionTag() {
         return "";
     }
@@ -212,6 +217,11 @@
     }
 
     @Override
+    public Looper getMainLooper() {
+        return Looper.getMainLooper();
+    }
+
+    @Override
     public Handler getMainThreadHandler() {
         return new Handler(Looper.getMainLooper());
     }
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
index bda2313..5190b21 100644
--- a/tests/src/com/android/phone/CarrierConfigLoaderTest.java
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -41,7 +41,6 @@
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.PermissionEnforcer;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
@@ -51,10 +50,10 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyRegistryManager;
+import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.IccCardConstants;
@@ -72,7 +71,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -81,7 +79,8 @@
 /**
  * Unit Test for CarrierConfigLoader.
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class CarrierConfigLoaderTest extends TelephonyTestBase {
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -108,7 +107,6 @@
     private TelephonyManager mTelephonyManager;
     private CarrierConfigLoader mCarrierConfigLoader;
     private Handler mHandler;
-    private HandlerThread mHandlerThread;
     private TestableLooper mTestableLooper;
 
     // The AIDL stub will use PermissionEnforcer to check permission from the caller.
@@ -117,7 +115,6 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        MockitoAnnotations.initMocks(this);
         doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName(
                 eq(PermissionEnforcer.class));
         doReturn(mFakePermissionEnforcer).when(mContext).getSystemService(
@@ -151,10 +148,7 @@
         when(mContext.getSystemService(TelephonyRegistryManager.class)).thenReturn(
                 mTelephonyRegistryManager);
 
-        mHandlerThread = new HandlerThread("CarrierConfigLoaderTest");
-        mHandlerThread.start();
-
-        mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
+        mTestableLooper = TestableLooper.get(this);
         mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper(),
                 mFeatureFlags);
         mHandler = mCarrierConfigLoader.getHandler();
@@ -169,8 +163,6 @@
         mFakePermissionEnforcer.revoke(android.Manifest.permission.DUMP);
         mFakePermissionEnforcer.revoke(android.Manifest.permission.MODIFY_PHONE_STATE);
         mFakePermissionEnforcer.revoke(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
-        mTestableLooper.destroy();
-        mHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
index 0e902a8..5521ac0 100644
--- a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
+++ b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
@@ -46,7 +46,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.Looper;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyRegistryManager;
 import android.testing.TestableLooper;
@@ -70,7 +69,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -110,7 +108,6 @@
     @Captor ArgumentCaptor<FeatureConnector.Listener<ImsManager>> mMmTelConnectorListenerSlot1;
     @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mRcsConnectorListenerSlot0;
     @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mRcsConnectorListenerSlot1;
-    @Mock private PhoneGlobals mPhone;
     @Mock ImsStateCallbackController.PhoneFactoryProxy mPhoneFactoryProxy;
     @Mock Phone mPhoneSlot0;
     @Mock Phone mPhoneSlot1;
@@ -134,16 +131,16 @@
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        super.setUp();
 
-        when(mPhone.getMainExecutor()).thenReturn(mExecutor);
-        when(mPhone.getSystemServiceName(eq(SubscriptionManager.class)))
+        when(mPhoneGlobals.getMainExecutor()).thenReturn(mExecutor);
+        when(mPhoneGlobals.getSystemServiceName(eq(SubscriptionManager.class)))
                 .thenReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
-        when(mPhone.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+        when(mPhoneGlobals.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
                 .thenReturn(mSubscriptionManager);
-        when(mPhone.getSystemServiceName(eq(TelephonyRegistryManager.class)))
+        when(mPhoneGlobals.getSystemServiceName(eq(TelephonyRegistryManager.class)))
                 .thenReturn(Context.TELEPHONY_REGISTRY_SERVICE);
-        when(mPhone.getSystemService(eq(Context.TELEPHONY_REGISTRY_SERVICE)))
+        when(mPhoneGlobals.getSystemService(eq(Context.TELEPHONY_REGISTRY_SERVICE)))
                 .thenReturn(mTelephonyRegistryManager);
         when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(mPhoneSlot0);
         when(mPhoneFactoryProxy.getPhone(eq(1))).thenReturn(mPhoneSlot1);
@@ -937,9 +934,6 @@
     }
 
     private void createController(int slotCount) throws Exception {
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
         makeFakeActiveSubIds(slotCount);
 
         when(mMmTelFeatureFactory
@@ -956,7 +950,7 @@
                 .thenReturn(mRcsFeatureConnectorSlot1);
 
         mImsStateCallbackController =
-                new ImsStateCallbackController(mPhone, mHandlerThread.getLooper(),
+                new ImsStateCallbackController(mPhoneGlobals, mHandlerThread.getLooper(),
                         slotCount, mMmTelFeatureFactory, mRcsFeatureFactory, mImsResolver,
                         mFeatureFlags);
 
diff --git a/tests/src/com/android/phone/LocationAccessPolicyTest.java b/tests/src/com/android/phone/LocationAccessPolicyTest.java
index 58e7fbd..551c2cb 100644
--- a/tests/src/com/android/phone/LocationAccessPolicyTest.java
+++ b/tests/src/com/android/phone/LocationAccessPolicyTest.java
@@ -225,6 +225,8 @@
         try {
             when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
                     .thenReturn(fakeAppInfo);
+            when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(),
+                    any(UserHandle.class))).thenReturn(fakeAppInfo);
         } catch (Exception e) {
             // this is a formality
         }
diff --git a/tests/src/com/android/phone/NotificationMgrTest.java b/tests/src/com/android/phone/NotificationMgrTest.java
index 98c6a4a..0c1f8a3 100644
--- a/tests/src/com/android/phone/NotificationMgrTest.java
+++ b/tests/src/com/android/phone/NotificationMgrTest.java
@@ -59,6 +59,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.os.Build;
+import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -76,11 +77,8 @@
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SignalStrengthController;
-import com.android.internal.telephony.data.DataConfigManager;
-import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.data.DataSettingsManager;
 import com.android.internal.telephony.util.NotificationChannelController;
 
@@ -89,9 +87,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
-import java.util.Collections;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -110,13 +106,12 @@
     private static final String MOBILE_NETWORK_SELECTION_CLASS = ".testClass";
     private static final String CARRIER_NAME = "CoolCarrier";
 
-    @Mock PhoneGlobals mApp;
+    PhoneGlobals mApp; // mPhoneGlobals alias
     @Mock StatusBarManager mStatusBarManager;
     @Mock UserManager mUserManager;
     @Mock SubscriptionManager mSubscriptionManager;
     @Mock TelecomManager mTelecomManager;
     @Mock TelephonyManager mTelephonyManager;
-    @Mock Phone mPhone;
     @Mock SharedPreferences mSharedPreferences;
     @Mock NotificationManager mNotificationManager;
     @Mock SubscriptionInfo mSubscriptionInfo;
@@ -125,20 +120,16 @@
     @Mock ServiceStateTracker mServiceStateTracker;
     @Mock ServiceState mServiceState;
     @Mock CarrierConfigManager mCarrierConfigManager;
-    @Mock DataNetworkController mDataNetworkController;
     @Mock DataSettingsManager mDataSettingsManager;
-    @Mock DataConfigManager mDataConfigManager;
     @Mock SignalStrengthController mSignalStrengthController;
 
-    private Phone[] mPhones;
     private NotificationMgr mNotificationMgr;
     private TestableLooper mTestableLooper;
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mPhones = new Phone[]{mPhone};
-        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        super.setUp();
+        mApp = mPhoneGlobals;
         when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
         when(mPhone.getContext()).thenReturn(mMockedContext);
         when(mMockedContext.getResources()).thenReturn(mResources);
@@ -151,10 +142,6 @@
         when(mPhone.getServiceStateTracker()).thenReturn(mServiceStateTracker);
         mServiceStateTracker.mSS = mServiceState;
         when(mPhone.getSignalStrengthController()).thenReturn(mSignalStrengthController);
-        when(mPhone.getDataNetworkController()).thenReturn(mDataNetworkController);
-        when(mDataNetworkController.getInternetDataDisallowedReasons()).thenReturn(
-                Collections.emptyList());
-        when(mDataNetworkController.getDataConfigManager()).thenReturn(mDataConfigManager);
         when(mPhone.getDataSettingsManager()).thenReturn(mDataSettingsManager);
         when(mDataSettingsManager.isDataEnabledForReason(anyInt())).thenReturn(true);
         when(mApp.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
@@ -409,6 +396,35 @@
     }
 
     @Test
+    public void testUpdateNetworkSelection_opportunisticSubscription_notificationNotSent()
+            throws Exception {
+        prepareResourcesForNetworkSelection();
+        when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+                mSubscriptionInfo);
+
+        when(mTelephonyManager.isManualNetworkSelectionAllowed()).thenReturn(true);
+        PersistableBundle config = new PersistableBundle();
+        config.putBoolean(CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
+        config.putBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+        config.putBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL, false);
+        config.putBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(TEST_SUB_ID)).thenReturn(config);
+
+        when(mSubscriptionInfo.isOpportunistic()).thenReturn(true);
+        when(mSubscriptionInfo.getGroupUuid()).thenReturn(
+                ParcelUuid.fromString("5be5c5f3-3412-452e-86a0-6f18558ae8c8"));
+
+        mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+        try {
+            Thread.sleep(10000);
+        } catch (InterruptedException ignored) {
+        }
+        mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+        verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+    }
+
+    @Test
     public void testUpdateNetworkSelection_worldMode_userSetLTE_notificationNotSent() {
         prepareResourcesForNetworkSelection();
 
@@ -632,6 +648,8 @@
         when(mApp.getString(R.string.mobile_network_settings_class)).thenReturn(
                 MOBILE_NETWORK_SELECTION_CLASS);
         when(mSubscriptionManager.isActiveSubId(anyInt())).thenReturn(true);
+        when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+                mSubscriptionInfo);
     }
 
     private void moveTimeForward(long seconds) {
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index 7464ba2..ef6a02a 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -45,11 +44,14 @@
 import android.os.UserHandle;
 import android.permission.flags.Flags;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.preference.PreferenceManager;
 import android.telephony.RadioAccessFamily;
 import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 
 import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.IIntegerConsumer;
@@ -57,6 +59,7 @@
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.phone.satellite.accesscontrol.SatelliteAccessController;
 
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
@@ -66,6 +69,7 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -75,20 +79,19 @@
 /**
  * Unit Test for PhoneInterfaceManager.
  */
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class PhoneInterfaceManagerTest extends TelephonyTestBase {
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
     private PhoneInterfaceManager mPhoneInterfaceManager;
     private SharedPreferences mSharedPreferences;
-    private IIntegerConsumer mIIntegerConsumer;
+    @Mock private IIntegerConsumer mIIntegerConsumer;
     private static final String sDebugPackageName =
             PhoneInterfaceManagerTest.class.getPackageName();
 
     @Mock
-    PhoneGlobals mPhoneGlobals;
-    @Mock
     Phone mPhone;
     @Mock
     FeatureFlags mFeatureFlags;
@@ -108,6 +111,18 @@
         super.setUp();
         doReturn(sDebugPackageName).when(mPhoneGlobals).getOpPackageName();
 
+        replaceInstance(SatelliteAccessController.class, "sInstance", null,
+                Mockito.mock(SatelliteAccessController.class));
+
+        mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(
+                InstrumentationRegistry.getInstrumentation().getTargetContext());
+        doReturn(mSharedPreferences).when(mPhoneGlobals)
+                .getSharedPreferences(anyString(), anyInt());
+        mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED).commit();
+        mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED).commit();
+
+        // Trigger sInstance restore in tearDown, after PhoneInterfaceManager.init.
+        replaceInstance(PhoneInterfaceManager.class, "sInstance", null, null);
         // Note that PhoneInterfaceManager is a singleton. Calling init gives us a handle to the
         // global singleton, but the context that is passed in is unused if the phone app is already
         // alive on a test devices. You must use the spy to mock behavior. Mocks stemming from the
@@ -119,10 +134,6 @@
         doReturn(mSubscriptionManagerService).when(mPhoneInterfaceManager)
                 .getSubscriptionManagerService();
         TelephonyManager.setupISubForTest(mSubscriptionManagerService);
-        mSharedPreferences = mPhoneInterfaceManager.getSharedPreferences();
-        mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED).commit();
-        mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED).commit();
-        mIIntegerConsumer = mock(IIntegerConsumer.class);
 
         // In order not to affect the existing implementation, define a telephony features
         // and disabled enforce_telephony_feature_mapping_for_public_apis feature flag
@@ -130,7 +141,9 @@
         doReturn(false).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(true).when(mFeatureFlags).hsumPackageManager();
         mPhoneInterfaceManager.setPackageManager(mPackageManager);
+        doReturn(mPackageManager).when(mPhoneGlobals).getPackageManager();
         doReturn(true).when(mPackageManager).hasSystemFeature(anyString());
+        doReturn(new String[]{sDebugPackageName}).when(mPackageManager).getPackagesForUid(anyInt());
 
         mPhoneInterfaceManager.setAppOpsManager(mAppOps);
     }
@@ -491,15 +504,11 @@
         mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
         doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
 
-        try {
-            // FEATURE_TELEPHONY_CALLING
-            mPhoneInterfaceManager.handlePinMmiForSubscriber(1, "123456789");
+        // FEATURE_TELEPHONY_CALLING
+        mPhoneInterfaceManager.getVoiceActivationState(1, "com.test.package");
 
-            // FEATURE_TELEPHONY_RADIO_ACCESS
-            mPhoneInterfaceManager.toggleRadioOnOffForSubscriber(1);
-        } catch (Exception e) {
-            fail("Not expect exception " + e.getMessage());
-        }
+        // FEATURE_TELEPHONY_RADIO_ACCESS
+        mPhoneInterfaceManager.toggleRadioOnOffForSubscriber(1);
     }
 
     @Test
diff --git a/tests/src/com/android/phone/PhoneUtilsTest.java b/tests/src/com/android/phone/PhoneUtilsTest.java
index 3d7815c..2d3d065 100644
--- a/tests/src/com/android/phone/PhoneUtilsTest.java
+++ b/tests/src/com/android/phone/PhoneUtilsTest.java
@@ -30,15 +30,11 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 public class PhoneUtilsTest extends TelephonyTestBase {
@@ -46,8 +42,6 @@
     private SubscriptionManager mMockSubscriptionManager;
     @Mock
     private SubscriptionInfo mMockSubscriptionInfo;
-    @Mock
-    private GsmCdmaPhone mMockPhone;
 
     private final int mPhoneAccountHandleIdInteger = 123;
     private final String mPhoneAccountHandleIdString = "123";
@@ -58,12 +52,10 @@
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        super.setUp();
         when(mMockSubscriptionManager.getActiveSubscriptionInfo(
                 eq(mPhoneAccountHandleIdInteger))).thenReturn(mMockSubscriptionInfo);
-        when(mMockPhone.getSubId()).thenReturn(mPhoneAccountHandleIdInteger);
-        Phone[] mPhones = new Phone[] {mMockPhone};
-        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        when(mPhone.getSubId()).thenReturn(mPhoneAccountHandleIdInteger);
     }
 
     @Test
@@ -74,7 +66,7 @@
 
     @Test
     public void testGetPhoneForPhoneAccountHandle() throws Exception {
-        assertEquals(mMockPhone, PhoneUtils.getPhoneForPhoneAccountHandle(
+        assertEquals(mPhone, PhoneUtils.getPhoneForPhoneAccountHandle(
                 mPhoneAccountHandleTest));
     }
 
diff --git a/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
index 817220c..1bd118a 100644
--- a/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
+++ b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.service.euicc.EuiccService;
+import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccManager;
 
 import androidx.test.InstrumentationRegistry;
@@ -50,6 +51,7 @@
 
     @Mock private Context mMockContext;
     @Mock private EuiccManager mMockEuiccManager;
+    @Mock private TelephonyManager mTelephonyManager;
     private ActivityInfo mActivityInfo = ACTIVITY_INFO;
     private Intent mIntent = MANAGE_INTENT;
     private EuiccUiDispatcherActivity mActivity;
@@ -59,6 +61,8 @@
         MockitoAnnotations.initMocks(this);
         when(mMockEuiccManager.isEnabled()).thenReturn(true);
         when(mMockContext.getSystemService(Context.EUICC_SERVICE)).thenReturn(mMockEuiccManager);
+        when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .thenReturn(mTelephonyManager);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 new Runnable() {
                     @Override
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
index 16a256d..27f3ef7 100644
--- a/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
+++ b/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
@@ -16,13 +16,21 @@
 
 package com.android.phone.satellite.accesscontrol;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.Nullable;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.storage.s2.S2LevelRange;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.utils.TestUtils;
 import com.android.telephony.sats2range.write.SatS2RangeFileWriter;
 
@@ -33,6 +41,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
@@ -44,6 +53,9 @@
 public class S2RangeSatelliteOnDeviceAccessControllerTest {
     private File mFile;
 
+    @Mock
+    private FeatureFlags mMockFeatureFlags;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -60,18 +72,42 @@
 
     @Test
     public void testSatelliteAccessControl_AllowedList() throws Exception {
-        testSatelliteAccessControl(true);
+        testSatelliteAccessControl(true, null);
     }
 
     @Test
     public void testSatelliteAccessControl_DisallowedList() throws Exception {
-        testSatelliteAccessControl(false);
+        testSatelliteAccessControl(false, null);
     }
 
-    private void testSatelliteAccessControl(boolean isAllowedList) throws Exception {
+    @Test
+    public void testSatelliteAccessControl_AllowedList_validEntryValue() throws Exception {
+        testSatelliteAccessControl(true, 1);
+    }
+
+    @Test
+    public void testSatelliteAccessControl_DisallowedList_validEntryValue() {
+        assertThrows(IllegalArgumentException.class,
+                () -> testSatelliteAccessControl(false, 1));
+    }
+
+    private void testSatelliteAccessControl(boolean isAllowedList, @Nullable Integer entryValue)
+            throws Exception {
+        final int defaultEntryValue = -1;
+
+        if (!isAllowedList && entryValue != null) {
+            throw new IllegalArgumentException(
+                    "isAllowedList must be true when entryValue is present.");
+        }
+
+        List<Integer> expectedConfigIds = List.of(1, 1, 3);
         SatS2RangeFileFormat fileFormat = null;
         try {
-            fileFormat = createSatS2File(mFile, isAllowedList);
+            if (entryValue == null) {
+                fileFormat = createSatS2File(mFile, isAllowedList);
+            } else {
+                fileFormat = createSatS2FileWithEntryValue(mFile, isAllowedList, expectedConfigIds);
+            }
         } catch (Exception ex) {
             fail("Got unexpected exception in createSatS2File, ex=" + ex);
         }
@@ -79,8 +115,12 @@
         // Validate the output block file
         SatelliteOnDeviceAccessController accessController = null;
         try {
-            accessController = SatelliteOnDeviceAccessController.create(mFile);
+            accessController = SatelliteOnDeviceAccessController.create(mFile, mMockFeatureFlags);
             int s2Level = accessController.getS2Level();
+            if (entryValue == null) {
+                expectedConfigIds = List.of(defaultEntryValue, defaultEntryValue,
+                        defaultEntryValue);
+            }
 
             // Verify an edge cell of range 1 not in the output file
             S2CellId s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 999));
@@ -88,9 +128,18 @@
             SatelliteOnDeviceAccessController.LocationToken locationToken =
                     SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
                             s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
+
+            // Verify if the return value is null, when the carrierRoamingNbIotNtn is disabled.
+            doReturn(false).when(mMockFeatureFlags).carrierRoamingNbIotNtn();
+            assertNull(accessController.getRegionalConfigIdForLocation(locationToken));
+
+            doReturn(true).when(mMockFeatureFlags).carrierRoamingNbIotNtn();
             boolean isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
+            Integer configId = accessController.getRegionalConfigIdForLocation(locationToken);
+            assertNull(configId);
+
             // Verify cells in range1 present in the output file
             for (int suffix = 1000; suffix < 2000; suffix++) {
                 s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, suffix));
@@ -98,9 +147,13 @@
 
                 // Lookup using location token
                 locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
-                                s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
+                        s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
                 isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
                 assertTrue(isAllowed == isAllowedList);
+
+                configId = accessController.getRegionalConfigIdForLocation(locationToken);
+                assertNotNull(configId);
+                assertEquals((int) expectedConfigIds.get(0), (int) configId);
             }
 
             // Verify the middle cell not in the output file
@@ -111,6 +164,10 @@
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
+            configId = accessController.getRegionalConfigIdForLocation(locationToken);
+            assertNull(configId);
+
+
             // Verify cells in range2 present in the output file
             for (int suffix = 2001; suffix < 3000; suffix++) {
                 s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, suffix));
@@ -119,6 +176,10 @@
                         s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
                 isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
                 assertTrue(isAllowed == isAllowedList);
+
+                configId = accessController.getRegionalConfigIdForLocation(locationToken);
+                assertNotNull(configId);
+                assertEquals((int) expectedConfigIds.get(1), (int) configId);
             }
 
             // Verify an edge cell of range 2 not in the output file
@@ -129,6 +190,9 @@
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
+            configId = accessController.getRegionalConfigIdForLocation(locationToken);
+            assertNull(configId);
+
             // Verify an edge cell of range 3 not in the output file
             s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, 999));
             s2LatLng = s2CellId.toLatLng();
@@ -137,6 +201,9 @@
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
+            configId = accessController.getRegionalConfigIdForLocation(locationToken);
+            assertNull(configId);
+
             // Verify cells in range1 present in the output file
             for (int suffix = 1000; suffix < 2000; suffix++) {
                 s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, suffix));
@@ -145,6 +212,10 @@
                         s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
                 isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
                 assertTrue(isAllowed == isAllowedList);
+
+                configId = accessController.getRegionalConfigIdForLocation(locationToken);
+                assertNotNull(configId);
+                assertEquals((int) expectedConfigIds.get(2), (int) configId);
             }
 
             // Verify an edge cell of range 3 not in the output file
@@ -154,6 +225,10 @@
                     s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
+
+            configId = accessController.getRegionalConfigIdForLocation(locationToken);
+            assertNull(configId);
+
         } catch (Exception ex) {
             fail("Unexpected exception when validating the output ex=" + ex);
         } finally {
@@ -163,27 +238,61 @@
         }
     }
 
-    private SatS2RangeFileFormat createSatS2File(
-            File file, boolean isAllowedList) throws Exception {
+    private SatS2RangeFileFormat createSatS2File(File file, boolean isAllowedList)
+            throws Exception {
         SatS2RangeFileFormat fileFormat;
-        S2LevelRange range1, range2, range3;
+        SuffixTableRange range1, range2, range3;
         try (SatS2RangeFileWriter satS2RangeFileWriter = SatS2RangeFileWriter.open(
                 file, TestUtils.createS2RangeFileFormat(isAllowedList))) {
             fileFormat = satS2RangeFileWriter.getFileFormat();
 
             // Two ranges that share a prefix.
-            range1 = new S2LevelRange(
+            range1 = new SuffixTableRange(
                     TestUtils.createCellId(fileFormat, 1, 1000, 1000),
                     TestUtils.createCellId(fileFormat, 1, 1000, 2000));
-            range2 = new S2LevelRange(
+            range2 = new SuffixTableRange(
                     TestUtils.createCellId(fileFormat, 1, 1000, 2001),
                     TestUtils.createCellId(fileFormat, 1, 1000, 3000));
             // This range has a different prefix, so will be in a different suffix table.
-            range3 = new S2LevelRange(
+            range3 = new SuffixTableRange(
                     TestUtils.createCellId(fileFormat, 1, 1001, 1000),
                     TestUtils.createCellId(fileFormat, 1, 1001, 2000));
 
-            List<S2LevelRange> ranges = new ArrayList<>();
+            List<SuffixTableRange> ranges = new ArrayList<>();
+            ranges.add(range1);
+            ranges.add(range2);
+            ranges.add(range3);
+            satS2RangeFileWriter.createSortedSuffixBlocks(ranges.iterator());
+        }
+        assertTrue(file.length() > 0);
+        return fileFormat;
+    }
+
+    private SatS2RangeFileFormat createSatS2FileWithEntryValue(
+            File file, boolean isAllowedList, List<Integer> entryValues) throws Exception {
+
+        SatS2RangeFileFormat fileFormat;
+        SuffixTableRange range1, range2, range3;
+        try (SatS2RangeFileWriter satS2RangeFileWriter = SatS2RangeFileWriter.open(
+                file, TestUtils.createS2RangeFileFormat(isAllowedList, 4, 1))) {
+            fileFormat = satS2RangeFileWriter.getFileFormat();
+
+            // Two ranges that share a prefix.
+            range1 = new SuffixTableRange(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 1000),
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2000),
+                    entryValues.get(0));
+            range2 = new SuffixTableRange(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2001),
+                    TestUtils.createCellId(fileFormat, 1, 1000, 3000),
+                    entryValues.get(1));
+            // This range has a different prefix, so will be in a different suffix table.
+            range3 = new SuffixTableRange(
+                    TestUtils.createCellId(fileFormat, 1, 1001, 1000),
+                    TestUtils.createCellId(fileFormat, 1, 1001, 2000),
+                    entryValues.get(2));
+
+            List<SuffixTableRange> ranges = new ArrayList<>();
             ranges.add(range1);
             ranges.add(range2);
             ranges.add(range3);
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
index 55f72fc..5c18cc5 100644
--- a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
+++ b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
@@ -17,6 +17,8 @@
 package com.android.phone.satellite.accesscontrol;
 
 import static android.location.LocationManager.MODE_CHANGED_ACTION;
+import static android.telephony.SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_ACCESS_CONFIGURATION;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_PROVISIONED;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_SUPPORTED;
@@ -29,13 +31,17 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.ALLOWED_STATE_CACHE_VALID_DURATION_NANOS;
-import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.EVENT_COUNTRY_CODE_CHANGED;
 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.CMD_IS_SATELLITE_COMMUNICATION_ALLOWED;
 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.DEFAULT_DELAY_MINUTES_BEFORE_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION;
+import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.DEFAULT_MAX_RETRY_COUNT_FOR_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION;
+import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.DEFAULT_REGIONAL_SATELLITE_CONFIG_ID;
+import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.DEFAULT_S2_LEVEL;
 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.DEFAULT_THROTTLE_INTERVAL_FOR_LOCATION_QUERY_MINUTES;
 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.EVENT_CONFIG_DATA_UPDATED;
+import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.EVENT_COUNTRY_CODE_CHANGED;
+import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT;
 import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.GOOGLE_US_SAN_SAT_S2_FILE_NAME;
-import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.DEFAULT_MAX_RETRY_COUNT_FOR_VALIDATING_POSSIBLE_CHANGE_IN_ALLOWED_REGION;
+import static com.android.phone.satellite.accesscontrol.SatelliteAccessController.UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -49,6 +55,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
@@ -61,26 +68,38 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
+import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.os.AsyncResult;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.DropBoxManager;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.telecom.TelecomManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
+import android.telephony.satellite.SatelliteAccessConfiguration;
+import android.telephony.satellite.SatelliteInfo;
 import android.telephony.satellite.SatelliteManager;
 import android.testing.TestableLooper;
 import android.util.Log;
@@ -115,6 +134,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
@@ -179,14 +199,30 @@
     @Mock
     private SharedPreferences.Editor mMockSharedPreferencesEditor;
     @Mock
-    private Map<SatelliteOnDeviceAccessController.LocationToken, Boolean>
+    private Map<SatelliteOnDeviceAccessController.LocationToken, Integer>
             mMockCachedAccessRestrictionMap;
     @Mock
+    HashMap<Integer, SatelliteAccessConfiguration> mMockSatelliteAccessConfigMap;
+
+    @Mock
     private Intent mMockLocationIntent;
     @Mock
     private Set<ResultReceiver> mMockSatelliteAllowResultReceivers;
     @Mock
-    private ResultReceiver mMockSatelliteSupportedResultReceiver;
+    private TelephonyManager mMockTelephonyManager;
+    @Mock
+    private PackageManager mMockPackageManager;
+    @Mock
+    private List<ResolveInfo> mMockResolveInfoList;
+    @Mock
+    private NotificationManager mMockNotificationManager;
+    @Mock
+    private ApplicationInfo mMockApplicationInfo;
+    @Mock
+    private ResultReceiver mMockResultReceiver;
+    @Mock
+    private ConcurrentHashMap<IBinder, ISatelliteCommunicationAllowedStateCallback>
+            mSatelliteCommunicationAllowedStateCallbackMap;
 
     private Looper mLooper;
     private TestableLooper mTestableLooper;
@@ -221,6 +257,8 @@
     private ArgumentCaptor<Integer> mResultCodeIntCaptor;
     @Captor
     private ArgumentCaptor<Bundle> mResultDataBundleCaptor;
+    @Captor
+    private ArgumentCaptor<ISatelliteCommunicationAllowedStateCallback> mAllowedStateCallbackCaptor;
 
     private boolean mQueriedSatelliteAllowed = false;
     private int mQueriedSatelliteAllowedResultCode = SATELLITE_RESULT_SUCCESS;
@@ -284,6 +322,7 @@
                 mMockCountryDetector);
         when(mMockSatelliteController.getSatellitePhone()).thenReturn(mMockPhone);
         when(mMockPhone.getSubId()).thenReturn(SubscriptionManager.getDefaultSubscriptionId());
+
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources.getStringArray(
                 com.android.internal.R.array.config_oem_enabled_satellite_country_codes))
@@ -318,8 +357,9 @@
         when(mMockLocation0.getLongitude()).thenReturn(0.0);
         when(mMockLocation1.getLatitude()).thenReturn(1.0);
         when(mMockLocation1.getLongitude()).thenReturn(1.0);
-        when(mMockSatelliteOnDeviceAccessController.isSatCommunicationAllowedAtLocation(
-                any(SatelliteOnDeviceAccessController.LocationToken.class))).thenReturn(true);
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID);
 
         when(mMockContext.getSharedPreferences(anyString(), anyInt())).thenReturn(
                 mMockSharedPreferences);
@@ -338,7 +378,24 @@
         when(mMockFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         when(mMockFeatureFlags.geofenceEnhancementForBetterUx()).thenReturn(true);
         when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        when(mMockFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
 
+        when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .thenReturn(mMockTelephonyManager);
+        when(mMockTelephonyManager.isSmsCapable()).thenReturn(true);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+        mMockResolveInfoList = new ArrayList<>();
+        when(mMockPackageManager.queryBroadcastReceiversAsUser(any(Intent.class), anyInt(), any(
+                UserHandle.class)))
+                .thenReturn(mMockResolveInfoList);
+        when(mMockContext.getSystemServiceName(
+                NotificationManager.class)).thenReturn(Context.NOTIFICATION_SERVICE);
+        when(mMockContext.getSystemService(Context.NOTIFICATION_SERVICE))
+                .thenReturn(mMockNotificationManager);
+        when(mMockContext.getApplicationInfo()).thenReturn(mMockApplicationInfo);
+        mMockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt()))
+                .thenReturn(mMockApplicationInfo);
         mSatelliteAccessControllerUT = new TestSatelliteAccessController(mMockContext,
                 mMockFeatureFlags, mLooper, mMockLocationManager, mMockTelecomManager,
                 mMockSatelliteOnDeviceAccessController, mMockSatS2File);
@@ -369,6 +426,49 @@
     }
 
     @Test
+    public void testOnCurrentLocationNotAvailable() throws Exception {
+        // Verify the cache is used when the location is null and the cache is valid and true.
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos =
+                ALLOWED_STATE_CACHE_VALID_DURATION_NANOS - 1;
+        mSatelliteAccessControllerUT
+                .setIsSatelliteCommunicationAllowedForCurrentLocationCache("cache_allowed");
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(false);
+
+        sendCurrentLocationTimeoutEvent();
+        assertTrue(mSatelliteAccessControllerUT.isCurrentSatelliteAllowedState());
+
+        // Verify the cache is used when the location is null and the cache is valid and false.
+        mSatelliteAccessControllerUT
+                .setIsSatelliteCommunicationAllowedForCurrentLocationCache("cache_not_allowed");
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(false);
+
+        sendCurrentLocationTimeoutEvent();
+        assertFalse(mSatelliteAccessControllerUT.isCurrentSatelliteAllowedState());
+
+        // Verify the result code is SATELLITE_RESULT_LOCATION_NOT_AVAILABLE
+        // and allowedState is false when the location is null and the cache is expired
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos =
+                ALLOWED_STATE_CACHE_VALID_DURATION_NANOS + 1;
+        Iterator<ResultReceiver> mockResultReceiverIterator = mock(Iterator.class);
+        doReturn(mockResultReceiverIterator).when(mMockSatelliteAllowResultReceivers).iterator();
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        doNothing().when(mMockSatelliteAllowResultReceivers).clear();
+        doReturn(mMockResultReceiver).when(mockResultReceiverIterator).next();
+        replaceInstance(SatelliteAccessController.class, "mSatelliteAllowResultReceivers",
+                mSatelliteAccessControllerUT, mMockSatelliteAllowResultReceivers);
+        mSatelliteAccessControllerUT.setIsSatelliteCommunicationAllowedForCurrentLocationCache(
+                "cache_clear_and_not_allowed");
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(false);
+
+        sendCurrentLocationTimeoutEvent();
+        verify(mMockResultReceiver)
+                .send(mResultCodeIntCaptor.capture(), any());
+        assertEquals(Integer.valueOf(SATELLITE_RESULT_LOCATION_NOT_AVAILABLE),
+                mResultCodeIntCaptor.getValue());
+        assertFalse(mSatelliteAccessControllerUT.isCurrentSatelliteAllowedState());
+    }
+
+    @Test
     public void testIsSatelliteAccessAllowedForLocation() {
         when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
 
@@ -398,7 +498,7 @@
         assertFalse(mSatelliteAccessControllerUT
                 .isSatelliteAccessAllowedForLocation(List.of(TEST_SATELLITE_COUNTRY_CODE_US)));
         assertFalse(mSatelliteAccessControllerUT.isSatelliteAccessAllowedForLocation(
-                        List.of(TEST_SATELLITE_COUNTRY_CODE_US, TEST_SATELLITE_COUNTRY_CODE_KR)));
+                List.of(TEST_SATELLITE_COUNTRY_CODE_US, TEST_SATELLITE_COUNTRY_CODE_KR)));
         assertTrue(mSatelliteAccessControllerUT
                 .isSatelliteAccessAllowedForLocation(List.of(TEST_SATELLITE_COUNTRY_CODE_KR)));
 
@@ -433,6 +533,314 @@
                 .isSatelliteAccessAllowedForLocation(List.of(TEST_SATELLITE_COUNTRY_CODE_US)));
     }
 
+
+    private void setSatelliteCommunicationAllowed() throws Exception {
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockResources.getBoolean(
+                com.android.internal.R.bool.config_oem_enabled_satellite_access_allow))
+                .thenReturn(TEST_SATELLITE_ALLOW);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockLocationManager).isLocationEnabled();
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID);
+        replaceInstance(SatelliteAccessController.class, "mCachedAccessRestrictionMap",
+                mSatelliteAccessControllerUT, mMockCachedAccessRestrictionMap);
+        doReturn(true).when(mMockCachedAccessRestrictionMap).containsKey(any());
+        doReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID)
+                .when(mMockCachedAccessRestrictionMap).get(any());
+    }
+
+    @Test
+    public void testRequestSatelliteAccessConfigurationForCurrentLocation() throws Exception {
+        // setup result receiver and satellite access configuration data
+        ResultReceiver mockResultReceiver = mock(ResultReceiver.class);
+        ArgumentCaptor<Integer> resultCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        SatelliteAccessConfiguration satelliteAccessConfig = getSatelliteAccessConfiguration();
+
+        // setup satellite communication allwed state as true
+        setSatelliteCommunicationAllowed();
+
+        // setup map data of location and configId.
+        replaceInstance(SatelliteAccessController.class, "mSatelliteAccessConfigMap",
+                mSatelliteAccessControllerUT, mMockSatelliteAccessConfigMap);
+        doReturn(satelliteAccessConfig).when(mMockSatelliteAccessConfigMap).get(anyInt());
+        doReturn(null).when(mMockSatelliteAccessConfigMap).get(eq(null));
+        doReturn(null).when(mMockSatelliteAccessConfigMap)
+                .get(eq(UNKNOWN_REGIONAL_SATELLITE_CONFIG_ID));
+
+        // setup callback
+        ISatelliteCommunicationAllowedStateCallback mockSatelliteAllowedStateCallback = mock(
+                ISatelliteCommunicationAllowedStateCallback.class);
+        ArgumentCaptor<SatelliteAccessConfiguration> satelliteAccessConfigurationCaptor =
+                ArgumentCaptor.forClass(SatelliteAccessConfiguration.class);
+
+        when(mSatelliteCommunicationAllowedStateCallbackMap.values())
+                .thenReturn(List.of(mockSatelliteAllowedStateCallback));
+        replaceInstance(SatelliteAccessController.class,
+                "mSatelliteCommunicationAllowedStateChangedListeners", mSatelliteAccessControllerUT,
+                mSatelliteCommunicationAllowedStateCallbackMap);
+
+        // Test when the featureFlags.carrierRoamingNbIotNtn() is false
+        doReturn(false).when(mMockFeatureFlags).carrierRoamingNbIotNtn();
+
+        clearInvocations(mockResultReceiver);
+        mSatelliteAccessControllerUT
+                .requestSatelliteAccessConfigurationForCurrentLocation(mockResultReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mockResultReceiver, times(1)).send(resultCodeCaptor.capture(),
+                bundleCaptor.capture());
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, (int) resultCodeCaptor.getValue());
+        assertNull(bundleCaptor.getValue());
+        verify(mockSatelliteAllowedStateCallback, never())
+                .onSatelliteAccessConfigurationChanged(any());
+
+        doReturn(true).when(mMockFeatureFlags).carrierRoamingNbIotNtn();
+
+        // satellite communication allowed state is enabled and
+        // regional config id is DEFAULT_REGIONAL_SATELLITE_CONFIG_ID.
+        clearInvocations(mockResultReceiver);
+        clearInvocations(mockSatelliteAllowedStateCallback);
+        mSatelliteAccessControllerUT
+                .requestSatelliteAccessConfigurationForCurrentLocation(mockResultReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mockResultReceiver, times(1)).send(resultCodeCaptor.capture(),
+                bundleCaptor.capture());
+        assertEquals(SatelliteManager.SATELLITE_RESULT_SUCCESS, (int) resultCodeCaptor.getValue());
+        assertTrue(bundleCaptor.getValue().containsKey(KEY_SATELLITE_ACCESS_CONFIGURATION));
+        assertSame(bundleCaptor.getValue().getParcelable(KEY_SATELLITE_ACCESS_CONFIGURATION,
+                SatelliteAccessConfiguration.class), satelliteAccessConfig);
+        verify(mockSatelliteAllowedStateCallback, times(1))
+                .onSatelliteAccessConfigurationChanged(
+                        satelliteAccessConfigurationCaptor.capture());
+        assertEquals(satelliteAccessConfigurationCaptor.getValue(), satelliteAccessConfig);
+
+        // satellite communication allowed state is disabled and
+        // regional config id is null.
+        clearInvocations(mockResultReceiver);
+        clearInvocations(mockSatelliteAllowedStateCallback);
+        when(mMockCachedAccessRestrictionMap.get(any())).thenReturn(null);
+        mSatelliteAccessControllerUT
+                .requestSatelliteAccessConfigurationForCurrentLocation(mockResultReceiver);
+        mTestableLooper.processAllMessages();
+
+        verify(mockResultReceiver, times(1)).send(resultCodeCaptor.capture(),
+                bundleCaptor.capture());
+        assertEquals(SATELLITE_RESULT_SUCCESS, (int) resultCodeCaptor.getValue());
+        assertTrue(bundleCaptor.getValue().containsKey(KEY_SATELLITE_ACCESS_CONFIGURATION));
+        assertNull(bundleCaptor.getValue().getParcelable(KEY_SATELLITE_ACCESS_CONFIGURATION,
+                SatelliteAccessConfiguration.class));
+        verify(mockSatelliteAllowedStateCallback, times(1))
+                .onSatelliteAccessConfigurationChanged(
+                        satelliteAccessConfigurationCaptor.capture());
+        assertNull(satelliteAccessConfigurationCaptor.getValue());
+    }
+
+    private SatelliteAccessConfiguration getSatelliteAccessConfiguration() {
+        Parcel satelliteAccessconfigParcel = Parcel.obtain();
+
+        List<SatelliteInfo> satelliteInfoList = new ArrayList<>();
+        satelliteInfoList.add(mock(SatelliteInfo.class));
+        satelliteAccessconfigParcel.writeTypedList(satelliteInfoList);
+
+        List<Integer> tagIds = new ArrayList<>(List.of(1, 2));
+        satelliteAccessconfigParcel.writeList(tagIds);
+
+        return new SatelliteAccessConfiguration(satelliteAccessconfigParcel);
+    }
+
+    @Test
+    public void testRegisterForCommunicationAllowedStateChanged() throws Exception {
+        ISatelliteCommunicationAllowedStateCallback mockSatelliteAllowedStateCallback = mock(
+                ISatelliteCommunicationAllowedStateCallback.class);
+        doReturn(true).when(mSatelliteCommunicationAllowedStateCallbackMap)
+                .put(any(IBinder.class), any(ISatelliteCommunicationAllowedStateCallback.class));
+        replaceInstance(SatelliteAccessController.class,
+                "mSatelliteCommunicationAllowedStateChangedListeners", mSatelliteAccessControllerUT,
+                mSatelliteCommunicationAllowedStateCallbackMap);
+
+        doReturn(false).when(mMockFeatureFlags).oemEnabledSatelliteFlag();
+        int result = mSatelliteAccessControllerUT.registerForCommunicationAllowedStateChanged(
+                DEFAULT_SUBSCRIPTION_ID, mockSatelliteAllowedStateCallback);
+        mTestableLooper.processAllMessages();
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, result);
+        verify(mockSatelliteAllowedStateCallback, never())
+                .onSatelliteCommunicationAllowedStateChanged(anyBoolean());
+        verify(mockSatelliteAllowedStateCallback, never())
+                .onSatelliteAccessConfigurationChanged(any(SatelliteAccessConfiguration.class));
+
+        doReturn(true).when(mMockFeatureFlags).oemEnabledSatelliteFlag();
+        result = mSatelliteAccessControllerUT.registerForCommunicationAllowedStateChanged(
+                DEFAULT_SUBSCRIPTION_ID, mockSatelliteAllowedStateCallback);
+        mTestableLooper.processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS, result);
+        verify(mockSatelliteAllowedStateCallback, times(1))
+                .onSatelliteCommunicationAllowedStateChanged(anyBoolean());
+        verify(mockSatelliteAllowedStateCallback, times(1))
+                .onSatelliteAccessConfigurationChanged(
+                        nullable(SatelliteAccessConfiguration.class));
+    }
+
+    @Test
+    public void testNotifyRegionalSatelliteConfigurationChanged() throws Exception {
+        // setup test
+        ISatelliteCommunicationAllowedStateCallback mockSatelliteAllowedStateCallback = mock(
+                ISatelliteCommunicationAllowedStateCallback.class);
+        ArgumentCaptor<SatelliteAccessConfiguration> satelliteAccessConfigurationCaptor =
+                ArgumentCaptor.forClass(SatelliteAccessConfiguration.class);
+
+        when(mSatelliteCommunicationAllowedStateCallbackMap.values())
+                .thenReturn(List.of(mockSatelliteAllowedStateCallback));
+        replaceInstance(SatelliteAccessController.class,
+                "mSatelliteCommunicationAllowedStateChangedListeners", mSatelliteAccessControllerUT,
+                mSatelliteCommunicationAllowedStateCallbackMap);
+
+        // register callback
+        mSatelliteAccessControllerUT.registerForCommunicationAllowedStateChanged(
+                DEFAULT_SUBSCRIPTION_ID, mockSatelliteAllowedStateCallback);
+
+        // verify if the callback is
+        // the same instance from onmSatelliteCommunicationAllowedStateCallbackMap
+        verify(mSatelliteCommunicationAllowedStateCallbackMap).put(any(),
+                mAllowedStateCallbackCaptor.capture());
+        assertSame(mockSatelliteAllowedStateCallback, mAllowedStateCallbackCaptor.getValue());
+
+        // create SatelliteAccessConfiguration data for this test
+        SatelliteAccessConfiguration satelliteAccessConfig = getSatelliteAccessConfiguration();
+
+        // trigger notifyRegionalSatelliteConfigurationChanged
+        mSatelliteAccessControllerUT
+                .notifyRegionalSatelliteConfigurationChanged(satelliteAccessConfig);
+
+        // verify if the satelliteAccessConfig is the same instance with the captured one.
+        verify(mockSatelliteAllowedStateCallback).onSatelliteAccessConfigurationChanged(
+                satelliteAccessConfigurationCaptor.capture());
+        assertSame(satelliteAccessConfig, satelliteAccessConfigurationCaptor.getValue());
+    }
+
+    @Test
+    public void testCheckSatelliteAccessRestrictionForLocation() throws Exception {
+        // Setup
+        logd("testCheckSatelliteAccessRestrictionForLocation : setup");
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        ArgumentCaptor<Integer> regionalConfigIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        replaceInstance(SatelliteAccessController.class, "mS2Level",
+                mSatelliteAccessControllerUT, DEFAULT_S2_LEVEL);
+        Iterator<ResultReceiver> mockResultReceiverIterator = mock(Iterator.class);
+        mSatelliteAccessControllerUT.setRegionalConfigId(null);
+
+        doReturn(mockResultReceiverIterator).when(mMockSatelliteAllowResultReceivers).iterator();
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        doNothing().when(mMockSatelliteAllowResultReceivers).clear();
+        doReturn(mMockResultReceiver).when(mockResultReceiverIterator).next();
+        replaceInstance(SatelliteAccessController.class, "mSatelliteAllowResultReceivers",
+                mSatelliteAccessControllerUT, mMockSatelliteAllowResultReceivers);
+        replaceInstance(SatelliteAccessController.class, "mCachedAccessRestrictionMap",
+                mSatelliteAccessControllerUT, mMockCachedAccessRestrictionMap);
+
+        // when mMockCachedAccessRestrictionMap is hit and has DEFAULT_REGIONAL_SATELLITE_CONFIG_ID,
+        // verify belows
+        // - the bundle data of KEY_SATELLITE_COMMUNICATION_ALLOWED is true
+        // - the newRegionalConfigId is the same as DEFAULT_REGIONAL_SATELLITE_CONFIG_ID
+        // - the regionalConfigId is the same as DEFAULT_REGIONAL_SATELLITE_CONFIG_ID
+        logd("testCheckSatelliteAccessRestrictionForLocation : case 1");
+        clearInvocations(mMockSatelliteOnDeviceAccessController);
+        clearInvocations(mMockCachedAccessRestrictionMap);
+
+        doReturn(true).when(mMockCachedAccessRestrictionMap)
+                .containsKey(any(SatelliteOnDeviceAccessController.LocationToken.class));
+        doReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID).when(mMockCachedAccessRestrictionMap)
+                .get(any(SatelliteOnDeviceAccessController.LocationToken.class));
+
+        mSatelliteAccessControllerUT.checkSatelliteAccessRestrictionForLocation(mMockLocation0);
+        verify(mMockResultReceiver, times(1))
+                .send(mResultCodeIntCaptor.capture(), bundleCaptor.capture());
+        verify(mMockSatelliteOnDeviceAccessController, never()).getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class));
+        assertEquals(Integer.valueOf(SATELLITE_RESULT_SUCCESS), mResultCodeIntCaptor.getValue());
+        assertTrue(bundleCaptor.getValue().getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED));
+        assertEquals(Integer.valueOf(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID),
+                mSatelliteAccessControllerUT.getNewRegionalConfigId());
+        assertEquals(Integer.valueOf(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID),
+                mSatelliteAccessControllerUT.getRegionalConfigId());
+
+        // when mMockCachedAccessRestrictionMap is not hit and regionalConfigId is null
+        // verify belows
+        // - the bundle data of KEY_SATELLITE_COMMUNICATION_ALLOWED is false
+        // - the regionalConfigId is null
+        logd("testCheckSatelliteAccessRestrictionForLocation : case 2");
+        clearInvocations(mMockCachedAccessRestrictionMap);
+        doReturn(false).when(mMockCachedAccessRestrictionMap)
+                .containsKey(any(SatelliteOnDeviceAccessController.LocationToken.class));
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(null);
+
+        mSatelliteAccessControllerUT.checkSatelliteAccessRestrictionForLocation(mMockLocation0);
+        verify(mMockResultReceiver, times(2))
+                .send(mResultCodeIntCaptor.capture(), bundleCaptor.capture());
+        assertEquals(Integer.valueOf(SATELLITE_RESULT_SUCCESS), mResultCodeIntCaptor.getValue());
+        assertFalse(bundleCaptor.getValue().getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED));
+        verify(mMockCachedAccessRestrictionMap, times(1))
+                .put(any(), regionalConfigIdCaptor.capture());
+        assertNull(regionalConfigIdCaptor.getValue());
+        assertNull(mSatelliteAccessControllerUT.getNewRegionalConfigId());
+        assertNull(mSatelliteAccessControllerUT.getRegionalConfigId());
+
+        // when mMockCachedAccessRestrictionMap is not hit and
+        // regionalConfigId is DEFAULT_REGIONAL_SATELLITE_CONFIG_ID
+        // verify belows
+        // - the bundle data of KEY_SATELLITE_COMMUNICATION_ALLOWED is true
+        // - the regionalConfigId is DEFAULT_REGIONAL_SATELLITE_CONFIG_ID
+        logd("testCheckSatelliteAccessRestrictionForLocation : case 3");
+        clearInvocations(mMockCachedAccessRestrictionMap);
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+
+        mSatelliteAccessControllerUT.checkSatelliteAccessRestrictionForLocation(mMockLocation0);
+        verify(mMockResultReceiver, times(3))
+                .send(mResultCodeIntCaptor.capture(), bundleCaptor.capture());
+        assertEquals(Integer.valueOf(SATELLITE_RESULT_SUCCESS), mResultCodeIntCaptor.getValue());
+        assertTrue(bundleCaptor.getValue().getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED));
+        verify(mMockCachedAccessRestrictionMap, times(1))
+                .put(any(), regionalConfigIdCaptor.capture());
+
+        assertEquals(Integer.valueOf(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID),
+                regionalConfigIdCaptor.getValue());
+        assertEquals(Integer.valueOf(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID),
+                mSatelliteAccessControllerUT.getNewRegionalConfigId());
+        assertEquals(Integer.valueOf(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID),
+                mSatelliteAccessControllerUT.getRegionalConfigId());
+
+
+        // when mMockCachedAccessRestrictionMap is not hit and regionalConfigId is null
+        // verify belows
+        // - the bundle data of KEY_SATELLITE_COMMUNICATION_ALLOWED is false
+        // - the regionalConfigId is null
+        logd("testCheckSatelliteAccessRestrictionForLocation : case 4");
+        clearInvocations(mMockCachedAccessRestrictionMap);
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(null);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+
+        mSatelliteAccessControllerUT.checkSatelliteAccessRestrictionForLocation(mMockLocation0);
+        verify(mMockResultReceiver, times(4))
+                .send(mResultCodeIntCaptor.capture(), bundleCaptor.capture());
+        assertEquals(Integer.valueOf(SATELLITE_RESULT_SUCCESS), mResultCodeIntCaptor.getValue());
+        assertFalse(bundleCaptor.getValue().getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED));
+        verify(mMockCachedAccessRestrictionMap, times(1))
+                .put(any(), regionalConfigIdCaptor.capture());
+        assertNull(regionalConfigIdCaptor.getValue());
+        assertNull(mSatelliteAccessControllerUT.getNewRegionalConfigId());
+        assertNull(mSatelliteAccessControllerUT.getRegionalConfigId());
+    }
+
     @Test
     public void testIsRegionDisallowed() throws Exception {
         // setup to make the return value of mQueriedSatelliteAllowed 'true'
@@ -444,12 +852,14 @@
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         doReturn(true).when(mMockLocationManager).isLocationEnabled();
-        when(mMockSatelliteOnDeviceAccessController.isSatCommunicationAllowedAtLocation(
-                any(SatelliteOnDeviceAccessController.LocationToken.class))).thenReturn(true);
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID);
         replaceInstance(SatelliteAccessController.class, "mCachedAccessRestrictionMap",
                 mSatelliteAccessControllerUT, mMockCachedAccessRestrictionMap);
         doReturn(true).when(mMockCachedAccessRestrictionMap).containsKey(any());
-        doReturn(true).when(mMockCachedAccessRestrictionMap).get(any());
+        doReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID)
+                .when(mMockCachedAccessRestrictionMap).get(any());
 
         // get allowed country codes EMPTY from resources
         when(mMockResources.getStringArray(
@@ -681,7 +1091,7 @@
         mTestableLooper.processAllMessages();
         assertTrue(
                 mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted());
-        verify(mMockSatelliteOnDeviceAccessController).isSatCommunicationAllowedAtLocation(
+        verify(mMockSatelliteOnDeviceAccessController).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
                 mSatelliteAllowedSemaphore, 1));
@@ -726,7 +1136,7 @@
         sendLocationRequestResult(mMockLocation0);
         assertFalse(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted());
         // The LocationToken should be already in the cache
-        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+        verify(mMockSatelliteOnDeviceAccessController, never()).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
                 mSatelliteAllowedSemaphore, 1));
@@ -758,7 +1168,7 @@
                 mSatelliteAccessControllerUT.getWaitForCurrentLocationTimeoutMillis());
         mTestableLooper.processAllMessages();
         assertFalse(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted());
-        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+        verify(mMockSatelliteOnDeviceAccessController, never()).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
                 mSatelliteAllowedSemaphore, 1));
@@ -784,7 +1194,7 @@
         verify(mMockLocationManager, never()).getCurrentLocation(anyString(),
                 any(LocationRequest.class), any(CancellationSignal.class), any(Executor.class),
                 any(Consumer.class));
-        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+        verify(mMockSatelliteOnDeviceAccessController, never()).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
         assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
                 mSatelliteAllowedSemaphore, 1));
@@ -793,6 +1203,91 @@
     }
 
     @Test
+    public void testLocationQueryThrottleTimeUpdate() {
+        long firstMccChangedTime = 1;
+        long lastKnownLocationElapsedRealtime =
+                firstMccChangedTime + TEST_LOCATION_QUERY_THROTTLE_INTERVAL_NANOS;
+
+        // OEM-enabled satellite is supported
+        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+
+        verify(mMockCountryDetector).registerForCountryCodeChanged(
+                mCountryDetectorHandlerCaptor.capture(), mCountryDetectorIntCaptor.capture(),
+                mCountryDetectorObjCaptor.capture());
+
+        assertSame(mCountryDetectorHandlerCaptor.getValue(), mSatelliteAccessControllerUT);
+        assertSame(mCountryDetectorIntCaptor.getValue(), EVENT_COUNTRY_CODE_CHANGED);
+        assertNull(mCountryDetectorObjCaptor.getValue());
+
+        // Setup to invoke GPS query
+        clearInvocations(mMockSatelliteOnDeviceAccessController);
+        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
+        setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
+        doReturn(true).when(mMockLocationManager).isLocationEnabled();
+        when(mMockLocationManager.getLastKnownLocation(LocationManager.FUSED_PROVIDER))
+                .thenReturn(null);
+        when(mMockLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER))
+                .thenReturn(null);
+
+        // When mcc changed first, so queried a location with GPS,
+        // verify if the mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos
+        // is the same with firstMccChangedTime.
+        // verify mMockLocationManager.getCurrentLocation() is invoked
+        // verify time(mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos) is
+        // firstMccChangedTime
+        clearInvocations(mMockLocationManager);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos = firstMccChangedTime;
+        sendCommandValidateCountryCodeChangeEvent(mMockContext);
+        verify(mMockLocationManager, times(1))
+                .getCurrentLocation(any(), any(), any(), any(), any());
+        assertEquals(firstMccChangedTime, mSatelliteAccessControllerUT
+                .mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
+
+        // set current time less than throttle_interval
+        // verify mMockLocationManager.getCurrentLocation() is not invoked
+        // verify time(mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos) is not updated
+        clearInvocations(mMockLocationManager);
+        doReturn(lastKnownLocationElapsedRealtime).when(mMockLocation1).getElapsedRealtimeNanos();
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos =
+                (firstMccChangedTime + TEST_LOCATION_QUERY_THROTTLE_INTERVAL_NANOS - 1);
+        sendCommandValidateCountryCodeChangeEvent(mMockContext);
+        verify(mMockLocationManager, never())
+                .getCurrentLocation(any(), any(), any(), any(), any());
+        assertEquals(firstMccChangedTime, mSatelliteAccessControllerUT
+                .mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
+
+        // Test the scenario when last know location is fresh and
+        // current time is greater than the location query throttle interval
+        // verify mMockLocationManager.getCurrentLocation() is not invoked
+        // verify time(mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos) is not updated
+        clearInvocations(mMockLocationManager);
+        doReturn(lastKnownLocationElapsedRealtime).when(mMockLocation1).getElapsedRealtimeNanos();
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos =
+                (lastKnownLocationElapsedRealtime + TEST_LOCATION_FRESH_DURATION_NANOS - 1);
+        sendCommandValidateCountryCodeChangeEvent(mMockContext);
+        verify(mMockLocationManager, never())
+                .getCurrentLocation(any(), any(), any(), any(), any());
+        assertEquals(firstMccChangedTime, mSatelliteAccessControllerUT
+                .mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
+
+        // Test the scenario when last know location is not fresh and
+        // current time is greater than the location query throttle interval
+        // verify mMockLocationManager.getCurrentLocation() is invoked
+        // verify time(mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos) is updated
+        clearInvocations(mMockLocationManager);
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(true);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos =
+                (lastKnownLocationElapsedRealtime + TEST_LOCATION_FRESH_DURATION_NANOS + 1);
+        sendCommandValidateCountryCodeChangeEvent(mMockContext);
+        verify(mMockLocationManager, times(1))
+                .getCurrentLocation(any(), any(), any(), any(), any());
+        assertEquals(lastKnownLocationElapsedRealtime + TEST_LOCATION_FRESH_DURATION_NANOS + 1,
+                mSatelliteAccessControllerUT
+                        .mLastLocationQueryForPossibleChangeInAllowedRegionTimeNanos);
+    }
+
+
+    @Test
     public void testAllowLocationQueryForSatelliteAllowedCheck() {
         mSatelliteAccessControllerUT.mLatestSatelliteCommunicationAllowedSetTime = 1;
 
@@ -849,7 +1344,7 @@
         assertNull(mCountryDetectorObjCaptor.getValue());
 
         // Normal case that invokes
-        // mMockSatelliteOnDeviceAccessController.isSatCommunicationAllowedAtLocation
+        // mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation
         clearInvocations(mMockSatelliteOnDeviceAccessController);
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
@@ -857,14 +1352,14 @@
         mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS;
         sendCommandValidateCountryCodeChangeEvent(mMockContext);
         verify(mMockSatelliteOnDeviceAccessController,
-                times(1)).isSatCommunicationAllowedAtLocation(
+                times(1)).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
 
         // Case that isCommunicationAllowedCacheValid is true
         clearInvocations(mMockSatelliteOnDeviceAccessController);
         mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
         sendCommandValidateCountryCodeChangeEvent(mMockContext);
-        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+        verify(mMockSatelliteOnDeviceAccessController, never()).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
 
         // Case that mLatestCacheEnforcedValidateTimeNanos is over
@@ -881,11 +1376,11 @@
         when(mMockLocation0.getLongitude()).thenReturn(2.0);
         when(mMockLocation1.getLatitude()).thenReturn(3.0);
         when(mMockLocation1.getLongitude()).thenReturn(3.0);
-        when(mMockSatelliteOnDeviceAccessController.isSatCommunicationAllowedAtLocation(
-                any(SatelliteOnDeviceAccessController.LocationToken.class))).thenReturn(false);
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class))).thenReturn(null);
         sendCommandValidateCountryCodeChangeEvent(mMockContext);
         verify(mMockSatelliteOnDeviceAccessController,
-                times(1)).isSatCommunicationAllowedAtLocation(
+                times(1)).getRegionalConfigIdForLocation(
                 any(SatelliteOnDeviceAccessController.LocationToken.class));
     }
 
@@ -1005,19 +1500,18 @@
                 .thenReturn(TEST_SATELLITE_ALLOW);
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
-        when(mMockSatelliteOnDeviceAccessController.isSatCommunicationAllowedAtLocation(
-                any(SatelliteOnDeviceAccessController.LocationToken.class))).thenReturn(true);
+        when(mMockSatelliteOnDeviceAccessController.getRegionalConfigIdForLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class)))
+                .thenReturn(DEFAULT_REGIONAL_SATELLITE_CONFIG_ID);
         replaceInstance(SatelliteAccessController.class, "mCachedAccessRestrictionMap",
                 mSatelliteAccessControllerUT, mMockCachedAccessRestrictionMap);
         doReturn(false).when(mMockCachedAccessRestrictionMap).containsKey(any());
         mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
 
         // Captor and Verify if the mockReceiver and mocContext is registered well
-        verify(mMockContext).registerReceiver(mLocationBroadcastReceiverCaptor.capture(),
+        verify(mMockContext, times(2))
+                .registerReceiver(mLocationBroadcastReceiverCaptor.capture(),
                 mIntentFilterCaptor.capture());
-        assertSame(mSatelliteAccessControllerUT.getLocationBroadcastReceiver(),
-                mLocationBroadcastReceiverCaptor.getValue());
-        assertSame(MODE_CHANGED_ACTION, mIntentFilterCaptor.getValue().getAction(0));
 
         // When the intent action is not MODE_CHANGED_ACTION,
         // verify if the location manager never invoke isLocationEnabled()
@@ -1056,7 +1550,7 @@
         // In emergency case,
         // verify if the location manager get FUSED provider and ignore location settings
         doReturn(true).when(mMockTelecomManager).isInEmergencyCall();
-        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull();
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(true);
         mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
         mSatelliteAccessControllerUT.checkSatelliteAccessRestrictionUsingGPS();
 
@@ -1074,7 +1568,7 @@
         doReturn(false).when(mMockPhone2).isInEcm();
         doReturn(false).when(mMockSatelliteController).isInEmergencyMode();
         doReturn(true).when(mMockLocationManager).isLocationEnabled();
-        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull();
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(true);
         mSatelliteAccessControllerUT.checkSatelliteAccessRestrictionUsingGPS();
 
         verify(mMockLocationManager, times(1))
@@ -1087,10 +1581,10 @@
     @Test
     public void testHandleIsSatelliteSupportedResult() throws Exception {
         // Setup for this test case
-        Iterator<ResultReceiver> mockIterator = mock(Iterator.class);
-        doReturn(mockIterator).when(mMockSatelliteAllowResultReceivers).iterator();
-        doReturn(true, false).when(mockIterator).hasNext();
-        doReturn(mMockSatelliteSupportedResultReceiver).when(mockIterator).next();
+        Iterator<ResultReceiver> mockResultReceiverIterator = mock(Iterator.class);
+        doReturn(mockResultReceiverIterator).when(mMockSatelliteAllowResultReceivers).iterator();
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        doReturn(mMockResultReceiver).when(mockResultReceiverIterator).next();
 
         replaceInstance(SatelliteAccessController.class, "mSatelliteAllowResultReceivers",
                 mSatelliteAccessControllerUT, mMockSatelliteAllowResultReceivers);
@@ -1099,10 +1593,10 @@
         // case that resultCode is not SATELLITE_RESULT_SUCCESS
         int resultCode = SATELLITE_RESULT_ERROR;
         Bundle bundle = new Bundle();
-        doReturn(true, false).when(mockIterator).hasNext();
-        clearInvocations(mMockSatelliteSupportedResultReceiver);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        clearInvocations(mMockResultReceiver);
         mSatelliteAccessControllerUT.handleIsSatelliteSupportedResult(resultCode, bundle);
-        verify(mMockSatelliteSupportedResultReceiver)
+        verify(mMockResultReceiver)
                 .send(mResultCodeIntCaptor.capture(), any());
         assertEquals(Integer.valueOf(SATELLITE_RESULT_ERROR), mResultCodeIntCaptor.getValue());
 
@@ -1110,20 +1604,19 @@
         // verify that the resultCode is delivered as it were
         resultCode = SATELLITE_RESULT_SUCCESS;
         bundle.putBoolean(KEY_SATELLITE_PROVISIONED, false);
-        doReturn(true, false).when(mockIterator).hasNext();
-        clearInvocations(mMockSatelliteSupportedResultReceiver);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        clearInvocations(mMockResultReceiver);
         mSatelliteAccessControllerUT.handleIsSatelliteSupportedResult(resultCode, bundle);
-        verify(mMockSatelliteSupportedResultReceiver)
-                .send(mResultCodeIntCaptor.capture(), any());
+        verify(mMockResultReceiver).send(mResultCodeIntCaptor.capture(), any());
         assertEquals(Integer.valueOf(SATELLITE_RESULT_SUCCESS), mResultCodeIntCaptor.getValue());
 
         // case KEY_SATELLITE_SUPPORTED is false
         // verify SATELLITE_RESULT_NOT_SUPPORTED is captured
         bundle.putBoolean(KEY_SATELLITE_SUPPORTED, false);
-        doReturn(true, false).when(mockIterator).hasNext();
-        clearInvocations(mMockSatelliteSupportedResultReceiver);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        clearInvocations(mMockResultReceiver);
         mSatelliteAccessControllerUT.handleIsSatelliteSupportedResult(resultCode, bundle);
-        verify(mMockSatelliteSupportedResultReceiver)
+        verify(mMockResultReceiver)
                 .send(mResultCodeIntCaptor.capture(), mResultDataBundleCaptor.capture());
         assertEquals(Integer.valueOf(SATELLITE_RESULT_NOT_SUPPORTED),
                 mResultCodeIntCaptor.getValue());
@@ -1135,10 +1628,10 @@
         bundle.putBoolean(KEY_SATELLITE_SUPPORTED, true);
         when(mMockCountryDetector.getCurrentNetworkCountryIso())
                 .thenReturn(List.of(TEST_SATELLITE_COUNTRY_CODE_KR));
-        doReturn(true, false).when(mockIterator).hasNext();
-        clearInvocations(mMockSatelliteSupportedResultReceiver);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        clearInvocations(mMockResultReceiver);
         mSatelliteAccessControllerUT.handleIsSatelliteSupportedResult(resultCode, bundle);
-        verify(mMockSatelliteSupportedResultReceiver)
+        verify(mMockResultReceiver)
                 .send(mResultCodeIntCaptor.capture(), mResultDataBundleCaptor.capture());
         assertEquals(Integer.valueOf(SATELLITE_RESULT_SUCCESS),
                 mResultCodeIntCaptor.getValue());
@@ -1150,10 +1643,10 @@
         when(mMockCountryDetector.getCurrentNetworkCountryIso())
                 .thenReturn(List.of(TEST_SATELLITE_COUNTRY_CODE_US));
         doReturn(false).when(mMockLocationManager).isLocationEnabled();
-        doReturn(true, false).when(mockIterator).hasNext();
-        clearInvocations(mMockSatelliteSupportedResultReceiver);
+        doReturn(true, false).when(mockResultReceiverIterator).hasNext();
+        clearInvocations(mMockResultReceiver);
         mSatelliteAccessControllerUT.handleIsSatelliteSupportedResult(resultCode, bundle);
-        verify(mMockSatelliteSupportedResultReceiver)
+        verify(mMockResultReceiver)
                 .send(mResultCodeIntCaptor.capture(), mResultDataBundleCaptor.capture());
         assertEquals(Integer.valueOf(SATELLITE_RESULT_LOCATION_DISABLED),
                 mResultCodeIntCaptor.getValue());
@@ -1173,7 +1666,7 @@
         doReturn(false).when(mMockPhone2).isInEcm();
         doReturn(false).when(mMockSatelliteController).isInEmergencyMode();
         doReturn(true).when(mMockLocationManager).isLocationEnabled();
-        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull();
+        mSatelliteAccessControllerUT.setLocationRequestCancellationSignalAsNull(true);
         mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
 
         // Invoking requestIsCommunicationAllowedForCurrentLocation(resultReceiver, "false");
@@ -1200,7 +1693,7 @@
 
     private void sendSatelliteCommunicationAllowedEvent() {
         Pair<Integer, ResultReceiver> requestPair =
-                new Pair<>(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                new Pair<>(DEFAULT_SUBSCRIPTION_ID,
                         mSatelliteAccessControllerUT.getResultReceiverCurrentLocation());
         Message msg = mSatelliteAccessControllerUT.obtainMessage(
                 CMD_IS_SATELLITE_COMMUNICATION_ALLOWED);
@@ -1217,6 +1710,13 @@
         mTestableLooper.processAllMessages();
     }
 
+    private void sendCurrentLocationTimeoutEvent() {
+        Message msg = mSatelliteAccessControllerUT
+                .obtainMessage(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
+        msg.sendToTarget();
+        mTestableLooper.processAllMessages();
+    }
+
     private void sendCommandValidateCountryCodeChangeEvent(Context context) {
         Message msg = mSatelliteAccessControllerUT.obtainMessage(EVENT_COUNTRY_CODE_CHANGED);
         msg.obj = new AsyncResult(context, SATELLITE_RESULT_SUCCESS, null);
@@ -1382,9 +1882,35 @@
             return mLocationModeChangedBroadcastReceiver;
         }
 
-        public void setLocationRequestCancellationSignalAsNull() {
+        public void setLocationRequestCancellationSignalAsNull(boolean isNull) {
             synchronized (mLock) {
-                mLocationRequestCancellationSignal = null;
+                mLocationRequestCancellationSignal = isNull ? null : new CancellationSignal();
+            }
+        }
+
+        public boolean isCurrentSatelliteAllowedState() {
+            synchronized (mSatelliteCommunicationAllowStateLock) {
+                return mCurrentSatelliteAllowedState;
+            }
+        }
+
+        @Nullable
+        public Integer getRegionalConfigId() {
+            synchronized (mLock) {
+                return mRegionalConfigId;
+            }
+        }
+
+        @Nullable
+        public Integer getNewRegionalConfigId() {
+            synchronized (mLock) {
+                return mNewRegionalConfigId;
+            }
+        }
+
+        public void setRegionalConfigId(@Nullable Integer regionalConfigId) {
+            synchronized (mLock) {
+                mRegionalConfigId = regionalConfigId;
             }
         }
     }
diff --git a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java
index e663519..9f8a733 100644
--- a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java
+++ b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java
@@ -20,6 +20,7 @@
 import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_DISABLED;
 import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_ENABLED;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -46,21 +47,22 @@
 import android.net.NetworkCapabilities;
 import android.net.wifi.WifiInfo;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
-import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Log;
 import android.util.Pair;
 
 import androidx.annotation.NonNull;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.metrics.MetricsCollector;
+import com.android.internal.telephony.metrics.PersistAtomsStorage;
 import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.libraries.entitlement.ServiceEntitlementException;
@@ -70,7 +72,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -83,7 +85,8 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class SatelliteEntitlementControllerTest extends TelephonyTestBase {
     private static final String TAG = "SatelliteEntitlementControllerTest";
     private static final int SUB_ID = 0;
@@ -97,13 +100,12 @@
     private static final int CMD_RETRY_QUERY_ENTITLEMENT = 2;
     private static final int CMD_SIM_REFRESH = 3;
     private static final int MAX_RETRY_COUNT = 5;
-    @Mock
-    CarrierConfigManager mCarrierConfigManager;
-    @Mock
-    ConnectivityManager mConnectivityManager;
+    @Mock CarrierConfigManager mCarrierConfigManager;
+    @Mock ConnectivityManager mConnectivityManager;
     @Mock Network mNetwork;
     @Mock TelephonyManager mTelephonyManager;
     @Mock SubscriptionManagerService mMockSubscriptionManagerService;
+    @Mock private MetricsCollector mMetricsCollector;
     @Mock SatelliteEntitlementApi mSatelliteEntitlementApi;
     @Mock SatelliteEntitlementResult mSatelliteEntitlementResult;
     @Mock SatelliteController mSatelliteController;
@@ -113,23 +115,19 @@
     private TestableLooper mTestableLooper;
     private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
             mCarrierConfigChangedListenerList = new ArrayList<>();
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        MockitoAnnotations.initMocks(this);
 
         replaceInstance(SubscriptionManagerService.class, "sInstance", null,
                 mMockSubscriptionManagerService);
         replaceInstance(SatelliteController.class, "sInstance", null, mSatelliteController);
+        replaceInstance(PhoneFactory.class, "sMetricsCollector", null, mMetricsCollector);
+        doReturn(Mockito.mock(PersistAtomsStorage.class)).when(mMetricsCollector).getAtomsStorage();
 
-        HandlerThread handlerThread = new HandlerThread("SatelliteEntitlementController");
-        handlerThread.start();
-        mHandler = new Handler(handlerThread.getLooper()) {
-            @Override
-            public void handleMessage(Message msg) {
-            }
-        };
-        mTestableLooper = new TestableLooper(mHandler.getLooper());
+        mTestableLooper = TestableLooper.get(this);
+        mHandler = new Handler(mTestableLooper.getLooper());
         doReturn(Context.TELEPHONY_SERVICE).when(mContext).getSystemServiceName(
                 TelephonyManager.class);
         doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
@@ -160,9 +158,8 @@
                 Context.CONNECTIVITY_SERVICE);
         doReturn(mNetwork).when(mConnectivityManager).getActiveNetwork();
         doReturn(ACTIVE_SUB_ID).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
-        mSatelliteEntitlementController = new TestSatelliteEntitlementController(mContext,
-                mHandler.getLooper(), mSatelliteEntitlementApi);
-        mSatelliteEntitlementController = spy(mSatelliteEntitlementController);
+        mSatelliteEntitlementController = spy(new TestSatelliteEntitlementController(mContext,
+                mTestableLooper.getLooper(), mSatelliteEntitlementApi));
         doReturn(mSatelliteEntitlementResult).when(
                 mSatelliteEntitlementApi).checkEntitlementStatus();
     }
@@ -174,7 +171,6 @@
 
     @Test
     public void testShouldStartQueryEntitlement() throws Exception {
-        logd("testShouldStartQueryEntitlement");
         doReturn(ACTIVE_SUB_ID).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
 
         // Verify don't start the query when KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL is false.
@@ -249,7 +245,6 @@
 
     @Test
     public void testCheckSatelliteEntitlementStatus() throws Exception {
-        logd("testCheckSatelliteEntitlementStatus");
         setIsQueryAvailableTrue();
         // Verify don't call the checkSatelliteEntitlementStatus when getActiveSubIdList is empty.
         doReturn(new int[]{}).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
@@ -340,7 +335,6 @@
 
     @Test
     public void testCheckSatelliteEntitlementStatusWhenInternetConnected() throws Exception {
-        logd("testCheckSatelliteEntitlementStatusWhenInternetConnected");
         ConnectivityManager.NetworkCallback networkCallback =
                 (ConnectivityManager.NetworkCallback) getValue("mNetworkCallback");
         Network mockNetwork = mock(Network.class);
@@ -362,7 +356,6 @@
 
     @Test
     public void testCheckSatelliteEntitlementStatusWhenCarrierConfigChanged() throws Exception {
-        logd("testCheckSatelliteEntitlementStatusWhenCarrierConfigChanged");
         // Verify the called the checkSatelliteEntitlementStatus when CarrierConfigChanged
         // occurred and Internet is connected.
         setInternetConnected(true);
@@ -379,7 +372,6 @@
 
     @Test
     public void testCheckWhenStartCmdIsReceivedDuringRetry() throws Exception {
-        logd("testCheckWhenStartCmdIsReceivedDuringRetry");
         // Verify that start cmd is ignored and retry is performed up to 5 times when start cmd
         // occurs during retries.
         setIsQueryAvailableTrue();
@@ -393,47 +385,47 @@
         verify(mSatelliteEntitlementApi, times(1)).checkEntitlementStatus();
         // Verify that the retry count is 0 after receiving a 503 with retry-after header in
         // response.
-        assertTrue(retryCountPerSub.getOrDefault(SUB_ID, 0) == 0);
+        assertEquals(0, retryCountPerSub.getOrDefault(SUB_ID, 0).longValue());
 
         // Verify that the retry count is 1 for the second query when receiving a 503 with
         // retry-after header in response.
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(2)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 1);
+        assertEquals(1, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that the retry count is 2 for the third query when receiving a 503 with
         // retry-after header in response.
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(3)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 2);
+        assertEquals(2, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that start CMD is ignored during retries.
         sendMessage(CMD_START_QUERY_ENTITLEMENT, SUB_ID);
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(3)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 2);
+        assertEquals(2, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that the retry count is 3 for the forth query when receiving a 503 with
         // retry-after header in response.
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(4)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 3);
+        assertEquals(3, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that the retry count is 4 for the fifth query when receiving a 503 with
         // retry-after header in response.
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(5)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 4);
+        assertEquals(4, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that start CMD is ignored during retries.
         sendMessage(CMD_START_QUERY_ENTITLEMENT, SUB_ID);
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(5)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 4);
+        assertEquals(4, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that the retry count is 5 for the sixth query when receiving a 503 with
         // retry-after header in response.
@@ -463,7 +455,6 @@
 
     @Test
     public void testCheckAfterInternetConnectionChangedDuringRetry() throws Exception {
-        logd("testCheckAfterInternetConnectionChangedDuringRetry");
         // Verify that the retry count is maintained even when internet connection is lost and
         // connected during retries, and that up to 5 retries are performed.
         setIsQueryAvailableTrue();
@@ -477,48 +468,48 @@
         verify(mSatelliteEntitlementApi, times(1)).checkEntitlementStatus();
         // Verify that the retry count is 0 after receiving a 503 with retry-after header in
         // response.
-        assertTrue(retryCountPerSub.getOrDefault(SUB_ID, 0) == 0);
+        assertEquals(0, retryCountPerSub.getOrDefault(SUB_ID, 0).longValue());
 
         // Verify that the retry count is 1 for the second query when receiving a 503 with
         // retry-after header in response.
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(2)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 1);
+        assertEquals(1, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that no query is executed and the retry count does not increase when internet
         // connection is lost during the second retry.
         setInternetConnected(false);
-        mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
+        mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(2));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(2)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 1);
+        assertEquals(1, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that the query is started when internet connection is restored and that the
         // retry count does not increase.
         setInternetConnected(true);
-        logd("internet connected again");
+        Log.d(TAG, "internet connected again");
         sendMessage(CMD_START_QUERY_ENTITLEMENT, SUB_ID);
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(3)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 1);
+        assertEquals(1, retryCountPerSub.get(SUB_ID).longValue());
 
         // Verify that the retry count is increases after received a 503 with retry-after header
         // in response.
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(4)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 2);
+        assertEquals(2, retryCountPerSub.get(SUB_ID).longValue());
 
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(5)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 3);
+        assertEquals(3, retryCountPerSub.get(SUB_ID).longValue());
 
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(6)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 4);
+        assertEquals(4, retryCountPerSub.get(SUB_ID).longValue());
 
         mTestableLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(1));
         mTestableLooper.processAllMessages();
@@ -546,7 +537,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_error500() throws Exception {
-        logd("testStartQueryEntitlementStatus_error500");
         setIsQueryAvailableTrue();
         Map<Integer, Integer> retryCountPerSub =
                 (Map<Integer, Integer>) getValue("mRetryCountPerSub");
@@ -562,7 +552,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_error503_retrySuccess() throws Exception {
-        logd("testStartQueryEntitlementStatus_error503_retrySuccess");
         setIsQueryAvailableTrue();
         set503RetryAfterResponse();
         Map<Integer, Integer> retryCountPerSub =
@@ -590,7 +579,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_otherError_retrySuccess() throws Exception {
-        logd("testStartQueryEntitlementStatus_otherError_retrySuccess");
         setIsQueryAvailableTrue();
         Map<Integer, Integer> retryCountPerSub =
                 (Map<Integer, Integer>) getValue("mRetryCountPerSub");
@@ -615,7 +603,7 @@
         sendMessage(CMD_RETRY_QUERY_ENTITLEMENT, SUB_ID);
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(2)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 1);
+        assertEquals(1, retryCountPerSub.get(SUB_ID).longValue());
         // Verify don't call the onSatelliteEntitlementStatusUpdated.
         verify(mSatelliteController, never()).onSatelliteEntitlementStatusUpdated(anyInt(),
                 anyBoolean(), anyList(), anyList(), any());
@@ -630,14 +618,13 @@
         sendMessage(CMD_RETRY_QUERY_ENTITLEMENT, SUB_ID);
         mTestableLooper.processAllMessages();
         verify(mSatelliteEntitlementApi, times(3)).checkEntitlementStatus();
-        assertTrue(retryCountPerSub.get(SUB_ID) == 1);
+        assertEquals(1, retryCountPerSub.get(SUB_ID).longValue());
         verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(eq(SUB_ID), eq(true),
                 eq(PLMN_ALLOWED_LIST), eq(PLMN_BARRED_LIST), any());
     }
 
     @Test
     public void testSatelliteEntitlementSupportedChangedFromSupportToNotSupport() throws Exception {
-        logd("testSatelliteEntitlementSupportedChangedFromSupportToNotSupport");
         setIsQueryAvailableTrue();
 
         // KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL changed from Support(entitlement status
@@ -692,7 +679,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_refreshStatus() throws Exception {
-        logd("testStartQueryEntitlementStatus_refreshStatus");
         setIsQueryAvailableTrue();
         mCarrierConfigBundle.putInt(
                 CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 1);
@@ -722,7 +708,6 @@
     @Test
     public void testStartQueryEntitlementStatus_internetDisconnectedAndConnectedAgain()
             throws Exception {
-        logd("testStartQueryEntitlementStatus_internetDisconnectedAndConnectedAgain");
         setIsQueryAvailableTrue();
 
         // Verify the query does not start if there is no internet connection.
@@ -750,7 +735,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_error503_error500() throws Exception {
-        logd("testStartQueryEntitlementStatus_error503_error500");
         setIsQueryAvailableTrue();
         set503RetryAfterResponse();
 
@@ -775,7 +759,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_error503_otherError() throws Exception {
-        logd("testStartQueryEntitlementStatus_error503_otherError");
         setIsQueryAvailableTrue();
         set503RetryAfterResponse();
 
@@ -813,7 +796,6 @@
 
     @Test
     public void testStartQueryEntitlementStatus_AfterSimRefresh() throws Exception {
-        logd("testStartQueryEntitlementStatus_AfterSimRefresh");
         setIsQueryAvailableTrue();
 
         // Verify the first query complete.
@@ -952,12 +934,8 @@
 
         @Override
         public SatelliteEntitlementApi getSatelliteEntitlementApi(int subId) {
-            logd("getSatelliteEntitlementApi");
+            Log.d(TAG, "getSatelliteEntitlementApi");
             return mInjectSatelliteEntitlementApi;
         }
     }
-
-    private static void logd(String log) {
-        Log.d(TAG, log);
-    }
 }
diff --git a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
index 71a23e6..fe6d6f7 100644
--- a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
+++ b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
@@ -22,7 +22,6 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.TestCase.assertEquals;
 
-import static org.mockito.Mockito.mock;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
@@ -38,15 +37,11 @@
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.CallFailCause;
-import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
 import com.android.phone.R;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 
 import java.util.Locale;
 
@@ -57,13 +52,6 @@
     public static final int PHONE_ID = 123;
     public static final String EMPTY_STRING = "";
 
-    // dynamic
-    private Context mContext;
-
-    //Mocks
-    @Mock
-    private GsmCdmaPhone mMockPhone;
-
     private final FlagsAdapter mFeatureFlags = new FlagsAdapter(){
         @Override
         public boolean doNotOverridePreciseLabel() {
@@ -74,11 +62,6 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        // objects that call static getInstance()
-        mMockPhone = mock(GsmCdmaPhone.class);
-        mContext = InstrumentationRegistry.getTargetContext();
-        // set mocks
-        setSinglePhone();
     }
 
     /**
@@ -253,11 +236,6 @@
         return config;
     }
 
-    private void setSinglePhone() throws Exception {
-        Phone[] mPhones = new Phone[]{mMockPhone};
-        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
-    }
-
     private Resources getResourcesForLocale(Context context, Locale locale) {
         Configuration config = new Configuration();
         config.setToDefaults();
@@ -268,7 +246,7 @@
 
     private void safeAssertLabel(Integer resourceId,
             android.telecom.DisconnectCause disconnectCause) {
-        Resources r = getResourcesForLocale(mContext, Locale.US);
+        Resources r = getResourcesForLocale(InstrumentationRegistry.getTargetContext(), Locale.US);
         if (resourceId == null || r == null) {
             return;
         }
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
index b1572f1..eacb001 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -25,21 +25,27 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.os.Looper;
+import android.content.pm.PackageManager;
 import android.telecom.PhoneAccountHandle;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
+import com.android.TelephonyTestBase;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 /**
  * Tests the functionality in ImsConferenceController.java
  */
-
-public class ImsConferenceControllerTest {
+@RunWith(AndroidJUnit4.class)
+public class ImsConferenceControllerTest extends TelephonyTestBase {
+    @Mock
+    PackageManager mPackageManager;
 
     @Mock
     private TelephonyConnectionServiceProxy mMockTelephonyConnectionServiceProxy;
@@ -64,11 +70,14 @@
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-        mTelecomAccountRegistry = TelecomAccountRegistry.getInstance(null);
+        super.setUp();
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING))
+                .thenReturn(true);
+
+        mTelecomAccountRegistry = TelecomAccountRegistry.getInstance(mContext);
         mTestTelephonyConnectionA = new TestTelephonyConnection();
         mTestTelephonyConnectionB = new TestTelephonyConnection();
 
@@ -79,6 +88,11 @@
                 mMockTelephonyConnectionServiceProxy, () -> false);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     /**
      * Behavior: add telephony connections B and A to conference controller,
      *           set status for connections, remove one call
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index ca16bc7..b6cb11a 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -124,6 +124,30 @@
     }
 
     /**
+     * Verifies that the default address presentation of an ImsConference is
+     * {@link TelecomManager#PRESENTATION_UNKNOWN}
+     */
+    @Test
+    @SmallTest
+    public void testDefaultNumberPresentationIsValid() {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+        mConferenceHost.setConnectionProperties(Connection.PROPERTY_ASSISTED_DIALING
+                | Connection.PROPERTY_WIFI);
+        Bundle extras = new Bundle();
+        extras.putInt(TelecomManager.EXTRA_CALL_NETWORK_TYPE, TelephonyManager.NETWORK_TYPE_IWLAN);
+        mConferenceHost.putTelephonyExtras(extras);
+        mConferenceHost.setStatusHints(new StatusHints("WIFIs", null, null));
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
+
+        assertEquals(TelecomManager.PRESENTATION_UNKNOWN, imsConference.getAddressPresentation());
+    }
+
+    /**
      * Verifies that an ImsConference will inform listeners when the "fullness" of the conference
      * changes as participants come and go.
      */
diff --git a/tests/src/com/android/services/telephony/TelecomAccountRegistryTest.java b/tests/src/com/android/services/telephony/TelecomAccountRegistryTest.java
index fc544b0..d0fc69d 100644
--- a/tests/src/com/android/services/telephony/TelecomAccountRegistryTest.java
+++ b/tests/src/com/android/services/telephony/TelecomAccountRegistryTest.java
@@ -50,9 +50,8 @@
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SimultaneousCallingTracker;
 import com.android.internal.telephony.flags.Flags;
-import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneInterfaceManager;
 import com.android.phone.R;
 
@@ -63,7 +62,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -83,7 +82,6 @@
     @Mock ImsManager mImsManager;
     @Mock SubscriptionManager mSubscriptionManager;
     @Mock ContentProvider mContentProvider;
-    @Mock PhoneGlobals mPhoneGlobals;
     @Mock Phone mPhone;
     @Mock Resources mResources;
     @Mock Drawable mDrawable;
@@ -96,18 +94,13 @@
     private BroadcastReceiver mUserSwitchedAndConfigChangedReceiver;
     private BroadcastReceiver mLocaleChangedBroadcastReceiver;
     private ContentResolver mContentResolver;
-    private Phone[] mPhones;
     private TestableLooper mTestableLooper;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
         mSetFlagsRule.disableFlags(Flags.FLAG_DELAY_PHONE_ACCOUNT_REGISTRATION);
-        MockitoAnnotations.initMocks(this);
 
-        mPhones = new Phone[]{mPhone};
-        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
-        replaceInstance(PhoneGlobals.class, "sMe", null, mPhoneGlobals);
         replaceInstance(PhoneInterfaceManager.class, "sInstance", null, mPhoneInterfaceManager);
         when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
         when(mPhone.getContext()).thenReturn(mMockedContext);
@@ -182,6 +175,9 @@
                 broadcastReceiverArgumentCaptor.getAllValues().get(0);
         mLocaleChangedBroadcastReceiver = broadcastReceiverArgumentCaptor.getAllValues().get(1);
 
+        replaceInstance(SimultaneousCallingTracker.class, "sInstance", null,
+                Mockito.mock(SimultaneousCallingTracker.class));
+
         mTestableLooper.processAllMessages();
     }
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 463e9ae..6a10d75 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -61,7 +61,6 @@
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.telecom.Conference;
 import android.telecom.Conferenceable;
@@ -283,7 +282,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        doReturn(Looper.getMainLooper()).when(mContext).getMainLooper();
+
         mTestConnectionService = new TestTelephonyConnectionService(mContext);
         mTestConnectionService.setFeatureFlags(mFeatureFlags);
         mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
@@ -314,7 +313,8 @@
                 mTestConnectionService, mEmergencyStateTracker);
         replaceInstance(TelephonyConnectionService.class, "mSatelliteSOSMessageRecommender",
                 mTestConnectionService, mSatelliteSOSMessageRecommender);
-        doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(),
+                anyBoolean());
         doNothing().when(mSatelliteSOSMessageRecommender).onEmergencyCallConnectionStateChanged(
                 anyString(), anyInt());
         doReturn(CompletableFuture.completedFuture(NOT_DISCONNECTED))
@@ -1308,7 +1308,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(0), eq(false));
+                eq(testPhone), eq(false), eq(0));
 
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
@@ -1336,7 +1336,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(0), eq(false));
+                eq(testPhone), eq(false), eq(0));
 
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
@@ -1353,7 +1353,7 @@
             // This shouldn't happen
             fail();
         }
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
     }
 
     /**
@@ -1439,7 +1439,7 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_exitingSatellite_placeCall() {
-        when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+        when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(true);
         doReturn(true).when(mMockResources).getBoolean(anyInt());
         doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
                 anyString());
@@ -1448,14 +1448,14 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(0), eq(false));
+                eq(testPhone), eq(false), eq(0));
 
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
         when(mSST.isRadioOn()).thenReturn(true);
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
-        when(mSatelliteController.isSatelliteEnabled()).thenReturn(false);
+        when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(false);
         assertTrue(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
 
@@ -1468,7 +1468,7 @@
             // This shouldn't happen
             fail();
         }
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
     }
 
     /**
@@ -1480,7 +1480,7 @@
     public void testCreateOutgoingEmergencyConnection_exitingSatellite_EmergencySatellite()
             throws Exception {
         doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn();
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
 
         // Set config_turn_off_oem_enabled_satellite_during_emergency_call as false
         doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
@@ -1499,7 +1499,7 @@
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_exitingSatellite_OEM() throws Exception {
         doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn();
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
 
         // Set config_turn_off_oem_enabled_satellite_during_emergency_call as false
         doReturn(false).when(mMockResources).getBoolean(anyInt());
@@ -1508,9 +1508,7 @@
 
         // Satellite is for emergency
         doReturn(true).when(mSatelliteController).getRequestIsEmergency();
-        Phone phone = mock(Phone.class);
-        doReturn(1).when(phone).getSubId();
-        doReturn(phone).when(mSatelliteController).getSatellitePhone();
+        doReturn(1).when(mSatelliteController).getSelectedSatelliteSubId();
         SubscriptionManagerService isub = mock(SubscriptionManagerService.class);
         replaceInstance(SubscriptionManagerService.class, "sInstance", null, isub);
         SubscriptionInfoInternal info = mock(SubscriptionInfoInternal.class);
@@ -1538,7 +1536,7 @@
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_exitingSatellite_Carrier() throws Exception {
         doReturn(true).when(mFeatureFlags).carrierRoamingNbIotNtn();
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
 
         // Set config_turn_off_oem_enabled_satellite_during_emergency_call as false
         doReturn(false).when(mMockResources).getBoolean(anyInt());
@@ -1547,9 +1545,7 @@
 
         // Satellite is for emergency
         doReturn(true).when(mSatelliteController).getRequestIsEmergency();
-        Phone phone = mock(Phone.class);
-        doReturn(1).when(phone).getSubId();
-        doReturn(phone).when(mSatelliteController).getSatellitePhone();
+        doReturn(1).when(mSatelliteController).getSelectedSatelliteSubId();
         SubscriptionManagerService isub = mock(SubscriptionManagerService.class);
         replaceInstance(SubscriptionManagerService.class, "sInstance", null, isub);
         SubscriptionInfoInternal info = mock(SubscriptionInfoInternal.class);
@@ -1603,7 +1599,7 @@
                 PHONE_ACCOUNT_HANDLE_1, connectionRequest);
 
         verify(mRadioOnHelper).triggerRadioOnAndListen(any(), eq(false),
-                eq(testPhone0), eq(false), eq(0), eq(false));
+                eq(testPhone0), eq(false), eq(0));
     }
 
     /**
@@ -1637,7 +1633,7 @@
                 PHONE_ACCOUNT_HANDLE_1, connectionRequest);
 
         verify(mRadioOnHelper).triggerRadioOnAndListen(any(), eq(false),
-                eq(testPhone0), eq(false), eq(0), eq(false));
+                eq(testPhone0), eq(false), eq(0));
     }
 
     /**
@@ -1670,7 +1666,7 @@
                 PHONE_ACCOUNT_HANDLE_1, connectionRequest);
 
         verify(mRadioOnHelper, times(0)).triggerRadioOnAndListen(any(),
-                eq(true), eq(testPhone0), eq(false), eq(0), eq(false));
+                eq(true), eq(testPhone0), eq(false), eq(0));
     }
 
     /**
@@ -2291,7 +2287,8 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any(),
+                anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
@@ -2330,7 +2327,8 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any(),
+                anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
@@ -2374,7 +2372,8 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any(),
+                anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
@@ -2522,7 +2521,8 @@
 
         listener.onDisconnect(0);
 
-        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any(),
+                anyBoolean());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -2628,7 +2628,7 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -2725,7 +2725,8 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any(),
+                anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
@@ -2796,7 +2797,7 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -2818,7 +2819,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS), eq(true));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
         ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -2857,7 +2858,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS), eq(true));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
         ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -2882,7 +2883,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS), eq(true));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
         ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_IN_SERVICE);
@@ -2918,7 +2919,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS), eq(true));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
         ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_IN_SERVICE);
@@ -2955,7 +2956,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS), eq(true));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
         ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_IN_SERVICE);
@@ -2992,7 +2993,7 @@
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS), eq(true));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
         mConnection.setDisconnected(null);
 
@@ -3029,7 +3030,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
@@ -3082,7 +3083,7 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
@@ -3417,7 +3418,7 @@
 
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any(), anyBoolean());
 
         android.telecom.Connection tc = connectionCaptor.getValue();
 
@@ -3493,7 +3494,8 @@
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
                 .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
-        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, times(2)).onEmergencyCallStarted(any(),
+                anyBoolean());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
         verify(mPhone0).dial(anyString(), any(), any());
 
@@ -3703,7 +3705,8 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
-        verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any(),
+                anyBoolean());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -3728,7 +3731,8 @@
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(false));
         verify(mNormalCallDomainSelectionConnection).createNormalConnection(any(), any());
-        verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any());
+        verify(mSatelliteSOSMessageRecommender, never()).onEmergencyCallStarted(any(),
+                anyBoolean());
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
@@ -3744,7 +3748,7 @@
     @Test
     public void testNormalCallSatelliteEnabled() {
         setupForCallTest();
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
 
         mConnection = mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
@@ -3757,7 +3761,7 @@
     @Test
     public void testEmergencyCallSatelliteEnabled_blockEmergencyCall() {
         setupForCallTest();
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
         doReturn(false).when(mMockResources).getBoolean(anyInt());
         doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
                 anyString());
diff --git a/tests/src/com/android/services/telephony/TelephonyManagerTest.java b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
index 20c062f..efb7375 100644
--- a/tests/src/com/android/services/telephony/TelephonyManagerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
@@ -15,10 +15,12 @@
  */
 
 package com.android.services.telephony;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assume.assumeTrue;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -34,7 +36,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.OutcomeReceiver;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.telecom.PhoneAccountHandle;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -43,9 +47,10 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.IPhoneSubInfo;
+import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -53,13 +58,16 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 /** Unit tests for {@link TelephonyManager}. */
 @RunWith(AndroidJUnit4.class)
@@ -131,6 +139,55 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetImsPcscfAddresses() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        List<String> pcscfs = Arrays.asList(new String[] { "1.1.1.1 ", " 2.2.2.2"});
+        when(mMockIPhoneSubInfo.getImsPcscfAddresses(anyInt(), anyString()))
+                .thenReturn(pcscfs);
+
+        List<String> actualResult = mTelephonyManager.getImsPcscfAddresses();
+
+        assertTrue(pcscfs.equals(actualResult));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetImsPcscfAddresses_ReturnEmptyListWhenNotAvailable() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        List<String> pcscfs = new ArrayList<>();
+        when(mMockIPhoneSubInfo.getImsPcscfAddresses(anyInt(), anyString()))
+                .thenReturn(pcscfs);
+
+        List<String> actualResult = mTelephonyManager.getImsPcscfAddresses();
+
+        assertTrue(pcscfs.equals(actualResult));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetImsPcscfAddresses_ReturnEmptyListForInvalidSubId() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        List<String> pcscfs = new ArrayList<>();
+        when(mMockIPhoneSubInfo.getImsPcscfAddresses(anyInt(), anyString()))
+                .thenThrow(new IllegalArgumentException("Invalid subscription"));
+
+        List<String> actualResult = mTelephonyManager.getImsPcscfAddresses();
+
+        assertTrue(pcscfs.equals(actualResult));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetImsPcscfAddresses_ThrowRuntimeException() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        when(mMockIPhoneSubInfo.getImsPcscfAddresses(anyInt(), anyString()))
+                .thenThrow(new IllegalStateException("ISIM is not loaded"));
+
+        assertThrows(RuntimeException.class, () -> mTelephonyManager.getImsPcscfAddresses());
+    }
+
+    @Test
     public void testFilterEmergencyNumbersByCategories() throws Exception {
         Map<Integer, List<EmergencyNumber>> emergencyNumberLists = new HashMap<>();
         List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
@@ -260,6 +317,70 @@
         assertEquals(null, mTelephonyManager.getSimServiceTable(PhoneConstants.APPTYPE_RUIM));
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetSimServiceTableFromIsimAsByteArrayType() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        String simServiceTable = "34FA754E8390BD02";
+        Executor executor = Executors.newSingleThreadExecutor();
+        TestOutcomeReceiver<byte[], Exception> receiver = new TestOutcomeReceiver<>();
+        when(mMockIPhoneSubInfo.getIsimIst(anyInt())).thenReturn(simServiceTable);
+
+        mTelephonyManager.getSimServiceTable(
+                PhoneConstants.APPTYPE_ISIM, executor, receiver);
+
+        byte[] actualResult = receiver.getResult();
+        assertArrayEquals(hexStringToBytes(simServiceTable), actualResult);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetSimServiceTableFromUsimAsByteArrayType() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        String simServiceTable = "34FA754E8390BD02";
+        Executor executor = Executors.newSingleThreadExecutor();
+        TestOutcomeReceiver<byte[], Exception> receiver = new TestOutcomeReceiver<>();
+        when(mMockIPhoneSubInfo.getSimServiceTable(anyInt(), anyInt()))
+                .thenReturn(simServiceTable);
+
+        mTelephonyManager.getSimServiceTable(
+                PhoneConstants.APPTYPE_USIM, executor, receiver);
+
+        byte[] actualResult = receiver.getResult();
+        assertArrayEquals(hexStringToBytes(simServiceTable), actualResult);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetSimServiceTable_ReturnEmptyArrayWhenNotAvailable() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        Executor executor = Executors.newSingleThreadExecutor();
+        TestOutcomeReceiver<byte[], Exception> receiver = new TestOutcomeReceiver<>();
+        when(mMockIPhoneSubInfo.getSimServiceTable(anyInt(), anyInt())).thenReturn(null);
+
+        mTelephonyManager.getSimServiceTable(
+                PhoneConstants.APPTYPE_RUIM, executor, receiver);
+
+        byte[] actualResult = receiver.getResult();
+        assertArrayEquals(new byte[0], actualResult);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUPPORT_ISIM_RECORD)
+    public void testGetSimServiceTable_CallbackErrorIfExceptionIsThrown() throws Exception {
+        assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, true));
+        Executor executor = Executors.newSingleThreadExecutor();
+        TestOutcomeReceiver<byte[], Exception> receiver = new TestOutcomeReceiver<>();
+        when(mMockIPhoneSubInfo.getIsimIst(anyInt()))
+                .thenThrow(new IllegalStateException("ISIM is not loaded"));
+
+        mTelephonyManager.getSimServiceTable(
+                PhoneConstants.APPTYPE_ISIM, executor, receiver);
+
+        Exception error = receiver.getError();
+        assertTrue(error instanceof IllegalStateException);
+    }
+
     private boolean hasFeature(String feature, boolean status) {
         doReturn(status)
                 .when(mPackageManager).hasSystemFeature(
@@ -295,4 +416,58 @@
         });
 
     }
+
+    private static class TestOutcomeReceiver<R, E extends Throwable>
+            implements OutcomeReceiver<R, E> {
+        final int mTimeoutSeconds = 3;
+        CountDownLatch mLatch = new CountDownLatch(1);
+        AtomicReference<R> mResult = new AtomicReference<>();
+        AtomicReference<E> mError = new AtomicReference<>();
+
+        public R getResult() throws InterruptedException {
+            assertTrue(mLatch.await(mTimeoutSeconds, TimeUnit.SECONDS));
+            assertNotNull(mResult.get());
+            return mResult.get();
+        }
+
+        public E getError() throws InterruptedException {
+            assertTrue(mLatch.await(mTimeoutSeconds, TimeUnit.SECONDS));
+            assertNotNull(mError.get());
+            return mError.get();
+        }
+
+        @Override
+        public void onResult(R result) {
+            mResult.set(result);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onError(E error) {
+            mError.set(error);
+            mLatch.countDown();
+        }
+    }
+
+    private static byte[] hexStringToBytes(String s) {
+        byte[] ret;
+        if (s == null) return null;
+        int sz = s.length();
+        ret = new byte[sz / 2];
+
+        for (int i = 0; i < sz; i += 2) {
+            ret[i / 2] = (byte) ((hexCharToInt(s.charAt(i)) << 4)
+                                | hexCharToInt(s.charAt(i + 1)));
+        }
+
+        return ret;
+    }
+
+    private static int hexCharToInt(char c) {
+        if (c >= '0' && c <= '9') return (c - '0');
+        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
+        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
+
+        throw new RuntimeException("invalid hex char '" + c + "'");
+    }
 }
\ No newline at end of file
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index d91435c..9f1a0ec 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -16,27 +16,31 @@
 
 package com.android.services.telephony;
 
-import android.content.AttributionSource;
-import android.content.ContentResolver;
-import android.os.Process;
-import android.os.UserHandle;
-import android.telephony.TelephonyManager;
-
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.content.AttributionSource;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.Process;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
 
 import com.android.ims.ImsCall;
 import com.android.internal.telephony.Call;
@@ -70,9 +74,6 @@
     Context mMockContext;
 
     @Mock
-    ContentResolver mMockContentResolver;
-
-    @Mock
     Resources mMockResources;
 
     @Mock
@@ -96,6 +97,7 @@
     @Mock
     CarrierConfigManager mCarrierConfigManager;
 
+    private MockContentResolver mMockContentResolver;
     private boolean mIsImsConnection;
     private boolean mIsImsExternalConnection;
     private boolean mIsConferenceSupported = true;
@@ -136,6 +138,14 @@
         mMockContext = mock(Context.class);
         mMockTelephonyManager = mock(TelephonyManager.class);
         mOriginalConnection = mMockRadioConnection;
+
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+        doReturn(applicationInfo).when(mMockContext).getApplicationInfo();
+        mMockContentResolver = new MockContentResolver(mMockContext);
+        mMockContentResolver.addProvider(Settings.AUTHORITY,
+                new EmptyContentProvider(mMockContext));
+
         // Set up mMockRadioConnection and mMockPhone to contain an active call
         when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
         when(mOriginalConnection.getState()).thenReturn(Call.State.ACTIVE);
@@ -159,8 +169,7 @@
         when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
                 .thenReturn(mMockTelephonyManager);
         when(mMockContext.getAttributionSource()).thenReturn(attributionSource);
-        when(mMockContentResolver.getUserId()).thenReturn(UserHandle.USER_CURRENT);
-        when(mMockContentResolver.getAttributionSource()).thenReturn(attributionSource);
+        when(mMockContext.getUserId()).thenReturn(UserHandle.USER_CURRENT);
         when(mMockResources.getBoolean(anyInt())).thenReturn(false);
         when(mMockPhone.getDefaultPhone()).thenReturn(mMockPhone);
         when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
@@ -320,4 +329,15 @@
     public void setMockImsPhoneConnection(ImsPhoneConnection connection) {
         mImsPhoneConnection = connection;
     }
+
+    static class EmptyContentProvider extends MockContentProvider {
+        EmptyContentProvider(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Bundle call(String method, String request, Bundle args) {
+            return new Bundle();
+        }
+    }
 }
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index 8a83ab0..51493d3 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -127,6 +127,7 @@
 import android.testing.TestableLooper;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.Display;
 
 import androidx.test.filters.SmallTest;
 
@@ -230,6 +231,11 @@
             public Resources getResources() {
                 return mResources;
             }
+
+            @Override
+            public int getDisplayId() {
+                return Display.DEFAULT_DISPLAY;
+            }
         };
 
         if (Looper.myLooper() == null) {
diff --git a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
index 49411bd..7acc7d6 100644
--- a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
@@ -743,6 +743,27 @@
                 mNormalCallDomainSelector.getSelectorState());
     }
 
+    @Test
+    public void testReselectDomainNoTimeoutMessage() {
+        final TestTransportSelectorCallback transportSelectorCallback =
+                new TestTransportSelectorCallback(mNormalCallDomainSelector);
+
+        DomainSelectionService.SelectionAttributes attributes =
+                new DomainSelectionService.SelectionAttributes.Builder(
+                        SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+                        .setAddress(TEST_URI)
+                        .setCallId(TEST_CALLID)
+                        .setEmergency(false)
+                        .setVideoCall(false)
+                        .setExitedFromAirplaneMode(false)
+                        .build();
+
+        mNormalCallDomainSelector.selectDomain(null, transportSelectorCallback);
+        mNormalCallDomainSelector.reselectDomain(attributes);
+        assertFalse(mNormalCallDomainSelector.hasMessages(
+                NormalCallDomainSelector.MSG_WAIT_FOR_IMS_STATE_TIMEOUT));
+    }
+
     static class TestTransportSelectorCallback implements TransportSelectorCallback,
             WwanSelectorCallback {
         public boolean mCreated;
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index 07c9fd0..649d3dd 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -45,6 +45,7 @@
 import com.android.ims.FeatureConnector;
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
+import com.android.phone.ImsStateCallbackController;
 
 import org.junit.After;
 import org.junit.Before;
@@ -84,6 +85,9 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+
+        replaceInstance(ImsStateCallbackController.class, "sInstance", null,
+                mock(ImsStateCallbackController.class));
     }
 
     @After
diff --git a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
index 42a45f4..df7a37e 100644
--- a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
@@ -147,12 +147,16 @@
 
     @After
     public void tearDown() throws Exception {
-        super.tearDown();
-        boolean isShutdown = mExecutorService == null || mExecutorService.isShutdown();
-        if (!isShutdown) {
+        var monitor = RcsProvisioningMonitor.getInstance();
+        if (monitor != null) {
+            monitor.overrideImsFeatureValidation(TEST_SUB_ID, null);
+        }
+
+        if (mExecutorService != null && !mExecutorService.isShutdown()) {
             mExecutorService.shutdownNow();
         }
-        RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(TEST_SUB_ID, null);
+
+        super.tearDown();
     }
 
     @SmallTest
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index 4cabf95..34ed5c6 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -40,6 +41,7 @@
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.phone.ImsStateCallbackController;
 
 import org.junit.After;
 import org.junit.Before;
@@ -103,6 +105,9 @@
                 eq(1), anyInt());
         doReturn(true).when(mResourceProxy).getDeviceUceEnabled(any());
 
+        replaceInstance(ImsStateCallbackController.class, "sInstance", null,
+                mock(ImsStateCallbackController.class));
+
         replaceInstance(TelephonyManager.class, "sInstance", null, mTelephonyManager);
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
     }
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/HeaderBlock.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/HeaderBlock.java
index 9895d1a..9592e1e 100644
--- a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/HeaderBlock.java
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/HeaderBlock.java
@@ -40,10 +40,26 @@
         int suffixBitCount = blockData.getUnsignedByte(offset++);
         int suffixRecordBitCount = blockData.getUnsignedByte(offset++);
         int suffixTableBlockIdOffset = blockData.getUnsignedByte(offset++);
-        boolean isAllowedList = (blockData.getUnsignedByte(offset) == TRUE);
-        mFileFormat = new SatS2RangeFileFormat(
-                dataS2Level, prefixBitCount, suffixBitCount, suffixTableBlockIdOffset,
-                suffixRecordBitCount, isAllowedList);
+        boolean isAllowedList = (blockData.getUnsignedByte(offset++) == TRUE);
+
+
+        // Check if the block is in the original format or the enhanced format.
+        // If the offset is equal to the block data size, this block is in the original format.
+        // If the offset is less than the block data size, this block is an enhanced block, which
+        // has additional fields:
+        //  - the size of an entry value in bytes
+        //  - version number of header block
+        if (offset < blockData.getSize()) {
+            int entryValueSizeInBytes = blockData.getUnsignedByte(offset++);
+            int versionNumber = blockData.getInt(offset);
+            mFileFormat = new SatS2RangeFileFormat(
+                    dataS2Level, prefixBitCount, suffixBitCount, suffixTableBlockIdOffset,
+                    suffixRecordBitCount, isAllowedList, entryValueSizeInBytes, versionNumber);
+        } else {
+            mFileFormat = new SatS2RangeFileFormat(
+                    dataS2Level, prefixBitCount, suffixBitCount, suffixTableBlockIdOffset,
+                    suffixRecordBitCount, isAllowedList);
+        }
     }
 
     /** Creates a {@link HeaderBlock} from low-level block data from a block file. */
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/PopulatedSuffixTableBlock.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/PopulatedSuffixTableBlock.java
index 9aa56b2..8cb9d1d 100644
--- a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/PopulatedSuffixTableBlock.java
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/PopulatedSuffixTableBlock.java
@@ -20,7 +20,6 @@
 import static com.android.storage.s2.S2Support.cellIdToString;
 import static com.android.storage.util.Conditions.checkStateInRange;
 
-import com.android.storage.s2.S2LevelRange;
 import com.android.storage.table.packed.read.IntValueTypedPackedTable;
 import com.android.storage.table.reader.IntValueTable;
 
@@ -59,7 +58,8 @@
             SatS2RangeFileFormat fileFormat, IntValueTypedPackedTable packedTable) {
         mFileFormat = Objects.requireNonNull(fileFormat);
         mPackedTable = Objects.requireNonNull(packedTable);
-        mSuffixTableSharedData = SuffixTableSharedData.fromBytes(packedTable.getSharedData());
+        mSuffixTableSharedData = SuffixTableSharedData.fromTypedData(
+                packedTable.getSharedDataAsTyped(), fileFormat);
 
         // Obtain the prefix. All cellIds in this table will share the same prefix except for end
         // range values (which are exclusive so can be for mPrefix + 1 with a suffix value of 0).
@@ -141,7 +141,7 @@
 
         private final IntValueTable.TableEntry mSuffixTableEntry;
 
-        private S2LevelRange mSuffixTableRange;
+        private SuffixTableRange mSuffixTableRange;
 
         Entry(IntValueTable.TableEntry suffixTableEntry) {
             mSuffixTableEntry = Objects.requireNonNull(suffixTableEntry);
@@ -154,7 +154,7 @@
 
         /** Returns the data for this entry. */
         @Override
-        public S2LevelRange getSuffixTableRange() {
+        public SuffixTableRange getSuffixTableRange() {
             // Creating SuffixTableRange is relatively expensive so it is created lazily and
             // memoized.
             if (mSuffixTableRange == null) {
@@ -190,7 +190,8 @@
                     endCellIdSuffix = 0;
                 }
                 long endCellId = mFileFormat.createCellId(endCellPrefixValue, endCellIdSuffix);
-                mSuffixTableRange = new S2LevelRange(startCellId, endCellId);
+                int entryValue = getEntryValue();
+                mSuffixTableRange = new SuffixTableRange(startCellId, endCellId, entryValue);
             }
             return mSuffixTableRange;
         }
@@ -218,5 +219,9 @@
                     + "mSuffixTableEntry=" + mSuffixTableEntry
                     + '}';
         }
+
+        private int getEntryValue() {
+            return mSuffixTableSharedData.getEntryValue(mSuffixTableEntry.getIndex());
+        }
     }
 }
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileFormat.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileFormat.java
index 39507aa..a335766 100644
--- a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileFormat.java
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileFormat.java
@@ -53,6 +53,10 @@
     /** The format version of the satellite S2 data file, read and written. */
     public static final int VERSION = 1;
 
+    private static final int DEFAULT_ENTRY_VALUE_SIZE_IN_BYTES = 0;
+    private static final int DEFAULT_VERSION_NUMBER = 0;
+    private static final int MAX_ENTRY_BYTE_COUNT = 4;
+
     private final int mDataS2Level;
 
     private final int mPrefixBitCount;
@@ -87,11 +91,28 @@
     private final boolean mIsAllowedList;
 
     /**
+     * Entry value size in bytes
+     */
+    private final int mEntryValueSizeInBytes;
+
+    /**
+     * Version number
+     */
+    private final int mVersionNumber;
+
+    public SatS2RangeFileFormat(int s2Level, int prefixBitCount, int suffixBitCount,
+            int suffixTableBlockIdOffset, int tableEntryBitCount, boolean isAllowedList) {
+        this(s2Level, prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, tableEntryBitCount,
+                isAllowedList, DEFAULT_ENTRY_VALUE_SIZE_IN_BYTES, DEFAULT_VERSION_NUMBER);
+    }
+
+    /**
      * Creates a new file format. This constructor validates the values against various hard-coded
      * constraints and will throw an {@link IllegalArgumentException} if they are not satisfied.
      */
     public SatS2RangeFileFormat(int s2Level, int prefixBitCount, int suffixBitCount,
-            int suffixTableBlockIdOffset, int tableEntryBitCount, boolean isAllowedList) {
+            int suffixTableBlockIdOffset, int tableEntryBitCount, boolean isAllowedList,
+            int entryValueSizeInBytes, int versionNumber) {
 
         Conditions.checkArgInRange("s2Level", s2Level, 0, MAX_S2_LEVEL);
 
@@ -180,6 +201,12 @@
         mSuffixTableBlockIdOffset = suffixTableBlockIdOffset;
 
         mIsAllowedList = isAllowedList;
+
+        Conditions.checkArgInRange("entryValueSizeInBytes", entryValueSizeInBytes, 0,
+                MAX_ENTRY_BYTE_COUNT);
+        mEntryValueSizeInBytes = entryValueSizeInBytes;
+
+        mVersionNumber = versionNumber;
     }
 
     /** Returns the S2 level of all geo data stored in the file. */
@@ -345,6 +372,18 @@
                 + "}";
     }
 
+    /**
+     * Returns the length of entry value in Bytes.
+     * @return the length of entry value
+     */
+    public int getEntryValueSizeInBytes() {
+        return mEntryValueSizeInBytes;
+    }
+
+    public int getVersionNumber() {
+        return mVersionNumber;
+    }
+
     @Override
     public String toString() {
         return "SatS2RangeFileFormat{"
@@ -359,6 +398,8 @@
                 + ", mSuffixTableBlockIdOffset=" + mSuffixTableBlockIdOffset
                 + ", mUnusedCellIdBitCount=" + mUnusedCellIdBitCount
                 + ", mIsAllowedList=" + mIsAllowedList
+                + ", mEntryValueSizeInBytes=" + mEntryValueSizeInBytes
+                + ", mVersionNumber=" + mVersionNumber
                 + '}';
     }
 
@@ -381,7 +422,9 @@
                 && mTableEntryMaxRangeLengthValue == that.mTableEntryMaxRangeLengthValue
                 && mSuffixTableBlockIdOffset == that.mSuffixTableBlockIdOffset
                 && mIsAllowedList == that.mIsAllowedList
-                && mUnusedCellIdBitCount == that.mUnusedCellIdBitCount;
+                && mUnusedCellIdBitCount == that.mUnusedCellIdBitCount
+                && mEntryValueSizeInBytes == that.mEntryValueSizeInBytes
+                && mVersionNumber == that.mVersionNumber;
     }
 
     @Override
@@ -389,7 +432,7 @@
         return Objects.hash(mDataS2Level, mPrefixBitCount, mMaxPrefixValue, mSuffixBitCount,
                 mMaxSuffixValue, mTableEntryBitCount, mTableEntryRangeLengthBitCount,
                 mTableEntryMaxRangeLengthValue, mSuffixTableBlockIdOffset, mIsAllowedList,
-                mUnusedCellIdBitCount);
+                mUnusedCellIdBitCount, mEntryValueSizeInBytes, mVersionNumber);
     }
 
     private void checkS2Level(String name, long cellId) {
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileReader.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileReader.java
index ecfa0a9..2c6c4af 100644
--- a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileReader.java
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SatS2RangeFileReader.java
@@ -19,7 +19,6 @@
 import com.android.storage.block.read.Block;
 import com.android.storage.block.read.BlockFileReader;
 import com.android.storage.block.read.BlockInfo;
-import com.android.storage.s2.S2LevelRange;
 import com.android.storage.s2.S2Support;
 import com.android.storage.util.Conditions;
 import com.android.storage.util.Visitor;
@@ -144,11 +143,11 @@
     }
 
     /**
-     * Finds an {@link S2LevelRange} associated with a range covering {@code cellId}.
+     * Finds an {@link SuffixTableRange} associated with a range covering {@code cellId}.
      * Returns {@code null} if no range exists. Throws {@link IllegalArgumentException} if
      * {@code cellId} is not the correct S2 level for the file. See {@link #getS2Level()}.
      */
-    public S2LevelRange findEntryByCellId(long cellId) throws IOException {
+    public SuffixTableRange findEntryByCellId(long cellId) throws IOException {
         checkNotClosed();
         int dataS2Level = mFileFormat.getS2Level();
         int searchS2Level = S2Support.getS2Level(cellId);
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableBlock.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableBlock.java
index 90ddd89..ffd28d5 100644
--- a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableBlock.java
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableBlock.java
@@ -20,7 +20,6 @@
 import static com.android.storage.s2.S2Support.getS2Level;
 
 import com.android.storage.block.read.BlockData;
-import com.android.storage.s2.S2LevelRange;
 import com.android.storage.table.packed.read.IntValueTypedPackedTable;
 import com.android.storage.util.BitwiseUtils;
 import com.android.storage.util.Visitor;
@@ -180,6 +179,6 @@
         public abstract int getIndex();
 
         /** Returns the data for this entry. */
-        public abstract S2LevelRange getSuffixTableRange();
+        public abstract SuffixTableRange getSuffixTableRange();
     }
 }
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableRange.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableRange.java
new file mode 100644
index 0000000..8c1466f
--- /dev/null
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableRange.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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.telephony.sats2range.read;
+
+import static com.android.storage.s2.S2Support.cellIdToString;
+
+import com.android.storage.s2.S2LevelRange;
+
+import java.util.Objects;
+
+public final class SuffixTableRange extends S2LevelRange {
+    private static final int DEFAULT_ENTRY_VALUE = -1;
+    private final int mEntryValue;
+
+    // For backward compatibility
+    public SuffixTableRange(long startCellId, long endCellId) {
+        this(startCellId, endCellId, DEFAULT_ENTRY_VALUE);
+    }
+
+    public SuffixTableRange(long startCellId, long endCellId, int entryValue) {
+        super(startCellId, endCellId);
+        mEntryValue = entryValue;
+    }
+
+    /** Returns the entry value associated with this range. */
+    public int getEntryValue() {
+        return mEntryValue;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        if (super.equals(o)) {
+            int entryValue = ((SuffixTableRange) o).mEntryValue;
+            return mEntryValue == entryValue;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStartCellId, mEndCellId, mEntryValue);
+    }
+
+    @Override
+    public String toString() {
+        return "SuffixTableRange{"
+                + "mS2Level=" + mS2Level
+                + ", mStartCellId=" + cellIdToString(mStartCellId)
+                + ", mEndCellId=" + cellIdToString(mEndCellId)
+                + ", mEntryValue=" + mEntryValue
+                + '}';
+    }
+}
diff --git a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableSharedData.java b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableSharedData.java
index 2221b2c..14cb92f 100644
--- a/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableSharedData.java
+++ b/utils/satellite/s2storage/src/readonly/java/com/android/telephony/sats2range/read/SuffixTableSharedData.java
@@ -16,11 +16,13 @@
 
 package com.android.telephony.sats2range.read;
 
+import com.android.storage.block.read.TypedData;
 import com.android.storage.io.read.TypedInputStream;
 import com.android.storage.table.reader.Table;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -28,14 +30,98 @@
  * entries in the table and is required when interpreting the table's block data.
  */
 public final class SuffixTableSharedData {
-
+    public static final int INVALID_ENTRY_VALUE = -1;
     private final int mTablePrefix;
+    private final int mEntryValueSizeInBytes;
+    private final int mNumberOfEntryValues;
+    private final int mHeaderByteOffsetToRead;
+    private List<Integer> mEntryValuesToWrite = List.of(); // This is used for write path
+    private final TypedData mSharedDataToRead; // This is used for read path
 
     /**
      * Creates a {@link SuffixTableSharedData}. See also {@link #fromBytes(byte[])}.
      */
     public SuffixTableSharedData(int tablePrefix) {
         mTablePrefix = tablePrefix;
+        mEntryValueSizeInBytes = 0;
+        mNumberOfEntryValues = 0;
+        mHeaderByteOffsetToRead = 0;
+        mSharedDataToRead = null;
+    }
+
+    /**
+     * This constructor is used for write path
+     */
+    public SuffixTableSharedData(int tablePrefix, List<Integer> entryValues,
+            SatS2RangeFileFormat fileFormat) {
+        mSharedDataToRead = null;
+        mTablePrefix = tablePrefix;
+        mNumberOfEntryValues = entryValues.size();
+        mEntryValuesToWrite = entryValues;
+        mEntryValueSizeInBytes = fileFormat.getEntryValueSizeInBytes();
+        mHeaderByteOffsetToRead = 0;
+    }
+
+    /**
+     * This constructor is used for read path
+     */
+    public SuffixTableSharedData(TypedData sharedDataToRead, SatS2RangeFileFormat fileFormat) {
+        mSharedDataToRead = Objects.requireNonNull(sharedDataToRead);
+        int offset = 0;
+        // extract prefix value
+        mTablePrefix = mSharedDataToRead.getInt(offset);
+        offset += Integer.BYTES;
+
+        // If the size of shared data is greater than the offset, extract the number of entry
+        // values.
+        if ((offset + Integer.BYTES) < mSharedDataToRead.getSize()) {
+            mNumberOfEntryValues = mSharedDataToRead.getInt(offset);
+            mHeaderByteOffsetToRead = offset + Integer.BYTES;
+            mEntryValueSizeInBytes = fileFormat.getEntryValueSizeInBytes();
+        } else {
+            mNumberOfEntryValues = 0;
+            mHeaderByteOffsetToRead = offset;
+            mEntryValueSizeInBytes = 0;
+        }
+    }
+
+    /**
+     * This is used for read path
+     */
+    public static SuffixTableSharedData fromTypedData(TypedData sharedData,
+            SatS2RangeFileFormat fileFormat) {
+        return new SuffixTableSharedData(sharedData, fileFormat);
+    }
+
+    /**
+     * Reads the entry value at a specific position in the byte buffer and returns it.
+     *
+     * @param entryIndex The index of entry to be read.
+     * @return entry value (integer) read from the byte buffer.
+     */
+    public int getEntryValue(int entryIndex) {
+        if (mSharedDataToRead == null || entryIndex < 0 || mNumberOfEntryValues == 0) {
+            return INVALID_ENTRY_VALUE;
+        }
+
+        if (mNumberOfEntryValues == 1) {
+            entryIndex = 0;
+        }
+
+        int offset;
+        if (entryIndex < mNumberOfEntryValues) {
+            // offset = table prefix(4) + entry value count(4) + size of entry * entry index
+            offset = mHeaderByteOffsetToRead + (mEntryValueSizeInBytes * entryIndex);
+        } else {
+            return INVALID_ENTRY_VALUE;
+        }
+
+        return getValueInternal(mSharedDataToRead, mEntryValueSizeInBytes, offset);
+    }
+
+    // Entry lists to be written to a byte buffer.
+    public List<Integer> getEntryValuesToWrite() {
+        return mEntryValuesToWrite;
     }
 
     /**
@@ -46,6 +132,20 @@
         return mTablePrefix;
     }
 
+    /**
+     * Returns the number of entry values.
+     */
+    public int getNumberOfEntryValues() {
+        return mNumberOfEntryValues;
+    }
+
+    /**
+     * Returns the size of entry value in Bytes.
+     */
+    public int getEntryValueSizeInBytes() {
+        return mEntryValueSizeInBytes;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -55,18 +155,22 @@
             return false;
         }
         SuffixTableSharedData that = (SuffixTableSharedData) o;
-        return mTablePrefix == that.mTablePrefix;
+        return mTablePrefix == that.mTablePrefix
+                && mNumberOfEntryValues == that.mNumberOfEntryValues
+                && mEntryValuesToWrite.equals(that.mEntryValuesToWrite);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mTablePrefix);
+        return Objects.hash(mTablePrefix, mNumberOfEntryValues, mEntryValuesToWrite);
     }
 
     @Override
     public String toString() {
         return "SuffixTableSharedData{"
                 + "mTablePrefix=" + mTablePrefix
+                + "mNumberOfEntries=" + mNumberOfEntryValues
+                + "mEntryValuesToWrite=" + mEntryValuesToWrite
                 + '}';
     }
 
@@ -82,4 +186,21 @@
             throw new RuntimeException(e);
         }
     }
+
+    private int getValueInternal(TypedData buffer, int valueSizeBytes, int byteOffset) {
+        if (byteOffset < 0) {
+            throw new IllegalArgumentException(
+                    "byteOffset=" + byteOffset + " must not be negative");
+        }
+
+        // High bytes read first.
+        int value = 0;
+        int bytesRead = 0;
+        while (bytesRead++ < valueSizeBytes) {
+            value <<= Byte.SIZE;
+            value |= buffer.getUnsignedByte(byteOffset++);
+        }
+
+        return value;
+    }
 }
diff --git a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileFormatTest.java b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileFormatTest.java
index 80ef467..65da1e4 100644
--- a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileFormatTest.java
+++ b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileFormatTest.java
@@ -67,10 +67,12 @@
         int suffixBitCount = 16;
         int suffixTableBlockIdOffset = 5;
         int suffixTableEntryBitCount = 24;
+        int suffixTableEntryValueSizeInBytes = 4;
+        int versionNumber = 2;
         boolean isAllowedList = false;
         SatS2RangeFileFormat satS2RangeFileFormat = new SatS2RangeFileFormat(s2Level,
                 prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, suffixTableEntryBitCount,
-                isAllowedList);
+                isAllowedList, suffixTableEntryValueSizeInBytes, versionNumber);
 
         assertEquals(2, satS2RangeFileFormat.calculateRangeLength(
                 cellId(s2Level, 0, 0), cellId(s2Level, 0, 2)));
@@ -92,9 +94,11 @@
         int suffixTableBlockIdOffset = 5;
         int suffixTableEntryBitCount = 24;
         boolean isAllowedList = true;
+        int suffixTableEntryValueSizeInBytes = 4;
+        int versionNumber = 2;
         SatS2RangeFileFormat satS2RangeFileFormat = new SatS2RangeFileFormat(s2Level,
                 prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, suffixTableEntryBitCount,
-                isAllowedList);
+                isAllowedList, suffixTableEntryValueSizeInBytes, versionNumber);
 
         // Too many bits for prefixValue
         assertThrows(IllegalArgumentException.class,
@@ -127,9 +131,11 @@
         int suffixTableBlockIdOffset = 5;
         int suffixTableEntryBitCount = 24;
         boolean isAllowedList = true;
+        int suffixTableEntryValueSizeInBytes = 4;
+        int versionNumber = 2;
         SatS2RangeFileFormat satS2RangeFileFormat = new SatS2RangeFileFormat(s2Level,
                 prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, suffixTableEntryBitCount,
-                isAllowedList);
+                isAllowedList, suffixTableEntryValueSizeInBytes, versionNumber);
 
         assertEquals(0, satS2RangeFileFormat.extractFaceIdFromPrefix(0b00000000000));
         assertEquals(5, satS2RangeFileFormat.extractFaceIdFromPrefix(0b10100000000));
@@ -147,9 +153,11 @@
         int suffixTableBlockIdOffset = 5;
         int suffixTableEntryBitCount = 24;
         boolean isAllowedList = true;
+        int suffixTableEntryValueSizeInBytes = 4;
+        int versionNumber = 2;
         SatS2RangeFileFormat satS2RangeFileFormat = new SatS2RangeFileFormat(s2Level,
                 prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, suffixTableEntryBitCount,
-                isAllowedList);
+                isAllowedList, suffixTableEntryValueSizeInBytes, versionNumber);
 
         // Too many bits for rangeLength
         assertThrows(IllegalArgumentException.class,
@@ -161,6 +169,33 @@
         assertTrue(satS2RangeFileFormat.isAllowedList());
     }
 
+    @Test
+    public void extractEntryValueByteCount() {
+        int s2Level = 12;
+        int prefixBitCount = 11;
+        int suffixBitCount = 16;
+        int suffixTableBlockIdOffset = 5;
+        int suffixTableEntryBitCount = 24;
+        boolean isAllowedList = true;
+        final int[] suffixTableEntryValueSizeInBytes = {5};
+        int versionNumber = 1;
+
+        // Table entry byte count exceeds BYTE range.
+        assertThrows(IllegalArgumentException.class,
+                () -> new SatS2RangeFileFormat(s2Level, prefixBitCount, suffixBitCount,
+                        suffixTableBlockIdOffset, suffixTableEntryBitCount, isAllowedList,
+                        suffixTableEntryValueSizeInBytes[0], versionNumber));
+
+        suffixTableEntryValueSizeInBytes[0] = 1;
+        SatS2RangeFileFormat satS2RangeFileFormat = new SatS2RangeFileFormat(s2Level,
+                prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, suffixTableEntryBitCount,
+                isAllowedList, suffixTableEntryValueSizeInBytes[0], versionNumber);
+
+        assertEquals(suffixTableEntryValueSizeInBytes[0],
+                satS2RangeFileFormat.getEntryValueSizeInBytes());
+        assertEquals(versionNumber, satS2RangeFileFormat.getVersionNumber());
+    }
+
     private static int maxValForBits(int bits) {
         return intPow2(bits) - 1;
     }
diff --git a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileReaderTest.java b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileReaderTest.java
index bbfaef7..6de40e1 100644
--- a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileReaderTest.java
+++ b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SatS2RangeFileReaderTest.java
@@ -17,10 +17,11 @@
 package com.android.telephony.sats2range;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 
-import com.android.storage.s2.S2LevelRange;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
 import com.android.telephony.sats2range.read.SatS2RangeFileReader;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.utils.TestUtils;
 import com.android.telephony.sats2range.write.SatS2RangeFileWriter;
 
@@ -38,24 +39,24 @@
 
         SatS2RangeFileFormat fileFormat;
         boolean isAllowedList = true;
-        S2LevelRange expectedRange1, expectedRange2, expectedRange3;
+        SuffixTableRange expectedRange1, expectedRange2, expectedRange3;
         try (SatS2RangeFileWriter satS2RangeFileWriter = SatS2RangeFileWriter.open(
                 file, TestUtils.createS2RangeFileFormat(isAllowedList))) {
             fileFormat = satS2RangeFileWriter.getFileFormat();
 
             // Two ranges that share a prefix.
-            expectedRange1 = new S2LevelRange(
+            expectedRange1 = new SuffixTableRange(
                     TestUtils.createCellId(fileFormat, 1, 1000, 1000),
                     TestUtils.createCellId(fileFormat, 1, 1000, 2000));
-            expectedRange2 = new S2LevelRange(
+            expectedRange2 = new SuffixTableRange(
                     TestUtils.createCellId(fileFormat, 1, 1000, 2000),
                     TestUtils.createCellId(fileFormat, 1, 1000, 3000));
             // This range has a different prefix, so will be in a different suffix table.
-            expectedRange3 = new S2LevelRange(
+            expectedRange3 = new SuffixTableRange(
                     TestUtils.createCellId(fileFormat, 1, 1001, 1000),
                     TestUtils.createCellId(fileFormat, 1, 1001, 2000));
 
-            List<S2LevelRange> ranges = new ArrayList<>();
+            List<SuffixTableRange> ranges = new ArrayList<>();
             ranges.add(expectedRange1);
             ranges.add(expectedRange2);
             ranges.add(expectedRange3);
@@ -65,17 +66,80 @@
         try (SatS2RangeFileReader satS2RangeFileReader = SatS2RangeFileReader.open(file)) {
             assertEquals(isAllowedList, satS2RangeFileReader.isAllowedList());
 
-            S2LevelRange range1 = satS2RangeFileReader.findEntryByCellId(
+            SuffixTableRange range1 = satS2RangeFileReader.findEntryByCellId(
                     TestUtils.createCellId(fileFormat, 1, 1000, 1500));
             assertEquals(expectedRange1, range1);
 
-            S2LevelRange range2 = satS2RangeFileReader.findEntryByCellId(
+            SuffixTableRange range2 = satS2RangeFileReader.findEntryByCellId(
                     TestUtils.createCellId(fileFormat, 1, 1000, 2500));
             assertEquals(expectedRange2, range2);
 
-            S2LevelRange range3 = satS2RangeFileReader.findEntryByCellId(
+            SuffixTableRange range3 = satS2RangeFileReader.findEntryByCellId(
                     TestUtils.createCellId(fileFormat, 1, 1001, 1500));
             assertEquals(expectedRange3, range3);
         }
     }
+
+    @Test
+    public void findEntryByCellIdWithEntryValue() throws IOException {
+        final boolean isAllowedList = true;
+        final int entryValueSizeInBytes = 4;
+        final int versionNumber = 0;
+        final int entryValue1 = 1;
+        final int entryValue2 = 2;
+        final int entryValue3 = 3;
+
+        File file = File.createTempFile("test", ".dat");
+        SatS2RangeFileFormat fileFormat;
+
+        SuffixTableRange expectedRange1, expectedRange2, expectedRange3;
+        try (SatS2RangeFileWriter satS2RangeFileWriter = SatS2RangeFileWriter.open(file,
+                TestUtils.createS2RangeFileFormat(isAllowedList, entryValueSizeInBytes,
+                        versionNumber))) {
+            fileFormat = satS2RangeFileWriter.getFileFormat();
+
+            // Two ranges that share a prefix.
+            expectedRange1 = new SuffixTableRange(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 1000),
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2000),
+                    entryValue1);
+            expectedRange2 = new SuffixTableRange(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2000),
+                    TestUtils.createCellId(fileFormat, 1, 1000, 3000),
+                    entryValue2);
+            // This range has a different prefix, so will be in a different suffix table.
+            expectedRange3 = new SuffixTableRange(
+                    TestUtils.createCellId(fileFormat, 1, 1001, 1000),
+                    TestUtils.createCellId(fileFormat, 1, 1001, 2000),
+                    entryValue3);
+
+            List<SuffixTableRange> ranges = new ArrayList<>();
+            ranges.add(expectedRange1);
+            ranges.add(expectedRange2);
+            ranges.add(expectedRange3);
+            satS2RangeFileWriter.createSortedSuffixBlocks(ranges.iterator());
+        }
+
+        try (SatS2RangeFileReader satS2RangeFileReader = SatS2RangeFileReader.open(file)) {
+            assertEquals(isAllowedList, satS2RangeFileReader.isAllowedList());
+
+            SuffixTableRange range1 = satS2RangeFileReader.findEntryByCellId(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 1500));
+            assertNotNull(range1);
+            assertEquals(expectedRange1, range1);
+            assertEquals(entryValue1, range1.getEntryValue());
+
+            SuffixTableRange range2 = satS2RangeFileReader.findEntryByCellId(
+                    TestUtils.createCellId(fileFormat, 1, 1000, 2500));
+            assertNotNull(range2);
+            assertEquals(expectedRange2, range2);
+            assertEquals(entryValue2, range2.getEntryValue());
+
+            SuffixTableRange range3 = satS2RangeFileReader.findEntryByCellId(
+                    TestUtils.createCellId(fileFormat, 1, 1001, 1500));
+            assertNotNull(range3);
+            assertEquals(expectedRange3, range3);
+            assertEquals(entryValue3, range3.getEntryValue());
+        }
+    }
 }
diff --git a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableBlockTest.java b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableBlockTest.java
index 04b915b..7b9ce4a 100644
--- a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableBlockTest.java
+++ b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableBlockTest.java
@@ -24,9 +24,9 @@
 import static org.mockito.ArgumentMatchers.argThat;
 
 import com.android.storage.block.write.BlockWriter;
-import com.android.storage.s2.S2LevelRange;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
 import com.android.telephony.sats2range.read.SuffixTableBlock;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.read.SuffixTableSharedData;
 import com.android.telephony.sats2range.utils.TestUtils;
 import com.android.telephony.sats2range.write.SuffixTableWriter;
@@ -78,12 +78,14 @@
         long invalidEndCellId = fileFormat.createCellId(tablePrefixValue + 1, maxSuffixValue);
         long validEndCellId = fileFormat.createCellId(tablePrefixValue, maxSuffixValue);
         {
-            S2LevelRange badStartCellId = new S2LevelRange(invalidStartCellId, validEndCellId);
+            SuffixTableRange badStartCellId = new SuffixTableRange(invalidStartCellId,
+                    validEndCellId);
             assertThrows(IllegalArgumentException.class,
                     () -> suffixTableWriter.addRange(badStartCellId));
         }
         {
-            S2LevelRange badEndCellId = new S2LevelRange(validStartCellId, invalidEndCellId);
+            SuffixTableRange badEndCellId = new SuffixTableRange(validStartCellId,
+                    invalidEndCellId);
             assertThrows(IllegalArgumentException.class,
                     () -> suffixTableWriter.addRange(badEndCellId));
         }
@@ -101,13 +103,13 @@
 
         SuffixTableWriter suffixTableWriter =
                 SuffixTableWriter.createPopulated(fileFormat, suffixTableSharedData);
-        S2LevelRange suffixTableRange1 = new S2LevelRange(
+        SuffixTableRange suffixTableRange1 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefixValue, 1000),
                 fileFormat.createCellId(tablePrefixValue, 1001));
         suffixTableWriter.addRange(suffixTableRange1);
 
         // It's fine to add a range that starts adjacent to the last one.
-        S2LevelRange suffixTableRange2 = new S2LevelRange(
+        SuffixTableRange suffixTableRange2 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefixValue, 1001),
                 fileFormat.createCellId(tablePrefixValue, 1002));
         suffixTableWriter.addRange(suffixTableRange2);
@@ -117,7 +119,7 @@
                 () -> suffixTableWriter.addRange(suffixTableRange2));
 
         // Try similar checks at the top end of the table.
-        S2LevelRange suffixTableRange3 = new S2LevelRange(
+        SuffixTableRange suffixTableRange3 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefixValue, maxSuffixValue - 1),
                 fileFormat.createCellId(tablePrefixValue, maxSuffixValue));
         suffixTableWriter.addRange(suffixTableRange3);
@@ -131,7 +133,7 @@
                 () -> suffixTableWriter.addRange(suffixTableRange3));
 
         // Now "complete" the table: there can be no entry after this one.
-        S2LevelRange suffixTableRange4 = new S2LevelRange(
+        SuffixTableRange suffixTableRange4 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefixValue, maxSuffixValue),
                 fileFormat.createCellId(tablePrefixValue + 1, 0));
         suffixTableWriter.addRange(suffixTableRange4);
@@ -180,23 +182,23 @@
 
         long entry1StartCellId = fileFormat.createCellId(tablePrefix, 1000);
         long entry1EndCellId = fileFormat.createCellId(tablePrefix, 2000);
-        S2LevelRange entry1 = new S2LevelRange(entry1StartCellId, entry1EndCellId);
+        SuffixTableRange entry1 = new SuffixTableRange(entry1StartCellId, entry1EndCellId);
         suffixTableWriter.addRange(entry1);
 
         long entry2StartCellId = fileFormat.createCellId(tablePrefix, 2000);
         long entry2EndCellId = fileFormat.createCellId(tablePrefix, 3000);
-        S2LevelRange entry2 = new S2LevelRange(entry2StartCellId, entry2EndCellId);
+        SuffixTableRange entry2 = new SuffixTableRange(entry2StartCellId, entry2EndCellId);
         suffixTableWriter.addRange(entry2);
 
         // There is a deliberate gap here between entry2 and entry3.
         long entry3StartCellId = fileFormat.createCellId(tablePrefix, 4000);
         long entry3EndCellId = fileFormat.createCellId(tablePrefix, 5000);
-        S2LevelRange entry3 = new S2LevelRange(entry3StartCellId, entry3EndCellId);
+        SuffixTableRange entry3 = new SuffixTableRange(entry3StartCellId, entry3EndCellId);
         suffixTableWriter.addRange(entry3);
 
         long entry4StartCellId = fileFormat.createCellId(tablePrefix, maxSuffix - 999);
         long entry4EndCellId = fileFormat.createCellId(tablePrefix + 1, 0);
-        S2LevelRange entry4 = new S2LevelRange(entry4StartCellId, entry4EndCellId);
+        SuffixTableRange entry4 = new SuffixTableRange(entry4StartCellId, entry4EndCellId);
         suffixTableWriter.addRange(entry4);
 
         BlockWriter.ReadBack blockReadback = suffixTableWriter.close();
@@ -251,7 +253,7 @@
                 SuffixTableWriter.createPopulated(fileFormat, suffixTableSharedData);
         long entry1StartCellId = fileFormat.createCellId(tablePrefix, 1000);
         long entry1EndCellId = fileFormat.createCellId(tablePrefix, 2000);
-        S2LevelRange entry1 = new S2LevelRange(entry1StartCellId, entry1EndCellId);
+        SuffixTableRange entry1 = new SuffixTableRange(entry1StartCellId, entry1EndCellId);
         suffixTableWriter.addRange(entry1);
         BlockWriter.ReadBack blockReadback = suffixTableWriter.close();
 
@@ -276,12 +278,12 @@
         SuffixTableWriter suffixTableWriter =
                 SuffixTableWriter.createPopulated(fileFormat, sharedData);
 
-        S2LevelRange entry1 = new S2LevelRange(
+        SuffixTableRange entry1 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefix, 1001),
                 fileFormat.createCellId(tablePrefix, 1101));
         suffixTableWriter.addRange(entry1);
 
-        S2LevelRange entry2 = new S2LevelRange(
+        SuffixTableRange entry2 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefix, 2001),
                 fileFormat.createCellId(tablePrefix, 2101));
         suffixTableWriter.addRange(entry2);
@@ -302,7 +304,7 @@
         inOrder.verify(mockVisitor).end();
     }
 
-    private S2LevelRange findEntryByCellId(SatS2RangeFileFormat fileFormat,
+    private SuffixTableRange findEntryByCellId(SatS2RangeFileFormat fileFormat,
             SuffixTableBlock suffixTableBlock, int prefix, int suffix) {
         long cellId = fileFormat.createCellId(prefix, suffix);
         SuffixTableBlock.Entry entry = suffixTableBlock.findEntryByCellId(cellId);
diff --git a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableExtraInfoTest.java b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableExtraInfoTest.java
index f992ae7..f978bd5 100644
--- a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableExtraInfoTest.java
+++ b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableExtraInfoTest.java
@@ -20,9 +20,9 @@
 
 import com.android.storage.block.read.BlockInfo;
 import com.android.storage.block.write.BlockWriter;
-import com.android.storage.s2.S2LevelRange;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
 import com.android.telephony.sats2range.read.SuffixTableExtraInfo;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.read.SuffixTableSharedData;
 import com.android.telephony.sats2range.utils.TestUtils;
 import com.android.telephony.sats2range.write.SuffixTableWriter;
@@ -54,13 +54,13 @@
                 SuffixTableWriter.createPopulated(fileFormat, suffixTableSharedData);
 
         int tablePrefix = suffixTableSharedData.getTablePrefix();
-        S2LevelRange range1 = new S2LevelRange(
+        SuffixTableRange range1 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefix, 1000),
                 fileFormat.createCellId(tablePrefix, 1001));
-        S2LevelRange range2 = new S2LevelRange(
+        SuffixTableRange range2 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefix, 1002),
                 fileFormat.createCellId(tablePrefix, 1003));
-        S2LevelRange range3 = new S2LevelRange(
+        SuffixTableRange range3 = new SuffixTableRange(
                 fileFormat.createCellId(tablePrefix, 1004),
                 fileFormat.createCellId(tablePrefix, 1005));
 
diff --git a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableSharedDataTest.java b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableSharedDataTest.java
index 2baefa9..ee21626 100644
--- a/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableSharedDataTest.java
+++ b/utils/satellite/s2storage/src/test/java/com/android/telephony/sats2range/SuffixTableSharedDataTest.java
@@ -18,11 +18,18 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.storage.block.read.BlockData;
+import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
 import com.android.telephony.sats2range.read.SuffixTableSharedData;
 import com.android.telephony.sats2range.write.SuffixTableSharedDataWriter;
 
 import org.junit.Test;
 
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 /** Tests for {@link SuffixTableSharedData} and {@link SuffixTableSharedDataWriter}. */
 public class SuffixTableSharedDataTest {
     @Test
@@ -33,5 +40,106 @@
 
         assertEquals(sharedData, SuffixTableSharedData.fromBytes(bytes));
     }
-}
 
+    @Test
+    public void testSuffixTableSharedDataWithEntryValues() {
+        int prefix = 321;
+        int entryValueSizeInBytes = 1;
+        List<Integer> entryValues = new ArrayList<>(Arrays.asList(0x01, 0x7F, 0xFF));
+        int versionNumber = 1;
+
+        // Verify whether fromTypedData returns correct SuffixTableSharedData when entryByte is 1
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+
+        // Verify when entryValueSizeInBytes is 2
+        entryValueSizeInBytes = 2;
+        entryValues = new ArrayList<>(Arrays.asList(0x001, 0x5FFF, 0xAFFF, 0xFFFF));
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+
+        // Verify when entryValueSizeInBytes is 3
+        entryValueSizeInBytes = 3;
+        entryValues = new ArrayList<>(
+                Arrays.asList(0x000001, 0x4FFFFF, 0x8FFFFF, 0xBFFFFF, 0xFFFFFF));
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+
+        // Verify when entryValueSizeInBytes is 4, max int value is 0x7FFFFFFF.
+        // ConfigID is supported up to 0x7FFFFFFF for now.
+        entryValueSizeInBytes = 4;
+        entryValues = new ArrayList<>(
+                Arrays.asList(0x00000001, 0x2FFFFFFF, 0x3FFFFFFF, 0x4FFFFFFF, 0x5FFFFFFF,
+                        0x6FFFFFFF, 0x7FFFFFFF));
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+
+        // Verify when every entry has same value.
+        entryValues = new ArrayList<>(
+                Arrays.asList(0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF,
+                        0x3FFFFFFF, 0x3FFFFFFF));
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+
+        // Verify when entry is empty
+        // entryValueSizeInBytes is set as 4, but there is no entry list
+        entryValues = new ArrayList<>(List.of());
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+        // entryValueSizeInBytes is 0, no entry list
+        entryValueSizeInBytes = 0;
+        verifySharedData(prefix, entryValueSizeInBytes, entryValues.size(), entryValues,
+                versionNumber);
+    }
+
+    private BlockData createBlockedDataFromByteBuffer(int prefix,
+            List<Integer> entryValues, SatS2RangeFileFormat fileFormat) {
+        SuffixTableSharedData sharedDataToWrite = new SuffixTableSharedData(prefix, entryValues,
+                fileFormat);
+        ByteBuffer byteBuffer = ByteBuffer.wrap(
+                SuffixTableSharedDataWriter.toBytes(sharedDataToWrite));
+        return new BlockData(byteBuffer.asReadOnlyBuffer());
+    }
+
+    private void verifySharedData(int expectedTablePrefix, int expectedEntryValueSizeInBytes,
+            int expectedNumberOfEntryValues, List<Integer> expectedEntryValues, int versionNumber) {
+        SatS2RangeFileFormat fileFormat = createSatS2RangeFileFormat(expectedEntryValueSizeInBytes,
+                versionNumber);
+        BlockData blockData = createBlockedDataFromByteBuffer(
+                expectedTablePrefix, expectedEntryValues, fileFormat);
+        SuffixTableSharedData sharedData = SuffixTableSharedData.fromTypedData(blockData,
+                fileFormat);
+
+        assertEquals(expectedTablePrefix, sharedData.getTablePrefix());
+        if (!expectedEntryValues.isEmpty()) {
+            assertEquals(expectedEntryValueSizeInBytes, sharedData.getEntryValueSizeInBytes());
+        } else {
+            assertEquals(0, sharedData.getEntryValueSizeInBytes());
+        }
+
+        // If every entry has same value, block data contains only 1 entry info
+        if (expectedEntryValues.stream().distinct().count() == 1) {
+            assertEquals(3 * Integer.BYTES, blockData.getSize());
+            // Verify whether the entry value count has been set to 1.
+            assertEquals(1, sharedData.getNumberOfEntryValues());
+        } else {
+            assertEquals(expectedNumberOfEntryValues, sharedData.getNumberOfEntryValues());
+        }
+        for (int i = 0; i < expectedNumberOfEntryValues; i++) {
+            assertEquals((int) expectedEntryValues.get(i), sharedData.getEntryValue(i));
+        }
+    }
+
+    private SatS2RangeFileFormat createSatS2RangeFileFormat(int entryByteCount, int versionNumber) {
+        int s2Level = 12;
+        int prefixBitCount = 11;
+        int suffixBitCount = 16;
+        int suffixTableBlockIdOffset = 5;
+        int suffixTableEntryBitCount = 24;
+        boolean isAllowedList = true;
+
+        return new SatS2RangeFileFormat(s2Level,
+                prefixBitCount, suffixBitCount, suffixTableBlockIdOffset, suffixTableEntryBitCount,
+                isAllowedList, entryByteCount, versionNumber);
+    }
+}
diff --git a/utils/satellite/s2storage/src/testutils/java/com/android/telephony/sats2range/testutils/TestUtils.java b/utils/satellite/s2storage/src/testutils/java/com/android/telephony/sats2range/testutils/TestUtils.java
index 3dfc720..ca0b754 100644
--- a/utils/satellite/s2storage/src/testutils/java/com/android/telephony/sats2range/testutils/TestUtils.java
+++ b/utils/satellite/s2storage/src/testutils/java/com/android/telephony/sats2range/testutils/TestUtils.java
@@ -38,6 +38,14 @@
 
     /** Returns a valid {@link SatS2RangeFileFormat}. */
     public static SatS2RangeFileFormat createS2RangeFileFormat(boolean isAllowedList) {
+        return createS2RangeFileFormat(isAllowedList,
+                /* entryValueSizeInBytes */0,
+                /* versionNumber */0);
+    }
+
+    /** Returns a valid {@link SatS2RangeFileFormat}. */
+    public static SatS2RangeFileFormat createS2RangeFileFormat(boolean isAllowedList,
+            int entryValueSizeInBytes, int versionNumber) {
         int dataS2Level = TEST_S2_LEVEL;
         int faceIdBits = 3;
         int bitCountPerLevel = 2;
@@ -48,7 +56,8 @@
         int suffixTableEntryBitCount = 4 * Byte.SIZE;
         int suffixTableBlockIdOffset = 5;
         return new SatS2RangeFileFormat(dataS2Level, prefixBitCount, suffixBitCount,
-                suffixTableBlockIdOffset, suffixTableEntryBitCount, isAllowedList);
+                suffixTableBlockIdOffset, suffixTableEntryBitCount, isAllowedList,
+                entryValueSizeInBytes, versionNumber);
     }
 
     /** Create an S2 cell ID */
diff --git a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/HeaderBlockWriter.java b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/HeaderBlockWriter.java
index d4e9310..0435922 100644
--- a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/HeaderBlockWriter.java
+++ b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/HeaderBlockWriter.java
@@ -63,6 +63,8 @@
             tos.writeUnsignedByte(mFileFormat.getSuffixTableBlockIdOffset());
             tos.writeUnsignedByte(mFileFormat.isAllowedList()
                     ? HeaderBlock.TRUE : HeaderBlock.FALSE);
+            tos.writeUnsignedByte(mFileFormat.getEntryValueSizeInBytes());
+            tos.writeInt(mFileFormat.getVersionNumber());
         }
 
         FileChannel fileChannel = FileChannel.open(mFile.toPath(), StandardOpenOption.READ);
diff --git a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SatS2RangeFileWriter.java b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SatS2RangeFileWriter.java
index 9b3c20e..375707a 100644
--- a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SatS2RangeFileWriter.java
+++ b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SatS2RangeFileWriter.java
@@ -19,9 +19,9 @@
 import com.android.storage.block.write.BlockFileWriter;
 import com.android.storage.block.write.BlockWriter;
 import com.android.storage.block.write.EmptyBlockWriter;
-import com.android.storage.s2.S2LevelRange;
 import com.android.storage.s2.S2Support;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.read.SuffixTableSharedData;
 
 import java.io.File;
@@ -64,8 +64,8 @@
      * needed to fit them into suffix blocks. The ranges must be of the expected S2 level
      * and ordered by cell ID.
      */
-    public void createSortedSuffixBlocks(Iterator<S2LevelRange> ranges) throws IOException {
-        PushBackIterator<S2LevelRange> pushBackIterator = new PushBackIterator<>(ranges);
+    public void createSortedSuffixBlocks(Iterator<SuffixTableRange> ranges) throws IOException {
+        PushBackIterator<SuffixTableRange> pushBackIterator = new PushBackIterator<>(ranges);
 
         // For each prefix value, collect all the ranges that match.
         for (int currentPrefix = 0;
@@ -74,7 +74,7 @@
 
             // Step 1:
             // populate samePrefixRanges, which holds ranges that have a prefix of currentPrefix.
-            List<S2LevelRange> samePrefixRanges =
+            List<SuffixTableRange> samePrefixRanges =
                     collectSamePrefixRanges(pushBackIterator, currentPrefix);
 
             // Step 2: Write samePrefixRanges to a suffix table.
@@ -88,11 +88,11 @@
         }
     }
 
-    private List<S2LevelRange> collectSamePrefixRanges(
-            PushBackIterator<S2LevelRange> pushBackIterator, int currentPrefix) {
-        List<S2LevelRange> samePrefixRanges = new ArrayList<>();
+    private List<SuffixTableRange> collectSamePrefixRanges(
+            PushBackIterator<SuffixTableRange> pushBackIterator, int currentPrefix) {
+        List<SuffixTableRange> samePrefixRanges = new ArrayList<>();
         while (pushBackIterator.hasNext()) {
-            S2LevelRange currentRange = pushBackIterator.next();
+            SuffixTableRange currentRange = pushBackIterator.next();
 
             long startCellId = currentRange.getStartCellId();
             if (mFileFormat.getS2Level() != S2Support.getS2Level(startCellId)) {
@@ -123,17 +123,18 @@
                 // Create a range for the current prefix.
                 {
                     long newEndCellId = mFileFormat.createCellId(startCellPrefix + 1, 0);
-                    S2LevelRange satS2Range = new S2LevelRange(startCellId, newEndCellId);
+                    SuffixTableRange satS2Range = new SuffixTableRange(startCellId, newEndCellId);
                     samePrefixRanges.add(satS2Range);
                 }
 
-                Deque<S2LevelRange> otherRanges = new ArrayDeque<>();
+                Deque<SuffixTableRange> otherRanges = new ArrayDeque<>();
                 // Intermediate prefixes.
                 startCellPrefix = startCellPrefix + 1;
                 while (startCellPrefix != endCellPrefixValue) {
                     long newStartCellId = mFileFormat.createCellId(startCellPrefix, 0);
                     long newEndCellId = mFileFormat.createCellId(startCellPrefix + 1, 0);
-                    S2LevelRange satS2Range = new S2LevelRange(newStartCellId, newEndCellId);
+                    SuffixTableRange satS2Range = new SuffixTableRange(newStartCellId,
+                            newEndCellId);
                     otherRanges.add(satS2Range);
                     startCellPrefix++;
                 }
@@ -142,7 +143,8 @@
                 {
                     long newStartCellId = mFileFormat.createCellId(endCellPrefixValue, 0);
                     if (newStartCellId != endCellId) {
-                        S2LevelRange satS2Range = new S2LevelRange(newStartCellId, endCellId);
+                        SuffixTableRange satS2Range = new SuffixTableRange(newStartCellId,
+                                endCellId);
                         otherRanges.add(satS2Range);
                     }
                 }
@@ -160,41 +162,22 @@
     }
 
     private BlockWriter writeSamePrefixRanges(
-            int currentPrefix, List<S2LevelRange> samePrefixRanges) throws IOException {
+            int currentPrefix, List<SuffixTableRange> samePrefixRanges) throws IOException {
         BlockWriter blockWriter;
         if (samePrefixRanges.size() == 0) {
             // Add an empty block.
             blockWriter = SuffixTableWriter.createEmptyBlockWriter();
         } else {
+            List<SuffixTableRange> suffixTableRanges = convertSamePrefixRangesToSuffixTableRanges(
+                    samePrefixRanges);
+            List<Integer> entryValues = getEntryValues(suffixTableRanges);
             // Create a suffix table block.
-            SuffixTableSharedData sharedData = new SuffixTableSharedData(currentPrefix);
+            SuffixTableSharedData sharedData = new SuffixTableSharedData(currentPrefix, entryValues,
+                    mFileFormat);
             SuffixTableWriter suffixTableWriter =
                     SuffixTableWriter.createPopulated(mFileFormat, sharedData);
-            S2LevelRange lastRange = null;
-            for (S2LevelRange currentRange : samePrefixRanges) {
-                // Validate ranges don't overlap.
-                if (lastRange != null) {
-                    if (lastRange.overlaps(currentRange)) {
-                        throw new IllegalStateException("lastRange=" + lastRange + " overlaps"
-                                + " currentRange=" + currentRange);
-                    }
-                }
-                lastRange = currentRange;
-
-                // Split the range so it fits.
-                final int maxRangeLength = mFileFormat.getTableEntryMaxRangeLengthValue();
-                long startCellId = currentRange.getStartCellId();
-                long endCellId = currentRange.getEndCellId();
-                int rangeLength = mFileFormat.calculateRangeLength(startCellId, endCellId);
-                while (rangeLength > maxRangeLength) {
-                    long newEndCellId = S2Support.offsetCellId(startCellId, maxRangeLength);
-                    S2LevelRange suffixTableRange = new S2LevelRange(startCellId, newEndCellId);
-                    suffixTableWriter.addRange(suffixTableRange);
-                    startCellId = newEndCellId;
-                    rangeLength = mFileFormat.calculateRangeLength(startCellId, endCellId);
-                }
-                S2LevelRange suffixTableRange = new S2LevelRange(startCellId, endCellId);
-                suffixTableWriter.addRange(suffixTableRange);
+            for (SuffixTableRange range : suffixTableRanges) {
+                suffixTableWriter.addRange(range);
             }
             blockWriter = suffixTableWriter;
         }
@@ -234,4 +217,48 @@
     public SatS2RangeFileFormat getFileFormat() {
         return mFileFormat;
     }
+
+    private List<SuffixTableRange> convertSamePrefixRangesToSuffixTableRanges(
+            List<SuffixTableRange> samePrefixRanges) {
+        List<SuffixTableRange> suffixTableRanges = new ArrayList<>();
+        SuffixTableRange lastRange = null;
+        for (SuffixTableRange currentRange : samePrefixRanges) {
+            // Validate ranges don't overlap.
+            if (lastRange != null) {
+                if (lastRange.overlaps(currentRange)) {
+                    throw new IllegalStateException("lastRange=" + lastRange + " overlaps"
+                            + " currentRange=" + currentRange);
+                }
+            }
+            lastRange = currentRange;
+            int entryValue = currentRange.getEntryValue();
+
+            // Split the range so it fits.
+            final int maxRangeLength = mFileFormat.getTableEntryMaxRangeLengthValue();
+            long startCellId = currentRange.getStartCellId();
+            long endCellId = currentRange.getEndCellId();
+            int rangeLength = mFileFormat.calculateRangeLength(startCellId, endCellId);
+            while (rangeLength > maxRangeLength) {
+                long newEndCellId = S2Support.offsetCellId(startCellId, maxRangeLength);
+                SuffixTableRange suffixTableRange =
+                        new SuffixTableRange(startCellId, newEndCellId, entryValue);
+                suffixTableRanges.add(suffixTableRange);
+                startCellId = newEndCellId;
+                rangeLength = mFileFormat.calculateRangeLength(startCellId, endCellId);
+            }
+            SuffixTableRange suffixTableRange =
+                    new SuffixTableRange(startCellId, endCellId, entryValue);
+            suffixTableRanges.add(suffixTableRange);
+        }
+        return suffixTableRanges;
+    }
+
+    private List<Integer> getEntryValues(List<SuffixTableRange> suffixTableRanges) {
+        List<Integer> entryValues = new ArrayList<>();
+        for (SuffixTableRange suffixTableRange : suffixTableRanges) {
+            entryValues.add(suffixTableRange.getEntryValue());
+        }
+        return entryValues;
+    }
+
 }
diff --git a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableSharedDataWriter.java b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableSharedDataWriter.java
index 5499148..a739e5e 100644
--- a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableSharedDataWriter.java
+++ b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableSharedDataWriter.java
@@ -21,21 +21,37 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.List;
 
 /**
  * Converts a {@link SuffixTableSharedData} to a byte[] for writing.
  * See also {@link SuffixTableSharedData#fromBytes(byte[])}.
  */
 public final class SuffixTableSharedDataWriter {
-
+    private static final int BUFFER_SIZE = (int) Math.pow(2, 20);
     private SuffixTableSharedDataWriter() {
     }
 
     /** Returns the byte[] for the supplied {@link SuffixTableSharedData} */
     public static byte[] toBytes(SuffixTableSharedData suffixTableSharedData) {
+        int entryValueSizeInBytes = suffixTableSharedData.getEntryValueSizeInBytes();
+        List<Integer> entryValues = suffixTableSharedData.getEntryValuesToWrite();
+        // If every entry has same value, compress to save memory
+        int numberOfEntryValues =
+                entryValues.stream().distinct().count() == 1 ? 1 : entryValues.size();
+
         try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                TypedOutputStream tos = new TypedOutputStream(baos)) {
+                TypedOutputStream tos = new TypedOutputStream(baos, BUFFER_SIZE)) {
             tos.writeInt(suffixTableSharedData.getTablePrefix());
+
+            if (entryValueSizeInBytes > 0 && !entryValues.isEmpty()) {
+                tos.writeInt(numberOfEntryValues);
+                for (int i = 0; i < numberOfEntryValues; i++) {
+                    // ConfigId is supported up to 0x7FFFFFFF
+                    tos.writeVarByteValue(entryValueSizeInBytes, entryValues.get(i));
+                }
+            }
+
             tos.flush();
             return baos.toByteArray();
         } catch (IOException e) {
diff --git a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableWriter.java b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableWriter.java
index dc265d5..31b35eb 100644
--- a/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableWriter.java
+++ b/utils/satellite/s2storage/src/write/java/com/android/telephony/sats2range/write/SuffixTableWriter.java
@@ -22,11 +22,11 @@
 import com.android.storage.block.write.BlockWriter;
 import com.android.storage.block.write.EmptyBlockWriter;
 import com.android.storage.io.write.TypedOutputStream;
-import com.android.storage.s2.S2LevelRange;
 import com.android.storage.s2.S2Support;
 import com.android.storage.table.packed.write.PackedTableWriter;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
 import com.android.telephony.sats2range.read.SuffixTableExtraInfo;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.read.SuffixTableSharedData;
 
 import java.io.ByteArrayOutputStream;
@@ -42,7 +42,7 @@
  * To write empty tables use {@link #createEmptyBlockWriter()}.
  * To write populated tables use {@link
  * #createPopulated(SatS2RangeFileFormat, SuffixTableSharedData)} and add entries with
- * {@link #addRange(S2LevelRange)}
+ * {@link #addRange(SuffixTableRange)}
  */
 public final class SuffixTableWriter implements BlockWriter {
 
@@ -54,7 +54,7 @@
 
     private final File mFile;
 
-    private S2LevelRange mLastRangeAdded;
+    private SuffixTableRange mLastRangeAdded;
 
     private SuffixTableWriter(SatS2RangeFileFormat fileFormat, SuffixTableSharedData sharedData)
             throws IOException {
@@ -90,7 +90,7 @@
      * called at least once. See {@link SuffixTableWriter#createEmptyBlockWriter()} for empty
      * tables.
      */
-    public void addRange(S2LevelRange suffixTableRange) throws IOException {
+    public void addRange(SuffixTableRange suffixTableRange) throws IOException {
         checkIsOpen();
 
         long rangeStartCellId = suffixTableRange.getStartCellId();
diff --git a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/CreateTestSatS2File.java b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/CreateTestSatS2File.java
index f9a9347..41ce416 100644
--- a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/CreateTestSatS2File.java
+++ b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/CreateTestSatS2File.java
@@ -16,8 +16,8 @@
 
 package com.android.telephony.tools.sats2;
 
-import com.android.storage.s2.S2LevelRange;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.write.SatS2RangeFileWriter;
 
 import java.io.File;
@@ -42,18 +42,18 @@
         try (SatS2RangeFileWriter satS2RangeFileWriter =
                      SatS2RangeFileWriter.open(file, fileFormat)) {
             // Two ranges that share a prefix.
-            S2LevelRange range1 = new S2LevelRange(
+            SuffixTableRange range1 = new SuffixTableRange(
                     fileFormat.createCellId(0b100_11111111, 1000),
                     fileFormat.createCellId(0b100_11111111, 2000));
-            S2LevelRange range2 = new S2LevelRange(
+            SuffixTableRange range2 = new SuffixTableRange(
                     fileFormat.createCellId(0b100_11111111, 2000),
                     fileFormat.createCellId(0b100_11111111, 3000));
             // This range has a different face, so a different prefix, and will be in a different
             // suffix table.
-            S2LevelRange range3 = new S2LevelRange(
+            SuffixTableRange range3 = new SuffixTableRange(
                     fileFormat.createCellId(0b101_11111111, 1000),
                     fileFormat.createCellId(0b101_11111111, 2000));
-            List<S2LevelRange> allRanges = listOf(range1, range2, range3);
+            List<SuffixTableRange> allRanges = listOf(range1, range2, range3);
             satS2RangeFileWriter.createSortedSuffixBlocks(allRanges.iterator());
         }
     }
diff --git a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2FileCreator.java b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2FileCreator.java
index dd7d8c0..1ed9680 100644
--- a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2FileCreator.java
+++ b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2FileCreator.java
@@ -16,9 +16,9 @@
 
 package com.android.telephony.tools.sats2;
 
-import com.android.storage.s2.S2LevelRange;
 import com.android.telephony.sats2range.read.SatS2RangeFileFormat;
 import com.android.telephony.sats2range.read.SatS2RangeFileReader;
+import com.android.telephony.sats2range.read.SuffixTableRange;
 import com.android.telephony.sats2range.write.SatS2RangeFileWriter;
 
 import com.google.common.base.Stopwatch;
@@ -73,15 +73,15 @@
                 FileFormats.getFileFormatForLevel(s2Level, isAllowedList);
         try (SatS2RangeFileWriter satS2RangeFileWriter =
                      SatS2RangeFileWriter.open(new File(outputFile), fileFormat)) {
-            Iterator<S2LevelRange> s2LevelRangeIterator = satS2Ranges
+            Iterator<SuffixTableRange> suffixTableRangeIterator = satS2Ranges
                     .stream()
-                    .map(x -> new S2LevelRange(x.rangeStart.id(), x.rangeEnd.id()))
+                    .map(x -> new SuffixTableRange(x.rangeStart.id(), x.rangeEnd.id()))
                     .iterator();
             /*
              * Group the sorted ranges into contiguous suffix blocks. Big ranges might get split as
              * needed to fit them into suffix blocks.
              */
-            satS2RangeFileWriter.createSortedSuffixBlocks(s2LevelRangeIterator);
+            satS2RangeFileWriter.createSortedSuffixBlocks(suffixTableRangeIterator);
         }
 
         // Validate the output block file