Merge Android 14 QPR3 to AOSP main

Bug: 346855327
Merged-In:
Change-Id: Ice588503c95f597a231dfdd0f02321f5231cdb21
diff --git a/Android.bp b/Android.bp
index 48222f1..c717d86 100644
--- a/Android.bp
+++ b/Android.bp
@@ -97,18 +97,6 @@
     ],
 }
 
-// Used by satellite unit tests temporarily during the development phase.
-// TODO: Remove this once the satellite code is wired into Telephony code.
-java_library {
-    name: "telephony-satellite",
-    srcs: ["src/com/android/phone/satellite/**/*.java"],
-    libs: [
-        "satellite-s2storage-ro",
-        "s2-geometry-library-java",
-        "telephony-common",
-    ],
-}
-
 platform_compat_config {
     name: "TeleService-platform-compat-config",
     src: ":TeleService",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7e56e8b..1190d38 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -71,6 +71,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.LOCATION_BYPASS" />
     <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
     <uses-permission android:name="android.permission.BROADCAST_SMS"/>
     <uses-permission android:name="android.permission.BROADCAST_WAP_PUSH"/>
@@ -137,6 +138,7 @@
     <uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
     <uses-permission android:name="android.permission.BIND_SATELLITE_GATEWAY_SERVICE" />
     <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE" />
+    <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
     <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
@@ -167,9 +169,17 @@
     <!-- Needed to register for UWB state changes for satellite communication -->
     <uses-permission android:name="android.permission.UWB_PRIVILEGED"/>
 
-    <permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"
-                android:label="Access last known cell identity."
-                android:protectionLevel="signature"/>
+    <!-- Needed to initiate configuration update -->
+    <uses-permission android:name="android.permission.UPDATE_CONFIG"/>
+
+    <!-- Needed to bind the domain selection service. -->
+    <uses-permission android:name="android.permission.BIND_DOMAIN_SELECTION_SERVICE" />
+
+    <!-- Needed to send safety center updates for cellular transparency features   -->
+    <uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE"/>
+
+    <!-- Needed because the DISPLAY_EMERGENCY_MESSAGE ConnectionEvent contains a PendingIntent to activate the satellite feature. -->
+    <uses-permission android:name="com.google.android.apps.stargate.permission.SEND_EMERGENCY_INTENTS"/>
 
     <application android:name="PhoneApp"
             android:persistent="true"
@@ -570,10 +580,29 @@
             </intent-filter>
         </receiver>
 
+        <!-- Update configuration data file -->
+        <receiver android:name="com.android.internal.telephony.configupdate.TelephonyConfigUpdateInstallReceiver"
+            android:exported="true"
+            android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.os.action.UPDATE_CONFIG" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
         <receiver
             android:name="com.android.internal.telephony.uicc.ShowInstallAppNotificationReceiver"
             android:exported="false"/>
 
+        <receiver
+            android:name=".security.SafetySourceReceiver"
+            android:exported="false"
+            androidprv:systemUserOnly="true">
+            <intent-filter>
+                <action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
+            </intent-filter>
+        </receiver>
+
         <activity
             android:name="com.android.phone.settings.PickSmsSubscriptionActivity"
             android:exported="false"
@@ -626,5 +655,13 @@
             android:multiprocess="false"
             android:singleUser="true"
             android:writePermission="android.permission.MODIFY_PHONE_STATE"/>
+
+        <service android:name="com.android.services.telephony.domainselection.TelephonyDomainSelectionService"
+            android:exported="true"
+            android:permission="android.permission.BIND_DOMAIN_SELECTION_SERVICE">
+            <intent-filter>
+                <action android:name="android.telephony.DomainSelectionService"/>
+            </intent-filter>
+        </service>
     </application>
 </manifest>
diff --git a/assets/CarrierRestrictionOperatorDetails.json b/assets/CarrierRestrictionOperatorDetails.json
index f3da100..8602d4e 100644
--- a/assets/CarrierRestrictionOperatorDetails.json
+++ b/assets/CarrierRestrictionOperatorDetails.json
@@ -1,5 +1,6 @@
 {
   "_comment": "Operator should register with its application package name, carrierId and all the corresponding  SHAIDs",
   "_comment": "Example format :: << \"packageName\" : {\"carrierId\":<int>, \"callerSHA1Id\":[<SHAID1>, <SHAID2>]} >>",
-  "com.vzw.hss.myverizon":{"carrierId":1839,"callerSHA1Id":["C58EE7871896786F8BF70EBDB137DE10074043E9","AE23A03436DF07B0CD70FE881CDA2EC1D21215D7B7B0CC68E67B67F5DF89526A"]}
+  "com.vzw.hss.myverizon":{"carrierId":1839,"callerSHA1Id":["C58EE7871896786F8BF70EBDB137DE10074043E9","AE23A03436DF07B0CD70FE881CDA2EC1D21215D7B7B0CC68E67B67F5DF89526A"]},
+  "com.google.android.apps.tycho":{"carrierId":1989,"callerSHA1Id":["B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350","4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"]}
 }
\ No newline at end of file
diff --git a/assets/google_us_san_sat_s2.dat b/assets/google_us_san_sat_s2.dat
new file mode 100644
index 0000000..60b00df
--- /dev/null
+++ b/assets/google_us_san_sat_s2.dat
Binary files differ
diff --git a/ecc/input/eccdata.txt b/ecc/input/eccdata.txt
index fe11383..c4edc9e 100644
--- a/ecc/input/eccdata.txt
+++ b/ecc/input/eccdata.txt
@@ -2359,77 +2359,6 @@
     types: POLICE
     types: AMBULANCE
     types: FIRE
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "984"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "985"
-    types: MOUNTAIN_RESCUE
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "986"
-    types: POLICE
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "997"
-    types: POLICE
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "998"
-    types: FIRE
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "999"
-    types: AMBULANCE
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "991"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "992"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "993"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "994"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "995"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "996"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "987"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
-  }
-  eccs {
-    phone_number: "989"
-    types: TYPE_UNSPECIFIED
-    routing: EMERGENCY
   }
   ecc_fallback: "112"
 }
diff --git a/ecc/output/eccdata b/ecc/output/eccdata
index 55d8151..482ed79 100644
--- a/ecc/output/eccdata
+++ b/ecc/output/eccdata
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index dab1dfb..277fb42 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -486,7 +486,7 @@
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Skakel vliegtuigmodus af om kontakte van die SIM-kaart af in te voer."</string>
     <string name="enable_pin" msgid="967674051730845376">"Aktiveer/deaktiveer SIM-PIN"</string>
     <string name="change_pin" msgid="3657869530942905790">"Verander SIM-PIN"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"SIM-PIN:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"Ou PIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"Nuwe PIN"</string>
     <string name="confirmPinLabel" msgid="7783531218662473778">"Bevestig nuwe PIN"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 660da5e..597d6c5 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -283,8 +283,8 @@
     <string name="data_enable_summary" msgid="696860063456536557">"Povolit používání dat"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"Pozor"</string>
     <string name="roaming" msgid="1576180772877858949">"Roaming"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"Používat datové služby při roamingu"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"Používat datové služby při roamingu"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"Při roamingu používat datové služby"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"Při roamingu používat datové služby"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"Datový roaming je vypnutý. Klepnutím ho zapnete."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"Mohou vám být účtovány poplatky za roaming. Upravte klepnutím."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Mobilní datové připojení bylo ztraceno"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 58f2670..801eb12 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -862,7 +862,7 @@
     <string name="radioInfo_phone_offhook" msgid="7564601639749936170">"Opkald i gang"</string>
     <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Afbrudt"</string>
     <string name="radioInfo_data_connecting" msgid="925092271092152472">"Forbindelsen oprettes"</string>
-    <string name="radioInfo_data_connected" msgid="7637335645634239508">"Tilsluttet"</string>
+    <string name="radioInfo_data_connected" msgid="7637335645634239508">"Forbundet"</string>
     <string name="radioInfo_data_suspended" msgid="8695262782642002785">"Suspenderet"</string>
     <string name="radioInfo_unknown" msgid="5401423738500672850">"Ukendt"</string>
     <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Primær"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 4790caa..d9fbf20 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -863,7 +863,7 @@
     <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Deskonektatuta"</string>
     <string name="radioInfo_data_connecting" msgid="925092271092152472">"Konektatzen"</string>
     <string name="radioInfo_data_connected" msgid="7637335645634239508">"Konektatuta"</string>
-    <string name="radioInfo_data_suspended" msgid="8695262782642002785">"Behin-behinean itxitakoak"</string>
+    <string name="radioInfo_data_suspended" msgid="8695262782642002785">"Aldi baterako itxitakoak"</string>
     <string name="radioInfo_unknown" msgid="5401423738500672850">"Ezezaguna"</string>
     <string name="radioInfo_imei_primary" msgid="5948747378637224400">"Nagusia"</string>
     <string name="radioInfo_display_packets" msgid="6794302192441084157">"pkts"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c52cb86..7319afb 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -453,7 +453,7 @@
     <string name="sum_fdn_change_pin" msgid="3510994280557335727">"‏تغییر پین برای دسترسی FDN"</string>
     <string name="sum_fdn_manage_list" msgid="3311397063233992907">"مدیریت فهرست شماره تلفن"</string>
     <string name="voice_privacy" msgid="7346935172372181951">"حریم خصوصی صوتی"</string>
-    <string name="voice_privacy_summary" msgid="3556460926168473346">"فعال کردن حالت رازداری پیشرفته"</string>
+    <string name="voice_privacy_summary" msgid="3556460926168473346">"فعال کردن حالت حریم خصوصی پیشرفته"</string>
     <string name="tty_mode_option_title" msgid="3843817710032641703">"‏حالت TTY"</string>
     <string name="tty_mode_option_summary" msgid="4770510287236494371">"‏تنظیم حالت TTY"</string>
     <string name="auto_retry_mode_title" msgid="2985801935424422340">"سعی مجدد خودکار"</string>
@@ -661,7 +661,7 @@
     <string name="description_dial_button" msgid="8614631902795087259">"شماره گیری"</string>
     <string name="description_dialpad_button" msgid="7395114120463883623">"نمایش صفحه شماره گیری"</string>
     <string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"صفحه شماره‌گیری اضطراری"</string>
-    <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"پست صوتی دیداری"</string>
+    <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"پست صوتی تصویری"</string>
     <string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"تنظیم پین"</string>
     <string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"تغییر پین"</string>
     <string name="preference_category_ringtone" msgid="8787281191375434976">"آهنگ‌ زنگ و لرزش"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index f24af53..2bd6ac9 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -617,7 +617,7 @@
     <string name="ota_title_activate" msgid="4049645324841263423">"Aktivoi puhelin"</string>
     <string name="ota_touch_activate" msgid="838764494319694754">"Sinun täytyy soittaa erityinen puhelu aktivoidaksesi puhelimesi palvelun. \n\nPaina Aktivoi ja aktivoi puhelimesi kuulemiesi ohjeiden avulla."</string>
     <string name="ota_hfa_activation_title" msgid="3300556778212729671">"Aktivoidaan…"</string>
-    <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"Puhelimen mobiilitiedonsiirtopalvelua aktivoidaan.\n\nAktivointi voi kestää viisi minuuttia."</string>
+    <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"Puhelimen mobiilidatapalvelua aktivoidaan.\n\nAktivointi voi kestää viisi minuuttia."</string>
     <string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"Ohitetaanko aktivointi?"</string>
     <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"Jos ohitat aktivoinnin, et voi soittaa puheluita etkä muodostaa verkkoyhteyttä (voit tosin muodostaa yhteyden Wi-Fi-verkkoihin). Sinua pyydetään aktivoimaan puhelimesi aina kun käynnistät sen siihen saakka, että aktivoit sen."</string>
     <string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"Ohita"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 85a5a93..8b3f378 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -595,7 +595,7 @@
     <string name="singleContactImportedMsg" msgid="3619804066300998934">"Contact importé"</string>
     <string name="failedToImportSingleContactMsg" msgid="228095510489830266">"Échec de l\'importation du contact."</string>
     <string name="hac_mode_title" msgid="4127986689621125468">"Assistance auditive"</string>
-    <string name="hac_mode_summary" msgid="7774989500136009881">"Activer la compatibilité avec les prothèses auditives"</string>
+    <string name="hac_mode_summary" msgid="7774989500136009881">"Activer la compatibilité avec les appareils auditifs"</string>
     <string name="rtt_mode_title" msgid="3075948111362818043">"Appel texte en temps réel"</string>
     <string name="rtt_mode_summary" msgid="8631541375609989562">"Autoriser l\'échange de messages pendant les appels vocaux"</string>
     <string name="rtt_mode_more_information" msgid="587500128658756318">"La fonctionnalité de texte en temps réel vient en aide aux personnes sourdes, malentendantes, qui ont un trouble de la parole, ou qui ont besoin d\'une transcription en plus de la voix.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;En savoir plus&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; - Les appels texte en temps réel sont enregistrés sous forme transcrite\n       &lt;br&gt; - Le mode texte en temps réel n\'est pas disponible pour les appels vidéo"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 4f8e559..febd572 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -165,7 +165,7 @@
     <string name="voicemail_default" msgid="6427575113775462077">"आपको सेवा देने वाली कंपनी"</string>
     <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"पुराना पिन"</string>
     <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"नया पिन"</string>
-    <string name="vm_change_pin_progress_message" msgid="626015184502739044">"कृपया प्रतीक्षा करें."</string>
+    <string name="vm_change_pin_progress_message" msgid="626015184502739044">"कृपया इंतज़ार करें."</string>
     <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"नया पिन बहुत छोटा है."</string>
     <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"नया पिन बहुत बड़ा है."</string>
     <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"नया पिन बहुत कमज़ोर है. किसी सशक्त पासवर्ड में निरंतर क्रम या अंकों का दोहराव नहीं होना चाहिए."</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 790b6be..cb58063 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -305,7 +305,7 @@
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"mobilni operater, esim, sim, euicc, promjena mobilnog operatera, dodavanje 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">"Mobilni podaci"</string>
-    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Pristupi podacima pomoću mobilne mreže"</string>
+    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Pristupi podacima putem mobilne mreže"</string>
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Želite li isključiti mobilne podatke?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Odabir je obvezan"</string>
     <string name="sim_change_data_title" msgid="9142726786345906606">"Promijeniti podatkovni SIM?"</string>
@@ -485,7 +485,7 @@
     <string name="simContacts_title" msgid="2714029230160136647">"Odabir kontakata za uvoz"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Isključite način rada u zrakoplovu da biste uvezli kontakte sa SIM kartice."</string>
     <string name="enable_pin" msgid="967674051730845376">"Omogući/onemogući PIN za SIM"</string>
-    <string name="change_pin" msgid="3657869530942905790">"Promijeni PIN za SIM"</string>
+    <string name="change_pin" msgid="3657869530942905790">"Promijenite PIN za SIM"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"PIN za SIM:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"Stari PIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"Novi PIN"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a51c72d..f5475b2 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -321,9 +321,9 @@
     <string name="throttle_help" msgid="2624535757028809735">"למידע נוסף"</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>%%) מתוך התקופה המרבית של <xliff:g id="USED_2">%3$s</xliff:g>\nהתקופה הבאה מתחילה בעוד <xliff:g id="USED_3">%4$d</xliff:g> ימים (<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>??)  מתוך התקופה המרבית של<xliff:g id="USED_2">%3$s</xliff:g>"</string>
-    <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"‏<xliff:g id="USED_0">%1$s</xliff:g> חריגה מהמקסימום\nקצב הנתונים ירד ל-‏‎<xliff:g id="USED_1">%2$d</xliff:g> Kb לשנייה"</string>
+    <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"‏<xliff:g id="USED_0">%1$s</xliff:g> חריגה מהמקסימום\nקצב הנתונים ירד ל-‏‎<xliff:g id="USED_1">%2$d</xliff:g>Kb לשנייה"</string>
     <string name="throttle_time_frame_subtext" msgid="6462089615392402127">"<xliff:g id="USED_0">%1$d</xliff:g>?? מהמחזור חלפו\nהתקופה הבאה מתחילה בעוד <xliff:g id="USED_1">%2$d</xliff:g> ימים (<xliff:g id="USED_2">%3$s</xliff:g>)"</string>
-    <string name="throttle_rate_subtext" msgid="7221971817325779535">"‏קצב הנתונים יורד ל-‏‎<xliff:g id="USED">%1$d</xliff:g> Kb לשנייה במקרה של חריגה ממגבלת השימוש בנתונים"</string>
+    <string name="throttle_rate_subtext" msgid="7221971817325779535">"‏קצב הנתונים יורד ל-‏‎<xliff:g id="USED">%1$d</xliff:g>Kb לשנייה במקרה של חריגה ממגבלת השימוש בנתונים"</string>
     <string name="throttle_help_subtext" msgid="2817114897095534807">"למידע נוסף על מדיניות השימוש בנתונים ברשת הסלולרית של הספק שלך"</string>
     <string name="cell_broadcast_sms" msgid="4053449797289031063">"‏SMS בשידור סלולרי"</string>
     <string name="enable_disable_cell_bc_sms" msgid="4759958924031721350">"‏SMS בשידור סלולרי"</string>
@@ -485,7 +485,7 @@
     <string name="simContacts_title" msgid="2714029230160136647">"בחירת אנשי קשר לייבוא"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"‏יש לבטל את מצב טיסה כדי לייבא אנשי קשר מכרטיס ה-SIM."</string>
     <string name="enable_pin" msgid="967674051730845376">"‏הפעלה/השבתה של קוד האימות של SIM"</string>
-    <string name="change_pin" msgid="3657869530942905790">"‏שינוי קוד אימות של SIM"</string>
+    <string name="change_pin" msgid="3657869530942905790">"‏שינוי קוד הגישה של ה-SIM"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"‏קוד אימות של SIM:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"קוד אימות ישן"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"קוד אימות חדש"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4127de8..91a1871 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -485,7 +485,7 @@
     <string name="simContacts_title" msgid="2714029230160136647">"インポートする連絡先の選択"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"SIMカードから連絡先をインポートするには機内モードをオフにしてください。"</string>
     <string name="enable_pin" msgid="967674051730845376">"SIM PINの有効/無効"</string>
-    <string name="change_pin" msgid="3657869530942905790">"SIM PINの変更"</string>
+    <string name="change_pin" msgid="3657869530942905790">"SIM PIN を変更"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"古いPIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"新しいPIN"</string>
@@ -538,7 +538,7 @@
     <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"通話を発信するには、モバイル ネットワークを ON にし、機内モードまたはバッテリー セーバー モードを OFF にしてください。"</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/res/values-kn/strings.xml b/res/values-kn/strings.xml
index fe39ede..58bcc3a 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -298,14 +298,14 @@
     <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ತಡೆಹಿಡಿಯಲಾಗಿರುವ SIP ಖಾತೆಗಳನ್ನು ಪತ್ತೆಮಾಡಲಾಗಿದೆ ಮತ್ತು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
     <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android ಪ್ಲ್ಯಾಟ್‌ಫಾರ್ಮ್ ಇನ್ನು ಮುಂದೆ SIP ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ.\nನಿಮ್ಮ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ SIP ಖಾತೆಗಳನ್ನು <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ತೆಗೆದುಹಾಕಲಾಗಿದೆ.\nನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಕರೆ ಮಾಡುವಿಕೆ ಖಾತೆ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ಖಚಿತಪಡಿಸಿ."</string>
     <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ"</string>
-    <string name="data_usage_title" msgid="8438592133893837464">"ಅಪ್ಲಿಕೇಶನ್‌ ಡೇಟಾ ಬಳಕೆ"</string>
+    <string name="data_usage_title" msgid="8438592133893837464">"ಆ್ಯಪ್ ಡೇಟಾ ಬಳಕೆ"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> ಬಳಸಲಾದ ಮೊಬೈಲ್ ಡೇಟಾ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"ಸುಧಾರಿತ"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"ವಾಹಕ"</string>
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"ವಾಹಕ, eSIM, ಸಿಮ್, euicc ವಾಹಕಗಳನ್ನು ಬದಲಿಸಿ, ವಾಹಕ ಸೇರಿಸಿ"</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">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
-    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"ಮೊಬೈಲ್ ನೆಟ್‌ವರ್ಕ್‌ ಬಳಸಿ ಡೇಟಾ ಪ್ರವೇಶಿಸಿ"</string>
+    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"ಮೊಬೈಲ್ ನೆಟ್‌ವರ್ಕ್‌ ಬಳಸಿ ಡೇಟಾ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string>
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"ಮೊಬೈಲ್ ಡೇಟಾ ಆಫ್ ಮಾಡಬೇಕೆ?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"ಆಯ್ಕೆ ಅಗತ್ಯವಿದೆ"</string>
     <string name="sim_change_data_title" msgid="9142726786345906606">"ಡೇಟಾ ಸಿಮ್‌ ಬದಲಾಯಿಸುವುದೇ?"</string>
@@ -466,7 +466,7 @@
     <string name="get_pin2" msgid="4221654606863196332">"PIN2 ಅನ್ನು ಟೈಪ್‌ ಮಾಡಿ"</string>
     <string name="name" msgid="1347432469852527784">"ಹೆಸರು"</string>
     <string name="number" msgid="1564053487748491000">"ಸಂಖ್ಯೆ"</string>
-    <string name="save" msgid="983805790346099749">"ಉಳಿಸು"</string>
+    <string name="save" msgid="983805790346099749">"ಸೇವ್ ಮಾಡಿ"</string>
     <string name="add_fdn_contact" msgid="1169713422306640887">"ಸ್ಥಿರ ಡಯಲಿಂಗ್ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಿ"</string>
     <string name="adding_fdn_contact" msgid="3112531600824361259">"ಸ್ಥಿರ ಡಯಲಿಂಗ್‌‌ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="fdn_contact_added" msgid="2840016151693394596">"ಸ್ಥಿರ ಡಯಲಿಂಗ್ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಲಾಗಿದೆ."</string>
@@ -485,7 +485,7 @@
     <string name="simContacts_title" msgid="2714029230160136647">"ಆಮದು ಮಾಡಲು ಸಂಪರ್ಕಗಳು"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"ಸಿಮ್ ಕಾರ್ಡ್‌ನಿಂದ ಸಂಪರ್ಕಗಳನ್ನು ಆಮದು ಮಾಡಲು ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್ ಆಫ್ ಮಾಡಿ."</string>
     <string name="enable_pin" msgid="967674051730845376">"ಸಿಮ್‌ ಪಿನ್‌ ಸಕ್ರಿಯಗೊಳಿಸಿ/ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="change_pin" msgid="3657869530942905790">"ಸಿಮ್‌ ಪಿನ್‌ ಬದಲಾಯಿಸಿ"</string>
+    <string name="change_pin" msgid="3657869530942905790">"SIM ಪಿನ್‌ ಬದಲಾಯಿಸಿ"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"ಸಿಮ್‌ ಪಿನ್‌:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"ಹಳೆಯ ಪಿನ್‌"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"ಹೊಸ ಪಿನ್‌"</string>
@@ -598,7 +598,7 @@
     <string name="hac_mode_summary" msgid="7774989500136009881">"ಶ್ರವಣ ಸಾಧನ ಹೊಂದಾಣಿಕೆಯನ್ನು ಆನ್‌ ಮಾಡಿ"</string>
     <string name="rtt_mode_title" msgid="3075948111362818043">"ನೈಜ-ಸಮಯ ಪಠ್ಯ (RTT) ಕರೆ"</string>
     <string name="rtt_mode_summary" msgid="8631541375609989562">"ಧ್ವನಿ ಕರೆಯ ಒಳಗೆ ಸಂದೇಶ ಕಳುಹಿಸುವಿಕೆಗೆ ಅನುಮತಿಸಿ"</string>
-    <string name="rtt_mode_more_information" msgid="587500128658756318">"ಕಿವುಡರು, ಆಲಿಸುವಿಕೆಯ ದೋಷಗಳನ್ನು ಹೊಂದಿದವರು, ಮಾತನಾಡುವಿಕೆಯಲ್ಲಿ ದೋಷಗಳನ್ನು ಹೊಂದಿದವರು ಅಥವಾ ಧ್ವನಿ ಮೀರಿ ಬೇರೆ ರೀತಿಯಲ್ಲಿ ಕರೆ ಮಾಡಲು ಕಠಿಣರಾಗಿರುವವರಿಗೆ RTT ಮೋಡ್ ಸಹಾಯ ಮಾಡುತ್ತದೆ.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ&lt;/a&gt;\n   &lt;br&gt;&lt;br&gt; - RTT ಕರೆಗಳನ್ನು ಸಂದೇಶ ಪ್ರತಿಲಿಪಿಗಳಂತೆ ಉಳಿಸಲಾಗಿದೆ \n    &lt;br&gt; - ವೀಡಿಯೊ ಕರೆಗಳಿಗೆ RTT ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="rtt_mode_more_information" msgid="587500128658756318">"ಕಿವುಡರು, ಆಲಿಸುವಿಕೆಯ ದೋಷಗಳನ್ನು ಹೊಂದಿದವರು, ಮಾತನಾಡುವಿಕೆಯಲ್ಲಿ ದೋಷಗಳನ್ನು ಹೊಂದಿದವರು ಅಥವಾ ಧ್ವನಿ ಮೀರಿ ಬೇರೆ ರೀತಿಯಲ್ಲಿ ಕರೆ ಮಾಡಲು ಕಠಿಣರಾಗಿರುವವರಿಗೆ RTT ಮೋಡ್ ಸಹಾಯ ಮಾಡುತ್ತದೆ.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ&lt;/a&gt;\n   &lt;br&gt;&lt;br&gt; - RTT ಕರೆಗಳನ್ನು ಸಂದೇಶ ಪ್ರತಿಲಿಪಿಗಳಂತೆ ಸೇವ್ ಮಾಡಲಾಗಿದೆ \n    &lt;br&gt; - ವೀಡಿಯೊ ಕರೆಗಳಿಗೆ RTT ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="no_rtt_when_roaming" msgid="5268008247378355389">"ಗಮನಿಸಿ: ರೋಮಿಂಗ್‌ನಲ್ಲಿ RTT ಲಭ್ಯವಿಲ್ಲ"</string>
   <string-array name="tty_mode_entries">
     <item msgid="3238070884803849303">"TTY ಆಫ್"</item>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 0ab1b47..fd24053 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -318,7 +318,7 @@
     <string name="throttle_current_usage" msgid="7483859109708658613">"Податоци кои се користат во тековниот период"</string>
     <string name="throttle_time_frame" msgid="1813452485948918791">"Период на потрошен интернет"</string>
     <string name="throttle_rate" msgid="7641913901133634905">"Политика на стапка на податоци"</string>
-    <string name="throttle_help" msgid="2624535757028809735">"Дознај повеќе"</string>
+    <string name="throttle_help" msgid="2624535757028809735">"Дознајте повеќе"</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> ٪) од максимум <xliff:g id="USED_2">%3$s</xliff:g> за периодот\nСледниот период започнува за <xliff:g id="USED_3">%4$d</xliff:g> дена (<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>٪) од <xliff:g id="USED_2">%3$s</xliff:g> максимален период"</string>
     <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> максимум е надминат\nСтапката на податоци е намалена на <xliff:g id="USED_1">%2$d</xliff:g> Kb/s"</string>
@@ -485,8 +485,8 @@
     <string name="simContacts_title" msgid="2714029230160136647">"Избери контакти за увоз"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Исклучете го авионскиот режим за да ги увезете контактите од SIM-картичката."</string>
     <string name="enable_pin" msgid="967674051730845376">"Овозможи/оневозможи PIN на SIM"</string>
-    <string name="change_pin" msgid="3657869530942905790">"Промени PIN на SIM"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"PIN на SIM:"</string>
+    <string name="change_pin" msgid="3657869530942905790">"Променете PIN за SIM-картичката"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"PIN за SIM-картичката:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"Стар PIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"Нов PIN"</string>
     <string name="confirmPinLabel" msgid="7783531218662473778">"Потврди нов PIN"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 76dee50..1201657 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -296,7 +296,7 @@
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> चे कॉल आणि डेटा सेवा <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> वापरताना ब्लॉक केले जाऊ शकतात."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> चे कॉल आणि डेटा सेवा दुसरे सिम वापरताना ब्लॉक केले जाऊ शकतात."</string>
     <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"आढळलेली आणि काढून टाकलेली कालबाह्य झालेली SIP खाती"</string>
-    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android प्लॅटफॉर्मवर आता SIP कॉलिंगला सपोर्ट नाही.\nतुमची अस्तित्वात असलेली SIP खाती <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> काढून टाकली आहेत.\nकृपया तुमच्या कॉलिंगसंबंधित डीफॉल्ट खात्याचे सेटिंग कंफर्म करा."</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android प्लॅटफॉर्मवर आता SIP कॉलिंगला सपोर्ट नाही.\nतुमची अस्तित्वात असलेली SIP खाती <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> काढून टाकली आहेत.\nकृपया तुमच्या कॉलिंगसंबंधित डीफॉल्ट खात्याचे सेटिंग कन्फर्म करा."</string>
     <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"सेटिंग्जवर जा"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"अ‍ॅप डेटा वापर"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> दरम्यान <xliff:g id="ID_1">%1$s</xliff:g> मोबाइल डेटा वापरला गेला"</string>
@@ -876,7 +876,7 @@
     <string name="radio_info_dds" msgid="1122593144425697126">"डीफॉल्ट डेटा सिम SubId:"</string>
     <string name="radio_info_dl_kbps" msgid="2382922659525318726">"DL बँडविड्थ (kbps):"</string>
     <string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL बँडविड्थ (kbps):"</string>
-    <string name="radio_info_phy_chan_config" msgid="608045501232211303">"प्रत्यक्ष चॅनलची कॉंफिगरेशन:"</string>
+    <string name="radio_info_phy_chan_config" msgid="608045501232211303">"प्रत्यक्ष चॅनलची कॉन्फिगरेशन:"</string>
     <string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"सेल माहिती रिफ्रेश रेट:"</string>
     <string name="radio_info_cellinfo_label" msgid="8199062974670377659">"सर्व सेल परिमाण माहिती:"</string>
     <string name="radio_info_gprs_service_label" msgid="6819204246355412952">"डेटा सर्व्हिस:"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 8576e1f..0f4321a 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -674,8 +674,8 @@
     <string name="sim_description_emergency_calls" msgid="5146872803938897296">"အရေးပေါ် ခေါ်ဆိုမှုသာလျှင်"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"SIM ကတ်၊ အပေါက်: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
     <string name="accessibility_settings_activity_title" msgid="7883415189273700298">"အများသုံးနိုင်မှု"</string>
-    <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"အောက်ပါမှ Wi-Fi ခေါ်ခြင်း"</string>
-    <string name="status_hint_label_wifi_call" msgid="942993035689809853">"ဝိုင်ဖိုင်ခေါ်ဆိုမှု"</string>
+    <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"Wi-Fi ဖုန်းခေါ်နေသည်"</string>
+    <string name="status_hint_label_wifi_call" msgid="942993035689809853">"Wi-Fi ခေါ်ဆိုမှု"</string>
     <string name="message_decode_error" msgid="1061856591500290887">"စာကို ကုဒ်ဖွင့်နေစဉ် အမှားရှိခဲ့သည်။"</string>
     <string name="callFailed_cdma_activation" msgid="5392057031552253550">"SIM ကဒ်သည် သင့် ဖုန်းဝန်ဆောင်မှုအား အသက်သွင်းခဲ့ပြီး သင့်ဖုန်း၏ ကွန်ယက်ပြင်ပဒေတာသုံးနိုင်စွမ်းအား ပြင်ဆင်မွမ်းမံပြီးဖြစ်၏။"</string>
     <string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"လက်ရှိခေါ်ဆိုမှုများ အလွန်များနေပါသည်။ ခေါ်ဆိုမှုအသစ်တစ်ခု မပြုလုပ်ခင် လက်ရှိဖုန်းခေါ်ဆိုမှုများကို အဆုံးသတ် (သို့) ပေါင်း လိုက်ပါ။"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 1b399ab..4428df0 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -71,7 +71,7 @@
     <string name="phone_accounts_settings_header" msgid="6296501692964706536">"सेटिङहरू"</string>
     <string name="phone_accounts_choose_accounts" msgid="4748805293314824974">"खाता छान्नुहोस्"</string>
     <string name="phone_accounts_selection_header" msgid="2945830843104108440">"फोन खाता"</string>
-    <string name="phone_accounts_add_sip_account" msgid="1437634802033309305">"SIP खाता थप्नुहोस्"</string>
+    <string name="phone_accounts_add_sip_account" msgid="1437634802033309305">"SIP खाता हाल्नुहोस्"</string>
     <string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"खाता सेटिङ कन्फिगर गर्नुहोस्"</string>
     <string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"सबै कलिङ खाताहरू"</string>
     <string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"कुन खाताहरूले कल गर्न सक्छन् चयन गर्नुहोस्"</string>
@@ -298,7 +298,7 @@
     <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"चल्तीबाट हटाइएका SIP खाताहरू भेट्टाइयो र हटाइयो"</string>
     <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"अबदेखि Android प्लेटफर्ममा SIP कल गर्न मिल्दैन।\nतपाईंका हालका SIP खाताहरू <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> हटाइएका छन्।\nकृपया आफ्नो डिफल्ट कल गर्ने खाताको सेटिङ पुष्टि गर्नुहोस्।"</string>
     <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"सेटिङमा जानुहोस्"</string>
-    <string name="data_usage_title" msgid="8438592133893837464">"अनुप्रयोगले गरेको डेटाको प्रयोग"</string>
+    <string name="data_usage_title" msgid="8438592133893837464">"एपले गरेको डेटाको प्रयोग"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> सम्म <xliff:g id="ID_1">%1$s</xliff:g> मोबाइल डेटा प्रयोग भयो"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"उन्नत"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"सेवा प्रदायक"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index da89764..f373f5b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -485,7 +485,7 @@
     <string name="simContacts_title" msgid="2714029230160136647">"Contacten selecteren om te importeren"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Zet de vliegtuigmodus uit om contacten van de simkaart te importeren."</string>
     <string name="enable_pin" msgid="967674051730845376">"SIM pincode aan-/uitzetten"</string>
-    <string name="change_pin" msgid="3657869530942905790">"Pincode simkaart wijzigen"</string>
+    <string name="change_pin" msgid="3657869530942905790">"Pincode van simkaart wijzigen"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"Pincode simkaart:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"Oude pincode"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"Nieuwe pincode"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 5eea570..fd8e728 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -19,7 +19,7 @@
     <string name="phoneAppLabel" product="tablet" msgid="1916019789885839910">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
     <string name="phoneAppLabel" product="default" msgid="130465039375347763">"ଫୋନ୍ ସେବା"</string>
     <string name="emergencyDialerIconLabel" msgid="8668005772339436680">"ଜରୁରୀକାଳିନ ଡାଏଲର୍"</string>
-    <string name="phoneIconLabel" msgid="3015941229249651419">"ଫୋନ୍"</string>
+    <string name="phoneIconLabel" msgid="3015941229249651419">"ଫୋନ"</string>
     <string name="fdnListLabel" msgid="4119121875004244097">"FDN ତାଲିକା"</string>
     <string name="unknown" msgid="8279698889921830815">"ଅଜଣା"</string>
     <string name="private_num" msgid="4487990167889159992">"ବ୍ୟକ୍ତିଗତ ନମ୍ବର୍"</string>
@@ -674,8 +674,8 @@
     <string name="sim_description_emergency_calls" msgid="5146872803938897296">"କେବଳ ଜରୁରିକାଳୀନ କଲ୍ ପାଇଁ"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"SIM କାର୍ଡ, ସ୍ଲଟ୍: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
     <string name="accessibility_settings_activity_title" msgid="7883415189273700298">"ଆକ୍ସେସିବିଲିଟୀ"</string>
-    <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"ଠାରୁ ୱାଇ-ଫାଇ କଲ୍ କରନ୍ତୁ"</string>
-    <string name="status_hint_label_wifi_call" msgid="942993035689809853">"ୱାଇ-ଫାଇ କଲ୍"</string>
+    <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"ୱାଇ-ଫାଇ କଲ କରିଛନ୍ତି"</string>
+    <string name="status_hint_label_wifi_call" msgid="942993035689809853">"ୱାଇ-ଫାଇ କଲ"</string>
     <string name="message_decode_error" msgid="1061856591500290887">"ମେସେଜ୍‌କୁ ଡିକୋଡ୍ କରିବା ବେଳେ ଗୋଟିଏ ତ୍ରୁଟି ଦେଖାଦେଲା।"</string>
     <string name="callFailed_cdma_activation" msgid="5392057031552253550">"ଗୋଟିଏ SIM କାର୍ଡ ଆପଣଙ୍କର ସେବାକୁ କାର୍ଯ୍ୟକ୍ଷମ କରିଛି ଏବଂ ଆପଣଙ୍କ ଫୋନ୍‌ର ରୋମିଙ୍ଗ କ୍ଷମତାକୁ ଅପଡେଟ୍ କରିଛି।"</string>
     <string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"ଏଠାରେ ଅନେକ ସକ୍ରିୟ କଲ୍ ଅଛି। ଗୋଟିଏ ନୂଆ କଲ୍‌କୁ ସ୍ଥାପନ କରିବା ପୂର୍ବରୁ ଦୟାକରି ବିଦ୍ୟମାନ ଥିବା କଲ୍‌କୁ ସମାପ୍ତ କିମ୍ବା ମର୍ଜ କରନ୍ତୁ।"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 1f86b13..975cdc7 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -168,7 +168,7 @@
     <string name="vm_change_pin_progress_message" msgid="626015184502739044">"Počakajte."</string>
     <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"Nova koda PIN je prekratka."</string>
     <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"Nova koda PIN je predolga."</string>
-    <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"Nova koda PIN je prešibka. Zapleteno geslo ne sme vsebovati zaporednih ali ponavljajočih se števk."</string>
+    <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"Nova koda PIN je prešibka. Močno geslo ne sme vsebovati zaporednih ali ponavljajočih se števk."</string>
     <string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"Stara koda PIN se ne ujema."</string>
     <string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"Nova koda PIN vsebuje neveljavne znake."</string>
     <string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"Ni mogoče spremeniti kode PIN"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 19ce90f..1906ed7 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -881,7 +881,7 @@
     <string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Alla information om mastmätning:"</string>
     <string name="radio_info_gprs_service_label" msgid="6819204246355412952">"Datatjänst:"</string>
     <string name="radio_info_roaming_label" msgid="6636932886446857120">"Roaming:"</string>
-    <string name="radio_info_imei_label" msgid="8947899706930120368">"IMEI-kod:"</string>
+    <string name="radio_info_imei_label" msgid="8947899706930120368">"IMEI-nummer:"</string>
     <string name="radio_info_call_redirect_label" msgid="4526480903023362276">"Vidarekoppling av samtal:"</string>
     <string name="radio_info_ppp_resets_label" msgid="9131901102339077661">"Antal återställningar av köpkraftsparitet sedan start:"</string>
     <string name="radio_info_current_network_label" msgid="3052098695239642450">"Aktuellt nätverk:"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2cbcafa..9e1f6fe 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -45,8 +45,8 @@
     <string name="pause_prompt_no" msgid="2145264674774138579">"Hapana"</string>
     <string name="wild_prompt_str" msgid="5858910969703305375">"Badilisha kibambo egemezi na"</string>
     <string name="no_vm_number" msgid="6623853880546176930">"Nambari ya sauti inayokosekana"</string>
-    <string name="no_vm_number_msg" msgid="5165161462411372504">"Hakuna nambari ya ujumbe wa sauti iliyohifadhiwa katika SIM kadi."</string>
-    <string name="add_vm_number_str" msgid="7368168964435881637">"Ongeza nambari"</string>
+    <string name="no_vm_number_msg" msgid="5165161462411372504">"Hakuna namba ya ujumbe wa sauti iliyohifadhiwa katika SIM kadi."</string>
+    <string name="add_vm_number_str" msgid="7368168964435881637">"Ongeza namba"</string>
     <string name="voice_number_setting_primary_user_only" msgid="3394706575741912843">"Mipangilio ya ujumbe wa sauti inaweza kubadilishwa na Mtumiaji wa Msingi Pekee."</string>
     <string name="puk_unlocked" msgid="4627340655215746511">"Kadi yako ya simu imefunguliwa. Simu yangu inafungua…."</string>
     <string name="label_ndp" msgid="7617392683877410341">"PIN ya kufungua mtandao wa SIM"</string>
@@ -82,7 +82,7 @@
     <string name="voicemail_abbreviated" msgid="7746778673131551185">"VM:"</string>
     <string name="make_and_receive_calls" msgid="4868913166494621109">"Kupiga na kupokea simu"</string>
     <string name="smart_forwarding_settings_menu" msgid="8850429887958938540">"Usambazaji Mahiri"</string>
-    <string name="smart_forwarding_settings_menu_summary" msgid="5096947726032885325">"Ikiwa nambari moja haiwezi kufikiwa, sambaza simu kwa nambari nyingine kila wakati"</string>
+    <string name="smart_forwarding_settings_menu_summary" msgid="5096947726032885325">"Ikiwa namba moja haiwezi kufikiwa, sambaza simu kwa namba nyingine kila wakati"</string>
     <string name="voicemail_notifications_preference_title" msgid="7829238858063382977">"Arifa"</string>
     <string name="cell_broadcast_settings" msgid="8135324242541809924">"Matangazo ya dharura"</string>
     <string name="call_settings" msgid="3677282690157603818">"Mipangilio ya simu"</string>
@@ -96,7 +96,7 @@
     <string name="sum_loading_settings" msgid="434063780286688775">"Mipangilio inapakia..."</string>
     <string name="sum_hide_caller_id" msgid="131100328602371933">"Nambari imefichwa kwa simu unayopiga"</string>
     <string name="sum_show_caller_id" msgid="3571854755324664591">"Namba inaonekana kwa simu zinazopigwa"</string>
-    <string name="sum_default_caller_id" msgid="1767070797135682959">"Tumia mipangilio ya mtoa huduma chaguomsingi kuonyesha nambari kwa simu unazopiga"</string>
+    <string name="sum_default_caller_id" msgid="1767070797135682959">"Tumia mipangilio ya mtoa huduma chaguomsingi kuonyesha namba kwa simu unazopiga"</string>
     <string name="labelCW" msgid="8449327023861428622">"Simu inayosubiri kupokewa"</string>
     <string name="sum_cw_enabled" msgid="3977308526187139996">"Wakati ninapokea simu, niarifu kuhusu simu zingine zinazoingia"</string>
     <string name="sum_cw_disabled" msgid="3658094589461768637">"Wakati ninapokea simu, niarifu kuhusu simu zingine zinazoingia"</string>
@@ -104,7 +104,7 @@
     <string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"Mipangilio ya kusambaza simu (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
     <string name="labelCF" msgid="3578719437928476078">"Kusambaza simu"</string>
     <string name="labelCFU" msgid="8870170873036279706">"Sambaza kila wakati"</string>
-    <string name="messageCFU" msgid="1361806450979589744">"Kila wakati tumia nambari hii"</string>
+    <string name="messageCFU" msgid="1361806450979589744">"Kila wakati tumia namba hii"</string>
     <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"Inasambaza  simu zote"</string>
     <string name="sum_cfu_enabled" msgid="5806923046528144526">"Inasambaza simu zote kwa <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
     <string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"Nambari haipatikani"</string>
@@ -145,7 +145,7 @@
     <string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"Imebadilisha ombi la SS kuwa ombi la USSD"</string>
     <string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"Imebadilishwa kuwa ombi jipya la SS"</string>
     <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"Imebadilisha ombi la SS kuwa simu ya video"</string>
-    <string name="fdn_check_failure" msgid="1833769746374185247">"Mipangilio ya programu ya simu yako ya nambari za simu zilizobainishwa pekee imewashwa. Kutokana na hayo, baadhi ya vipengele vya kupiga simu havifanyi kazi."</string>
+    <string name="fdn_check_failure" msgid="1833769746374185247">"Mipangilio ya programu ya simu yako ya namba za simu zilizobainishwa pekee imewashwa. Kutokana na hayo, baadhi ya vipengele vya kupiga simu havifanyi kazi."</string>
     <string name="radio_off_error" msgid="8321564164914232181">"Washa redio kabla ya kutazama mipangilio hii."</string>
     <string name="close_dialog" msgid="1074977476136119408">"Sawa"</string>
     <string name="enable" msgid="2636552299455477603">"Washa"</string>
@@ -153,13 +153,13 @@
     <string name="change_num" msgid="6982164494063109334">"Sasisha"</string>
   <string-array name="clir_display_values">
     <item msgid="8477364191403806960">"Chaguomsingi la mtandao"</item>
-    <item msgid="6813323051965618926">"Ficha nambari"</item>
-    <item msgid="9150034130629852635">"Onyesha nambari"</item>
+    <item msgid="6813323051965618926">"Ficha namba"</item>
+    <item msgid="9150034130629852635">"Onyesha namba"</item>
   </string-array>
     <string name="vm_changed" msgid="4739599044379692505">"Nambari ya ujumbe wa sauti haijabadilishwa"</string>
     <string name="vm_change_failed" msgid="7877733929455763566">"Haikuweza kubadilisha namba ya ujumbe wa sauti.\nWasiliana na mtoa huduma wako shida hii ikiendelea."</string>
     <string name="fw_change_failed" msgid="9179241823460192148">"Haikuweza kubadilisha namba ya kusambaza.\nWasiliana na mtoa huduma wako shida hii ikiendelea."</string>
-    <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"Haikuweza kuepua na kuhifadhi mipangilio ya nambari ya  usambazaji. \n Hata hivyo swichi kwa mtoahuduma  mpya?"</string>
+    <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"Haikuweza kuepua na kuhifadhi mipangilio ya namba ya  usambazaji. \n Hata hivyo swichi kwa mtoahuduma  mpya?"</string>
     <string name="no_change" msgid="3737264882821031892">"Hakuna mabadiliko yaliyofanywa"</string>
     <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"Chagua huduma ya barua ya sauti"</string>
     <string name="voicemail_default" msgid="6427575113775462077">"Mtoa huduma wako"</string>
@@ -449,9 +449,9 @@
     <string name="change_pin2" msgid="3110844547237754871">"Badilisha PIN2"</string>
     <string name="enable_fdn_ok" msgid="5080925177369329827">"Lemaza FDN"</string>
     <string name="disable_fdn_ok" msgid="3745475926874838676">"Washa FDN"</string>
-    <string name="sum_fdn" msgid="6152246141642323582">"Dhibiti nambari za simu zilizobainishwa"</string>
+    <string name="sum_fdn" msgid="6152246141642323582">"Dhibiti namba za simu zilizobainishwa"</string>
     <string name="sum_fdn_change_pin" msgid="3510994280557335727">"Badilisha nenosiri la kufikia FDN"</string>
-    <string name="sum_fdn_manage_list" msgid="3311397063233992907">"Dhibiti orodha ya nambari za simu"</string>
+    <string name="sum_fdn_manage_list" msgid="3311397063233992907">"Dhibiti orodha ya namba za simu"</string>
     <string name="voice_privacy" msgid="7346935172372181951">"Faragha ya sauti"</string>
     <string name="voice_privacy_summary" msgid="3556460926168473346">"Wezesha gumzo ya faragha iliyoboreshwa"</string>
     <string name="tty_mode_option_title" msgid="3843817710032641703">"Hali ya TTY"</string>
@@ -462,22 +462,22 @@
     <string name="menu_add" msgid="5616487894975773141">"Ongeza anwani"</string>
     <string name="menu_edit" msgid="3593856941552460706">"Hariri anwani"</string>
     <string name="menu_delete" msgid="6326861853830546488">"Futa anwani"</string>
-    <string name="menu_dial" msgid="4178537318419450012">"Piga nambari ya unayewasiliana naye"</string>
+    <string name="menu_dial" msgid="4178537318419450012">"Piga namba ya unayewasiliana naye"</string>
     <string name="get_pin2" msgid="4221654606863196332">"Chapa PIN2"</string>
     <string name="name" msgid="1347432469852527784">"Jina"</string>
     <string name="number" msgid="1564053487748491000">"Nambari"</string>
     <string name="save" msgid="983805790346099749">"Hifadhi"</string>
-    <string name="add_fdn_contact" msgid="1169713422306640887">"Ongeza nambari za upigaji simu uliobanwa"</string>
-    <string name="adding_fdn_contact" msgid="3112531600824361259">"Inaongeza nambari ya upigaji uliobanwa..."</string>
+    <string name="add_fdn_contact" msgid="1169713422306640887">"Ongeza namba za upigaji simu uliobanwa"</string>
+    <string name="adding_fdn_contact" msgid="3112531600824361259">"Inaongeza namba ya upigaji uliobanwa..."</string>
     <string name="fdn_contact_added" msgid="2840016151693394596">"Nambari ya upigaji simu uliobanwa imeongezwa."</string>
-    <string name="edit_fdn_contact" msgid="6030829994819587408">"Hariri nambari za kudumu"</string>
-    <string name="updating_fdn_contact" msgid="6989341376868227150">"Inasasisha nambari ya upigaji simu uliobanwa..."</string>
+    <string name="edit_fdn_contact" msgid="6030829994819587408">"Hariri namba za kudumu"</string>
+    <string name="updating_fdn_contact" msgid="6989341376868227150">"Inasasisha namba ya upigaji simu uliobanwa..."</string>
     <string name="fdn_contact_updated" msgid="6876330243323118937">"Nambari ya upigaji simu uliobanwa imesasishwa."</string>
     <string name="delete_fdn_contact" msgid="7027405651994507077">"Futa namba ya upigaji simu ya kudumu"</string>
-    <string name="deleting_fdn_contact" msgid="6872320570844460428">"Inafuta nambari ya upigaji simu uliobanwa..."</string>
+    <string name="deleting_fdn_contact" msgid="6872320570844460428">"Inafuta namba ya upigaji simu uliobanwa..."</string>
     <string name="fdn_contact_deleted" msgid="1680714996763848838">"Nambari ya upigaji simu uliobanwa imefutwa"</string>
     <string name="pin2_invalid" msgid="2313954262684494442">"FDN haikusasishwa kwa sababu uliweka PIN isiyo sahihi."</string>
-    <string name="fdn_invalid_number" msgid="9067189814657840439">"FDN haijasasishwa kwa sababu nambari inazidi tarakimu <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g>."</string>
+    <string name="fdn_invalid_number" msgid="9067189814657840439">"FDN haijasasishwa kwa sababu namba inazidi tarakimu <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g>."</string>
     <string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"FDN haikusasishwa. PIN2 haikuwa sahihi, au namba ya simu ilikataliwa."</string>
     <string name="fdn_failed" msgid="216592346853420250">"Utendakazi wa FDN ulishindwa."</string>
     <string name="simContacts_emptyLoading" msgid="4989040293858675483">"Inasoma kutoka kwa SIM kadi…"</string>
@@ -492,7 +492,7 @@
     <string name="confirmPinLabel" msgid="7783531218662473778">"Thibitisha PIN mpya"</string>
     <string name="badPin" msgid="4549286285015892321">"PIN ya zamani uliyochapa sio sahihi. Jaribu tena."</string>
     <string name="mismatchPin" msgid="1467254768290323845">"PIN ulizochapa hazilingani. Jaribu tena."</string>
-    <string name="invalidPin" msgid="7363723429414001979">"Chapisha nenosiri lenye nambari kati ya 4 na 8."</string>
+    <string name="invalidPin" msgid="7363723429414001979">"Chapisha nenosiri lenye namba kati ya 4 na 8."</string>
     <string name="disable_sim_pin" msgid="3112303905548613752">"Ondoa PIN ya SIM"</string>
     <string name="enable_sim_pin" msgid="445461050748318980">"Weka PIN ya SIM"</string>
     <string name="enable_in_progress" msgid="4135305985717272592">"Inaweka PIN…"</string>
@@ -508,8 +508,8 @@
     <string name="badPuk2" msgid="6438182906645832235">"PUK2 si sahihi. Jaribu tena."</string>
     <string name="badPin2" msgid="2760917538643074635">"PIN2 ya zamani si sahihi. Jaribu tena."</string>
     <string name="mismatchPin2" msgid="4952718725266700631">"PIN2 hazilingani. Jaribu tena."</string>
-    <string name="invalidPin2" msgid="6467957903056379343">"Weka PIN2 iliyo na kati ya nambari 4 hadi 8."</string>
-    <string name="invalidPuk2" msgid="713729511903849544">"Weka PUK2 yenye nambari 8."</string>
+    <string name="invalidPin2" msgid="6467957903056379343">"Weka PIN2 iliyo na kati ya namba 4 hadi 8."</string>
+    <string name="invalidPuk2" msgid="713729511903849544">"Weka PUK2 yenye namba 8."</string>
     <string name="pin2_changed" msgid="5710551850481287821">"PIN2 imesasishwa"</string>
     <string name="label_puk2_code" msgid="2852217004288085562">"Weka msimbo wa PUK2"</string>
     <string name="fdn_enable_puk2_requested" msgid="5793652792131588041">"Nenosiri si sahihi. PIN2 sasa Imezuiwa. Ili ujaribu tena, badilisha PIN 2."</string>
@@ -543,7 +543,7 @@
     <string name="incall_error_emergency_only" msgid="8786127461027964653">"Haijasajiliwa kwa mitandao"</string>
     <string name="incall_error_out_of_service" msgid="1927265196942672791">"Mtandao wa simu haupatikani."</string>
     <string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"Mtandao wa simu za mkononi haupatikani. Unganisha kwenye mtandao pasiwaya ili upige simu."</string>
-    <string name="incall_error_no_phone_number_supplied" msgid="8680831089508851894">"Ili upige simu, weka nambari sahihi."</string>
+    <string name="incall_error_no_phone_number_supplied" msgid="8680831089508851894">"Ili upige simu, weka namba sahihi."</string>
     <string name="incall_error_call_failed" msgid="393508653582682539">"Imeshindwa kupiga simu."</string>
     <string name="incall_error_cannot_add_call" msgid="5425764862628655443">"Simu haiwezi kuongezwa kwa sasa. Unaweza kujaribu kuwasiliana kwa kutuma ujumbe."</string>
     <string name="incall_error_supp_service_unknown" msgid="8751177117194592623">"Huduma haiwezi kutumika."</string>
@@ -568,8 +568,8 @@
     <string name="emergency_enable_radio_dialog_message" msgid="1695305158151408629">"Inawasha redio..."</string>
     <string name="emergency_enable_radio_dialog_retry" msgid="4329131876852608587">"Hakuna huduma. Inajaribu tena..."</string>
     <string name="radio_off_during_emergency_call" msgid="8011154134040481609">"Huwezi kuingia katika hali ya ndegeni huku simu ya dharura inaendelea."</string>
-    <string name="dial_emergency_error" msgid="825822413209026039">"Haiwezi kupiga simu. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> si nambari ya dharura."</string>
-    <string name="dial_emergency_empty_error" msgid="2785803395047793634">"Haiwezi kupiga simu. Piga nambari ya dharura."</string>
+    <string name="dial_emergency_error" msgid="825822413209026039">"Haiwezi kupiga simu. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> si namba ya dharura."</string>
+    <string name="dial_emergency_empty_error" msgid="2785803395047793634">"Haiwezi kupiga simu. Piga namba ya dharura."</string>
     <string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"Upigaji simu ya dharura haupatikani"</string>
     <string name="pin_puk_system_user_only" msgid="1045147220686867922">"Mmiliki wa kifaa pekee ndiye anaweza kuweka misimbo ya PIN/PUK."</string>
     <string name="police_type_description" msgid="2819533883972081757">"Polisi"</string>
@@ -692,7 +692,7 @@
     <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"Thibitisha PIN yako ya awali"</string>
     <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"Weka PIN yako ya ujumbe wa sauti ili uendelee."</string>
     <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Weka PIN mpya"</string>
-    <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN lazima iwe na nambari <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g>."</string>
+    <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN lazima iwe na namba <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g>."</string>
     <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"Thibitisha PIN yako"</string>
     <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN hazilingani"</string>
     <string name="change_pin_succeeded" msgid="2504705600693014403">"PIN ya ujumbe wa sauti imesasishwa"</string>
@@ -716,55 +716,55 @@
     <string name="clh_callFailed_simError_txt" msgid="5128538525762326413">"Imeshindwa kufikia SIM kadi"</string>
     <string name="clh_incall_error_out_of_service_txt" msgid="2736010617446749869">"Mtandao wa simu za mkononi haupatikani"</string>
     <string name="clh_callFailed_satelliteEnabled_txt" msgid="1675517238240377396">"Hali ya sateliti imewashwa"</string>
-    <string name="clh_callFailed_unassigned_number_txt" msgid="141967660286695682">"Nambari ya simu unayojaribu kupiga ina hitilafu. Msimbo wa hitilafu nambari 1."</string>
-    <string name="clh_callFailed_no_route_to_destination_txt" msgid="4805015149822352308">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 3."</string>
-    <string name="clh_callFailed_channel_unacceptable_txt" msgid="4062754579408613021">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 6."</string>
-    <string name="clh_callFailed_operator_determined_barring_txt" msgid="4202077821465974286">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 8."</string>
-    <string name="clh_callFailed_normal_call_clearing_txt" msgid="5677987959062976462">"Imeshidwa kupiga simu. Msimbo wa hitilafu nambari 16."</string>
+    <string name="clh_callFailed_unassigned_number_txt" msgid="141967660286695682">"Nambari ya simu unayojaribu kupiga ina hitilafu. Msimbo wa hitilafu namba 1."</string>
+    <string name="clh_callFailed_no_route_to_destination_txt" msgid="4805015149822352308">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 3."</string>
+    <string name="clh_callFailed_channel_unacceptable_txt" msgid="4062754579408613021">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 6."</string>
+    <string name="clh_callFailed_operator_determined_barring_txt" msgid="4202077821465974286">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 8."</string>
+    <string name="clh_callFailed_normal_call_clearing_txt" msgid="5677987959062976462">"Imeshidwa kupiga simu. Msimbo wa hitilafu namba 16."</string>
     <string name="clh_callFailed_user_busy_txt" msgid="8886432858568086854">"Mtumiaji ana shughuli"</string>
     <string name="clh_callFailed_no_user_responding_txt" msgid="341100226919865128">"Mtumiaji hajibu"</string>
-    <string name="clh_callFailed_user_alerting_txt" msgid="896082976264427969">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 19."</string>
+    <string name="clh_callFailed_user_alerting_txt" msgid="896082976264427969">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 19."</string>
     <string name="clh_callFailed_call_rejected_txt" msgid="3439435671153341709">"Simu imekataliwa"</string>
     <string name="clh_callFailed_number_changed_txt" msgid="2868476949771441667">"Nambari imebadilishwa"</string>
-    <string name="clh_callFailed_pre_emption_txt" msgid="8887998866342162724">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 25."</string>
-    <string name="clh_callFailed_non_selected_user_clearing_txt" msgid="4804529874810197550">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 26."</string>
-    <string name="clh_callFailed_destination_out_of_order_txt" msgid="1130697076352728824">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 27."</string>
-    <string name="clh_callFailed_invalid_number_format_txt" msgid="3171016382987224989">"Muundo usio sahihi wa nambari (nambari haijakamilika)"</string>
-    <string name="clh_callFailed_facility_rejected_txt" msgid="1054386430010898993">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 29."</string>
-    <string name="clh_callFailed_response_to_STATUS_ENQUIRY_txt" msgid="2763172551412307536">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 30."</string>
-    <string name="clh_callFailed_normal_unspecified_txt" msgid="978119938935737419">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 31."</string>
-    <string name="clh_callFailed_no_circuit_available_txt" msgid="1519684050419134605">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 34."</string>
-    <string name="clh_callFailed_network_out_of_order_txt" msgid="8689826504394592289">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 38."</string>
-    <string name="clh_callFailed_temporary_failure_txt" msgid="5065091554509067874">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 41."</string>
-    <string name="clh_callFailed_switching_equipment_congestion_txt" msgid="8681599376741988769">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 42."</string>
-    <string name="clh_callFailed_access_information_discarded_txt" msgid="2476199425130545428">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 43."</string>
-    <string name="clh_callFailed_requested_circuit_txt" msgid="7497497808928490219">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 44."</string>
-    <string name="clh_callFailed_resources_unavailable_unspecified_txt" msgid="144010529672928445">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 47."</string>
-    <string name="clh_callFailed_quality_of_service_unavailable_txt" msgid="4650329342288289290">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 49."</string>
-    <string name="clh_callFailed_requested_facility_not_subscribed_txt" msgid="9107977008516882170">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 50."</string>
-    <string name="clh_callFailed_incoming_calls_barred_within_the_CUG_txt" msgid="501037491908315591">"Imeshidwa kupiga simu. Msimbo wa hitilafu nambari 55."</string>
-    <string name="clh_callFailed_bearer_capability_not_authorized_txt" msgid="4344366517528362620">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 57."</string>
-    <string name="clh_callFailed_bearer_capability_not_presently_available_txt" msgid="1436957294571545381">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 58."</string>
-    <string name="clh_callFailed_service_or_option_not_available_unspecified_txt" msgid="2149878874722675428">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 63."</string>
-    <string name="clh_callFailed_bearer_service_not_implemented_txt" msgid="1074983013965612410">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 65."</string>
-    <string name="clh_callFailed_ACM_equal_to_or_greater_than_ACMmax_txt" msgid="7889034195264205333">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 68."</string>
-    <string name="clh_callFailed_requested_facility_not_implemented_txt" msgid="7996646684699167978">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 69."</string>
-    <string name="clh_callFailed_only_restricted_digital_information_bearer_capability_is_available_txt" msgid="2358958110447385682">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 70."</string>
-    <string name="clh_callFailed_service_or_option_not_implemented_unspecified_txt" msgid="3046428509531159481">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 79."</string>
-    <string name="clh_callFailed_invalid_transaction_identifier_value_txt" msgid="1727401871777396619">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 81."</string>
-    <string name="clh_callFailed_user_not_member_of_CUG_txt" msgid="442282135105229307">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 87."</string>
-    <string name="clh_callFailed_incompatible_destination_txt" msgid="5900394706344969020">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 88."</string>
-    <string name="clh_callFailed_invalid_transit_network_selection_txt" msgid="6274621838349037741">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 91."</string>
-    <string name="clh_callFailed_semantically_incorrect_message_txt" msgid="7000705190197981937">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 95."</string>
-    <string name="clh_callFailed_invalid_mandatory_information_txt" msgid="3609204152671052123">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 96."</string>
-    <string name="clh_callFailed_message_type_non_existent_or_not_implemented_txt" msgid="1552110431052032814">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 97."</string>
-    <string name="clh_callFailed_message_type_not_compatible_with_protocol_state_txt" msgid="7717048934226300032">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 98."</string>
-    <string name="clh_callFailed_information_element_non_existent_or_not_implemented_txt" msgid="8931396541061612169">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 99."</string>
-    <string name="clh_callFailed_conditional_IE_error_txt" msgid="4630685477888727741">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 100."</string>
-    <string name="clh_callFailed_message_not_compatible_with_protocol_state_txt" msgid="3014075977395922947">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 101."</string>
-    <string name="clh_callFailed_recovery_on_timer_expiry_txt" msgid="5637581978978731672">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 102."</string>
-    <string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 111."</string>
-    <string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"Imeshindwa kupiga simu. Msimbo wa hitilafu nambari 127."</string>
+    <string name="clh_callFailed_pre_emption_txt" msgid="8887998866342162724">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 25."</string>
+    <string name="clh_callFailed_non_selected_user_clearing_txt" msgid="4804529874810197550">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 26."</string>
+    <string name="clh_callFailed_destination_out_of_order_txt" msgid="1130697076352728824">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 27."</string>
+    <string name="clh_callFailed_invalid_number_format_txt" msgid="3171016382987224989">"Muundo usio sahihi wa namba (namba haijakamilika)"</string>
+    <string name="clh_callFailed_facility_rejected_txt" msgid="1054386430010898993">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 29."</string>
+    <string name="clh_callFailed_response_to_STATUS_ENQUIRY_txt" msgid="2763172551412307536">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 30."</string>
+    <string name="clh_callFailed_normal_unspecified_txt" msgid="978119938935737419">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 31."</string>
+    <string name="clh_callFailed_no_circuit_available_txt" msgid="1519684050419134605">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 34."</string>
+    <string name="clh_callFailed_network_out_of_order_txt" msgid="8689826504394592289">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 38."</string>
+    <string name="clh_callFailed_temporary_failure_txt" msgid="5065091554509067874">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 41."</string>
+    <string name="clh_callFailed_switching_equipment_congestion_txt" msgid="8681599376741988769">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 42."</string>
+    <string name="clh_callFailed_access_information_discarded_txt" msgid="2476199425130545428">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 43."</string>
+    <string name="clh_callFailed_requested_circuit_txt" msgid="7497497808928490219">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 44."</string>
+    <string name="clh_callFailed_resources_unavailable_unspecified_txt" msgid="144010529672928445">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 47."</string>
+    <string name="clh_callFailed_quality_of_service_unavailable_txt" msgid="4650329342288289290">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 49."</string>
+    <string name="clh_callFailed_requested_facility_not_subscribed_txt" msgid="9107977008516882170">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 50."</string>
+    <string name="clh_callFailed_incoming_calls_barred_within_the_CUG_txt" msgid="501037491908315591">"Imeshidwa kupiga simu. Msimbo wa hitilafu namba 55."</string>
+    <string name="clh_callFailed_bearer_capability_not_authorized_txt" msgid="4344366517528362620">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 57."</string>
+    <string name="clh_callFailed_bearer_capability_not_presently_available_txt" msgid="1436957294571545381">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 58."</string>
+    <string name="clh_callFailed_service_or_option_not_available_unspecified_txt" msgid="2149878874722675428">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 63."</string>
+    <string name="clh_callFailed_bearer_service_not_implemented_txt" msgid="1074983013965612410">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 65."</string>
+    <string name="clh_callFailed_ACM_equal_to_or_greater_than_ACMmax_txt" msgid="7889034195264205333">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 68."</string>
+    <string name="clh_callFailed_requested_facility_not_implemented_txt" msgid="7996646684699167978">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 69."</string>
+    <string name="clh_callFailed_only_restricted_digital_information_bearer_capability_is_available_txt" msgid="2358958110447385682">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 70."</string>
+    <string name="clh_callFailed_service_or_option_not_implemented_unspecified_txt" msgid="3046428509531159481">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 79."</string>
+    <string name="clh_callFailed_invalid_transaction_identifier_value_txt" msgid="1727401871777396619">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 81."</string>
+    <string name="clh_callFailed_user_not_member_of_CUG_txt" msgid="442282135105229307">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 87."</string>
+    <string name="clh_callFailed_incompatible_destination_txt" msgid="5900394706344969020">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 88."</string>
+    <string name="clh_callFailed_invalid_transit_network_selection_txt" msgid="6274621838349037741">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 91."</string>
+    <string name="clh_callFailed_semantically_incorrect_message_txt" msgid="7000705190197981937">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 95."</string>
+    <string name="clh_callFailed_invalid_mandatory_information_txt" msgid="3609204152671052123">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 96."</string>
+    <string name="clh_callFailed_message_type_non_existent_or_not_implemented_txt" msgid="1552110431052032814">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 97."</string>
+    <string name="clh_callFailed_message_type_not_compatible_with_protocol_state_txt" msgid="7717048934226300032">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 98."</string>
+    <string name="clh_callFailed_information_element_non_existent_or_not_implemented_txt" msgid="8931396541061612169">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 99."</string>
+    <string name="clh_callFailed_conditional_IE_error_txt" msgid="4630685477888727741">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 100."</string>
+    <string name="clh_callFailed_message_not_compatible_with_protocol_state_txt" msgid="3014075977395922947">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 101."</string>
+    <string name="clh_callFailed_recovery_on_timer_expiry_txt" msgid="5637581978978731672">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 102."</string>
+    <string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 111."</string>
+    <string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"Imeshindwa kupiga simu. Msimbo wa hitilafu namba 127."</string>
     <string name="labelCallBarring" msgid="4180377113052853173">"Kuzuia upigaji simu"</string>
     <string name="sum_call_barring_enabled" msgid="5184331188926370824">"Washa"</string>
     <string name="sum_call_barring_disabled" msgid="5699448000600153096">"Zima"</string>
@@ -802,7 +802,7 @@
     <string name="supp_service_notification_call_deflected" msgid="4980942818105909813">"Simu unayopiga imeelekezwa kwingine."</string>
     <string name="supp_service_notification_call_forwarded" msgid="7102930311735433088">"Simu imesambazwa."</string>
     <string name="supp_service_notification_call_waiting" msgid="4577403881609445324">"Simu inasubiri."</string>
-    <string name="supp_service_clir_suppression_rejected" msgid="6105737020194776121">"Ombi la kuzuia nambari limekataliwa."</string>
+    <string name="supp_service_clir_suppression_rejected" msgid="6105737020194776121">"Ombi la kuzuia namba limekataliwa."</string>
     <string name="supp_service_closed_user_group_call" msgid="2811636666505250689">"Simu kwenye kikundi cha watumiaji teule."</string>
     <string name="supp_service_incoming_calls_barred" msgid="2034627421274447674">"Simu zinazoingia zimezuiwa."</string>
     <string name="supp_service_outgoing_calls_barred" msgid="5205725332394087112">"Simu unazopiga zimezuiwa."</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 45417a6..da15813 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -669,7 +669,7 @@
     <string name="enable_video_calling_title" msgid="7246600931634161830">"వీడియో కాలింగ్‌ని ఆన్ చేయండి"</string>
     <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"వీడియో కాలింగ్‌ను ఆన్ చేయడానికి, మీరు నెట్‌వర్క్ సెట్టింగ్‌ల్లో మెరుగుపరిచిన 4G LTE మోడ్‌ను ప్రారంభించాలి."</string>
     <string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"నెట్‌వర్క్ సెట్టింగ్‌లు"</string>
-    <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"మూసివేయి"</string>
+    <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"మూసివేయండి"</string>
     <string name="sim_label_emergency_calls" msgid="9078241989421522310">"అత్యవసర కాల్స్‌"</string>
     <string name="sim_description_emergency_calls" msgid="5146872803938897296">"అత్యవసర కాలింగ్ మాత్రమే"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"SIM కార్డ్, స్లాట్: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index f439c6b..8fabe04 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -561,7 +561,7 @@
     <string name="emergency_information_hint" msgid="9208897544917793012">"急救信息"</string>
     <string name="emergency_information_owner_hint" msgid="6256909888049185316">"所有者"</string>
     <string name="emergency_information_confirm_hint" msgid="5109017615894918914">"再次点按即可查看信息"</string>
-    <string name="emergency_enable_radio_dialog_title" msgid="2667568200755388829">"紧急呼救"</string>
+    <string name="emergency_enable_radio_dialog_title" msgid="2667568200755388829">"紧急呼叫"</string>
     <string name="single_emergency_number_title" msgid="8413371079579067196">"紧急电话号码"</string>
     <string name="numerous_emergency_numbers_title" msgid="8972398932506755510">"紧急电话号码"</string>
     <string name="emergency_call_shortcut_hint" msgid="1290485125107779500">"再次点按即可呼叫 <xliff:g id="EMERGENCY_NUMBER">%s</xliff:g>"</string>
@@ -670,7 +670,7 @@
     <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"要开启视频通话功能,您需要在网络设置中启用增强型 4G LTE 模式。"</string>
     <string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"网络设置"</string>
     <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"关闭"</string>
-    <string name="sim_label_emergency_calls" msgid="9078241989421522310">"紧急呼救"</string>
+    <string name="sim_label_emergency_calls" msgid="9078241989421522310">"紧急呼叫"</string>
     <string name="sim_description_emergency_calls" msgid="5146872803938897296">"只能拨打紧急呼救电话"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"SIM 卡,插槽:<xliff:g id="SLOT_ID">%s</xliff:g>"</string>
     <string name="accessibility_settings_activity_title" msgid="7883415189273700298">"无障碍功能"</string>
@@ -853,7 +853,7 @@
     <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS 注册:<xliff:g id="STATUS">%1$s</xliff:g>\nLTE 语音通话:<xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nWLAN 语音通话:<xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\n视频通话:<xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT 接口:<xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
     <string name="radioInfo_service_in" msgid="45753418231446400">"服务中"</string>
     <string name="radioInfo_service_out" msgid="287972405416142312">"不在服务区"</string>
-    <string name="radioInfo_service_emergency" msgid="4763879891415016848">"只能拨打紧急呼救电话"</string>
+    <string name="radioInfo_service_emergency" msgid="4763879891415016848">"只能拨打紧急呼叫电话"</string>
     <string name="radioInfo_service_off" msgid="3456583511226783064">"关闭无线装置"</string>
     <string name="radioInfo_roaming_in" msgid="3156335577793145965">"漫游"</string>
     <string name="radioInfo_roaming_not" msgid="1904547918725478110">"未使用漫游服务"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index dcfa364..f8aeb5e 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -337,7 +337,17 @@
         <item>de</item>
     </string-array>
 
-    <!-- Flag specifying whether the AOSP domain selection is enabled or
-         the device should fallback to the modem based domain selection architecture. -->
-    <bool name="config_enable_aosp_domain_selection">false</bool>
+    <!-- Array of countries that a normal service capable subscription is preferred
+         for emergency calls. Values should be ISO3166 country codes in lowercase. -->
+    <string-array name="config_countries_prefer_normal_service_capable_subscription"
+            translatable="false">
+        <!-- b/317945295 -->
+        <item>in</item>
+        <item>sg</item>
+    </string-array>
+
+    <!-- The component name(a flattened ComponentName string) for the telephony domain selection
+         service. The device should fallback to the modem based domain selection architecture
+         if this is not configured. -->
+    <string name="config_domain_selection_service_component_name" translatable="false"></string>
 </resources>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 3eafb24..47fd96e 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -16,12 +16,15 @@
 
 package com.android.phone;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION;
 import static android.service.carrier.CarrierService.ICarrierServiceWrapper.KEY_CONFIG_BUNDLE;
 import static android.service.carrier.CarrierService.ICarrierServiceWrapper.RESULT_ERROR;
+import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.app.compat.CompatChanges;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -44,11 +47,13 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.service.carrier.CarrierIdentifier;
 import android.service.carrier.CarrierService;
 import android.service.carrier.ICarrierService;
+import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -66,6 +71,7 @@
 import com.android.internal.telephony.PhoneConfigurationManager;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.telephony.util.TelephonyUtils;
@@ -90,6 +96,7 @@
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -214,6 +221,10 @@
             CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL
     };
 
+    // UUID to report anomaly when config changed reported with subId that map to invalid phone
+    private static final String UUID_NOTIFY_CONFIG_CHANGED_WITH_INVALID_PHONE =
+            "d81cef11-c2f1-4d76-955d-7f50e8590c48";
+
     // Handler to process various events.
     //
     // For each phoneId, the event sequence should be:
@@ -685,12 +696,18 @@
 
     @NonNull private final Handler mHandler;
 
+    @NonNull private final FeatureFlags  mFeatureFlags;
+
+    @NonNull private final PackageManager mPackageManager;
+    private final int mVendorApiLevel;
+
     /**
      * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast
      * receiver for relevant events.
      */
     @VisibleForTesting
-    /* package */ CarrierConfigLoader(@NonNull Context context, @NonNull Looper looper) {
+    /* package */ CarrierConfigLoader(@NonNull Context context, @NonNull Looper looper,
+            @NonNull FeatureFlags featureFlags) {
         super(PermissionEnforcer.fromContext(context));
         mContext = context;
         mPlatformCarrierConfigPackage =
@@ -719,6 +736,10 @@
             TelephonyManager.from(context).registerCarrierPrivilegesCallback(phoneId,
                     new HandlerExecutor(mHandler), mCarrierServiceChangeCallbacks[phoneId]);
         }
+        mFeatureFlags = featureFlags;
+        mPackageManager = context.getPackageManager();
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
         logd("CarrierConfigLoader has started");
 
         PhoneConfigurationManager.registerForMultiSimConfigChange(
@@ -733,10 +754,11 @@
      * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}.
      */
     @NonNull
-    /* package */ static CarrierConfigLoader init(@NonNull Context context) {
+    /* package */ static CarrierConfigLoader init(@NonNull Context context,
+            @NonNull FeatureFlags featureFlags) {
         synchronized (CarrierConfigLoader.class) {
             if (sInstance == null) {
-                sInstance = new CarrierConfigLoader(context, Looper.myLooper());
+                sInstance = new CarrierConfigLoader(context, Looper.myLooper(), featureFlags);
                 // Make this service available through ServiceManager.
                 TelephonyFrameworkInitializer.getTelephonyServiceManager()
                         .getCarrierConfigServiceRegisterer().register(sInstance);
@@ -1323,6 +1345,8 @@
             return new PersistableBundle();
         }
 
+        enforceTelephonyFeatureWithException(callingPackage, "getConfigForSubIdWithFeature");
+
         int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
@@ -1367,6 +1391,9 @@
         Objects.requireNonNull(keys, "Config keys must be non-null");
         enforceCallerIsSystemOrRequestingPackage(callingPackage);
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                "getConfigSubsetForSubIdWithFeature");
+
         // Permission check is performed inside and an empty bundle will return on failure.
         // No SecurityException thrown here since most clients expect to retrieve the overridden
         // value if present or use default one if not
@@ -1416,6 +1443,9 @@
             throw new IllegalArgumentException(
                     "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(), "overrideConfig");
+
         // Post to run on handler thread on which all states should be confined.
         mHandler.post(() -> {
             overrideConfig(mOverrideConfigs, phoneId, overrides);
@@ -1463,11 +1493,19 @@
 
         int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
-            throw new IllegalArgumentException(
-                    "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
+            final String msg =
+                    "Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId;
+            if (mFeatureFlags.addAnomalyWhenNotifyConfigChangedWithInvalidPhone()) {
+                AnomalyReporter.reportAnomaly(
+                        UUID.fromString(UUID_NOTIFY_CONFIG_CHANGED_WITH_INVALID_PHONE), msg);
+            }
+            logd(msg);
+            throw new IllegalArgumentException(msg);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                "notifyConfigChangedForSubId");
+
         logdWithLocalLog("Notified carrier config changed. phoneId=" + phoneId
                 + ", subId=" + subscriptionId);
 
@@ -1488,6 +1526,9 @@
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             throw new IllegalArgumentException("Invalid phoneId: " + phoneId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(), "updateConfigForPhoneId");
+
         // requires Java 7 for switch on string.
         switch (simState) {
             case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
@@ -1509,6 +1550,10 @@
     @NonNull
     public String getDefaultCarrierServicePackageName() {
         getDefaultCarrierServicePackageName_enforcePermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                "getDefaultCarrierServicePackageName");
+
         return mPlatformCarrierConfigPackage;
     }
 
@@ -1819,6 +1864,44 @@
                 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
     }
 
+    /**
+     * Get the current calling package name.
+     * @return the current calling package name
+     */
+    @Nullable
+    private String getCurrentPackageName() {
+        if (mPackageManager == null) return null;
+        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingUids == null) ? null : callingUids[0];
+    }
+
+    /**
+     * Make sure the device has required telephony feature
+     *
+     * @throws UnsupportedOperationException if the device does not have required telephony feature
+     */
+    private void enforceTelephonyFeatureWithException(@Nullable String callingPackage,
+            @NonNull String methodName) {
+        if (callingPackage == null || mPackageManager == null) {
+            return;
+        }
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
+            return;
+        }
+
+        if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY_SUBSCRIPTION)) {
+            throw new UnsupportedOperationException(
+                    methodName + " is unsupported without " + FEATURE_TELEPHONY_SUBSCRIPTION);
+        }
+    }
+
     private class CarrierServiceConnection implements ServiceConnection {
         final int phoneId;
         @NonNull final String pkgName;
diff --git a/src/com/android/phone/ChangeIccPinScreen.java b/src/com/android/phone/ChangeIccPinScreen.java
index 5369aa3..0784495 100644
--- a/src/com/android/phone/ChangeIccPinScreen.java
+++ b/src/com/android/phone/ChangeIccPinScreen.java
@@ -18,12 +18,14 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserManager;
 import android.text.method.DigitsKeyListener;
 import android.util.Log;
 import android.view.View;
@@ -46,12 +48,12 @@
     private static final boolean DBG = false;
 
     private static final int EVENT_PIN_CHANGED = 100;
-    
+
     private enum EntryState {
         ES_PIN,
         ES_PUK
     }
-    
+
     private EntryState mState;
 
     private static final int NO_ERROR = 0;
@@ -61,6 +63,8 @@
     private static final int MIN_PIN_LENGTH = 4;
     private static final int MAX_PIN_LENGTH = 8;
 
+    private UserManager mUserManager;
+    private boolean mDisallowedConfig;
     private Phone mPhone;
     private boolean mChangePin2;
     private TextView mBadPinError;
@@ -91,49 +95,74 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mUserManager = this.getSystemService(UserManager.class);
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
+            mDisallowedConfig = true;
+        }
+
         mPhone = PhoneGlobals.getPhone();
 
         resolveIntent();
 
         setContentView(R.layout.change_sim_pin_screen);
+        setupView();
 
+        mState = EntryState.ES_PIN;
+    }
+
+    private void setupView() {
         mOldPin = (EditText) findViewById(R.id.old_pin);
-        mOldPin.setKeyListener(DigitsKeyListener.getInstance());
-        mOldPin.setMovementMethod(null);
-        mOldPin.setOnClickListener(mClicked);
-
         mNewPin1 = (EditText) findViewById(R.id.new_pin1);
-        mNewPin1.setKeyListener(DigitsKeyListener.getInstance());
-        mNewPin1.setMovementMethod(null);
-        mNewPin1.setOnClickListener(mClicked);
-
         mNewPin2 = (EditText) findViewById(R.id.new_pin2);
-        mNewPin2.setKeyListener(DigitsKeyListener.getInstance());
-        mNewPin2.setMovementMethod(null);
-        mNewPin2.setOnClickListener(mClicked);
-
         mBadPinError = (TextView) findViewById(R.id.bad_pin);
         mMismatchError = (TextView) findViewById(R.id.mismatch);
-
         mButton = (Button) findViewById(R.id.button);
-        mButton.setOnClickListener(mClicked);
-
         mScrollView = (ScrollView) findViewById(R.id.scroll);
-        
         mPUKCode = (EditText) findViewById(R.id.puk_code);
-        mPUKCode.setKeyListener(DigitsKeyListener.getInstance());
-        mPUKCode.setMovementMethod(null);
-        mPUKCode.setOnClickListener(mClicked);
-        
         mPUKSubmit = (Button) findViewById(R.id.puk_submit);
-        mPUKSubmit.setOnClickListener(mClicked);
-
         mIccPUKPanel = (LinearLayout) findViewById(R.id.puk_panel);
-
         int id = mChangePin2 ? R.string.change_pin2 : R.string.change_pin;
         setTitle(getResources().getText(id));
-        
-        mState = EntryState.ES_PIN;
+
+        if (mDisallowedConfig) {
+            mOldPin.setEnabled(false);
+            mOldPin.setAlpha(.5f);
+
+            mNewPin1.setEnabled(false);
+            mNewPin1.setAlpha(.5f);
+
+            mNewPin2.setEnabled(false);
+            mNewPin2.setAlpha(.5f);
+
+            mButton.setEnabled(false);
+            mButton.setAlpha(.5f);
+
+            mPUKCode.setEnabled(false);
+            mPUKCode.setAlpha(.5f);
+
+            mPUKSubmit.setEnabled(false);
+            mPUKSubmit.setAlpha(.5f);
+        } else {
+            mOldPin.setKeyListener(DigitsKeyListener.getInstance());
+            mOldPin.setMovementMethod(null);
+            mOldPin.setOnClickListener(mClicked);
+
+            mNewPin1.setKeyListener(DigitsKeyListener.getInstance());
+            mNewPin1.setMovementMethod(null);
+            mNewPin1.setOnClickListener(mClicked);
+
+            mNewPin2.setKeyListener(DigitsKeyListener.getInstance());
+            mNewPin2.setMovementMethod(null);
+            mNewPin2.setOnClickListener(mClicked);
+
+            mButton.setOnClickListener(mClicked);
+
+            mPUKCode.setKeyListener(DigitsKeyListener.getInstance());
+            mPUKCode.setMovementMethod(null);
+            mPUKCode.setOnClickListener(mClicked);
+
+            mPUKSubmit.setOnClickListener(mClicked);
+        }
     }
 
     private void resolveIntent() {
diff --git a/src/com/android/phone/DiagnosticDataCollector.java b/src/com/android/phone/DiagnosticDataCollector.java
index e997270..e0b1dac 100644
--- a/src/com/android/phone/DiagnosticDataCollector.java
+++ b/src/com/android/phone/DiagnosticDataCollector.java
@@ -16,7 +16,6 @@
 
 package com.android.phone;
 
-import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.os.DropBoxManager;
@@ -25,7 +24,6 @@
 import android.telephony.AnomalyReporter;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import java.util.UUID;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -34,6 +32,7 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Locale;
+import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
@@ -75,16 +74,16 @@
     }
 
     public void persistEmergencyDianosticData(@NonNull DataCollectorConfig.Adapter dc,
-            @NonNull TelephonyManager.EmergencyCallDiagnosticParams edp, @NonNull String tag) {
+            @NonNull TelephonyManager.EmergencyCallDiagnosticData ecdData, @NonNull String tag) {
 
-        if (edp.isTelephonyDumpSysCollectionEnabled()) {
+        if (ecdData.isTelephonyDumpsysCollectionEnabled()) {
             persistTelephonyState(dc, tag);
         }
-        if (edp.isTelecomDumpSysCollectionEnabled()) {
+        if (ecdData.isTelecomDumpsysCollectionEnabled()) {
             persistTelecomState(dc, tag);
         }
-        if (edp.isLogcatCollectionEnabled()) {
-            persistLogcat(dc, tag, edp.getLogcatStartTime());
+        if (ecdData.isLogcatCollectionEnabled()) {
+            persistLogcat(dc, tag, ecdData.getLogcatCollectionStartTimeMillis());
         }
     }
 
diff --git a/src/com/android/phone/EnableIccPinScreen.java b/src/com/android/phone/EnableIccPinScreen.java
index 160978f..092fa64 100644
--- a/src/com/android/phone/EnableIccPinScreen.java
+++ b/src/com/android/phone/EnableIccPinScreen.java
@@ -17,10 +17,12 @@
 package com.android.phone;
 
 import android.app.Activity;
+import android.content.Context;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserManager;
 import android.text.TextUtils;
 import android.text.method.DigitsKeyListener;
 import android.util.Log;
@@ -41,11 +43,13 @@
     private static final int ENABLE_ICC_PIN_COMPLETE = 100;
     private static final boolean DBG = false;
 
+    private UserManager mUserManager;
     private LinearLayout mPinFieldContainer;
     private EditText mPinField;
     private TextView mStatusField;
     private boolean mEnable;
     private Phone mPhone;
+    private boolean mDisallowedConfig = false;
 
     private Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
@@ -64,6 +68,11 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mUserManager = this.getSystemService(UserManager.class);
+        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
+            mDisallowedConfig = true;
+        }
+
         setContentView(R.layout.enable_sim_pin_screen);
         setupView();
 
@@ -76,12 +85,20 @@
 
     private void setupView() {
         mPinField = (EditText) findViewById(R.id.pin);
-        mPinField.setKeyListener(DigitsKeyListener.getInstance());
-        mPinField.setMovementMethod(null);
-        mPinField.setOnClickListener(mClicked);
-
         mPinFieldContainer = (LinearLayout) findViewById(R.id.pinc);
         mStatusField = (TextView) findViewById(R.id.status);
+
+        if (mDisallowedConfig) {
+            mPinField.setEnabled(false);
+            mPinField.setAlpha(.5f);
+
+            mPinFieldContainer.setEnabled(false);
+            mPinFieldContainer.setAlpha(.5f);
+        } else {
+            mPinField.setKeyListener(DigitsKeyListener.getInstance());
+            mPinField.setMovementMethod(null);
+            mPinField.setOnClickListener(mClicked);
+        }
     }
 
     private void showStatus(CharSequence statusMsg) {
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 3f35454..766d719 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -16,7 +16,13 @@
 
 package com.android.phone;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION;
+import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING;
+
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -26,6 +32,7 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -53,6 +60,7 @@
 import com.android.internal.telephony.ISipDialogStateCallback;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.services.telephony.rcs.RcsFeatureController;
 import com.android.services.telephony.rcs.SipTransportController;
@@ -74,8 +82,11 @@
     private PhoneGlobals mApp;
     private TelephonyRcsService mRcsService;
     private ImsResolver mImsResolver;
+    private FeatureFlags mFeatureFlags;
+    private PackageManager mPackageManager;
     // set by shell cmd phone src set-device-enabled true/false
     private Boolean mSingleRegistrationOverride;
+    private final int mVendorApiLevel;
 
     /**
      * For apps targeting Android T and above, support the publishing state on APIs, such as
@@ -90,10 +101,10 @@
      * Initialize the singleton ImsRcsController instance.
      * This is only done once, at startup, from PhoneApp.onCreate().
      */
-    static ImsRcsController init(PhoneGlobals app) {
+    static ImsRcsController init(PhoneGlobals app, FeatureFlags featureFlags) {
         synchronized (ImsRcsController.class) {
             if (sInstance == null) {
-                sInstance = new ImsRcsController(app);
+                sInstance = new ImsRcsController(app, featureFlags);
             } else {
                 Log.wtf(TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -102,12 +113,16 @@
     }
 
     /** Private constructor; @see init() */
-    private ImsRcsController(PhoneGlobals app) {
+    private ImsRcsController(PhoneGlobals app, FeatureFlags featureFlags) {
         Log.i(TAG, "ImsRcsController");
         mApp = app;
+        mFeatureFlags = featureFlags;
+        mPackageManager = mApp.getPackageManager();
         TelephonyFrameworkInitializer
                 .getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this);
         mImsResolver = ImsResolver.getInstance();
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
     }
 
     /**
@@ -118,6 +133,10 @@
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerImsRegistrationCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "registerImsRegistrationCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
@@ -136,6 +155,10 @@
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterImsRegistrationCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "unregisterImsRegistrationCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
@@ -153,6 +176,10 @@
     public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsRcsRegistrationState");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsRcsRegistrationState");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).getRegistrationState(regState -> {
@@ -175,6 +202,10 @@
     public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsRcsRegistrationTransportType");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsRcsRegistrationTransportType");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).getRegistrationTech(regTech -> {
@@ -204,6 +235,10 @@
     @Override
     public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "registerRcsAvailabilityCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
@@ -224,6 +259,10 @@
     @Override
     public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "unregisterRcsAvailabilityCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
@@ -247,6 +286,10 @@
             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         enforceReadPrivilegedPermission("isCapable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isCapable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             return getRcsFeatureController(subId).isCapable(capability, radioTech);
@@ -273,6 +316,10 @@
             @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         enforceReadPrivilegedPermission("isAvailable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAvailable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             return getRcsFeatureController(subId).isAvailable(capability, radioTech);
@@ -290,6 +337,10 @@
             List<Uri> contactNumbers, IRcsUceControllerCallback c) {
         enforceAccessUserCapabilityExchangePermission("requestCapabilities");
         enforceReadContactsPermission("requestCapabilities");
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                FEATURE_TELEPHONY_IMS, "requestCapabilities");
+
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -311,6 +362,10 @@
             String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
         enforceAccessUserCapabilityExchangePermission("requestAvailability");
         enforceReadContactsPermission("requestAvailability");
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                FEATURE_TELEPHONY_IMS, "requestAvailability");
+
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -330,6 +385,10 @@
     @Override
     public @PublishState int getUcePublishState(int subId) {
         enforceReadPrivilegedPermission("getUcePublishState");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getUcePublishState");
+
         final int uid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         boolean isSupportPublishingState = false;
@@ -485,6 +544,10 @@
     @Override
     public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
         enforceReadPrivilegedPermission("registerUcePublishStateCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "registerUcePublishStateCallback");
+
         final int uid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         boolean isSupportPublishingState = false;
@@ -510,6 +573,10 @@
     @Override
     public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
         enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "unregisterUcePublishStateCallback");
+
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -534,6 +601,10 @@
                     + "isUceSettingEnabled");
             return false;
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                FEATURE_TELEPHONY_IMS, "isUceSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             return SubscriptionManager.getBooleanSubscriptionProperty(subId,
@@ -546,6 +617,10 @@
     @Override
     public void setUceSettingEnabled(int subId, boolean isEnabled) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setUceSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             SubscriptionManager.setSubscriptionProperty(subId,
@@ -680,6 +755,10 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "registerSipDialogStateCallback");
+
         try {
             SipTransportController transport = getRcsFeatureController(subId).getFeature(
                     SipTransportController.class);
@@ -707,6 +786,10 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "unregisterSipDialogStateCallback");
+
         try {
             SipTransportController transport = getRcsFeatureController(subId).getFeature(
                     SipTransportController.class);
@@ -897,6 +980,44 @@
                         PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
     }
 
+    /**
+     * Get the current calling package name.
+     * @return the current calling package name
+     */
+    @Nullable
+    private String getCurrentPackageName() {
+        if (mPackageManager == null) return null;
+        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingUids == null) ? null : callingUids[0];
+    }
+
+    /**
+     * Make sure the device has required telephony feature
+     *
+     * @throws UnsupportedOperationException if the device does not have required telephony feature
+     */
+    private void enforceTelephonyFeatureWithException(@Nullable String callingPackage,
+            @NonNull String telephonyFeature, @NonNull String methodName) {
+        if (callingPackage == null || mPackageManager == null) {
+            return;
+        }
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
+            return;
+        }
+
+        if (!mPackageManager.hasSystemFeature(telephonyFeature)) {
+            throw new UnsupportedOperationException(
+                    methodName + " is unsupported without " + telephonyFeature);
+        }
+    }
+
     void setRcsService(TelephonyRcsService rcsService) {
         mRcsService = rcsService;
     }
diff --git a/src/com/android/phone/PhoneApp.java b/src/com/android/phone/PhoneApp.java
index df151bf..bb663dc 100644
--- a/src/com/android/phone/PhoneApp.java
+++ b/src/com/android/phone/PhoneApp.java
@@ -38,7 +38,11 @@
             mPhoneGlobals = new PhoneGlobals(this);
             mPhoneGlobals.onCreate();
 
-            TelecomAccountRegistry.getInstance(this).setupOnBoot();
+            TelecomAccountRegistry telecomAccountRegistry =
+                    TelecomAccountRegistry.getInstance(this);
+            if (telecomAccountRegistry != null) {
+                telecomAccountRegistry.setupOnBoot();
+            }
         }
     }
 }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index f93b1e5..2ae39df 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -83,7 +83,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
-import com.android.services.telephony.domainselection.TelephonyDomainSelectionService;
 import com.android.services.telephony.rcs.TelephonyRcsService;
 
 import java.io.FileDescriptor;
@@ -166,7 +165,6 @@
     public ImsStateCallbackController mImsStateCallbackController;
     public ImsProvisioningController mImsProvisioningController;
     CarrierConfigLoader configLoader;
-    TelephonyDomainSelectionService mDomainSelectionService;
 
     private Phone phoneInEcm;
 
@@ -243,14 +241,14 @@
     private final CarrierVvmPackageInstalledReceiver mCarrierVvmPackageInstalledReceiver =
             new CarrierVvmPackageInstalledReceiver();
 
-    private final SettingsObserver mSettingsObserver;
+    private SettingsObserver mSettingsObserver;
     private BinderCallsStats.SettingsObserver mBinderCallsSettingsObserver;
 
     // Mapping of phone ID to the associated TelephonyCallback. These should be registered without
     // fine or coarse location since we only use ServiceState for
     private PhoneAppCallback[] mTelephonyCallbacks;
 
-    private FeatureFlags mFeatureFlags;
+    private FeatureFlags mFeatureFlags = new FeatureFlagsImpl();
 
     private class PhoneAppCallback extends TelephonyCallback implements
             TelephonyCallback.ServiceStateListener {
@@ -464,7 +462,13 @@
     public PhoneGlobals(Context context) {
         super(context);
         sMe = this;
-        mSettingsObserver = new SettingsObserver(context, mHandler);
+        if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) {
+            if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                mSettingsObserver = new SettingsObserver(context, mHandler);
+            }
+        } else {
+            mSettingsObserver = new SettingsObserver(context, mHandler);
+        }
     }
 
     public void onCreate() {
@@ -472,6 +476,13 @@
 
         ContentResolver resolver = getContentResolver();
 
+        if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) {
+            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                Log.v(LOG_TAG, "onCreate()... but not defined FEATURE_TELEPHONY");
+                return;
+            }
+        }
+
         // Initialize the shim from frameworks/opt/telephony into packages/services/Telephony.
         TelephonyLocalConnection.setInstance(new LocalConnectionImpl(this));
 
@@ -496,18 +507,17 @@
             // Create DomainSelectionResolver always, but it MUST be initialized only when
             // the device supports AOSP domain selection architecture and
             // has new IRadio that supports its related HAL APIs.
-            DomainSelectionResolver.make(this,
-                    getResources().getBoolean(R.bool.config_enable_aosp_domain_selection));
+            String dssComponentName = getResources().getString(
+                    R.string.config_domain_selection_service_component_name);
+            DomainSelectionResolver.make(this, dssComponentName);
 
             // Initialize the telephony framework
-            mFeatureFlags = new FeatureFlagsImpl();
             PhoneFactory.makeDefaultPhones(this, mFeatureFlags);
 
             // Initialize the DomainSelectionResolver after creating the Phone instance
             // to check the Radio HAL version.
             if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
-                mDomainSelectionService = new TelephonyDomainSelectionService(this);
-                DomainSelectionResolver.getInstance().initialize(mDomainSelectionService);
+                DomainSelectionResolver.getInstance().initialize();
                 // Initialize EmergencyStateTracker if domain selection is supported
                 boolean isSuplDdsSwitchRequiredForEmergencyCall = getResources()
                         .getBoolean(R.bool.config_gnss_supl_requires_default_data_for_emergency);
@@ -576,9 +586,9 @@
 
             phoneMgr = PhoneInterfaceManager.init(this, mFeatureFlags);
 
-            imsRcsController = ImsRcsController.init(this);
+            imsRcsController = ImsRcsController.init(this, mFeatureFlags);
 
-            configLoader = CarrierConfigLoader.init(this);
+            configLoader = CarrierConfigLoader.init(this, mFeatureFlags);
 
             if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
                 mImsStateCallbackController =
@@ -994,6 +1004,11 @@
         }
 
         ServiceState serviceState = phone.getServiceState();
+        if (serviceState == null) {
+            Log.e(LOG_TAG, "updateDataRoamingStatus: serviceState is null");
+            return;
+        }
+
         String roamingNumeric = serviceState.getOperatorNumeric();
         String roamingNumericReason = "RoamingNumeric=" + roamingNumeric;
         String callingReason = "CallingReason=" + reason;
@@ -1170,7 +1185,8 @@
             msg.arg1 = mDefaultDataSubId;
             msg.sendToTarget();
         } else if (dataAllowed && dataIsNowRoaming(mDefaultDataSubId)) {
-            if (!shouldShowRoamingNotification(roamingOperatorNumeric)) {
+            if (!shouldShowRoamingNotification(roamingOperatorNumeric != null
+                        ? roamingOperatorNumeric : phone.getServiceState().getOperatorNumeric())) {
                 Log.d(LOG_TAG, "Skip showing roaming connected notification.");
                 return;
             }
@@ -1385,9 +1401,6 @@
             e.printStackTrace();
         }
         pw.decreaseIndent();
-        if (mDomainSelectionService != null) {
-            mDomainSelectionService.dump(fd, pw, args);
-        }
         pw.decreaseIndent();
         if (mFeatureFlags.reorganizeRoamingNotification()) {
             pw.println("mShownNotificationReasons=" + mShownNotificationReasons);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 3bd29e3..3919d40 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -16,10 +16,17 @@
 
 package com.android.phone;
 
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.permission.flags.Flags.opEnableMobileDataByUser;
+import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING;
 import static android.telephony.TelephonyManager.HAL_SERVICE_NETWORK;
 import static android.telephony.TelephonyManager.HAL_SERVICE_RADIO;
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
@@ -69,6 +76,7 @@
 import android.os.ResultReceiver;
 import android.os.ServiceSpecificException;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
@@ -150,8 +158,8 @@
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
+import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
-import android.telephony.satellite.ISatelliteStateCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.NtnSignalStrengthCallback;
@@ -207,6 +215,7 @@
 import com.android.internal.telephony.SmsApplication;
 import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsPermissions;
+import com.android.internal.telephony.TelephonyCountryDetector;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.data.DataUtils;
@@ -240,6 +249,8 @@
 import com.android.phone.callcomposer.CallComposerPictureManager;
 import com.android.phone.callcomposer.CallComposerPictureTransfer;
 import com.android.phone.callcomposer.ImageData;
+import com.android.phone.satellite.accesscontrol.SatelliteAccessController;
+import com.android.phone.satellite.entitlement.SatelliteEntitlementController;
 import com.android.phone.settings.PickSmsSubscriptionActivity;
 import com.android.phone.slice.SlicePurchaseController;
 import com.android.phone.utils.CarrierAllowListInfo;
@@ -403,28 +414,32 @@
     private static final int MIN_NULL_CIPHER_AND_INTEGRITY_VERSION = 201;
     // Cellular identifier disclosure transparency was added in IRadioNetwork 2.2
     private static final int MIN_IDENTIFIER_DISCLOSURE_VERSION = 202;
+    // Null cipher notification support was added in IRadioNetwork 2.2
+    private static final int MIN_NULL_CIPHER_NOTIFICATION_VERSION = 202;
 
     /** The singleton instance. */
     private static PhoneInterfaceManager sInstance;
     private static List<String> sThermalMitigationAllowlistedPackages = new ArrayList<>();
 
     private final PhoneGlobals mApp;
-    private final FeatureFlags mFeatureFlags;
+    private FeatureFlags mFeatureFlags;
     private final CallManager mCM;
     private final ImsResolver mImsResolver;
 
     private final SatelliteController mSatelliteController;
+    private final SatelliteAccessController mSatelliteAccessController;
     private final UserManager mUserManager;
     private final AppOpsManager mAppOps;
     private final MainThreadHandler mMainThreadHandler;
     private final SharedPreferences mTelephonySharedPreferences;
     private final PhoneConfigurationManager mPhoneConfigurationManager;
     private final RadioInterfaceCapabilityController mRadioInterfaceCapabilities;
+    private PackageManager mPackageManager;
+    private final int mVendorApiLevel;
 
     /** User Activity */
     private final AtomicBoolean mNotifyUserActivity;
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
-
     private final Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
 
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
@@ -2460,9 +2475,19 @@
         mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
         mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
         mNotifyUserActivity = new AtomicBoolean(false);
+        mPackageManager = app.getPackageManager();
+        mSatelliteAccessController = SatelliteAccessController.getOrCreateInstance(
+                getDefaultPhone().getContext(), featureFlags);
+        mVendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
+
         PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
         publish();
         CarrierAllowListInfo.loadInstance(mApp);
+
+        // Create the SatelliteEntitlementController singleton, for using the get the
+        // entitlementStatus for satellite service.
+        SatelliteEntitlementController.make(mApp, mFeatureFlags);
     }
 
     @VisibleForTesting
@@ -2554,6 +2579,9 @@
     }
 
     public void dial(String number) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "dial");
+
         dialForSubscriber(getPreferredVoiceSubscription(), number);
     }
 
@@ -2599,6 +2627,9 @@
             return;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "call");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             String url = createTelUrl(number);
@@ -2642,6 +2673,10 @@
     public int[] supplyPinReportResultForSubscriber(int subId, String pin) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "supplyPinReportResultForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -2656,6 +2691,9 @@
     public int[] supplyPukReportResultForSubscriber(int subId, String puk, String pin) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "supplyPukForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -2861,6 +2899,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "isRadioOnWithFeature");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return isRadioOnForSubscriber(subId);
@@ -2890,6 +2931,9 @@
     public void toggleRadioOnOffForSubscriber(int subId) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "toggleRadioOnOffForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -2925,6 +2969,10 @@
 
     public boolean needMobileRadioShutdown() {
         enforceReadPrivilegedPermission("needMobileRadioShutdown");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "needMobileRadioShutdown");
+
         /*
          * If any of the Radios are available, it will need to be
          * shutdown. So return true if any Radio is available.
@@ -2946,6 +2994,9 @@
     public void shutdownMobileRadios() {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "shutdownMobileRadios");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
@@ -3027,6 +3078,9 @@
             @TelephonyManager.RadioPowerReason int reason) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestRadioPowerOffForReason");
+
         log("requestRadioPowerOffForReason: subId=" + subId
                 + ",reason=" + reason + ",callingPackage=" + getCurrentPackageName());
         final long identity = Binder.clearCallingIdentity();
@@ -3057,6 +3111,9 @@
             @TelephonyManager.RadioPowerReason int reason) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "clearRadioPowerOffForReason");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             boolean result = false;
@@ -3084,6 +3141,9 @@
     public List getRadioPowerOffReasons(int subId, String callingPackage, String callingFeatureId) {
         enforceReadPrivilegedPermission("getRadioPowerOffReasons");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioPowerOffReasons");
+
         final long identity = Binder.clearCallingIdentity();
         List result = new ArrayList();
         try {
@@ -3109,6 +3169,9 @@
     public boolean enableDataConnectivity(String callingPackage) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "enableDataConnectivity");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int subId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -3130,6 +3193,9 @@
     public boolean disableDataConnectivity(String callingPackage) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "disableDataConnectivity");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int subId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -3148,6 +3214,9 @@
 
     @Override
     public boolean isDataConnectivityPossible(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataConnectivityPossible");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3168,6 +3237,9 @@
     public void handleUssdRequest(int subId, String ussdRequest, ResultReceiver wrappedCallback) {
         enforceCallPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "handleUssdRequest");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -3183,6 +3255,9 @@
     public boolean handlePinMmiForSubscriber(int subId, String dialString) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "handlePinMmiForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -3230,6 +3305,10 @@
                         + "targeting API level 31+.");
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallStateForSubscription");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -3247,6 +3326,9 @@
 
     @Override
     public int getDataStateForSubId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "getDataStateForSubId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3268,6 +3350,9 @@
 
     @Override
     public @DataActivityType int getDataActivityForSubId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "getDataActivityForSubId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3305,6 +3390,9 @@
                         ? new CellIdentityCdma() : new CellIdentityGsm();
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getCellLocation");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -3318,6 +3406,9 @@
 
     @Override
     public String getNetworkCountryIsoForPhone(int phoneId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNetworkCountryIsoForPhone");
+
         // Reporting the correct network country is ambiguous when IWLAN could conflict with
         // registered cell info, so return a NULL country instead.
         final long identity = Binder.clearCallingIdentity();
@@ -3388,6 +3479,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNeighboringCellInfo");
+
         if (DBG_LOC) log("getNeighboringCellInfo: is active user");
 
         List<CellInfo> info = getAllCellInfo(callingPackage, callingFeatureId);
@@ -3441,6 +3535,9 @@
             return getCachedCellInfo();
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllCellInfo");
+
         if (DBG_LOC) log("getAllCellInfo: is active user");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
@@ -3509,6 +3606,8 @@
                 return;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestCellInfoUpdateInternal");
 
         final Phone phone = getPhoneFromSubId(subId);
         if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId);
@@ -3547,6 +3646,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_GSM, "getImeiForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getImei();
@@ -3562,6 +3664,10 @@
                 callingFeatureId, "getPrimaryImei")) {
             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()) {
@@ -3577,6 +3683,9 @@
 
     @Override
     public String getTypeAllocationCodeForSlot(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_GSM, "getTypeAllocationCodeForSlot");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
         String tac = null;
         if (phone != null) {
@@ -3611,6 +3720,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getMeidForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getMeid();
@@ -3621,6 +3733,9 @@
 
     @Override
     public String getManufacturerCodeForSlot(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getManufacturerCodeForSlot");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
         String manufacturerCode = null;
         if (phone != null) {
@@ -3650,6 +3765,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "getDeviceSoftwareVersionForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return phone.getDeviceSvn();
@@ -3660,6 +3778,9 @@
 
     @Override
     public int getSubscriptionCarrierId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriptionCarrierId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3671,6 +3792,9 @@
 
     @Override
     public String getSubscriptionCarrierName(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriptionCarrierName");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3682,6 +3806,9 @@
 
     @Override
     public int getSubscriptionSpecificCarrierId(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriptionSpecificCarrierId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3694,6 +3821,10 @@
 
     @Override
     public String getSubscriptionSpecificCarrierName(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getSubscriptionSpecificCarrierName");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3712,6 +3843,10 @@
         if (phone == null) {
             return TelephonyManager.UNKNOWN_CARRIER_ID;
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierIdFromMccMnc");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return CarrierResolver.getCarrierIdFromMccMnc(phone.getContext(), mccmnc);
@@ -3832,6 +3967,9 @@
 
     @Override
     public int getActivePhoneTypeForSlot(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "getActivePhoneTypeForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -3863,6 +4001,10 @@
             return -1;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CDMA,
+                "getCdmaEriIconIndexForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3948,6 +4090,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaMdn");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaMdn");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3970,6 +4115,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaMin");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaMin");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3999,6 +4147,9 @@
                     + ", configured package: " + authorizedPackage);
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "requestNumberVerification");
+
         if (range == null) {
             throw new NullPointerException("Range must be non-null");
         }
@@ -4013,6 +4164,9 @@
      * Returns true if CDMA provisioning needs to run.
      */
     public boolean needsOtaServiceProvisioning() {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "needsOtaServiceProvisioning");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getDefaultPhone().needsOtaServiceProvisioning();
@@ -4029,6 +4183,9 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
                 mApp, subId, "setVoiceMailNumber");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoiceMailNumber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Boolean success = (Boolean) sendRequest(CMD_SET_VOICEMAIL_NUMBER,
@@ -4048,6 +4205,9 @@
             throw new SecurityException("caller must be system dialer");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVisualVoicemailSettings");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             PhoneAccountHandle phoneAccountHandle = PhoneAccountHandleConverter.fromSubId(subId);
@@ -4070,6 +4230,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVisualVoicemailPackageName");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return RemoteVvmTaskManager.getRemotePackage(mApp, subId).getPackageName();
@@ -4083,6 +4246,9 @@
             VisualVoicemailSmsFilterSettings settings) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "enableVisualVoicemailSmsFilter");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             VisualVoicemailSmsFilterConfig.enableVisualVoicemailSmsFilter(
@@ -4096,6 +4262,9 @@
     public void disableVisualVoicemailSmsFilter(String callingPackage, int subId) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "disableVisualVoicemailSmsFilter");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             VisualVoicemailSmsFilterConfig.disableVisualVoicemailSmsFilter(
@@ -4139,6 +4308,10 @@
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         enforceVisualVoicemailPackage(callingPackage, subId);
         enforceSendSmsPermission();
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "sendVisualVoicemailSmsForSubscriber");
+
         SmsController smsController = PhoneFactory.getSmsController();
         smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
                 subId, number, port, text, sentIntent);
@@ -4152,6 +4325,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setVoiceActivationState");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoiceActivationState");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -4173,6 +4349,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setDataActivationState");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataActivationState");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -4193,6 +4372,9 @@
     public int getVoiceActivationState(int subId, String callingPackage) {
         enforceReadPrivilegedPermission("getVoiceActivationState");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoiceActivationState");
+
         final Phone phone = getPhone(subId);
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4213,6 +4395,9 @@
     public int getDataActivationState(int subId, String callingPackage) {
         enforceReadPrivilegedPermission("getDataActivationState");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "getDataActivationState");
+
         final Phone phone = getPhone(subId);
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4256,6 +4441,9 @@
      */
     @Override
     public boolean isConcurrentVoiceAndDataAllowed(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isConcurrentVoiceAndDataAllowed");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubIdOrDefault(subId).isConcurrentVoiceAndDataAllowed();
@@ -4280,6 +4468,9 @@
                     getDefaultSubscription(), "sendDialerSpecialCode");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "sendDialerSpecialCode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             defaultPhone.sendDialerSpecialCode(inputCode);
@@ -4293,6 +4484,10 @@
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getNetworkSelectionMode");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNetworkSelectionMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -4307,6 +4502,10 @@
     @Override
     public boolean isInEmergencySmsMode() {
         enforceReadPrivilegedPermission("isInEmergencySmsMode");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "isInEmergencySmsMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone : PhoneFactory.getPhones()) {
@@ -4391,6 +4590,76 @@
     }
 
     /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param c The callback that will be used to send the result.
+     */
+    @Override
+    public void registerImsEmergencyRegistrationCallback(int subId, IImsRegistrationCallback c)
+            throws RemoteException {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "registerImsEmergencyRegistrationCallback");
+
+        if (!ImsManager.isImsSupportedOnDevice(mApp)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS not available on device.");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            int slotId = getSlotIndexOrException(subId);
+            verifyImsMmTelConfiguredOrThrow(slotId);
+
+            ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+            if (controller != null) {
+                ImsManager imsManager = controller.getImsManager(subId);
+                if (imsManager != null) {
+                    imsManager.addEmergencyRegistrationCallbackForSubscription(c, subId);
+                } else {
+                    throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+                }
+            } else {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+            }
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param c The callback that will be used to send the result.
+     */
+    @Override
+    public void unregisterImsEmergencyRegistrationCallback(int subId, IImsRegistrationCallback c) {
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "unregisterImsEmergencyRegistrationCallback");
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
+        }
+        final long token = Binder.clearCallingIdentity();
+
+        try {
+            ImsStateCallbackController controller = ImsStateCallbackController.getInstance();
+            if (controller != null) {
+                ImsManager imsManager = controller.getImsManager(subId);
+                if (imsManager != null) {
+                    imsManager.removeEmergencyRegistrationCallbackForSubscription(c, subId);
+                } else {
+                    Log.i(LOG_TAG, "unregisterImsEmergencyRegistrationCallback: " + subId
+                            + "is inactive, ignoring unregister.");
+                    // If the ImsManager is not valid, just return, since the callback
+                    // will already have been removed internally.
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
      * Get the IMS service registration state for the MmTelFeature associated with this sub id.
      */
     @Override
@@ -4531,6 +4800,10 @@
     @Override
     public boolean isCapable(int subId, int capability, int regTech) {
         enforceReadPrivilegedPermission("isCapable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isCapable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4550,6 +4823,10 @@
     @Override
     public boolean isAvailable(int subId, int capability, int regTech) {
         enforceReadPrivilegedPermission("isAvailable");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAvailable");
+
         final long token = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -4613,6 +4890,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isAdvancedCallingSettingEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isAdvancedCallingSettingEnabled");
+
         final long token = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4629,6 +4909,10 @@
     public void setAdvancedCallingSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setAdvancedCallingSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setAdvancedCallingSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4650,6 +4934,10 @@
     public boolean isVtSettingEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVtSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isVtSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4666,6 +4954,10 @@
     public void setVtSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVtSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVtSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4687,6 +4979,10 @@
     public boolean isVoWiFiSettingEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isVoWiFiSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4703,6 +4999,10 @@
     public void setVoWiFiSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4725,6 +5025,10 @@
     public boolean isCrossSimCallingEnabledByUser(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isCrossSimCallingEnabledByUser");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isCrossSimCallingEnabledByUser");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4748,6 +5052,10 @@
     public void setCrossSimCallingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setCrossSimCallingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setCrossSimCallingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4770,6 +5078,10 @@
     public boolean isVoWiFiRoamingSettingEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiRoamingSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isVoWiFiRoamingSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4786,6 +5098,10 @@
     public void setVoWiFiRoamingSettingEnabled(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiRoamingSettingEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiRoamingSettingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4803,6 +5119,10 @@
     public void setVoWiFiNonPersistent(int subId, boolean isCapable, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiNonPersistent");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiNonPersistent");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4823,6 +5143,10 @@
     public int getVoWiFiModeSetting(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getVoWiFiModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getVoWiFiModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4839,6 +5163,10 @@
     public void setVoWiFiModeSetting(int subId, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4855,6 +5183,10 @@
     @Override
     public int getVoWiFiRoamingModeSetting(int subId) {
         enforceReadPrivilegedPermission("getVoWiFiRoamingModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getVoWiFiRoamingModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4871,6 +5203,10 @@
     public void setVoWiFiRoamingModeSetting(int subId, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setVoWiFiRoamingModeSetting");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setVoWiFiRoamingModeSetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4888,6 +5224,10 @@
     public void setRttCapabilitySetting(int subId, boolean isEnabled) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setRttCapabilityEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setRttCapabilitySetting");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -4909,6 +5249,10 @@
     public boolean isTtyOverVolteEnabled(int subId) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isTtyOverVolteEnabled");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isTtyOverVolteEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int slotId = getSlotIndexOrException(subId);
@@ -5034,6 +5378,9 @@
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5054,6 +5401,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getRcsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getRcsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5074,6 +5424,9 @@
             boolean isProvisioned) {
         checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5093,6 +5446,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getProvisioningStatusForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningStatusForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5113,6 +5469,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isProvisioningRequiredForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isProvisioningRequiredForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5133,6 +5492,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isProvisioningRequiredForCapability");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "isRcsProvisioningRequiredForCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsProvisioningController controller = ImsProvisioningController.getInstance();
@@ -5156,6 +5518,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsProvisioningInt");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningInt");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5196,6 +5561,9 @@
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsProvisioningString");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "getImsProvisioningString");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5223,6 +5591,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningInt");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningInt");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5262,6 +5633,10 @@
         }
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningString");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                FEATURE_TELEPHONY_IMS, "setImsProvisioningString");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -5335,6 +5710,9 @@
             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5372,6 +5750,9 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getDataNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5400,6 +5781,9 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoiceNetworkTypeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5427,6 +5811,9 @@
      */
     @Override
     public boolean hasIccCardUsingSlotIndex(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "hasIccCardUsingSlotIndex");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -5464,6 +5851,9 @@
             return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getLteOnCdmaModeForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -5545,6 +5935,7 @@
     @Override
     public IccOpenLogicalChannelResponse iccOpenLogicalChannel(
             @NonNull IccLogicalChannelRequest request) {
+
         Phone phone = getPhoneFromValidIccLogicalChannelRequest(request,
                 /*message=*/ "iccOpenLogicalChannel");
 
@@ -5552,6 +5943,9 @@
         // Verify that the callingPackage in the request belongs to the calling UID
         mAppOps.checkPackage(Binder.getCallingUid(), request.callingPackage);
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccOpenLogicalChannel");
+
         return iccOpenLogicalChannelWithPermission(phone, request);
     }
 
@@ -5598,6 +5992,9 @@
 
     @Override
     public boolean iccCloseLogicalChannel(@NonNull IccLogicalChannelRequest request) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccCloseLogicalChannel");
+
         Phone phone = getPhoneFromValidIccLogicalChannelRequest(request,
                 /*message=*/"iccCloseLogicalChannel");
 
@@ -5645,6 +6042,10 @@
             int command, int p1, int p2, int p3, String data) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccTransmitApduLogicalChannel");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccTransmitApduLogicalChannel");
+
         if (DBG) {
             log("iccTransmitApduLogicalChannel: subId=" + subId + " chnl=" + channel
                     + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3="
@@ -5658,6 +6059,11 @@
     public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
             int cla, int command, int p1, int p2, int p3, String data) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "iccTransmitApduLogicalChannelBySlot");
+
         if (DBG) {
             log("iccTransmitApduLogicalChannelByPort: slotIndex=" + slotIndex + " portIndex="
                     + portIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1="
@@ -5699,6 +6105,10 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccTransmitApduBasicChannel");
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccTransmitApduBasicChannel");
+
         if (DBG) {
             log("iccTransmitApduBasicChannel: subId=" + subId + " cla=" + cla + " cmd="
                     + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
@@ -5712,6 +6122,10 @@
             String callingPackage, int cla, int command, int p1, int p2, int p3, String data) {
         enforceModifyPermission();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccTransmitApduBasicChannelBySlot");
+
         if (DBG) {
             log("iccTransmitApduBasicChannelByPort: slotIndex=" + slotIndex + " portIndex="
                     + portIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2="
@@ -5764,6 +6178,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "iccExchangeSimIO");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "iccExchangeSimIO");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -5809,6 +6226,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getForbiddenPlmns");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (appType != TelephonyManager.APPTYPE_USIM
@@ -5845,6 +6265,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setForbiddenPlmns");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setForbiddenPlmns");
+
         if (appType != TelephonyManager.APPTYPE_USIM && appType != TelephonyManager.APPTYPE_SIM) {
             loge("setForbiddenPlmnList(): App Type must be USIM or SIM");
             throw new IllegalArgumentException("Invalid appType: App Type must be USIM or SIM");
@@ -5874,6 +6297,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "sendEnvelopeWithStatus");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "sendEnvelopeWithStatus");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             IccIoResult response = (IccIoResult) sendRequest(CMD_SEND_ENVELOPE, content, subId);
@@ -5979,6 +6405,9 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, phone.getSubId(), "resetModemConfig");
 
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "resetModemConfig");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 Boolean success = (Boolean) sendRequest(CMD_RESET_MODEM_CONFIG, null);
@@ -6005,6 +6434,9 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, phone.getSubId(), "rebootModem");
 
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "rebootModem");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 Boolean success = (Boolean) sendRequest(CMD_MODEM_REBOOT, null);
@@ -6025,6 +6457,9 @@
     public void resetIms(int slotIndex) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "resetIms");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mImsResolver == null) {
@@ -6304,6 +6739,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeAutomatic");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setNetworkSelectionModeAutomatic");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -6334,6 +6772,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeManual");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setNetworkSelectionModeManual");
+
         final long identity = Binder.clearCallingIdentity();
         if (!isActiveSubscription(subId)) {
             return false;
@@ -6364,6 +6805,9 @@
                 .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getManualNetworkSelectionPlmn");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getManualNetworkSelectionPlmn");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -6426,6 +6870,10 @@
     public void getCallForwarding(int subId, int callForwardingReason,
             ICallForwardingInfoCallback callback) {
         enforceReadPrivilegedPermission("getCallForwarding");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallForwarding");
+
         long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -6478,6 +6926,10 @@
     public void setCallForwarding(int subId, CallForwardingInfo callForwardingInfo,
             IIntegerConsumer callback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setCallForwarding");
+
         long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) {
@@ -6511,6 +6963,10 @@
     @Override
     public void getCallWaitingStatus(int subId, IIntegerConsumer callback) {
         enforceReadPrivilegedPermission("getCallWaitingStatus");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallWaitingStatus");
+
         long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -6562,6 +7018,10 @@
     @Override
     public void setCallWaitingStatus(int subId, boolean enable, IIntegerConsumer callback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setCallWaitingStatus");
+
         long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("setCallWaitingStatus: subId " + subId + " enable: " + enable);
@@ -6658,6 +7118,10 @@
                 }
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "requestNetworkScan");
+
         int callingUid = Binder.getCallingUid();
         int callingPid = Binder.getCallingPid();
         final long identity = Binder.clearCallingIdentity();
@@ -6733,6 +7197,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getAllowedNetworkTypesBitmask");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllowedNetworkTypesBitmask");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (DBG) log("getAllowedNetworkTypesBitmask");
@@ -6757,6 +7224,10 @@
             @TelephonyManager.AllowedNetworkTypesReason int reason) {
         TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getAllowedNetworkTypesForReason");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getAllowedNetworkTypesForReason");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubIdOrDefault(subId).getAllowedNetworkTypes(reason);
@@ -6850,6 +7321,10 @@
                     "setAllowedNetworkTypesForReason cannot be called with carrier privileges for"
                             + " reason " + reason);
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setAllowedNetworkTypesForReason");
+
         if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
             loge("setAllowedNetworkTypesForReason: Invalid allowed network type reason: " + reason);
             return false;
@@ -6895,6 +7370,10 @@
     @Override
     public boolean isTetheringApnRequiredForSubscriber(int subId) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isTetheringApnRequiredForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         final Phone phone = getPhone(subId);
         try {
@@ -6997,6 +7476,9 @@
             enforceReadPrivilegedPermission(functionName);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             int phoneId = SubscriptionManager.getPhoneId(subId);
@@ -7043,6 +7525,8 @@
             }
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabledForReason");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -7071,6 +7555,9 @@
 
     @Override
     public int getCarrierPrivilegeStatus(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierPrivilegeStatus");
+
         // No permission needed; this only lets the caller inspect their own status.
         return getCarrierPrivilegeStatusForUidWithPermission(subId, Binder.getCallingUid());
     }
@@ -7078,6 +7565,10 @@
     @Override
     public int getCarrierPrivilegeStatusForUid(int subId, int uid) {
         enforceReadPrivilegedPermission("getCarrierPrivilegeStatusForUid");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierPrivilegeStatusForUid");
+
         return getCarrierPrivilegeStatusForUidWithPermission(subId, uid);
     }
 
@@ -7098,6 +7589,10 @@
     @Override
     public int checkCarrierPrivilegesForPackage(int subId, String pkgName) {
         enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackage");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "checkCarrierPrivilegesForPackage");
+
         if (TextUtils.isEmpty(pkgName)) {
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
         }
@@ -7117,6 +7612,11 @@
     @Override
     public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
         enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackageAnyPhone");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "checkCarrierPrivilegesForPackageAnyPhone");
+
         return checkCarrierPrivilegesForPackageAnyPhoneWithPermission(pkgName);
     }
 
@@ -7145,6 +7645,11 @@
     @Override
     public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
         enforceReadPrivilegedPermission("getCarrierPackageNamesForIntentAndPhone");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getCarrierPackageNamesForIntentAndPhone");
+
         Phone phone = PhoneFactory.getPhone(phoneId);
         if (phone == null) {
             return Collections.emptyList();
@@ -7173,6 +7678,11 @@
     @Override
     public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
         enforceReadPrivilegedPermission("getPackagesWithCarrierPrivilegesForAllPhones");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getPackagesWithCarrierPrivilegesForAllPhones");
+
         Set<String> privilegedPackages = new ArraySet<>();
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -7189,6 +7699,10 @@
     public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
         enforceReadPrivilegedPermission("getCarrierServicePackageNameForLogicalSlot");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "getCarrierServicePackageNameForLogicalSlot");
+
         final Phone phone = PhoneFactory.getPhone(logicalSlotIndex);
         if (phone == null) {
             return null;
@@ -7217,6 +7731,9 @@
     public void setCallComposerStatus(int subId, int status) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setCallComposerStatus");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -7240,6 +7757,9 @@
     public int getCallComposerStatus(int subId) {
         enforceReadPrivilegedPermission("getCallComposerStatus");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getCallComposerStatus");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -7262,6 +7782,10 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setLine1NumberForDisplayForSubscriber");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "setLine1NumberForDisplayForSubscriber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final String iccId = getIccId(subId);
@@ -7318,6 +7842,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getLine1NumberForDisplay");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             String iccId = getIccId(subId);
@@ -7445,6 +7972,9 @@
     public String[] getMergedImsisFromGroup(int subId, String callingPackage) {
         enforceReadPrivilegedPermission("getMergedImsisFromGroup");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getMergedImsisFromGroup");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final TelephonyManager telephonyManager = mApp.getSystemService(
@@ -7490,6 +8020,9 @@
         TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setOperatorBrandOverride");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setOperatorBrandOverride");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -7536,6 +8069,9 @@
             throw e;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioAccessFamily");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             raf = ProxyController.getInstance().getRadioAccessFamily(phoneId);
@@ -7556,6 +8092,10 @@
         } catch (PackageManager.NameNotFoundException e) {
             throw new SecurityException("Invalid package:" + callingPackage);
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "uploadCallComposerPicture");
+
         RoleManager rm = mApp.getSystemService(RoleManager.class);
         List<String> dialerRoleHolders = rm.getRoleHolders(RoleManager.ROLE_DIALER);
         if (!dialerRoleHolders.contains(callingPackage)) {
@@ -7642,6 +8182,9 @@
         final Phone defaultPhone = getDefaultPhone();
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "enableVideoCalling");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             ImsManager.getInstance(defaultPhone.getContext(),
@@ -7659,6 +8202,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_IMS, "isVideoCallingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             // Check the user preference and the  system-level IMS setting. Even if the user has
@@ -7684,6 +8230,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "canChangeDtmfToneLength");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             CarrierConfigManager configManager =
@@ -7702,6 +8251,9 @@
             return false;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "isWorldPhone");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             CarrierConfigManager configManager =
@@ -7721,6 +8273,9 @@
 
     @Override
     public boolean isHearingAidCompatibilitySupported() {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "isHearingAidCompatibilitySupported");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mApp.getResources().getBoolean(R.bool.hac_enabled);
@@ -7737,6 +8292,9 @@
      */
     @Override
     public boolean isRttSupported(int subscriptionId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_IMS, "isRttSupported");
+
         final long identity = Binder.clearCallingIdentity();
         final Phone phone = getPhone(subscriptionId);
         if (phone == null) {
@@ -7746,8 +8304,9 @@
         try {
             boolean isCarrierSupported = mApp.getCarrierConfigForSubId(subscriptionId).getBoolean(
                     CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL);
-            boolean isDeviceSupported =
-                    phone.getContext().getResources().getBoolean(R.bool.config_support_rtt);
+            boolean isDeviceSupported = (phone.getContext().getResources() != null)
+                    ? phone.getContext().getResources().getBoolean(R.bool.config_support_rtt)
+                    : false;
             return isCarrierSupported && isDeviceSupported;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -7762,6 +8321,12 @@
     public boolean isRttEnabled(int subscriptionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
+            if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) {
+                if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+                    return false;
+                }
+            }
+
             boolean isRttSupported = isRttSupported(subscriptionId);
             boolean isUserRttSettingOn = Settings.Secure.getInt(
                     mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
@@ -7851,6 +8416,10 @@
                         subscriptionId,
                         "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: "
                                 + subscriptionId);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getPhoneAccountHandleForSubscriptionId");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -7918,6 +8487,10 @@
     @Override
     public void factoryReset(int subId, String callingPackage) {
         enforceSettingsPermission();
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "factoryReset");
+
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
         }
@@ -7936,7 +8509,8 @@
                 setNetworkSelectionModeAutomatic(subId);
                 Phone phone = getPhone(subId);
                 cleanUpAllowedNetworkTypes(phone, subId);
-                setDataRoamingEnabled(subId, getDefaultDataRoamingEnabled(subId));
+                setDataRoamingEnabled(subId, phone == null ? false
+                        : phone.getDataSettingsManager().isDefaultDataRoamingEnabled());
                 getPhone(subId).resetCarrierKeysForImsiEncryption();
             }
             // There has been issues when Sms raw table somehow stores orphan
@@ -7990,6 +8564,10 @@
     @Override
     public String getSimLocaleForSubscriber(int subId) {
         enforceReadPrivilegedPermission("getSimLocaleForSubscriber, subId: " + subId);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSimLocaleForSubscriber");
+
         final Phone phone = getPhone(subId);
         if (phone == null) {
             log("getSimLocaleForSubscriber, invalid subId");
@@ -8072,6 +8650,10 @@
     @Override
     public void requestModemActivityInfo(ResultReceiver result) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "requestModemActivityInfo");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -8216,6 +8798,9 @@
             return null;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getServiceStateForSubscriber");
+
         boolean hasFinePermission = false;
         boolean hasCoarsePermission = false;
         if (!renounceFineLocationAccess) {
@@ -8295,6 +8880,9 @@
      */
     @Override
     public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getVoicemailRingtoneUri");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(accountHandle);
@@ -8331,6 +8919,9 @@
                     "setVoicemailRingtoneUri");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoicemailRingtoneUri");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -8352,6 +8943,9 @@
      */
     @Override
     public boolean isVoicemailVibrationEnabled(PhoneAccountHandle accountHandle) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "isVoicemailVibrationEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(accountHandle);
@@ -8388,6 +8982,9 @@
                     "setVoicemailVibrationEnabled");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "setVoicemailVibrationEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -8464,6 +9061,10 @@
     @Override
     public String getAidForAppType(int subId, int appType) {
         enforceReadPrivilegedPermission("getAidForAppType");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getAidForAppType");
+
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -8521,6 +9122,10 @@
     @Override
     public String getCdmaPrlVersion(int subId) {
         enforceReadPrivilegedPermission("getCdmaPrlVersion");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaPrlVersion");
+
         Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -8570,6 +9175,10 @@
     @TelephonyManager.SetCarrierRestrictionResult
     public int setAllowedCarriers(CarrierRestrictionRules carrierRestrictionRules) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "setAllowedCarriers");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         if (carrierRestrictionRules == null) {
@@ -8596,6 +9205,10 @@
     @Override
     public CarrierRestrictionRules getAllowedCarriers() {
         enforceReadPrivilegedPermission("getAllowedCarriers");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "getAllowedCarriers");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
 
         final long identity = Binder.clearCallingIdentity();
@@ -8626,6 +9239,10 @@
     @Override
     public void getCarrierRestrictionStatus(IIntegerConsumer callback, String packageName) {
         enforceReadPermission("getCarrierRestrictionStatus");
+
+        enforceTelephonyFeatureWithException(packageName,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getCarrierRestrictionStatus");
+
         int carrierId = validateCallerAndGetCarrierId(packageName);
         if (carrierId == CarrierAllowListInfo.INVALID_CARRIER_ID) {
             Rlog.e(LOG_TAG, "getCarrierRestrictionStatus: caller is not registered");
@@ -8748,6 +9365,11 @@
     @Override
     public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS,
+                "carrierActionReportDefaultNetworkStatus");
+
         final Phone phone = getPhone(subId);
 
         final long identity = Binder.clearCallingIdentity();
@@ -8772,6 +9394,10 @@
     @Override
     public void carrierActionResetAll(int subId) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "carrierActionResetAll");
+
         final Phone phone = getPhone(subId);
         if (phone == null) {
             loge("carrierAction: ResetAll fails with invalid sibId: " + subId);
@@ -8834,14 +9460,17 @@
             enforceModifyPermission();
         }
 
-        if (reason == TelephonyManager.DATA_ENABLED_REASON_USER && enabled
-                && null != callingPackage && opEnableMobileDataByUser()) {
-            mAppOps.noteOp(AppOpsManager.OPSTR_ENABLE_MOBILE_DATA_BY_USER, Binder.getCallingUid(),
-                    callingPackage, null, null);
-        }
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataEnabledForReason");
 
+        int callingUid = Binder.getCallingUid();
         final long identity = Binder.clearCallingIdentity();
         try {
+            if (reason == TelephonyManager.DATA_ENABLED_REASON_USER && enabled
+                    && null != callingPackage && opEnableMobileDataByUser()) {
+                mAppOps.noteOp(AppOpsManager.OPSTR_ENABLE_MOBILE_DATA_BY_USER,
+                        callingUid, callingPackage, null, null);
+            }
             Phone phone = getPhone(subId);
             if (phone != null) {
                 if (reason == TelephonyManager.DATA_ENABLED_REASON_CARRIER) {
@@ -8910,6 +9539,10 @@
     @Override
     public void setSimPowerStateForSlot(int slotIndex, int state) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setSimPowerStateForSlot");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
 
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -8939,6 +9572,11 @@
     public void setSimPowerStateForSlotWithCallback(int slotIndex, int state,
             IIntegerConsumer callback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "setSimPowerStateForSlotWithCallback");
+
         Phone phone = PhoneFactory.getPhone(slotIndex);
 
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
@@ -8976,6 +9614,10 @@
     @Override
     public boolean getEmergencyCallbackMode(int subId) {
         enforceReadPrivilegedPermission("getEmergencyCallbackMode");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getEmergencyCallbackMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubIdOrDefault(subId).isInEcm();
@@ -8993,6 +9635,9 @@
      */
     @Override
     public SignalStrength getSignalStrength(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getSignalStrength");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone p = getPhone(subId);
@@ -9022,6 +9667,9 @@
                 return TelephonyManager.RADIO_POWER_UNAVAILABLE;
             }
 
+            enforceTelephonyFeatureWithException(callingPackage,
+                    PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getRadioPowerState");
+
             final long identity = Binder.clearCallingIdentity();
             try {
                 return phone.getRadioPowerState();
@@ -9062,6 +9710,9 @@
                     mApp, subId, functionName);
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataRoamingEnabled");
+
         boolean isEnabled = false;
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9089,6 +9740,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setDataRoamingEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setDataRoamingEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subId);
@@ -9106,6 +9760,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isManualNetworkSelectionAllowed");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "isManualNetworkSelectionAllowed");
+
         boolean isAllowed = true;
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9152,6 +9809,10 @@
                 throw new SecurityException("Caller does not have permission.");
             }
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getUiccCardsInfo");
+
         // checking compatibility, if calling app's target SDK is T and beyond.
         if (CompatChanges.isChangeEnabled(GET_API_SIGNATURES_FROM_UICC_PORT_INFO,
                 Binder.getCallingUid())) {
@@ -9262,6 +9923,9 @@
         // we are reading iccId which is PII data.
         enforceReadPrivilegedPermission("getUiccSlotsInfo");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getUiccSlotsInfo");
+
         // checking compatibility, if calling app's target SDK is T and beyond.
         if (CompatChanges.isChangeEnabled(GET_API_SIGNATURES_FROM_UICC_PORT_INFO,
                 Binder.getCallingUid())) {
@@ -9364,6 +10028,9 @@
     public boolean switchSlots(int[] physicalSlots) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "switchSlots");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             List<UiccSlotMapping> slotMappings = new ArrayList<>();
@@ -9383,6 +10050,9 @@
     public boolean setSimSlotMapping(@NonNull List<UiccSlotMapping> slotMapping) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setSimSlotMapping");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (Boolean) sendRequest(CMD_SWITCH_SLOTS, slotMapping);
@@ -9393,6 +10063,9 @@
 
     @Override
     public int getCardIdForDefaultEuicc(int subId, String callingPackage) {
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_EUICC, "getCardIdForDefaultEuicc");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return UiccController.getInstance().getCardIdForDefaultEuicc();
@@ -9440,20 +10113,6 @@
     }
 
     /**
-     * Returns true if the data roaming is enabled by default, i.e the system property
-     * of {@link #DEFAULT_DATA_ROAMING_PROPERTY_NAME} is true or the config of
-     * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} is true.
-     */
-    private boolean getDefaultDataRoamingEnabled(int subId) {
-        final CarrierConfigManager configMgr = (CarrierConfigManager)
-                mApp.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        boolean isDataRoamingEnabled = TelephonyProperties.data_roaming().orElse(false);
-        isDataRoamingEnabled |= configMgr.getConfigForSubId(subId).getBoolean(
-                CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL);
-        return isDataRoamingEnabled;
-    }
-
-    /**
      * Returns the default network type for the given {@code subId}, if the default network type is
      * not set, return {@link Phone#PREFERRED_NT_MODE}.
      */
@@ -9559,6 +10218,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getCdmaRoamingMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaRoamingMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (int) sendRequest(CMD_GET_CDMA_ROAMING_MODE, null /* argument */, subId);
@@ -9572,6 +10234,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setCdmaRoamingMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "setCdmaRoamingMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (boolean) sendRequest(CMD_SET_CDMA_ROAMING_MODE, mode, subId);
@@ -9586,6 +10251,9 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getCdmaSubscriptionMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "getCdmaSubscriptionMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (int) sendRequest(CMD_GET_CDMA_SUBSCRIPTION_MODE, null /* argument */, subId);
@@ -9599,6 +10267,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setCdmaSubscriptionMode");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CDMA, "setCdmaSubscriptionMode");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (boolean) sendRequest(CMD_SET_CDMA_SUBSCRIPTION_MODE, mode, subId);
@@ -9615,6 +10286,10 @@
                 "getEmergencyNumberList")) {
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getEmergencyNumberList");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Map<Integer, List<EmergencyNumber>> emergencyNumberListInternal = new HashMap<>();
@@ -9640,6 +10315,10 @@
                     .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, defaultPhone.getSubId(), "isEmergencyNumber(Potential)");
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "isEmergencyNumber");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9732,6 +10411,9 @@
     public int getEmergencyNumberDbVersion(int subId) {
         enforceReadPrivilegedPermission("getEmergencyNumberDbVersion");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "getEmergencyNumberDbVersion");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -9749,6 +10431,9 @@
     public void notifyOtaEmergencyNumberDbInstalled() {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "notifyOtaEmergencyNumberDbInstalled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9766,6 +10451,9 @@
     public void updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor) {
         enforceActiveEmergencySessionPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "updateOtaEmergencyNumberDbFilePath");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9783,6 +10471,9 @@
     public void resetOtaEmergencyNumberDbFilePath() {
         enforceActiveEmergencySessionPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CALLING, "resetOtaEmergencyNumberDbFilePath");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             for (Phone phone: PhoneFactory.getPhones()) {
@@ -9823,6 +10514,9 @@
     public boolean enableModemForSlot(int slotIndex, boolean enable) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "enableModemForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -9851,6 +10545,9 @@
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY, "isModemEnabledForSlot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             try {
@@ -9867,6 +10564,9 @@
     public void setMultiSimCarrierRestriction(boolean isMultiSimCarrierRestricted) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_CARRIERLOCK, "setMultiSimCarrierRestriction");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mTelephonySharedPreferences.edit()
@@ -9886,6 +10586,9 @@
             return TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE;
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isMultiSimSupported");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return isMultiSimSupportedInternal();
@@ -9937,6 +10640,10 @@
             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                     mApp, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, "switchMultiSimConfig");
         }
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "switchMultiSimConfig");
+
         final long identity = Binder.clearCallingIdentity();
 
         try {
@@ -9954,6 +10661,10 @@
     @Override
     public boolean isApplicationOnUicc(int subId, int appType) {
         enforceReadPrivilegedPermission("isApplicationOnUicc");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isApplicationOnUicc");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             return false;
@@ -9990,6 +10701,11 @@
                 "doesSwitchMultiSimConfigTriggerReboot")) {
             return false;
         }
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION,
+                "doesSwitchMultiSimConfigTriggerReboot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mPhoneConfigurationManager.isRebootRequiredForModemConfigChange();
@@ -10010,6 +10726,10 @@
         // Verify that the callingPackage belongs to the calling UID
         mApp.getSystemService(AppOpsManager.class)
                 .checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSlotsMapping");
+
         final long identity = Binder.clearCallingIdentity();
         List<UiccSlotMapping> slotMap = new ArrayList<>();
         try {
@@ -10054,11 +10774,14 @@
 
     /**
      * Get the current calling package name.
-     * @return the current calling package name
+     *
+     * @return the current calling package name, or null if there is no known package.
      */
     @Override
-    public String getCurrentPackageName() {
-        return mApp.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0];
+    public @Nullable String getCurrentPackageName() {
+        PackageManager pm = mApp.getPackageManager();
+        String[] packageNames = pm == null ? null : pm.getPackagesForUid(Binder.getCallingUid());
+        return packageNames == null ? null : packageNames[0];
     }
 
     /**
@@ -10080,6 +10803,9 @@
         enforceReadPrivilegedPermission("Needs READ_PRIVILEGED_PHONE_STATE for "
                 + "isDataEnabledForApn");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_DATA, "isDataEnabledForApn");
+
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10102,6 +10828,9 @@
     public boolean isApnMetered(@ApnType int apnType, int subId) {
         enforceReadPrivilegedPermission("isApnMetered");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isApnMetered");
+
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10119,6 +10848,10 @@
     public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
             int subscriptionId, IBooleanConsumer resultCallback) {
         enforceModifyPermission();
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setSystemSelectionChannels");
+
         long token = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10153,6 +10886,10 @@
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getSystemSelectionChannels");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "getSystemSelectionChannels");
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10171,6 +10908,10 @@
     @Override
     public boolean isMvnoMatched(int slotIndex, int mvnoType, @NonNull String mvnoMatchData) {
         enforceReadPrivilegedPermission("isMvnoMatched");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isMvnoMatched");
+
         return UiccController.getInstance().mvnoMatches(slotIndex, mvnoType, mvnoMatchData);
     }
 
@@ -10218,6 +10959,9 @@
 
     @Override
     public String getMmsUAProfUrl(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "getMmsUAProfUrl");
+
         //TODO investigate if this API should require proper permission check in R b/133791609
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10235,6 +10979,9 @@
 
     @Override
     public String getMmsUserAgent(int subId) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "getMmsUserAgent");
+
         //TODO investigate if this API should require proper permission check in R b/133791609
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10254,6 +11001,9 @@
     public boolean isMobileDataPolicyEnabled(int subscriptionId, int policy) {
         enforceReadPrivilegedPermission("isMobileDataPolicyEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isMobileDataPolicyEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10270,6 +11020,9 @@
             boolean enabled) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "setMobileDataPolicyEnabled");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -10324,10 +11077,19 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            // ProvisioningManager can not handle ServiceSpecificException.
-            // Throw the IllegalStateException and annotate ProvisioningManager.
-            throw new IllegalStateException("IMS not available on device.");
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                // ProvisioningManager can not handle ServiceSpecificException.
+                // Throw the IllegalStateException and annotate ProvisioningManager.
+                throw new IllegalStateException("IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION,
+                    "notifyRcsAutoConfigurationReceived");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -10342,6 +11104,9 @@
     public boolean isIccLockEnabled(int subId) {
         enforceReadPrivilegedPermission("isIccLockEnabled");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "isIccLockEnabled");
+
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -10371,6 +11136,9 @@
     public int setIccLockEnabled(int subId, boolean enabled, String password) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "setIccLockEnabled");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             return 0;
@@ -10403,6 +11171,9 @@
     public int changeIccLockPassword(int subId, String oldPassword, String newPassword) {
         enforceModifyPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "changeIccLockPassword");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             return 0;
@@ -10473,6 +11244,9 @@
             throw new SecurityException("Requires READ_PHONE_STATE permission.");
         }
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getEquivalentHomePlmns");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             throw new RuntimeException("phone is not available");
@@ -10489,6 +11263,10 @@
     @Override
     public boolean isRadioInterfaceCapabilitySupported(
             final @NonNull @TelephonyManager.RadioInterfaceCapability String capability) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS,
+                "isRadioInterfaceCapabilitySupported");
+
         Set<String> radioInterfaceCapabilities =
                 mRadioInterfaceCapabilities.getCapabilities();
         if (radioInterfaceCapabilities == null) {
@@ -10505,6 +11283,10 @@
                 Binder.getCallingUid(), "bootstrapAuthenticationRequest",
                 Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
                 Manifest.permission.MODIFY_PHONE_STATE);
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "bootstrapAuthenticationRequest");
+
         if (DBG) {
             log("bootstrapAuthenticationRequest, subId:" + subId + ", appType:"
                     + appType + ", NAF:" + nafUrl + ", sp:" + securityProtocol
@@ -10664,6 +11446,10 @@
         enforceModifyPermission();
 
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "sendThermalMitigationRequest");
+
         if (!getThermalMitigationAllowlist(getDefaultPhone().getContext())
                 .contains(callingPackage)) {
             throw new SecurityException("Calling package must be configured in the device config. "
@@ -10894,9 +11680,16 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "registerRcsProvisioningCallback");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -10925,10 +11718,19 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            // operation failed silently
-            Rlog.w(LOG_TAG, "IMS not available on device.");
-            return;
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                // operation failed silently
+                Rlog.w(LOG_TAG, "IMS not available on device.");
+                return;
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION,
+                    "unregisterRcsProvisioningCallback");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -10951,10 +11753,17 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            // ProvisioningManager can not handle ServiceSpecificException.
-            // Throw the IllegalStateException and annotate ProvisioningManager.
-            throw new IllegalStateException("IMS not available on device.");
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                // ProvisioningManager can not handle ServiceSpecificException.
+                // Throw the IllegalStateException and annotate ProvisioningManager.
+                throw new IllegalStateException("IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "triggerRcsReconfiguration");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -10976,9 +11785,16 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!isImsAvailableOnDevice()) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "IMS not available on device.");
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+                Binder.getCallingUserHandle())) {
+            if (!isImsAvailableOnDevice()) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "IMS not available on device.");
+            }
+        } else {
+            enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                    FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION, "setRcsClientConfiguration");
         }
 
         final long identity = Binder.clearCallingIdentity();
@@ -11392,6 +12208,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setSignalStrengthUpdateRequest");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "setSignalStrengthUpdateRequest");
+
         final int callingUid = Binder.getCallingUid();
         // Verify that tha callingPackage belongs to the calling UID
         mApp.getSystemService(AppOpsManager.class)
@@ -11418,6 +12237,9 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "clearSignalStrengthUpdateRequest");
 
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS, "clearSignalStrengthUpdateRequest");
+
         final int callingUid = Binder.getCallingUid();
         // Verify that tha callingPackage belongs to the calling UID
         mApp.getSystemService(AppOpsManager.class)
@@ -11484,6 +12306,10 @@
     @Override
     public PhoneCapability getPhoneCapability() {
         enforceReadPrivilegedPermission("getPhoneCapability");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY, "getPhoneCapability");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return mPhoneConfigurationManager.getCurrentPhoneCapability();
@@ -11502,6 +12328,9 @@
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         enforceRebootPermission();
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "prepareForUnattendedReboot");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return (int) sendRequest(CMD_PREPARE_UNATTENDED_REBOOT, null, workSource);
@@ -11522,6 +12351,10 @@
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID, "getSlicingConfig");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS,
+                "getSlicingConfig");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getDefaultPhone();
@@ -11550,6 +12383,9 @@
                     + "permission READ_BASIC_PHONE_STATE.");
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "isPremiumCapabilityAvailableForPurchase");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             loge("isPremiumCapabilityAvailableForPurchase: phone is null, subId=" + subId);
@@ -11592,6 +12428,9 @@
             throw new SecurityException("purchasePremiumCapability requires permission INTERNET.");
         }
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_DATA, "purchasePremiumCapability");
+
         Phone phone = getPhone(subId);
         if (phone == null) {
             try {
@@ -11727,8 +12566,9 @@
      * @return {@CellIdentity} last known cell identity {@CellIdentity}.
      *
      * Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
-     * com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID, otherwise throws
+     * {@link android.Manifest.permission#ACCESS_LAST_KNOWN_CELL_ID}, otherwise throws
      * SecurityException.
+     *
      * If there is current registered network this value will be same as the registered cell
      * identity. If the device goes out of service the previous cell identity is cached and
      * will be returned. If the cache age of the Cell identity is more than 24 hours
@@ -11804,6 +12644,27 @@
         return result;
     }
 
+    /**
+     * Get the aggregated satellite plmn list. This API collects plmn data from multiple sources,
+     * including carrier config, entitlement server, and config update.
+     *
+     * @param subId subId The subscription ID of the carrier.
+     *
+     * @return List of plmns for carrier satellite service. If no plmn is available, empty list will
+     * be returned.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @NonNull public List<String> getSatellitePlmnsForCarrier(int subId) {
+        enforceSatelliteCommunicationPermission("getSatellitePlmnsForCarrier");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.getSatellitePlmnsForCarrier(subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public void setVoiceServiceStateOverride(int subId, boolean hasService, String callingPackage) {
         // Only telecom (and shell, for CTS purposes) is allowed to call this method.
@@ -11872,6 +12733,10 @@
             boolean updateIfNeeded) {
         enforceInteractAcrossUsersPermission("getDefaultRespondViaMessageApplication");
 
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING,
+                "getDefaultRespondViaMessageApplication");
+
         Context context = getPhoneFromSubIdOrDefault(subId).getContext();
 
         UserHandle userHandle = null;
@@ -11952,6 +12817,17 @@
         }
     }
 
+    private void checkForNullCipherNotificationSupport() {
+        if (getHalVersion(HAL_SERVICE_NETWORK) < MIN_NULL_CIPHER_NOTIFICATION_VERSION) {
+            throw new UnsupportedOperationException(
+                    "Null cipher notification operations require HAL 2.2 or above");
+        }
+        if (!getDefaultPhone().isNullCipherNotificationSupported()) {
+            throw new UnsupportedOperationException(
+                    "Null cipher notification operations unsupported by modem");
+        }
+    }
+
     /**
      * Get the SIM state for the slot index.
      * For Remote-SIMs, this method returns {@link IccCardConstants.State#UNKNOWN}
@@ -11961,6 +12837,9 @@
     @Override
     @SimState
     public int getSimStateForSlotIndex(int slotIndex) {
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSimStateForSlotIndex");
+
         IccCardConstants.State simState;
         if (slotIndex < 0) {
             simState = IccCardConstants.State.UNKNOWN;
@@ -11990,22 +12869,26 @@
             long logcatStartTimestampMillis, boolean enableTelecomDump,
             boolean enableTelephonyDump) {
         DropBoxManager db = mApp.getSystemService(DropBoxManager.class);
-        TelephonyManager.EmergencyCallDiagnosticParams edp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        edp.setLogcatCollection(enableLogcat, logcatStartTimestampMillis);
-        edp.setTelephonyDumpSysCollection(enableTelephonyDump);
-        edp.setTelecomDumpSysCollection(enableTelecomDump);
-        Log.d(LOG_TAG, "persisting with Params " + edp.toString());
+        TelephonyManager.EmergencyCallDiagnosticData.Builder ecdDataBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticData.Builder();
+        ecdDataBuilder
+                .setTelecomDumpsysCollectionEnabled(enableTelecomDump)
+                .setTelephonyDumpsysCollectionEnabled(enableTelephonyDump);
+        if (enableLogcat) {
+            ecdDataBuilder.setLogcatCollectionStartTimeMillis(logcatStartTimestampMillis);
+        }
+        TelephonyManager.EmergencyCallDiagnosticData ecdData = ecdDataBuilder.build();
+        Log.d(LOG_TAG, "persisting with Params " + ecdData.toString());
         DiagnosticDataCollector ddc = new DiagnosticDataCollector(Runtime.getRuntime(),
                 Executors.newCachedThreadPool(), db,
                 mApp.getSystemService(ActivityManager.class).isLowRamDevice());
-        ddc.persistEmergencyDianosticData(new DataCollectorConfig.Adapter(), edp, dropboxTag);
+        ddc.persistEmergencyDianosticData(new DataCollectorConfig.Adapter(), ecdData, dropboxTag);
     }
 
     /**
      * Request telephony to persist state for debugging emergency call failures.
      *
-     * @param dropBoxTag                 Tag to use when persisting data to dropbox service.
+     * @param dropboxTag                 Tag to use when persisting data to dropbox service.
      * @param enableLogcat               whether to collect logcat output
      * @param logcatStartTimestampMillis timestamp from when logcat buffers would be persisted
      * @param enableTelecomDump          whether to collect telecom dumpsys
@@ -12036,6 +12919,10 @@
     public List<CellBroadcastIdRange> getCellBroadcastIdRanges(int subId) {
         mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
                 "getCellBroadcastIdRanges");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "getCellBroadcastIdRanges");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhone(subId).getCellBroadcastIdRanges();
@@ -12055,6 +12942,10 @@
             @Nullable IIntegerConsumer callback) {
         mApp.enforceCallingPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS,
                 "setCellBroadcastIdRanges");
+
+        enforceTelephonyFeatureWithException(getCurrentPackageName(),
+                PackageManager.FEATURE_TELEPHONY_MESSAGING, "setCellBroadcastIdRanges");
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhoneFromSubId(subId);
@@ -12102,16 +12993,50 @@
      * @param enableSatellite {@code true} to enable the satellite modem and
      *                        {@code false} to disable.
      * @param enableDemoMode {@code true} to enable demo mode and {@code false} to disable.
+     * @param isEmergency {@code true} to enable emergency mode, {@code false} otherwise.
      * @param callback The callback to get the result of the request.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
     public void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
-            @NonNull IIntegerConsumer callback) {
+            boolean isEmergency, @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("requestSatelliteEnabled");
-        mSatelliteController.requestSatelliteEnabled(subId, enableSatellite, enableDemoMode,
-                callback);
+        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);
+                        } else {
+                            loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                        }
+                    } else {
+                        result.accept(resultCode);
+                        return;
+                    }
+                    if (isAllowed) {
+                        mSatelliteController.requestSatelliteEnabled(
+                                subId, enableSatellite, enableDemoMode, callback);
+                    } else {
+                        result.accept(SATELLITE_RESULT_ACCESS_BARRED);
+                    }
+                }
+            };
+            mSatelliteAccessController.requestIsCommunicationAllowedForCurrentLocation(
+                    subId, resultReceiver);
+        } else {
+            // No need to check if satellite is allowed at current location when disabling satellite
+            mSatelliteController.requestSatelliteEnabled(
+                    subId, enableSatellite, enableDemoMode, callback);
+        }
     }
 
     /**
@@ -12146,6 +13071,22 @@
     }
 
     /**
+     * Request to get whether the satellite service is enabled with emergency mode.
+     *
+     * @param subId The subId of the subscription to check whether the satellite demo mode
+     *              is enabled for.
+     * @param result The result receiver that returns whether the satellite emergency mode is
+     *               enabled 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 requestIsEmergencyModeEnabled(int subId, @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("requestIsEmergencyModeEnabled");
+        result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+    }
+
+    /**
      * Request to get whether the satellite service is supported on the device.
      *
      * @param subId The subId of the subscription to check satellite service support for.
@@ -12315,7 +13256,7 @@
      */
     @Override
     @SatelliteManager.SatelliteResult public int registerForSatelliteModemStateChanged(int subId,
-            @NonNull ISatelliteStateCallback callback) {
+            @NonNull ISatelliteModemStateCallback callback) {
         enforceSatelliteCommunicationPermission("registerForSatelliteModemStateChanged");
         return mSatelliteController.registerForSatelliteModemStateChanged(subId, callback);
     }
@@ -12326,15 +13267,15 @@
      *
      * @param subId The subId of the subscription to unregister for satellite modem state changed.
      * @param callback The callback that was passed to
-     *                 {@link #registerForSatelliteModemStateChanged(int, ISatelliteStateCallback)}.
+     * {@link #registerForModemStateChanged(int, ISatelliteModemStateCallback)}.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public void unregisterForSatelliteModemStateChanged(int subId,
-            @NonNull ISatelliteStateCallback callback) {
-        enforceSatelliteCommunicationPermission("unregisterForSatelliteModemStateChanged");
-        mSatelliteController.unregisterForSatelliteModemStateChanged(subId, callback);
+    public void unregisterForModemStateChanged(int subId,
+            @NonNull ISatelliteModemStateCallback callback) {
+        enforceSatelliteCommunicationPermission("unregisterForModemStateChanged");
+        mSatelliteController.unregisterForModemStateChanged(subId, callback);
     }
 
     /**
@@ -12348,10 +13289,10 @@
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId,
+    @SatelliteManager.SatelliteResult public int registerForIncomingDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
-        enforceSatelliteCommunicationPermission("registerForSatelliteDatagram");
-        return mSatelliteController.registerForSatelliteDatagram(subId, callback);
+        enforceSatelliteCommunicationPermission("registerForIncomingDatagram");
+        return mSatelliteController.registerForIncomingDatagram(subId, callback);
     }
 
     /**
@@ -12360,15 +13301,15 @@
      *
      * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
      * @param callback The callback that was passed to
-     *                 {@link #registerForSatelliteDatagram(int, ISatelliteDatagramCallback)}.
+     *                 {@link #registerForIncomingDatagram(int, ISatelliteDatagramCallback)}.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public void unregisterForSatelliteDatagram(int subId,
+    public void unregisterForIncomingDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
-        enforceSatelliteCommunicationPermission("unregisterForSatelliteDatagram");
-        mSatelliteController.unregisterForSatelliteDatagram(subId, callback);
+        enforceSatelliteCommunicationPermission("unregisterForIncomingDatagram");
+        mSatelliteController.unregisterForIncomingDatagram(subId, callback);
     }
 
     /**
@@ -12383,10 +13324,9 @@
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
-    @Override
-    public void pollPendingSatelliteDatagrams(int subId, IIntegerConsumer callback) {
-        enforceSatelliteCommunicationPermission("pollPendingSatelliteDatagrams");
-        mSatelliteController.pollPendingSatelliteDatagrams(subId, callback);
+    public void pollPendingDatagrams(int subId, IIntegerConsumer callback) {
+        enforceSatelliteCommunicationPermission("pollPendingDatagrams");
+        mSatelliteController.pollPendingDatagrams(subId, callback);
     }
 
     /**
@@ -12408,12 +13348,12 @@
      * @throws SecurityException if the caller doesn't have required permission.
      */
     @Override
-    public void sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+    public void sendDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull IIntegerConsumer callback) {
-        enforceSatelliteCommunicationPermission("sendSatelliteDatagram");
-        mSatelliteController.sendSatelliteDatagram(subId, datagramType, datagram,
-                needFullScreenPointingUI, callback);
+        enforceSatelliteCommunicationPermission("sendDatagram");
+        mSatelliteController.sendDatagram(subId, datagramType, datagram, needFullScreenPointingUI,
+                callback);
     }
 
     /**
@@ -12428,11 +13368,10 @@
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public void requestIsSatelliteCommunicationAllowedForCurrentLocation(int subId,
+    public void requestIsCommunicationAllowedForCurrentLocation(int subId,
             @NonNull ResultReceiver result) {
-        enforceSatelliteCommunicationPermission(
-                "requestIsSatelliteCommunicationAllowedForCurrentLocation");
-        mSatelliteController.requestIsSatelliteCommunicationAllowedForCurrentLocation(subId,
+        enforceSatelliteCommunicationPermission("requestIsCommunicationAllowedForCurrentLocation");
+        mSatelliteAccessController.requestIsCommunicationAllowedForCurrentLocation(subId,
                 result);
     }
 
@@ -12478,13 +13417,13 @@
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
-    public void addSatelliteAttachRestrictionForCarrier(int subId,
+    public void addAttachRestrictionForCarrier(int subId,
             @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
             @NonNull IIntegerConsumer callback) {
-        enforceSatelliteCommunicationPermission("addSatelliteAttachRestrictionForCarrier");
+        enforceSatelliteCommunicationPermission("addAttachRestrictionForCarrier");
         final long identity = Binder.clearCallingIdentity();
         try {
-            mSatelliteController.addSatelliteAttachRestrictionForCarrier(subId, reason, callback);
+            mSatelliteController.addAttachRestrictionForCarrier(subId, reason, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -12501,14 +13440,13 @@
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
-    public void removeSatelliteAttachRestrictionForCarrier(int subId,
+    public void removeAttachRestrictionForCarrier(int subId,
             @SatelliteManager.SatelliteCommunicationRestrictionReason int reason,
             @NonNull IIntegerConsumer callback) {
-        enforceSatelliteCommunicationPermission("removeSatelliteAttachRestrictionForCarrier");
+        enforceSatelliteCommunicationPermission("removeAttachRestrictionForCarrier");
         final long identity = Binder.clearCallingIdentity();
         try {
-            mSatelliteController.removeSatelliteAttachRestrictionForCarrier(subId, reason,
-                    callback);
+            mSatelliteController.removeAttachRestrictionForCarrier(subId, reason, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -12524,13 +13462,13 @@
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
-    public @NonNull int[] getSatelliteAttachRestrictionReasonsForCarrier(
+    public @NonNull int[] getAttachRestrictionReasonsForCarrier(
             int subId) {
-        enforceSatelliteCommunicationPermission("getSatelliteAttachRestrictionReasonsForCarrier");
+        enforceSatelliteCommunicationPermission("getAttachRestrictionReasonsForCarrier");
         final long identity = Binder.clearCallingIdentity();
         try {
             Set<Integer> reasonSet =
-                    mSatelliteController.getSatelliteAttachRestrictionReasonsForCarrier(subId);
+                    mSatelliteController.getAttachRestrictionReasonsForCarrier(subId);
             return reasonSet.stream().mapToInt(i->i).toArray();
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -12618,12 +13556,12 @@
      * @throws SecurityException if the caller doesn't have required permission.
      */
     @Override
-    @SatelliteManager.SatelliteResult public int registerForSatelliteCapabilitiesChanged(
+    @SatelliteManager.SatelliteResult public int registerForCapabilitiesChanged(
             int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
-        enforceSatelliteCommunicationPermission("registerForSatelliteCapabilitiesChanged");
+        enforceSatelliteCommunicationPermission("registerForCapabilitiesChanged");
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mSatelliteController.registerForSatelliteCapabilitiesChanged(subId, callback);
+            return mSatelliteController.registerForCapabilitiesChanged(subId, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -12635,17 +13573,17 @@
      *
      * @param subId The subId of the subscription to unregister for satellite capabilities change.
      * @param callback The callback that was passed to.
-     * {@link #registerForSatelliteCapabilitiesChanged(int, ISatelliteCapabilitiesCallback)}.
+     * {@link #registerForCapabilitiesChanged(int, ISatelliteCapabilitiesCallback)}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
     @Override
-    public void unregisterForSatelliteCapabilitiesChanged(
-            int subId, @NonNull ISatelliteCapabilitiesCallback callback) {
-        enforceSatelliteCommunicationPermission("unregisterForSatelliteCapabilitiesChanged");
+    public void unregisterForCapabilitiesChanged(int subId,
+            @NonNull ISatelliteCapabilitiesCallback callback) {
+        enforceSatelliteCommunicationPermission("unregisterForCapabilitiesChanged");
         final long identity = Binder.clearCallingIdentity();
         try {
-            mSatelliteController.unregisterForSatelliteCapabilitiesChanged(subId, callback);
+            mSatelliteController.unregisterForCapabilitiesChanged(subId, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -12724,20 +13662,43 @@
     }
 
     /**
-     * This API can be used by only CTS to update the timeout duration in milliseconds whether
-     * the device is aligned with the satellite for demo mode
+     * This API can be used by only CTS to override the timeout durations used by the
+     * DatagramController module.
      *
      * @param timeoutMillis The timeout duration in millisecond.
      * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
      */
-    public boolean setSatelliteDeviceAlignedTimeoutDuration(long timeoutMillis) {
-        Log.d(LOG_TAG, "setDeviceAlignedTimeoutDuration - " + timeoutMillis);
+    public boolean setDatagramControllerTimeoutDuration(
+            boolean reset, int timeoutType, long timeoutMillis) {
+        Log.d(LOG_TAG, "setDatagramControllerTimeoutDuration - " + timeoutMillis + ", reset="
+                + reset + ", timeoutMillis=" + timeoutMillis);
         TelephonyPermissions.enforceShellOnly(
-                Binder.getCallingUid(), "setDeviceAlignedTimeoutDuration");
+                Binder.getCallingUid(), "setDatagramControllerTimeoutDuration");
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                "setDeviceAlignedTimeoutDuration");
-        return mSatelliteController.setSatelliteDeviceAlignedTimeoutDuration(timeoutMillis);
+                "setDatagramControllerTimeoutDuration");
+        return mSatelliteController.setDatagramControllerTimeoutDuration(
+                reset, timeoutType, timeoutMillis);
+    }
+
+    /**
+     * This API can be used by only CTS to override the timeout durations used by the
+     * SatelliteController module.
+     *
+     * @param timeoutMillis The timeout duration in millisecond.
+     * @return {@code true} if the timeout duration is set successfully, {@code false} otherwise.
+     */
+    public boolean setSatelliteControllerTimeoutDuration(
+            boolean reset, int timeoutType, long timeoutMillis) {
+        Log.d(LOG_TAG, "setSatelliteControllerTimeoutDuration - " + timeoutMillis + ", reset="
+                + reset + ", timeoutMillis=" + timeoutMillis);
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setSatelliteControllerTimeoutDuration");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setSatelliteControllerTimeoutDuration");
+        return mSatelliteController.setSatelliteControllerTimeoutDuration(
+                reset, timeoutType, timeoutMillis);
     }
 
     /**
@@ -12765,6 +13726,74 @@
     }
 
     /**
+     * This API can be used in only testing to override oem-enabled satellite provision status.
+     *
+     * @param reset {@code true} mean the overriding status should not be used, {@code false}
+     *              otherwise.
+     * @param isProvisioned The overriding provision status.
+     * @return {@code true} if the provision status is set successfully, {@code false} otherwise.
+     */
+    public boolean setOemEnabledSatelliteProvisionStatus(boolean reset, boolean isProvisioned) {
+        Log.d(LOG_TAG, "setOemEnabledSatelliteProvisionStatus - reset=" + reset
+                + ", isProvisioned=" + isProvisioned);
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setOemEnabledSatelliteProvisionStatus");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setOemEnabledSatelliteProvisionStatus");
+        return mSatelliteController.setOemEnabledSatelliteProvisionStatus(reset, isProvisioned);
+    }
+
+    /**
+     * This API should be used by only CTS tests to forcefully set telephony country codes.
+     *
+     * @return {@code true} if the country code is set successfully, {@code false} otherwise.
+     */
+    public boolean setCountryCodes(boolean reset, List<String> currentNetworkCountryCodes,
+            Map cachedNetworkCountryCodes, String locationCountryCode,
+            long locationCountryCodeTimestampNanos) {
+        Log.d(LOG_TAG, "setCountryCodes: currentNetworkCountryCodes="
+                + String.join(", ", currentNetworkCountryCodes)
+                + ", locationCountryCode=" + locationCountryCode
+                + ", locationCountryCodeTimestampNanos" + locationCountryCodeTimestampNanos
+                + ", reset=" + reset + ", cachedNetworkCountryCodes="
+                + String.join(", ", cachedNetworkCountryCodes.keySet()));
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setCachedLocationCountryCode");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setCachedLocationCountryCode");
+        return TelephonyCountryDetector.getInstance(getDefaultPhone().getContext()).setCountryCodes(
+                reset, currentNetworkCountryCodes, cachedNetworkCountryCodes, locationCountryCode,
+                locationCountryCodeTimestampNanos);
+    }
+
+    /**
+     * This API should be used by only CTS tests to override the overlay configs of satellite
+     * access controller.
+     *
+     * @param reset {@code true} mean the overridden configs should not be used, {@code false}
+     *              otherwise.
+     * @return {@code true} if the overlay configs are set successfully, {@code false} otherwise.
+     */
+    public boolean setSatelliteAccessControlOverlayConfigs(boolean reset, boolean isAllowed,
+            String s2CellFile, long locationFreshDurationNanos,
+            List<String> satelliteCountryCodes) {
+        Log.d(LOG_TAG, "setSatelliteAccessControlOverlayConfigs: reset=" + reset
+                + ", isAllowed" + isAllowed + ", s2CellFile=" + s2CellFile
+                + ", locationFreshDurationNanos=" + locationFreshDurationNanos
+                + ", satelliteCountryCodes=" + ((satelliteCountryCodes != null)
+                ? String.join(", ", satelliteCountryCodes) : null));
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setSatelliteAccessControlOverlayConfigs");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setSatelliteAccessControlOverlayConfigs");
+        return mSatelliteAccessController.setSatelliteAccessControlOverlayConfigs(reset, isAllowed,
+                s2CellFile, locationFreshDurationNanos, satelliteCountryCodes);
+    }
+
+    /**
      * This API can be used by only CTS to override the cached value for the device overlay config
      * value : config_send_satellite_datagram_to_modem_in_demo_mode, which determines whether
      * outgoing satellite datagrams should be sent to modem in demo mode.
@@ -12791,6 +13820,62 @@
     }
 
     /**
+     * Sets the service defined in ComponentName to be bound.
+     *
+     * This should only be used for testing.
+     * @return {@code true} if the DomainSelectionService to bind to was set,
+     *         {@code false} otherwise.
+     */
+    @Override
+    public boolean setDomainSelectionServiceOverride(ComponentName componentName) {
+        Log.i(LOG_TAG, "setDomainSelectionServiceOverride component=" + componentName);
+
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setDomainSelectionServiceOverride");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                getDefaultSubscription(), "setDomainSelectionServiceOverride");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                return DomainSelectionResolver.getInstance()
+                        .setDomainSelectionServiceOverride(componentName);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return false;
+    }
+
+    /**
+     * Clears the DomainSelectionService override.
+     *
+     * This should only be used for testing.
+     * @return {@code true} if the DomainSelectionService override was cleared,
+     *         {@code false} otherwise.
+     */
+    @Override
+    public boolean clearDomainSelectionServiceOverride() {
+        Log.i(LOG_TAG, "clearDomainSelectionServiceOverride");
+
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "clearDomainSelectionServiceOverride");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                getDefaultSubscription(), "clearDomainSelectionServiceOverride");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                return DomainSelectionResolver.getInstance()
+                        .clearDomainSelectionServiceOverride();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return false;
+    }
+
+    /**
      * Enable or disable notifications sent for cellular identifier disclosure events.
      *
      * Disclosure events are defined as instances where a device has sent a cellular identifier
@@ -12802,7 +13887,7 @@
      * @throws UnsupportedOperationException if the modem does not support this feature.
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    public void enableCellularIdentifierDisclosureNotifications(boolean enable) {
+    public void setEnableCellularIdentifierDisclosureNotifications(boolean enable) {
         enforceModifyPermission();
         checkForIdentifierDisclosureNotificationSupport();
 
@@ -12824,13 +13909,56 @@
      * @throws UnsupportedOperationException if the modem does not support this feature.
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isCellularIdentifierDisclosureNotificationEnabled() {
+    public boolean isCellularIdentifierDisclosureNotificationsEnabled() {
         enforceReadPrivilegedPermission("isCellularIdentifierDisclosureNotificationEnabled");
         checkForIdentifierDisclosureNotificationSupport();
         return getDefaultPhone().getIdentifierDisclosureNotificationsPreferenceEnabled();
     }
 
     /**
+     * Enables or disables notifications sent when cellular null cipher or integrity algorithms
+     * are in use by the cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setNullCipherNotificationsEnabled(boolean enable) {
+        enforceModifyPermission();
+        checkForNullCipherNotificationSupport();
+
+        SharedPreferences.Editor editor = mTelephonySharedPreferences.edit();
+        editor.putBoolean(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED, enable);
+        editor.apply();
+
+        // Each phone instance is responsible for updating its respective modem immediately
+        // after a preference change.
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.handleNullCipherNotificationPreferenceChanged();
+        }
+    }
+
+    /**
+     * Get whether notifications are enabled for null cipher or integrity algorithms in use by the
+     * cellular modem.
+     *
+     * @throws IllegalStateException if the Telephony process is not currently available
+     * @throws SecurityException if the caller does not have the required privileges
+     * @throws UnsupportedOperationException if the modem does not support reporting on ciphering
+     * and integrity algorithms in use
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isNullCipherNotificationsEnabled() {
+        enforceReadPrivilegedPermission("isNullCipherNotificationsEnabled");
+        checkForNullCipherNotificationSupport();
+        return getDefaultPhone().getNullCipherNotificationsPreferenceEnabled();
+    }
+
+    /**
      * Check whether the caller (or self, if not processing an IPC) can read device identifiers.
      *
      * <p>This method behaves in one of the following ways:
@@ -12881,4 +14009,51 @@
             return mCarrierId;
         }
     }
+
+    /*
+    * PhoneInterfaceManager is a singleton. Unit test calls the init() with context.
+    * But the context that is passed in is unused if the phone app is already alive.
+    * In this case PackageManager object is different in PhoneInterfaceManager and Unit test.
+    */
+    @VisibleForTesting
+    public void setPackageManager(PackageManager packageManager) {
+        mPackageManager = packageManager;
+    }
+
+    /*
+     * PhoneInterfaceManager is a singleton. Unit test calls the init() with FeatureFlags.
+     * But the FeatureFlags that is passed in is unused if the phone app is already alive.
+     * In this case FeatureFlags object is different in PhoneInterfaceManager and Unit test.
+     */
+    @VisibleForTesting
+    public void setFeatureFlags(FeatureFlags featureFlags) {
+        mFeatureFlags = featureFlags;
+    }
+
+    /**
+     * Make sure the device has required telephony feature
+     *
+     * @throws UnsupportedOperationException if the device does not have required telephony feature
+     */
+    private void enforceTelephonyFeatureWithException(@Nullable String callingPackage,
+            @NonNull String telephonyFeature, @NonNull String methodName) {
+        if (callingPackage == null || mPackageManager == null) {
+            return;
+        }
+
+        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
+                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+                Binder.getCallingUserHandle())
+                || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            // Skip to check associated telephony feature,
+            // if compatibility change is not enabled for the current process or
+            // the SDK version of vendor partition is less than Android V.
+            return;
+        }
+
+        if (!mPackageManager.hasSystemFeature(telephonyFeature)) {
+            throw new UnsupportedOperationException(
+                    methodName + " is unsupported without " + telephonyFeature);
+        }
+    }
 }
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 5986a7c..c55cc6c 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -24,6 +24,9 @@
 import static java.util.Map.entry;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Binder;
@@ -65,6 +68,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -187,13 +191,25 @@
             "set-satellite-listening-timeout-duration";
     private static final String SET_SATELLITE_POINTING_UI_CLASS_NAME =
             "set-satellite-pointing-ui-class-name";
-    private static final String SET_SATELLITE_DEVICE_ALIGNED_TIMEOUT_DURATION =
-            "set-satellite-device-aligned-timeout-duration";
+    private static final String SET_DATAGRAM_CONTROLLER_TIMEOUT_DURATION =
+            "set-datagram-controller-timeout-duration";
+
+    private static final String SET_SATELLITE_CONTROLLER_TIMEOUT_DURATION =
+            "set-satellite-controller-timeout-duration";
     private static final String SET_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE =
             "set-emergency-call-to-satellite-handover-type";
+    private static final String SET_COUNTRY_CODES = "set-country-codes";
+    private static final String SET_SATELLITE_ACCESS_CONTROL_OVERLAY_CONFIGS =
+            "set-satellite-access-control-overlay-configs";
+    private static final String SET_OEM_ENABLED_SATELLITE_PROVISION_STATUS =
+            "set-oem-enabled-satellite-provision-status";
     private static final String SET_SHOULD_SEND_DATAGRAM_TO_MODEM_IN_DEMO_MODE =
             "set-should-send-datagram-to-modem-in-demo-mode";
 
+    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";
+
     private static final String INVALID_ENTRY_ERROR = "An emergency number (only allow '0'-'9', "
             + "'*', '#' or '+') needs to be specified after -a in the command ";
 
@@ -374,6 +390,8 @@
                 return setCarrierServicePackageOverride();
             case CLEAR_CARRIER_SERVICE_PACKAGE_OVERRIDE:
                 return clearCarrierServicePackageOverride();
+            case DOMAIN_SELECTION_SUBCOMMAND:
+                return handleDomainSelectionCommand();
             case SET_SATELLITE_SERVICE_PACKAGE_NAME:
                 return handleSetSatelliteServicePackageNameCommand();
             case SET_SATELLITE_GATEWAY_SERVICE_PACKAGE_NAME:
@@ -382,12 +400,20 @@
                 return handleSetSatelliteListeningTimeoutDuration();
             case SET_SATELLITE_POINTING_UI_CLASS_NAME:
                 return handleSetSatellitePointingUiClassNameCommand();
-            case SET_SATELLITE_DEVICE_ALIGNED_TIMEOUT_DURATION:
-                return handleSettSatelliteDeviceAlignedTimeoutDuration();
+            case SET_DATAGRAM_CONTROLLER_TIMEOUT_DURATION:
+                return handleSetDatagramControllerTimeoutDuration();
+            case SET_SATELLITE_CONTROLLER_TIMEOUT_DURATION:
+                return handleSetSatelliteControllerTimeoutDuration();
             case SET_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE:
                 return handleSetEmergencyCallToSatelliteHandoverType();
             case SET_SHOULD_SEND_DATAGRAM_TO_MODEM_IN_DEMO_MODE:
                 return handleSetShouldSendDatagramToModemInDemoMode();
+            case SET_SATELLITE_ACCESS_CONTROL_OVERLAY_CONFIGS:
+                return handleSetSatelliteAccessControlOverlayConfigs();
+            case SET_COUNTRY_CODES:
+                return handleSetCountryCodes();
+            case SET_OEM_ENABLED_SATELLITE_PROVISION_STATUS:
+                return handleSetOemEnabledSatelliteProvisionStatus();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -442,6 +468,7 @@
         onHelpRadio();
         onHelpImei();
         onHelpSatellite();
+        onHelpDomainSelection();
     }
 
     private void onHelpD2D() {
@@ -795,6 +822,29 @@
         pw.println("          If no option is specified, override is disabled.");
         pw.println("      -d: the delay in seconds in sending EVENT_DISPLAY_EMERGENCY_MESSAGE.");
         pw.println("          If no option is specified, there is no delay in sending the event.");
+        pw.println("  set-satellite-access-control-overlay-configs [-r -a -f SATELLITE_S2_FILE ");
+        pw.println("    -d LOCATION_FRESH_DURATION_NANOS -c COUNTRY_CODES] Override the overlay");
+        pw.println("    configs of satellite access controller.");
+        pw.println("    Options are:");
+        pw.println("      -r: clear the overriding. Absent means enable overriding.");
+        pw.println("      -a: the country codes is an allowed list. Absent means disallowed.");
+        pw.println("      -f: the satellite s2 file.");
+        pw.println("      -d: the location fresh duration nanos.");
+        pw.println("      -c: the list of satellite country codes separated by comma.");
+        pw.println("  set-country-codes [-r -n CURRENT_NETWORK_COUNTRY_CODES -c");
+        pw.println("    CACHED_NETWORK_COUNTRY_CODES -l LOCATION_COUNTRY_CODE -t");
+        pw.println("    LOCATION_COUNTRY_CODE_TIMESTAMP] ");
+        pw.println("    Override the cached location country code and its update timestamp. ");
+        pw.println("    Options are:");
+        pw.println("      -r: clear the overriding. Absent means enable overriding.");
+        pw.println("      -n: the current network country code ISOs.");
+        pw.println("      -c: the cached network country code ISOs.");
+        pw.println("      -l: the location country code ISO.");
+        pw.println("      -t: the update timestamp nanos of the location country code.");
+        pw.println("  set-oem-enabled-satellite-provision-status [-p true/false]");
+        pw.println("    Sets the OEM-enabled satellite provision status. Options are:");
+        pw.println("      -p: the overriding satellite provision status. If no option is ");
+        pw.println("          specified, reset the overridden provision status.");
     }
 
     private void onHelpImei() {
@@ -806,6 +856,15 @@
         pw.println("          is specified, it will choose the default voice SIM slot.");
     }
 
+    private void onHelpDomainSelection() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Domain Selection Commands:");
+        pw.println("  domainselection set-dss-override COMPONENT_NAME");
+        pw.println("    Sets the service defined in COMPONENT_NAME to be bound");
+        pw.println("  domainselection clear-dss-override");
+        pw.println("    Clears DomainSelectionService override.");
+    }
+
     private int handleImsCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -3314,31 +3373,85 @@
         return 0;
     }
 
-    private int handleSettSatelliteDeviceAlignedTimeoutDuration() {
+    private int handleSetDatagramControllerTimeoutDuration() {
         PrintWriter errPw = getErrPrintWriter();
+        boolean reset = false;
+        int timeoutType = 0;
         long timeoutMillis = 0;
 
         String opt;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "-t": {
+                case "-d": {
                     timeoutMillis = Long.parseLong(getNextArgRequired());
                     break;
                 }
+                case "-r": {
+                    reset = true;
+                    break;
+                }
+                case "-t": {
+                    timeoutType = Integer.parseInt(getNextArgRequired());
+                    break;
+                }
             }
         }
-        Log.d(LOG_TAG, "handleSettSatelliteDeviceAlignedTimeoutDuration: timeoutMillis="
-                + timeoutMillis);
+        Log.d(LOG_TAG, "setDatagramControllerTimeoutDuration: timeoutMillis="
+                + timeoutMillis + ", reset=" + reset + ", timeoutType=" + timeoutType);
 
         try {
-            boolean result = mInterface.setSatelliteDeviceAlignedTimeoutDuration(timeoutMillis);
+            boolean result = mInterface.setDatagramControllerTimeoutDuration(
+                    reset, timeoutType, timeoutMillis);
             if (VDBG) {
-                Log.v(LOG_TAG, "setSatelliteDeviceAlignedTimeoutDuration " + timeoutMillis
+                Log.v(LOG_TAG, "setDatagramControllerTimeoutDuration " + timeoutMillis
                         + ", result = " + result);
             }
             getOutPrintWriter().println(result);
         } catch (RemoteException e) {
-            Log.w(LOG_TAG, "setSatelliteDeviceAlignedTimeoutDuration: " + timeoutMillis
+            Log.w(LOG_TAG, "setDatagramControllerTimeoutDuration: " + timeoutMillis
+                    + ", error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleSetSatelliteControllerTimeoutDuration() {
+        PrintWriter errPw = getErrPrintWriter();
+        boolean reset = false;
+        int timeoutType = 0;
+        long timeoutMillis = 0;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-d": {
+                    timeoutMillis = Long.parseLong(getNextArgRequired());
+                    break;
+                }
+                case "-r": {
+                    reset = true;
+                    break;
+                }
+                case "-t": {
+                    timeoutType = Integer.parseInt(getNextArgRequired());
+                    break;
+                }
+            }
+        }
+        Log.d(LOG_TAG, "setSatelliteControllerTimeoutDuration: timeoutMillis="
+                + timeoutMillis + ", reset=" + reset + ", timeoutType=" + timeoutType);
+
+        try {
+            boolean result = mInterface.setSatelliteControllerTimeoutDuration(
+                    reset, timeoutType, timeoutMillis);
+            if (VDBG) {
+                Log.v(LOG_TAG, "setSatelliteControllerTimeoutDuration " + timeoutMillis
+                        + ", result = " + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "setSatelliteControllerTimeoutDuration: " + timeoutMillis
                     + ", error = " + e.getMessage());
             errPw.println("Exception: " + e.getMessage());
             return -1;
@@ -3394,6 +3507,186 @@
         return 0;
     }
 
+    private int handleSetSatelliteAccessControlOverlayConfigs() {
+        PrintWriter errPw = getErrPrintWriter();
+        boolean reset = false;
+        boolean isAllowed = false;
+        String s2CellFile = null;
+        long locationFreshDurationNanos = 0;
+        List<String> satelliteCountryCodes = null;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-r": {
+                    reset = true;
+                    break;
+                }
+                case "-a": {
+                    isAllowed = true;
+                    break;
+                }
+                case "-f": {
+                    s2CellFile = getNextArgRequired();
+                    break;
+                }
+                case "-d": {
+                    locationFreshDurationNanos = Long.parseLong(getNextArgRequired());
+                    break;
+                }
+                case "-c": {
+                    String countryCodeStr = getNextArgRequired();
+                    satelliteCountryCodes = Arrays.asList(countryCodeStr.split(","));
+                    break;
+                }
+            }
+        }
+        Log.d(LOG_TAG, "handleSetSatelliteAccessControlOverlayConfigs: reset=" + reset
+                + ", isAllowed=" + isAllowed + ", s2CellFile=" + s2CellFile
+                + ", locationFreshDurationNanos=" + locationFreshDurationNanos
+                + ", satelliteCountryCodes=" + satelliteCountryCodes);
+
+        try {
+            boolean result = mInterface.setSatelliteAccessControlOverlayConfigs(reset, isAllowed,
+                    s2CellFile, locationFreshDurationNanos, satelliteCountryCodes);
+            if (VDBG) {
+                Log.v(LOG_TAG, "setSatelliteAccessControlOverlayConfigs result =" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "setSatelliteAccessControlOverlayConfigs: ex=" + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleSetCountryCodes() {
+        PrintWriter errPw = getErrPrintWriter();
+        List<String> currentNetworkCountryCodes = new ArrayList<>();
+        String locationCountryCode = null;
+        long locationCountryCodeTimestampNanos = 0;
+        Map<String, Long> cachedNetworkCountryCodes = new HashMap<>();
+        boolean reset = false;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-r": {
+                    reset = true;
+                    break;
+                }
+                case "-n": {
+                    String countryCodeStr = getNextArgRequired();
+                    currentNetworkCountryCodes = Arrays.asList(countryCodeStr.split(","));
+                    break;
+                }
+                case "-c": {
+                    String cachedNetworkCountryCodeStr = getNextArgRequired();
+                    cachedNetworkCountryCodes = parseStringLongMap(cachedNetworkCountryCodeStr);
+                    break;
+                }
+                case "-l": {
+                    locationCountryCode = getNextArgRequired();
+                    break;
+                }
+                case "-t": {
+                    locationCountryCodeTimestampNanos = Long.parseLong(getNextArgRequired());
+                    break;
+                }
+            }
+        }
+        Log.d(LOG_TAG, "setCountryCodes: locationCountryCode="
+                + locationCountryCode + ", locationCountryCodeTimestampNanos="
+                + locationCountryCodeTimestampNanos + ", currentNetworkCountryCodes="
+                + currentNetworkCountryCodes);
+
+        try {
+            boolean result = mInterface.setCountryCodes(reset, currentNetworkCountryCodes,
+                    cachedNetworkCountryCodes, locationCountryCode,
+                    locationCountryCodeTimestampNanos);
+            if (VDBG) {
+                Log.v(LOG_TAG, "setCountryCodes result =" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "setCountryCodes: ex=" + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleSetOemEnabledSatelliteProvisionStatus() {
+        PrintWriter errPw = getErrPrintWriter();
+        boolean isProvisioned = false;
+        boolean reset = true;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-p": {
+                    try {
+                        isProvisioned = Boolean.parseBoolean(getNextArgRequired());
+                        reset = false;
+                    } catch (Exception e) {
+                        errPw.println("setOemEnabledSatelliteProvisionStatus requires a boolean "
+                                + "after -p indicating provision status");
+                        return -1;
+                    }
+                }
+            }
+        }
+        Log.d(LOG_TAG, "setOemEnabledSatelliteProvisionStatus: reset=" + reset
+                + ", isProvisioned=" + isProvisioned);
+
+        try {
+            boolean result = mInterface.setOemEnabledSatelliteProvisionStatus(reset, isProvisioned);
+            if (VDBG) {
+                Log.v(LOG_TAG, "setOemEnabledSatelliteProvisionStatus result = " + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "setOemEnabledSatelliteProvisionStatus: error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    /**
+     * Sample inputStr = "US,UK,CA;2,1,3"
+     * Sample output: {[US,2], [UK,1], [CA,3]}
+     */
+    @NonNull private Map<String, Long> parseStringLongMap(@Nullable String inputStr) {
+        Map<String, Long> result = new HashMap<>();
+        if (!TextUtils.isEmpty(inputStr)) {
+            String[] stringLongArr = inputStr.split(";");
+            if (stringLongArr.length != 2) {
+                Log.e(LOG_TAG, "parseStringLongMap: invalid inputStr=" + inputStr);
+                return result;
+            }
+
+            String[] stringArr = stringLongArr[0].split(",");
+            String[] longArr = stringLongArr[1].split(",");
+            if (stringArr.length != longArr.length) {
+                Log.e(LOG_TAG, "parseStringLongMap: invalid inputStr=" + inputStr);
+                return result;
+            }
+
+            for (int i = 0; i < stringArr.length; i++) {
+                try {
+                    result.put(stringArr[i], Long.parseLong(longArr[i]));
+                } catch (Exception ex) {
+                    Log.e(LOG_TAG, "parseStringLongMap: invalid inputStr=" + inputStr
+                            + ", ex=" + ex);
+                    return result;
+                }
+            }
+        }
+        return result;
+    }
+
     private int handleCarrierRestrictionStatusCommand() {
         try {
             String MOCK_MODEM_SERVICE_NAME = "android.telephony.mockmodem.MockModemService";
@@ -3527,6 +3820,66 @@
         return 0;
     }
 
+    private int handleDomainSelectionCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpDomainSelection();
+            return 0;
+        }
+
+        switch (arg) {
+            case DOMAIN_SELECTION_SET_SERVICE_OVERRIDE: {
+                return handleDomainSelectionSetServiceOverrideCommand();
+            }
+            case DOMAIN_SELECTION_CLEAR_SERVICE_OVERRIDE: {
+                return handleDomainSelectionClearServiceOverrideCommand();
+            }
+        }
+
+        return -1;
+    }
+
+    // domainselection set-dss-override
+    private int handleDomainSelectionSetServiceOverrideCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+
+        String componentName = getNextArg();
+
+        try {
+            boolean result = mInterface.setDomainSelectionServiceOverride(
+                    ComponentName.unflattenFromString(componentName));
+            if (VDBG) {
+                Log.v(LOG_TAG, "domainselection set-dss-override "
+                        + componentName + ", result=" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "domainselection set-dss-override "
+                    + componentName + ", error=" + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    // domainselection clear-dss-override
+    private int handleDomainSelectionClearServiceOverrideCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+
+        try {
+            boolean result = mInterface.clearDomainSelectionServiceOverride();
+            if (VDBG) {
+                Log.v(LOG_TAG, "domainselection clear-dss-override result=" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "domainselection clear-dss-override error=" + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     /**
      * Building the string that can be used to build the JsonObject which supports to stub the data
      * in CarrierAllowListInfo for CTS testing. sample format is like
diff --git a/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java b/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java
index 62fbd18..4490460 100644
--- a/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessController.java
@@ -63,9 +63,9 @@
         return new S2RangeSatelliteOnDeviceAccessController(reader, s2Level);
     }
 
-    @Override
-    public LocationToken createLocationTokenForLatLng(double latDegrees, double lngDegrees) {
-        return new LocationTokenImpl(getS2CellId(latDegrees, lngDegrees).id());
+    public static LocationToken createLocationTokenForLatLng(
+            double latDegrees, double lngDegrees, int s2Level) {
+        return new LocationTokenImpl(getS2CellId(latDegrees, lngDegrees, s2Level).id());
     }
 
     @Override
@@ -78,6 +78,11 @@
         return isSatCommunicationAllowedAtLocation(locationTokenImpl.getS2CellId());
     }
 
+    @Override
+    public int getS2Level() {
+        return mS2Level;
+    }
+
     private boolean isSatCommunicationAllowedAtLocation(long s2CellId) throws IOException {
         S2LevelRange entry = mSatS2RangeFileReader.findEntryByCellId(s2CellId);
         if (mSatS2RangeFileReader.isAllowedList()) {
@@ -91,12 +96,12 @@
         }
     }
 
-    private S2CellId getS2CellId(double latDegrees, double lngDegrees) {
+    private static S2CellId getS2CellId(double latDegrees, double lngDegrees, int s2Level) {
         // Create the leaf S2 cell containing the given S2LatLng
         S2CellId cellId = S2CellId.fromLatLng(S2LatLng.fromDegrees(latDegrees, lngDegrees));
 
         // Return the S2 cell at the expected S2 level
-        return cellId.parent(mS2Level);
+        return cellId.parent(s2Level);
     }
 
     @Override
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
index 7f9c1aa..36280e9 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
@@ -16,17 +16,63 @@
 
 package com.android.phone.satellite.accesscontrol;
 
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+
+import android.annotation.ArrayRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.provider.DeviceConfig;
+import android.telecom.TelecomManager;
+import android.telephony.AnomalyReporter;
 import android.telephony.Rlog;
-import android.telephony.satellite.SatelliteManager;
+import android.text.TextUtils;
+import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyCountryDetector;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.SatelliteConfig;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.phone.PhoneGlobals;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * This module is responsible for making sure that satellite communication can be used by devices
@@ -34,34 +80,147 @@
  */
 public class SatelliteAccessController extends Handler {
     private static final String TAG = "SatelliteAccessController";
+    /**
+     * UUID to report an anomaly when getting an exception in looking up on-device data for the
+     * current location.
+     */
+    private static final String UUID_ON_DEVICE_LOOKUP_EXCEPTION =
+            "dbea1641-630e-4780-9f25-8337ba6c3563";
+    /**
+     * UUID to report an anomaly when getting an exception in creating the on-device access
+     * controller.
+     */
+    private static final String UUID_CREATE_ON_DEVICE_ACCESS_CONTROLLER_EXCEPTION =
+            "3ac767d8-2867-4d60-97c2-ae9d378a5521";
+    protected static final long WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS =
+            TimeUnit.SECONDS.toMillis(180);
+    protected static final long KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS =
+            TimeUnit.MINUTES.toMillis(30);
+    protected static final int DEFAULT_S2_LEVEL = 12;
+    private static final int DEFAULT_LOCATION_FRESH_DURATION_SECONDS = 600;
+    private static final boolean DEFAULT_SATELLITE_ACCESS_ALLOW = true;
+    private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+    private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
+    private static final boolean DEBUG = !"user".equals(Build.TYPE);
+    private static final int MAX_CACHE_SIZE = 50;
 
     private static final int CMD_IS_SATELLITE_COMMUNICATION_ALLOWED = 1;
+    protected static final int EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT = 2;
+    protected static final int EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT = 3;
+    protected static final int EVENT_CONFIG_DATA_UPDATED = 4;
+
+    private static SatelliteAccessController sInstance;
 
     /** Feature flags to control behavior and errors. */
     @NonNull private final FeatureFlags mFeatureFlags;
-    @Nullable private final SatelliteOnDeviceAccessController mSatelliteOnDeviceAccessController;
+    @GuardedBy("mLock")
+    @Nullable protected SatelliteOnDeviceAccessController mSatelliteOnDeviceAccessController;
+    @NonNull private final LocationManager mLocationManager;
+    @NonNull private final TelecomManager mTelecomManager;
+    @NonNull private final TelephonyCountryDetector mCountryDetector;
+    @NonNull private final SatelliteController mSatelliteController;
+    @NonNull private final ResultReceiver mInternalSatelliteAllowResultReceiver;
+    @NonNull protected final Object mLock = new Object();
+    @GuardedBy("mLock")
+    @NonNull
+    private final Set<ResultReceiver> mSatelliteAllowResultReceivers = new HashSet<>();
+    @NonNull private List<String> mSatelliteCountryCodes;
+    private boolean mIsSatelliteAllowAccessControl;
+    @Nullable private File mSatelliteS2CellFile;
+    private long mLocationFreshDurationNanos;
+    @GuardedBy("mLock")
+    private boolean mIsOverlayConfigOverridden = false;
+    @NonNull private List<String> mOverriddenSatelliteCountryCodes;
+    private boolean mOverriddenIsSatelliteAllowAccessControl;
+    @Nullable private File mOverriddenSatelliteS2CellFile;
+    private long mOverriddenLocationFreshDurationNanos;
+    @GuardedBy("mLock")
+    @NonNull
+    private final Map<SatelliteOnDeviceAccessController.LocationToken, Boolean>
+            mCachedAccessRestrictionMap = new LinkedHashMap<>() {
+                @Override
+                protected boolean removeEldestEntry(
+                        Entry<SatelliteOnDeviceAccessController.LocationToken, Boolean> eldest) {
+                    return size() > MAX_CACHE_SIZE;
+                }
+            };
+    @GuardedBy("mLock")
+    @Nullable
+    CancellationSignal mLocationRequestCancellationSignal = null;
+    private int mS2Level = DEFAULT_S2_LEVEL;
+    @GuardedBy("mLock")
+    @Nullable private Location mFreshLastKnownLocation = null;
+
+    /** These are used for CTS test */
+    private Path mCtsSatS2FilePath = null;
+    private static final String GOOGLE_US_SAN_SAT_S2_FILE_NAME = "google_us_san_sat_s2.dat";
 
     /**
      * Create a SatelliteAccessController instance.
      *
+     * @param context The context associated with the {@link SatelliteAccessController} instance.
      * @param featureFlags The FeatureFlags that are supported.
+     * @param locationManager The LocationManager for querying current location of the device.
      * @param looper The Looper to run the SatelliteAccessController on.
-     * @param satelliteOnDeviceAccessController The location-based satellite restriction lookup.
+     * @param satelliteOnDeviceAccessController The on-device satellite access controller instance.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public SatelliteAccessController(@NonNull FeatureFlags featureFlags, @NonNull Looper looper,
-            @Nullable SatelliteOnDeviceAccessController satelliteOnDeviceAccessController) {
+    protected SatelliteAccessController(@NonNull Context context,
+            @NonNull FeatureFlags featureFlags, @NonNull Looper looper,
+            @NonNull LocationManager locationManager, @NonNull TelecomManager telecomManager,
+            @Nullable SatelliteOnDeviceAccessController satelliteOnDeviceAccessController,
+            @Nullable File s2CellFile) {
         super(looper);
         mFeatureFlags = featureFlags;
+        mLocationManager = locationManager;
+        mTelecomManager = telecomManager;
         mSatelliteOnDeviceAccessController = satelliteOnDeviceAccessController;
+        mCountryDetector = TelephonyCountryDetector.getInstance(context);
+        mSatelliteController = SatelliteController.getInstance();
+        loadOverlayConfigs(context);
+        mSatelliteController.registerForConfigUpdateChanged(this, EVENT_CONFIG_DATA_UPDATED,
+                context);
+        if (s2CellFile != null) {
+            mSatelliteS2CellFile = s2CellFile;
+        }
+        mInternalSatelliteAllowResultReceiver = new ResultReceiver(this) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                handleSatelliteAllowResultFromSatelliteController(resultCode, resultData);
+            }
+        };
+        // Init the SatelliteOnDeviceAccessController so that the S2 level can be cached
+        initSatelliteOnDeviceAccessController();
+    }
+
+    /** @return the singleton instance of {@link SatelliteAccessController} */
+    public static synchronized SatelliteAccessController getOrCreateInstance(
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (sInstance == null) {
+            HandlerThread handlerThread = new HandlerThread("SatelliteAccessController");
+            handlerThread.start();
+            sInstance = new SatelliteAccessController(context, featureFlags,
+                    handlerThread.getLooper(), context.getSystemService(LocationManager.class),
+                    context.getSystemService(TelecomManager.class), null, null);
+        }
+        return sInstance;
     }
 
     @Override
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case CMD_IS_SATELLITE_COMMUNICATION_ALLOWED:
-                handleRequestIsSatelliteCommunicationAllowedForCurrentLocation(
-                        (ResultReceiver) msg.obj);
+                handleCmdIsSatelliteAllowedForCurrentLocation(
+                        (Pair<Integer, ResultReceiver>) msg.obj);
+                break;
+            case EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT:
+                handleWaitForCurrentLocationTimedOutEvent();
+                break;
+            case EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT:
+                cleanupOnDeviceAccessControllerResources();
+                break;
+            case EVENT_CONFIG_DATA_UPDATED:
+                updateSatelliteConfigData((Context) msg.obj);
                 break;
             default:
                 logw("SatelliteAccessControllerHandler: unexpected message code: " + msg.what);
@@ -72,23 +231,730 @@
     /**
      * Request to get whether satellite communication is allowed for the current location.
      *
+     * @param subId The subId of the subscription to check whether satellite communication is
+     *              allowed for the current location for.
      * @param result The result receiver that returns whether satellite communication is allowed
      *               for the current location if the request is successful or an error code
      *               if the request failed.
      */
-    public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
+    public void requestIsCommunicationAllowedForCurrentLocation(int subId,
             @NonNull ResultReceiver result) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             logd("oemEnabledSatelliteFlag is disabled");
-            result.send(SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
             return;
         }
-        sendRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED, result);
+        sendRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED, new Pair<>(subId, result));
     }
 
-    private void handleRequestIsSatelliteCommunicationAllowedForCurrentLocation(
-            @NonNull ResultReceiver result) {
-        // To be implemented
+    /**
+     * This API should be used by only CTS tests to override the overlay configs of satellite
+     * access controller.
+     */
+    public boolean setSatelliteAccessControlOverlayConfigs(boolean reset, boolean isAllowed,
+            @Nullable String s2CellFile, long locationFreshDurationNanos,
+            @Nullable List<String> satelliteCountryCodes) {
+        if (!isMockModemAllowed()) {
+            logd("setSatelliteAccessControllerOverlayConfigs: mock modem is not allowed");
+            return false;
+        }
+        logd("setSatelliteAccessControlOverlayConfigs: reset=" + reset
+                + ", isAllowed" + isAllowed + ", s2CellFile=" + s2CellFile
+                + ", locationFreshDurationNanos=" + locationFreshDurationNanos
+                + ", satelliteCountryCodes=" + ((satelliteCountryCodes != null)
+                ? String.join(", ", satelliteCountryCodes) : null));
+        synchronized (mLock) {
+            if (reset) {
+                mIsOverlayConfigOverridden = false;
+                cleanUpCtsResources();
+            } else {
+                mIsOverlayConfigOverridden = true;
+                mOverriddenIsSatelliteAllowAccessControl = isAllowed;
+                if (!TextUtils.isEmpty(s2CellFile)) {
+                    mOverriddenSatelliteS2CellFile = getTestSatelliteS2File(s2CellFile);
+                    if (!mOverriddenSatelliteS2CellFile.exists()) {
+                        logd("The overriding file "
+                                + mOverriddenSatelliteS2CellFile.getAbsolutePath()
+                                + " does not exist");
+                        mOverriddenSatelliteS2CellFile = null;
+                    }
+                } else {
+                    mOverriddenSatelliteS2CellFile = null;
+                }
+                mOverriddenLocationFreshDurationNanos = locationFreshDurationNanos;
+                if (satelliteCountryCodes != null) {
+                    mOverriddenSatelliteCountryCodes = satelliteCountryCodes;
+                } else {
+                    mOverriddenSatelliteCountryCodes = new ArrayList<>();
+                }
+            }
+            cleanupOnDeviceAccessControllerResources();
+            initSatelliteOnDeviceAccessController();
+        }
+        return true;
+    }
+
+    private File getTestSatelliteS2File(String fileName) {
+        logd("getTestSatelliteS2File: fileName=" + fileName);
+        if (TextUtils.equals(fileName, GOOGLE_US_SAN_SAT_S2_FILE_NAME)) {
+            mCtsSatS2FilePath = copyTestSatS2FileToPhoneDirectory(GOOGLE_US_SAN_SAT_S2_FILE_NAME);
+            if (mCtsSatS2FilePath != null) {
+                return mCtsSatS2FilePath.toFile();
+            } else {
+                loge("getTestSatelliteS2File: mCtsSatS2FilePath is null");
+            }
+        }
+        return new File(fileName);
+    }
+
+    @Nullable private static Path copyTestSatS2FileToPhoneDirectory(String sourceFileName) {
+        PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
+        File ctsFile = phoneGlobals.getDir("cts", Context.MODE_PRIVATE);
+        if (!ctsFile.exists()) {
+            ctsFile.mkdirs();
+        }
+
+        Path targetDir = ctsFile.toPath();
+        Path targetSatS2FilePath = targetDir.resolve(sourceFileName);
+        try {
+            InputStream inputStream = phoneGlobals.getAssets().open(sourceFileName);
+            if (inputStream == null) {
+                loge("copyTestSatS2FileToPhoneDirectory: Resource=" + sourceFileName
+                        + " not found");
+            } else {
+                Files.copy(inputStream, targetSatS2FilePath, StandardCopyOption.REPLACE_EXISTING);
+            }
+        } catch (IOException ex) {
+            loge("copyTestSatS2FileToPhoneDirectory: ex=" + ex);
+        }
+        return targetSatS2FilePath;
+    }
+
+    private void cleanUpCtsResources() {
+        if (mCtsSatS2FilePath != null) {
+            try {
+                Files.delete(mCtsSatS2FilePath);
+            } catch (IOException ex) {
+                loge("cleanUpCtsResources: ex=" + ex);
+            }
+        }
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected long getElapsedRealtimeNanos() {
+        return SystemClock.elapsedRealtimeNanos();
+    }
+
+    /**
+     * Update country codes, S2CellFile and satellite region allowed by ConfigUpdater
+     * or CarrierConfig
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void updateSatelliteConfigData(Context context) {
+        logd("updateSatelliteConfigData");
+
+        SatelliteConfig satelliteConfig = mSatelliteController.getSatelliteConfig();
+        if (satelliteConfig != null  && satelliteConfig.getSatelliteS2CellFile(context) != null) {
+            logd("Check mSatelliteS2CellFile from ConfigUpdater");
+            Path pathSatelliteS2CellFile = satelliteConfig.getSatelliteS2CellFile(context);
+            mSatelliteS2CellFile = pathSatelliteS2CellFile.toFile();
+            if (mSatelliteS2CellFile != null && !mSatelliteS2CellFile.exists()) {
+                loge("The satellite S2 cell file " + mSatelliteS2CellFile.getName()
+                        + " does not exist");
+                mSatelliteS2CellFile = null;
+            }
+        }
+
+        if (mSatelliteS2CellFile == null) {
+            logd("Check mSatelliteS2CellFile from CarrierConfig");
+            String satelliteS2CellFileName = getSatelliteS2CellFileFromOverlayConfig(context);
+            mSatelliteS2CellFile = TextUtils.isEmpty(satelliteS2CellFileName)
+                    ? null : new File(satelliteS2CellFileName);
+            if (mSatelliteS2CellFile != null && !mSatelliteS2CellFile.exists()) {
+                loge("The satellite S2 cell file " + mSatelliteS2CellFile.getName()
+                        + " does not exist");
+                mSatelliteS2CellFile = null;
+            }
+        }
+
+        if (mSatelliteS2CellFile == null) {
+            logd("Since mSatelliteS2CellFile is null, don't need to refer other configurations");
+            return;
+        }
+
+        if (satelliteConfig != null
+                && !satelliteConfig.getDeviceSatelliteCountryCodes().isEmpty()) {
+            logd("update mSatelliteCountryCodes by ConfigUpdater");
+            mSatelliteCountryCodes = satelliteConfig.getDeviceSatelliteCountryCodes();
+        } else {
+            mSatelliteCountryCodes = getSatelliteCountryCodesFromOverlayConfig(context);
+        }
+
+        if (satelliteConfig != null && satelliteConfig.isSatelliteDataForAllowedRegion() != null) {
+            logd("update mIsSatelliteAllowAccessControl by ConfigUpdater");
+            mIsSatelliteAllowAccessControl = satelliteConfig.isSatelliteDataForAllowedRegion();
+        } else {
+            mIsSatelliteAllowAccessControl = getSatelliteAccessAllowFromOverlayConfig(context);
+        }
+    }
+
+    private void loadOverlayConfigs(@NonNull Context context) {
+        mSatelliteCountryCodes = getSatelliteCountryCodesFromOverlayConfig(context);
+        mIsSatelliteAllowAccessControl = getSatelliteAccessAllowFromOverlayConfig(context);
+        String satelliteS2CellFileName = getSatelliteS2CellFileFromOverlayConfig(context);
+        mSatelliteS2CellFile = TextUtils.isEmpty(satelliteS2CellFileName)
+                ? null : new File(satelliteS2CellFileName);
+        if (mSatelliteS2CellFile != null && !mSatelliteS2CellFile.exists()) {
+            loge("The satellite S2 cell file " + satelliteS2CellFileName + " does not exist");
+            mSatelliteS2CellFile = null;
+        }
+        mLocationFreshDurationNanos = getSatelliteLocationFreshDurationFromOverlayConfig(context);
+    }
+
+    private long getLocationFreshDurationNanos() {
+        synchronized (mLock) {
+            if (mIsOverlayConfigOverridden) {
+                return mOverriddenLocationFreshDurationNanos;
+            }
+            return mLocationFreshDurationNanos;
+        }
+    }
+
+    @NonNull private List<String> getSatelliteCountryCodes() {
+        synchronized (mLock) {
+            if (mIsOverlayConfigOverridden) {
+                return mOverriddenSatelliteCountryCodes;
+            }
+            return mSatelliteCountryCodes;
+        }
+    }
+
+    @Nullable private File getSatelliteS2CellFile() {
+        synchronized (mLock) {
+            if (mIsOverlayConfigOverridden) {
+                return mOverriddenSatelliteS2CellFile;
+            }
+            return mSatelliteS2CellFile;
+        }
+    }
+
+    private boolean isSatelliteAllowAccessControl() {
+        synchronized (mLock) {
+            if (mIsOverlayConfigOverridden) {
+                return mOverriddenIsSatelliteAllowAccessControl;
+            }
+            return mIsSatelliteAllowAccessControl;
+        }
+    }
+
+    private void handleCmdIsSatelliteAllowedForCurrentLocation(
+            @NonNull Pair<Integer, ResultReceiver> requestArguments) {
+        synchronized (mLock) {
+            mSatelliteAllowResultReceivers.add(requestArguments.second);
+            if (mSatelliteAllowResultReceivers.size() > 1) {
+                logd("requestIsCommunicationAllowedForCurrentLocation is already being "
+                        + "processed");
+                return;
+            }
+            mSatelliteController.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    requestArguments.first, mInternalSatelliteAllowResultReceiver);
+        }
+    }
+
+    private void handleWaitForCurrentLocationTimedOutEvent() {
+        logd("Timed out to wait for current location");
+        synchronized (mLock) {
+            if (mLocationRequestCancellationSignal != null) {
+                mLocationRequestCancellationSignal.cancel();
+                mLocationRequestCancellationSignal = null;
+                onCurrentLocationAvailable(null);
+            } else {
+                loge("handleWaitForCurrentLocationTimedOutEvent: "
+                        + "mLocationRequestCancellationSignal is null");
+            }
+        }
+    }
+
+    private void handleSatelliteAllowResultFromSatelliteController(
+            int resultCode, Bundle resultData) {
+        logd("handleSatelliteAllowResultFromSatelliteController: resultCode=" + resultCode);
+        synchronized (mLock) {
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                    boolean isSatelliteAllowed = resultData.getBoolean(
+                            KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                    if (!isSatelliteAllowed) {
+                        logd("Satellite is not allowed by modem");
+                        sendSatelliteAllowResultToReceivers(resultCode, resultData);
+                    } else {
+                        checkSatelliteAccessRestrictionForCurrentLocation();
+                    }
+                } else {
+                    loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                    sendSatelliteAllowResultToReceivers(resultCode, resultData);
+                }
+            } else if (resultCode == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
+                checkSatelliteAccessRestrictionForCurrentLocation();
+            } else {
+                sendSatelliteAllowResultToReceivers(resultCode, resultData);
+            }
+        }
+    }
+
+    private void sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData) {
+        synchronized (mLock) {
+            for (ResultReceiver resultReceiver : mSatelliteAllowResultReceivers) {
+                resultReceiver.send(resultCode, resultData);
+            }
+            mSatelliteAllowResultReceivers.clear();
+        }
+    }
+
+    /**
+     * Telephony-internal logic to verify if satellite access is restricted at the current location.
+     */
+    private void checkSatelliteAccessRestrictionForCurrentLocation() {
+        synchronized (mLock) {
+            List<String> networkCountryIsoList = mCountryDetector.getCurrentNetworkCountryIso();
+            if (!networkCountryIsoList.isEmpty()) {
+                logd("Use current network country codes=" + String.join(", ",
+                        networkCountryIsoList));
+
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
+                        isSatelliteAccessAllowedForLocation(networkCountryIsoList));
+                sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle);
+            } else {
+                if (shouldUseOnDeviceAccessController()) {
+                    // This will be an asynchronous check when it needs to wait for the current
+                    // location from location service
+                    checkSatelliteAccessRestrictionUsingOnDeviceData();
+                } else {
+                    // This is always a synchronous check
+                    checkSatelliteAccessRestrictionUsingCachedCountryCodes();
+                }
+            }
+        }
+    }
+
+    /**
+     * This function synchronously checks if satellite is allowed at current location using cached
+     * country codes.
+     */
+    private void checkSatelliteAccessRestrictionUsingCachedCountryCodes() {
+        Pair<String, Long> locationCountryCodeInfo =
+                mCountryDetector.getCachedLocationCountryIsoInfo();
+        Map<String, Long> networkCountryCodeInfoMap =
+                mCountryDetector.getCachedNetworkCountryIsoInfo();
+        List<String> countryCodeList;
+
+        // Check if the cached location country code's timestamp is newer than all cached network
+        // country codes
+        if (!TextUtils.isEmpty(locationCountryCodeInfo.first) && isGreaterThanAll(
+                locationCountryCodeInfo.second, networkCountryCodeInfoMap.values())) {
+            // Use cached location country code
+            countryCodeList = Arrays.asList(locationCountryCodeInfo.first);
+        } else {
+            // Use cached network country codes
+            countryCodeList = networkCountryCodeInfoMap.keySet().stream().toList();
+        }
+        logd("Use cached country codes=" + String.join(", ", countryCodeList));
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
+                isSatelliteAccessAllowedForLocation(countryCodeList));
+        sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle);
+    }
+
+    /**
+     * This function asynchronously checks if satellite is allowed at the current location using
+     * on-device data. Asynchronous check happens when it needs to wait for the current location
+     * from location service.
+     */
+    private void checkSatelliteAccessRestrictionUsingOnDeviceData() {
+        synchronized (mLock) {
+            logd("Use on-device data");
+            if (mFreshLastKnownLocation != null) {
+                checkSatelliteAccessRestrictionForLocation(mFreshLastKnownLocation);
+                mFreshLastKnownLocation = null;
+            } else {
+                Location freshLastKnownLocation = getFreshLastKnownLocation();
+                if (freshLastKnownLocation != null) {
+                    checkSatelliteAccessRestrictionForLocation(freshLastKnownLocation);
+                } else {
+                    queryCurrentLocation();
+                }
+            }
+        }
+    }
+
+    private void queryCurrentLocation() {
+        synchronized (mLock) {
+            if (mLocationRequestCancellationSignal != null) {
+                logd("Request for current location was already sent to LocationManager");
+                return;
+            }
+            mLocationRequestCancellationSignal = new CancellationSignal();
+            mLocationManager.getCurrentLocation(LocationManager.GPS_PROVIDER,
+                    new LocationRequest.Builder(0)
+                            .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
+                            .setLocationSettingsIgnored(true)
+                            .build(),
+                    mLocationRequestCancellationSignal, this::post,
+                    this::onCurrentLocationAvailable);
+            startWaitForCurrentLocationTimer();
+        }
+    }
+
+    private void onCurrentLocationAvailable(@Nullable Location location) {
+        logd("onCurrentLocationAvailable " + (location != null));
+        synchronized (mLock) {
+            stopWaitForCurrentLocationTimer();
+            mLocationRequestCancellationSignal = null;
+            if (location != null) {
+                checkSatelliteAccessRestrictionForLocation(location);
+            } else {
+                checkSatelliteAccessRestrictionUsingCachedCountryCodes();
+            }
+        }
+    }
+
+    private void checkSatelliteAccessRestrictionForLocation(@NonNull Location location) {
+        synchronized (mLock) {
+            try {
+                SatelliteOnDeviceAccessController.LocationToken locationToken =
+                        SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                                location.getLatitude(),
+                                location.getLongitude(), mS2Level);
+                boolean satelliteAllowed;
+                if (mCachedAccessRestrictionMap.containsKey(locationToken)) {
+                    satelliteAllowed = mCachedAccessRestrictionMap.get(locationToken);
+                } else {
+                    if (!initSatelliteOnDeviceAccessController()) {
+                        loge("Failed to init SatelliteOnDeviceAccessController");
+                        checkSatelliteAccessRestrictionUsingCachedCountryCodes();
+                        return;
+                    }
+                    satelliteAllowed = mSatelliteOnDeviceAccessController
+                            .isSatCommunicationAllowedAtLocation(locationToken);
+                    updateCachedAccessRestrictionMap(locationToken, satelliteAllowed);
+                }
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, satelliteAllowed);
+                sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle);
+            } catch (Exception ex) {
+                loge("checkSatelliteAccessRestrictionForLocation: ex=" + ex);
+                reportAnomaly(UUID_ON_DEVICE_LOOKUP_EXCEPTION,
+                        "On-device satellite lookup exception");
+                checkSatelliteAccessRestrictionUsingCachedCountryCodes();
+            }
+        }
+    }
+
+    private void updateCachedAccessRestrictionMap(@NonNull
+            SatelliteOnDeviceAccessController.LocationToken locationToken,
+            boolean satelliteAllowed) {
+        synchronized (mLock) {
+            mCachedAccessRestrictionMap.put(locationToken, satelliteAllowed);
+        }
+    }
+
+    private boolean isGreaterThanAll(
+            long comparedItem, @NonNull Collection<Long> itemCollection) {
+        for (long item : itemCollection) {
+            if (comparedItem <= item) return false;
+        }
+        return true;
+    }
+
+    private boolean isSatelliteAccessAllowedForLocation(
+            @NonNull List<String> networkCountryIsoList) {
+        if (isSatelliteAllowAccessControl()) {
+            // The current country is unidentified, we're uncertain and thus returning false
+            if (networkCountryIsoList.isEmpty()) {
+                return false;
+            }
+
+            // In case of allowed list, satellite is allowed if all country codes are be in the
+            // allowed list
+            return getSatelliteCountryCodes().containsAll(networkCountryIsoList);
+        } else {
+            // No country is barred, thus returning true
+            if (getSatelliteCountryCodes().isEmpty()) {
+                return true;
+            }
+
+            // The current country is unidentified, we're uncertain and thus returning false
+            if (networkCountryIsoList.isEmpty()) {
+                return false;
+            }
+
+            // In case of disallowed list, if any country code is in the list, satellite will be
+            // disallowed
+            for (String countryCode : networkCountryIsoList) {
+                if (getSatelliteCountryCodes().contains(countryCode)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private boolean shouldUseOnDeviceAccessController() {
+        if (getSatelliteS2CellFile() == null) {
+            return false;
+        }
+
+        if (isInEmergency() || mLocationManager.isLocationEnabled()) {
+            return true;
+        }
+
+        Location freshLastKnownLocation = getFreshLastKnownLocation();
+        if (freshLastKnownLocation != null) {
+            synchronized (mLock) {
+                mFreshLastKnownLocation = freshLastKnownLocation;
+            }
+            return true;
+        } else {
+            synchronized (mLock) {
+                mFreshLastKnownLocation = null;
+            }
+        }
+        return false;
+    }
+
+    @Nullable private Location getFreshLastKnownLocation() {
+        Location lastKnownLocation = getLastKnownLocation();
+        if (lastKnownLocation != null) {
+            long lastKnownLocationAge =
+                    getElapsedRealtimeNanos() - lastKnownLocation.getElapsedRealtimeNanos();
+            if (lastKnownLocationAge <= getLocationFreshDurationNanos()) {
+                return lastKnownLocation;
+            }
+        }
+        return null;
+    }
+
+    private boolean isInEmergency() {
+        // Check if emergency call is ongoing
+        if (mTelecomManager.isInEmergencyCall()) {
+            return true;
+        }
+        // Check if the device is in emergency callback mode
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (phone.isInEcm()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Nullable
+    private Location getLastKnownLocation() {
+        Location result = null;
+        for (String provider : mLocationManager.getProviders(true)) {
+            Location location = mLocationManager.getLastKnownLocation(provider);
+            if (location != null && (result == null
+                    || result.getElapsedRealtimeNanos() < location.getElapsedRealtimeNanos())) {
+                result = location;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @return {@code true} if successfully initialize the {@link SatelliteOnDeviceAccessController}
+     * instance, {@code false} otherwise.
+     * @throws IllegalStateException in case of getting any exception in creating the
+     * {@link SatelliteOnDeviceAccessController} instance and the device is using a user build.
+     */
+    private boolean initSatelliteOnDeviceAccessController() throws IllegalStateException {
+        synchronized (mLock) {
+            if (getSatelliteS2CellFile() == null) return false;
+
+            // mSatelliteOnDeviceAccessController was already initialized successfully
+            if (mSatelliteOnDeviceAccessController != null) {
+                restartKeepOnDeviceAccessControllerResourcesTimer();
+                return true;
+            }
+
+            try {
+                mSatelliteOnDeviceAccessController =
+                        SatelliteOnDeviceAccessController.create(getSatelliteS2CellFile());
+                restartKeepOnDeviceAccessControllerResourcesTimer();
+                mS2Level = mSatelliteOnDeviceAccessController.getS2Level();
+                logd("mS2Level=" + mS2Level);
+            } catch (Exception ex) {
+                loge("Got exception in creating an instance of SatelliteOnDeviceAccessController,"
+                        + " ex=" + ex + ", sat s2 file="
+                        + getSatelliteS2CellFile().getAbsolutePath());
+                reportAnomaly(UUID_CREATE_ON_DEVICE_ACCESS_CONTROLLER_EXCEPTION,
+                        "Exception in creating on-device satellite access controller");
+                mSatelliteOnDeviceAccessController = null;
+                if (!mIsOverlayConfigOverridden) {
+                    mSatelliteS2CellFile = null;
+                }
+                return false;
+            }
+            return true;
+        }
+    }
+
+    private void cleanupOnDeviceAccessControllerResources() {
+        synchronized (mLock) {
+            logd("cleanupOnDeviceAccessControllerResources="
+                    + (mSatelliteOnDeviceAccessController != null));
+            if (mSatelliteOnDeviceAccessController != null) {
+                try {
+                    mSatelliteOnDeviceAccessController.close();
+                } catch (Exception ex) {
+                    loge("cleanupOnDeviceAccessControllerResources: ex=" + ex);
+                }
+                mSatelliteOnDeviceAccessController = null;
+                stopKeepOnDeviceAccessControllerResourcesTimer();
+            }
+        }
+    }
+
+    private static boolean getSatelliteAccessAllowFromOverlayConfig(@NonNull Context context) {
+        Boolean accessAllowed = null;
+        try {
+            accessAllowed = context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_oem_enabled_satellite_access_allow);
+        } catch (Resources.NotFoundException ex) {
+            loge("getSatelliteAccessAllowFromOverlayConfig: got ex=" + ex);
+        }
+        if (accessAllowed == null && isMockModemAllowed()) {
+            logd("getSatelliteAccessAllowFromOverlayConfig: Read "
+                    + "config_oem_enabled_satellite_access_allow from device config");
+            accessAllowed = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TELEPHONY,
+                    "config_oem_enabled_satellite_access_allow", DEFAULT_SATELLITE_ACCESS_ALLOW);
+        }
+        if (accessAllowed == null) {
+            logd("Use default satellite access allow=true control");
+            accessAllowed = true;
+        }
+        return accessAllowed;
+    }
+
+    @Nullable
+    private static String getSatelliteS2CellFileFromOverlayConfig(@NonNull Context context) {
+        String s2CellFile = null;
+        try {
+            s2CellFile = context.getResources().getString(
+                    com.android.internal.R.string.config_oem_enabled_satellite_s2cell_file);
+        } catch (Resources.NotFoundException ex) {
+            loge("getSatelliteS2CellFileFromOverlayConfig: got ex=" + ex);
+        }
+        if (TextUtils.isEmpty(s2CellFile) && isMockModemAllowed()) {
+            logd("getSatelliteS2CellFileFromOverlayConfig: Read "
+                    + "config_oem_enabled_satellite_s2cell_file from device config");
+            s2CellFile = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
+                    "config_oem_enabled_satellite_s2cell_file", null);
+        }
+        logd("s2CellFile=" + s2CellFile);
+        return s2CellFile;
+    }
+
+    @NonNull
+    private static List<String> getSatelliteCountryCodesFromOverlayConfig(
+            @NonNull Context context) {
+        String[] countryCodes = readStringArrayFromOverlayConfig(context,
+                com.android.internal.R.array.config_oem_enabled_satellite_country_codes);
+        if (countryCodes.length == 0 && isMockModemAllowed()) {
+            logd("getSatelliteCountryCodesFromOverlayConfig: Read "
+                    + "config_oem_enabled_satellite_country_codes from device config");
+            String countryCodesStr = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
+                    "config_oem_enabled_satellite_country_codes", "");
+            countryCodes = countryCodesStr.split(",");
+        }
+        return Arrays.stream(countryCodes)
+                .map(x -> x.toUpperCase(Locale.US))
+                .collect(Collectors.toList());
+    }
+
+    @NonNull
+    private static String[] readStringArrayFromOverlayConfig(
+            @NonNull Context context, @ArrayRes int id) {
+        String[] strArray = null;
+        try {
+            strArray = context.getResources().getStringArray(id);
+        } catch (Resources.NotFoundException ex) {
+            loge("readStringArrayFromOverlayConfig: id= " + id + ", ex=" + ex);
+        }
+        if (strArray == null) {
+            strArray = new String[0];
+        }
+        return strArray;
+    }
+
+    private static long getSatelliteLocationFreshDurationFromOverlayConfig(
+            @NonNull Context context) {
+        Integer freshDuration = null;
+        try {
+            freshDuration = context.getResources().getInteger(com.android.internal.R.integer
+                    .config_oem_enabled_satellite_location_fresh_duration);
+        } catch (Resources.NotFoundException ex) {
+            loge("getSatelliteLocationFreshDurationFromOverlayConfig: got ex=" + ex);
+        }
+        if (freshDuration == null && isMockModemAllowed()) {
+            logd("getSatelliteLocationFreshDurationFromOverlayConfig: Read "
+                    + "config_oem_enabled_satellite_location_fresh_duration from device config");
+            freshDuration = DeviceConfig.getInt(DeviceConfig.NAMESPACE_TELEPHONY,
+                    "config_oem_enabled_satellite_location_fresh_duration",
+                    DEFAULT_LOCATION_FRESH_DURATION_SECONDS);
+        }
+        if (freshDuration == null) {
+            logd("Use default satellite location fresh duration="
+                    + DEFAULT_LOCATION_FRESH_DURATION_SECONDS);
+            freshDuration = DEFAULT_LOCATION_FRESH_DURATION_SECONDS;
+        }
+        return TimeUnit.SECONDS.toNanos(freshDuration);
+    }
+
+    private void startWaitForCurrentLocationTimer() {
+        synchronized (mLock) {
+            if (hasMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT)) {
+                logw("WaitForCurrentLocationTimer is already started");
+                removeMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
+            }
+            sendEmptyMessageDelayed(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT,
+                    WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS);
+        }
+    }
+
+    private void stopWaitForCurrentLocationTimer() {
+        synchronized (mLock) {
+            removeMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
+        }
+    }
+
+    private void restartKeepOnDeviceAccessControllerResourcesTimer() {
+        synchronized (mLock) {
+            if (hasMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT)) {
+                logd("KeepOnDeviceAccessControllerResourcesTimer is already started. "
+                        + "Restarting it...");
+                removeMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
+            }
+            sendEmptyMessageDelayed(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT,
+                    KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS);
+        }
+    }
+
+    private void stopKeepOnDeviceAccessControllerResourcesTimer() {
+        synchronized (mLock) {
+            removeMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
+        }
+    }
+
+    private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
+        loge(log);
+        AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
+    }
+
+    private static boolean isMockModemAllowed() {
+        return (DEBUG || SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)
+                || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
     }
 
     /**
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java
index 9292f33..520699f 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteOnDeviceAccessController.java
@@ -45,11 +45,12 @@
 
     /**
      * Returns a token for a given location. See {@link LocationToken} for details.
-     *
-     * @throws IOException in the unlikely event of errors when reading the underlying file
      */
-    public abstract LocationToken createLocationTokenForLatLng(double latDegrees, double lngDegrees)
-            throws IOException;
+    public static LocationToken createLocationTokenForLatLng(double latDegrees, double lngDegrees,
+            int s2Level) {
+        return S2RangeSatelliteOnDeviceAccessController
+                .createLocationTokenForLatLng(latDegrees, lngDegrees, s2Level);
+    }
 
     /**
      * Returns {@code true} if the satellite communication is allowed at the provided location,
@@ -61,6 +62,11 @@
             throws IOException;
 
     /**
+     * Returns the S2 level of the file.
+     */
+    public abstract int getS2Level();
+
+    /**
      * A class that represents an area with the same value. Two locations with tokens that
      * {@link #equals(Object) equal each other} will definitely return the same value.
      *
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java
new file mode 100644
index 0000000..c856eb5
--- /dev/null
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java
@@ -0,0 +1,71 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+
+import com.android.libraries.entitlement.CarrierConfig;
+import com.android.libraries.entitlement.ServiceEntitlement;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+import com.android.libraries.entitlement.ServiceEntitlementRequest;
+
+/**
+ * Class that sends an HTTP request to the entitlement server and processes the response to check
+ * whether satellite service can be activated.
+ * @hide
+ */
+public class SatelliteEntitlementApi {
+    @NonNull
+    private final ServiceEntitlement mServiceEntitlement;
+    private final Context mContext;
+
+    public SatelliteEntitlementApi(@NonNull Context context,
+            @NonNull PersistableBundle carrierConfig, @NonNull int subId) {
+        mContext = context;
+        mServiceEntitlement = new ServiceEntitlement(mContext,
+                getCarrierConfigFromEntitlementServerUrl(carrierConfig), subId);
+    }
+
+    /**
+     * Returns satellite entitlement result from the entitlement server.
+     * @return The SatelliteEntitlementResult
+     */
+    public SatelliteEntitlementResult checkEntitlementStatus() throws ServiceEntitlementException {
+        ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
+        requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+        ServiceEntitlementRequest request = requestBuilder.build();
+
+        String response = mServiceEntitlement.queryEntitlementStatus(
+                ServiceEntitlement.APP_SATELLITE_ENTITLEMENT, request);
+        SatelliteEntitlementResponse satelliteEntitlementResponse =
+                new SatelliteEntitlementResponse(response);
+        return new SatelliteEntitlementResult(satelliteEntitlementResponse.getEntitlementStatus(),
+                satelliteEntitlementResponse.getPlmnAllowed());
+    }
+
+    @NonNull
+    private CarrierConfig getCarrierConfigFromEntitlementServerUrl(
+            @NonNull PersistableBundle carrierConfig) {
+        String entitlementServiceUrl = carrierConfig.getString(
+                CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
+                "");
+        return CarrierConfig.builder().setServerUrl(entitlementServiceUrl).build();
+    }
+}
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
new file mode 100644
index 0000000..94362a0
--- /dev/null
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
@@ -0,0 +1,542 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_ENABLED;
+
+import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
+import static java.time.temporal.ChronoUnit.SECONDS;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+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.Rlog;
+import android.telephony.SubscriptionManager;
+
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.libraries.entitlement.ServiceEntitlementException;
+
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class query the entitlement server to receive values for satellite services and passes the
+ * response to the {@link com.android.internal.telephony.satellite.SatelliteController}.
+ * @hide
+ */
+public class SatelliteEntitlementController extends Handler {
+    private static final String TAG = "SatelliteEntitlementController";
+    @NonNull private static SatelliteEntitlementController sInstance;
+    /** Message code used in handleMessage() */
+    private static final int CMD_START_QUERY_ENTITLEMENT = 1;
+    private static final int CMD_RETRY_QUERY_ENTITLEMENT = 2;
+    private static final int CMD_STOP_RETRY_QUERY_ENTITLEMENT = 3;
+
+    /** Retry on next trigger event. */
+    private static final int HTTP_RESPONSE_500 = 500;
+    /** Retry after the time specified in the “Retry-After” header. After retry count doesn't exceed
+     * MAX_RETRY_COUNT. */
+    private static final int HTTP_RESPONSE_503 = 503;
+    /** Default query refresh time is 1 month. */
+
+    private static final int DEFAULT_QUERY_REFRESH_DAYS = 30;
+    private static final long INITIAL_DELAY_MILLIS = TimeUnit.MINUTES.toMillis(10); // 10 min
+    private static final long MAX_DELAY_MILLIS = TimeUnit.DAYS.toMillis(5); // 5 days
+    private static final int MULTIPLIER = 2;
+    private static final int MAX_RETRY_COUNT = 5;
+    @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
+    @NonNull private final CarrierConfigManager mCarrierConfigManager;
+    @NonNull private final CarrierConfigManager.CarrierConfigChangeListener
+            mCarrierConfigChangeListener;
+    @NonNull private final ConnectivityManager mConnectivityManager;
+    @NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
+    @NonNull private final BroadcastReceiver mReceiver;
+    @NonNull private final Context mContext;
+    private final Object mLock = new Object();
+    /** Map key : subId, value : ExponentialBackoff. */
+    private Map<Integer, ExponentialBackoff> mExponentialBackoffPerSub = new HashMap<>();
+    /** Map key : subId, value : SatelliteEntitlementResult. */
+    private Map<Integer, SatelliteEntitlementResult> mSatelliteEntitlementResultPerSub =
+            new HashMap<>();
+    /** Map key : subId, value : the last query time to millis. */
+    private Map<Integer, Long> mLastQueryTimePerSub = new HashMap<>();
+    /** Map key : subId, value : Count the number of retries caused by the 'ExponentialBackoff' and
+     * '503 error case with the Retry-After header'. */
+    private Map<Integer, Integer> mRetryCountPerSub = new HashMap<>();
+
+    /**
+     * Create the SatelliteEntitlementController singleton instance.
+     * @param context      The Context to use to create the SatelliteEntitlementController.
+     * @param featureFlags The feature flag.
+     */
+    public static void make(@NonNull Context context, @NonNull FeatureFlags featureFlags) {
+        if (!featureFlags.carrierEnabledSatelliteFlag()) {
+            logd("carrierEnabledSatelliteFlag is disabled. don't created this.");
+            return;
+        }
+        if (sInstance == null) {
+            HandlerThread handlerThread = new HandlerThread(TAG);
+            handlerThread.start();
+            sInstance =
+                    new SatelliteEntitlementController(context, handlerThread.getLooper());
+        }
+    }
+
+    /**
+     * Create a SatelliteEntitlementController to request query to the entitlement server for
+     * satellite services and receive responses.
+     *
+     * @param context      The Context for the SatelliteEntitlementController.
+     * @param looper       The looper for the handler. It does not run on main thread.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public SatelliteEntitlementController(@NonNull Context context, @NonNull Looper looper) {
+        super(looper);
+        mContext = context;
+        mSubscriptionManagerService = SubscriptionManagerService.getInstance();
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mCarrierConfigChangeListener = (slotIndex, subId, carrierId, specificCarrierId) ->
+                handleCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
+        mCarrierConfigManager.registerCarrierConfigChangeListener(this::post,
+                mCarrierConfigChangeListener);
+        mConnectivityManager = context.getSystemService(ConnectivityManager.class);
+        mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+            @Override
+            public void onAvailable(Network network) {
+                handleInternetConnected();
+            }
+
+            @Override
+            public void onLost(Network network) {
+                handleInternetDisconnected();
+            }
+        };
+        NetworkRequest networkrequest = new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();
+        mConnectivityManager.registerNetworkCallback(networkrequest, mNetworkCallback, this);
+        mReceiver = new SatelliteEntitlementControllerReceiver();
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        context.registerReceiver(mReceiver, intentFilter);
+    }
+
+    @Override
+    public void handleMessage(@NonNull Message msg) {
+        switch (msg.what) {
+            case CMD_START_QUERY_ENTITLEMENT:
+                handleCmdStartQueryEntitlement();
+                break;
+            case CMD_RETRY_QUERY_ENTITLEMENT:
+                handleCmdRetryQueryEntitlement(msg.arg1);
+                break;
+            case CMD_STOP_RETRY_QUERY_ENTITLEMENT:
+                stopExponentialBackoff(msg.arg1);
+                break;
+            default:
+                logd("do not used this message");
+        }
+    }
+
+    private void handleCarrierConfigChanged(int slotIndex, int subId, int carrierId,
+            int specificCarrierId) {
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return;
+        }
+        logd("handleCarrierConfigChanged(): slotIndex(" + slotIndex + "), subId("
+                + subId + "), carrierId(" + carrierId + "), specificCarrierId("
+                + specificCarrierId + ")");
+
+        sendEmptyMessage(CMD_START_QUERY_ENTITLEMENT);
+    }
+
+    private class SatelliteEntitlementControllerReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+                boolean airplaneMode = intent.getBooleanExtra("state", false);
+                handleAirplaneModeChange(airplaneMode);
+            }
+        }
+    }
+
+    private void handleAirplaneModeChange(boolean airplaneMode) {
+        if (!airplaneMode) {
+            resetEntitlementQueryCounts(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        }
+    }
+
+    private boolean isInternetConnected() {
+        Network activeNetwork = mConnectivityManager.getActiveNetwork();
+        NetworkCapabilities networkCapabilities =
+                mConnectivityManager.getNetworkCapabilities(activeNetwork);
+        // TODO b/319780796 Add checking if it is not a satellite.
+        return networkCapabilities != null
+                && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+    }
+
+    private void handleInternetConnected() {
+        sendEmptyMessage(CMD_START_QUERY_ENTITLEMENT);
+    }
+
+    private void handleInternetDisconnected() {
+        mExponentialBackoffPerSub.forEach((key, value) -> {
+            Message message = obtainMessage();
+            message.what = CMD_STOP_RETRY_QUERY_ENTITLEMENT;
+            message.arg1 = key;
+            sendMessage(message);
+        });
+    }
+
+    /**
+     * Check if the device can request to entitlement server (if there is an internet connection and
+     * if the throttle time has passed since the last request), and then pass the response to
+     * SatelliteController if the response is received.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void handleCmdStartQueryEntitlement() {
+        if (!isInternetConnected()) {
+            logd("Internet disconnected");
+            return;
+        }
+
+        for (int subId : mSubscriptionManagerService.getActiveSubIdList(true)) {
+            if (!shouldQueryEntitlement(subId)) {
+                return;
+            }
+
+            // Check the satellite service query result from the entitlement server for the
+            // satellite service.
+            try {
+                mSatelliteEntitlementResultPerSub.remove(subId);
+                mSatelliteEntitlementResultPerSub.put(subId, getSatelliteEntitlementApi(
+                        subId).checkEntitlementStatus());
+            } catch (ServiceEntitlementException e) {
+                loge(e.toString());
+                if (!isInternetConnected()) {
+                    logd("handleCmdStartQueryEntitlement: disconnected. " + e);
+                    return;
+                }
+                if (shouldHandleErrorResponse(e, subId)) {
+                    logd("handleCmdStartQueryEntitlement: handle response.");
+                    return;
+                }
+                startExponentialBackoff(subId);
+                return;
+            }
+            queryCompleted(subId);
+        }
+    }
+
+    /** When airplane mode changes from on to off, reset the values required to start the first
+     * query. */
+    private void resetEntitlementQueryCounts(String event) {
+        logd("resetEntitlementQueryCounts: " + event);
+        mLastQueryTimePerSub = new HashMap<>();
+        mExponentialBackoffPerSub = new HashMap<>();
+        mRetryCountPerSub = new HashMap<>();
+    }
+
+    /**
+     * If the HTTP response does not receive a body containing the 200 ok with sat mode
+     * configuration,
+     *
+     * 1. If the 500 response received, then no more retry until next event occurred.
+     * 2. If the 503 response with Retry-After header received, then the query is retried until
+     * MAX_RETRY_COUNT.
+     * 3. If other response or exception is occurred, then the query is retried until
+     * MAX_RETRY_COUNT is reached using the ExponentialBackoff.
+     */
+    private void handleCmdRetryQueryEntitlement(int subId) {
+        logd("handleCmdRetryQueryEntitlement: " + subId);
+        try {
+            synchronized (mLock) {
+                mSatelliteEntitlementResultPerSub.put(subId, getSatelliteEntitlementApi(
+                        subId).checkEntitlementStatus());
+            }
+        } catch (ServiceEntitlementException e) {
+            if (!isInternetConnected()) {
+                logd("retryQuery: Internet disconnected. reset the retry and after the "
+                        + "internet is connected then the first query is triggered." + e);
+                stopExponentialBackoff(subId);
+                return;
+            }
+            if (shouldHandleErrorResponse(e, subId)) {
+                logd("retryQuery: handle response.");
+                stopExponentialBackoff(subId);
+                return;
+            }
+            mExponentialBackoffPerSub.get(subId).notifyFailed();
+            mRetryCountPerSub.put(subId,
+                    mRetryCountPerSub.getOrDefault(subId, 0) + 1);
+            logd("handleCmdRetryQueryEntitlement:" + e + "[" + subId + "] cnt="
+                    + mRetryCountPerSub.getOrDefault(subId, 0) + "] Retrying in "
+                    + mExponentialBackoffPerSub.get(subId).getCurrentDelay() + " ms.");
+        }
+    }
+
+    /** Only handle '500' and '503 with retry-after header' error responses received.
+     * If the 500 response is received, no retry until the next trigger event occurs.
+     * If the 503 response with Retry-After header, retry is attempted according to the value in the
+     * Retry-After header up to MAX_RETRY_COUNT.
+     * In other cases, it performs an exponential backoff process. */
+    private boolean shouldHandleErrorResponse(ServiceEntitlementException e, int subId) {
+        int responseCode = e.getHttpStatus();
+        logd("shouldHandleErrorResponse: received the " + responseCode);
+        if (responseCode == HTTP_RESPONSE_503 && e.getRetryAfter() != null
+                && !e.getRetryAfter().isEmpty()) {
+            if (mRetryCountPerSub.getOrDefault(subId, 0) >= MAX_RETRY_COUNT) {
+                logd("The 503 retry after reaching the " + MAX_RETRY_COUNT
+                        + "The retry will not be attempted until the next trigger event.");
+                queryCompleted(subId);
+                return true;
+            }
+            long retryAfterSeconds = parseSecondsFromRetryAfter(e.getRetryAfter());
+            if (retryAfterSeconds == -1) {
+                logd("Unable parsing the retry-after. try to exponential backoff.");
+                return false;
+            }
+            mRetryCountPerSub.put(subId, mRetryCountPerSub.getOrDefault(subId, 0) + 1);
+            logd("[" + subId + "] cnt=" + mRetryCountPerSub.getOrDefault(subId, 0)
+                    + " Retrying in " + TimeUnit.SECONDS.toMillis(retryAfterSeconds) + " sec");
+            Message message = obtainMessage();
+            message.what = CMD_RETRY_QUERY_ENTITLEMENT;
+            message.arg1 = subId;
+            sendMessageDelayed(message, TimeUnit.SECONDS.toMillis(retryAfterSeconds));
+            return true;
+        } else if (responseCode == HTTP_RESPONSE_500) {
+            logd("The retry on the next trigger event.");
+            queryCompleted(subId);
+            return true;
+        }
+        return false;
+    }
+
+    /** Parse the HTTP-date or a number of seconds in the retry-after value. */
+    private long parseSecondsFromRetryAfter(String retryAfter) {
+        try {
+            return Long.parseLong(retryAfter);
+        } catch (NumberFormatException numberFormatException) {
+        }
+
+        try {
+            return SECONDS.between(
+                    Instant.now(), RFC_1123_DATE_TIME.parse(retryAfter, Instant::from));
+        } catch (DateTimeParseException dateTimeParseException) {
+        }
+
+        return -1;
+    }
+
+    private void startExponentialBackoff(int subId) {
+        stopExponentialBackoff(subId);
+        mExponentialBackoffPerSub.put(subId,
+                new ExponentialBackoff(INITIAL_DELAY_MILLIS, MAX_DELAY_MILLIS,
+                        MULTIPLIER, this.getLooper(), () -> {
+                    synchronized (mLock) {
+                        if (mSatelliteEntitlementResultPerSub.containsKey(subId)) {
+                            logd("handleCmdStartQueryEntitlement: get the response "
+                                    + "successfully.");
+                            mExponentialBackoffPerSub.get(subId).stop();
+                            queryCompleted(subId);
+                            return;
+                        }
+
+                        if (mRetryCountPerSub.getOrDefault(subId, 0) >= MAX_RETRY_COUNT) {
+                            logd("The ExponentialBackoff is  stopped after reaching the "
+                                    + MAX_RETRY_COUNT + ". The retry don't attempted until the"
+                                    + " refresh time expires.");
+                            mExponentialBackoffPerSub.get(subId).stop();
+                            queryCompleted(subId);
+                            return;
+                        }
+                        if (!mSatelliteEntitlementResultPerSub.containsKey(subId)) {
+                            handleCmdRetryQueryEntitlement(subId);
+                        }
+                    }
+                }));
+        mExponentialBackoffPerSub.get(subId).start();
+        mRetryCountPerSub.put(subId, mRetryCountPerSub.getOrDefault(subId, 0) + 1);
+        logd("start ExponentialBackoff [" + mRetryCountPerSub.getOrDefault(subId, 0)
+                + "] Retrying in " + mExponentialBackoffPerSub.get(subId).getCurrentDelay()
+                + " ms.");
+    }
+
+    /** If the Internet connection is lost during the ExponentialBackoff, stop the
+     * ExponentialBackoff and reset it. */
+    private void stopExponentialBackoff(int subId) {
+        if (isExponentialBackoffInProgress(subId)) {
+            logd("stopExponentialBackoff: reset ExponentialBackoff");
+            mExponentialBackoffPerSub.get(subId).stop();
+            mExponentialBackoffPerSub.remove(subId);
+        }
+    }
+
+    /**
+     * No more query retry, update the result. If there is no response from the server, then used
+     * the default value - 'satellite disabled' and empty 'PLMN allowed list'.
+     * And then it send a delayed message to trigger the query again after A refresh day has passed.
+     */
+    private void queryCompleted(int subId) {
+        if (!mSatelliteEntitlementResultPerSub.containsKey(subId)) {
+            logd("queryCompleted: create default SatelliteEntitlementResult");
+            mSatelliteEntitlementResultPerSub.put(subId,
+                    SatelliteEntitlementResult.getDefaultResult());
+        }
+
+        saveLastQueryTime(subId);
+        Message message = obtainMessage();
+        message.what = CMD_START_QUERY_ENTITLEMENT;
+        message.arg1 = subId;
+        sendMessageDelayed(message, TimeUnit.DAYS.toMillis(
+                getSatelliteEntitlementStatusRefreshDays(subId)));
+        logd("queryCompleted: updateSatelliteEntitlementStatus");
+        updateSatelliteEntitlementStatus(subId,
+                mSatelliteEntitlementResultPerSub.get(subId).getEntitlementStatus()
+                        == SATELLITE_ENTITLEMENT_STATUS_ENABLED,
+                mSatelliteEntitlementResultPerSub.get(subId).getAllowedPLMNList());
+        stopExponentialBackoff(subId);
+        mRetryCountPerSub.remove(subId);
+    }
+
+    /** Check whether there is a saved subId. Returns true if there is a saved subId,
+     * otherwise return false.*/
+    private boolean isExponentialBackoffInProgress(int subId) {
+        return mExponentialBackoffPerSub.containsKey(subId);
+    }
+
+    /**
+     * Check if the subId can query the entitlement server to get the satellite configuration.
+     */
+    private boolean shouldQueryEntitlement(int subId) {
+        if (!isSatelliteEntitlementSupported(subId)) {
+            logd("Doesn't support entitlement query for satellite.");
+            return false;
+        }
+
+        if (isExponentialBackoffInProgress(subId)) {
+            logd("In progress ExponentialBackoff.");
+            return false;
+        }
+
+        return shouldRefreshEntitlementStatus(subId);
+    }
+
+    /**
+     * Compare the last query time to the refresh time from the CarrierConfig to see if the device
+     * can query the entitlement server.
+     */
+    private boolean shouldRefreshEntitlementStatus(int subId) {
+        long lastQueryTimeMillis = getLastQueryTime(subId);
+        long refreshTimeMillis = TimeUnit.DAYS.toMillis(
+                getSatelliteEntitlementStatusRefreshDays(subId));
+        boolean isAvailable =
+                (System.currentTimeMillis() - lastQueryTimeMillis) > refreshTimeMillis;
+        if (!isAvailable) {
+            logd("query is already done. can query after " + Instant.ofEpochMilli(
+                    refreshTimeMillis + lastQueryTimeMillis));
+        }
+        return isAvailable;
+    }
+
+    /**
+     * Get the SatelliteEntitlementApi.
+     *
+     * @param subId The subId of the subscription for creating SatelliteEntitlementApi
+     * @return A new SatelliteEntitlementApi object.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public SatelliteEntitlementApi getSatelliteEntitlementApi(int subId) {
+        return new SatelliteEntitlementApi(mContext, getConfigForSubId(subId), subId);
+    }
+
+    /** If there is a value stored in the cache, it is used. If there is no value stored in the
+     * cache, it is considered the first query. */
+    private long getLastQueryTime(int subId) {
+        return mLastQueryTimePerSub.getOrDefault(subId, 0L);
+    }
+
+    /** Return the satellite entitlement status refresh days from carrier config. */
+    private int getSatelliteEntitlementStatusRefreshDays(int subId) {
+        return getConfigForSubId(subId).getInt(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT,
+                DEFAULT_QUERY_REFRESH_DAYS);
+    }
+
+    /** Return the satellite entitlement supported bool from carrier config. */
+    private boolean isSatelliteEntitlementSupported(int subId) {
+        return getConfigForSubId(subId).getBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL);
+    }
+
+    @NonNull
+    private PersistableBundle getConfigForSubId(int subId) {
+        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
+                CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT,
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL);
+        if (config == null || config.isEmpty()) {
+            config = CarrierConfigManager.getDefaultConfig();
+        }
+        return config;
+    }
+
+    private void saveLastQueryTime(int subId) {
+        long lastQueryTimeMillis = System.currentTimeMillis();
+        mLastQueryTimePerSub.put(subId, lastQueryTimeMillis);
+    }
+
+    /**
+     * Send to satelliteController for update the satellite service enabled or not and plmn Allowed
+     * list.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public void updateSatelliteEntitlementStatus(int subId, boolean enabled,
+            List<String> plmnAllowedList) {
+        SatelliteController.getInstance().onSatelliteEntitlementStatusUpdated(subId, enabled,
+                plmnAllowedList, null);
+    }
+
+    private static void logd(String log) {
+        Rlog.d(TAG, log);
+    }
+
+    private static void loge(String log) {
+        Rlog.e(TAG, log);
+    }
+}
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
new file mode 100644
index 0000000..1fe0ecf
--- /dev/null
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
@@ -0,0 +1,151 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_DISABLED;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.satellite.SatelliteNetworkInfo;
+import com.android.libraries.entitlement.ServiceEntitlement;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * This class parses whether the satellite service configuration.
+ * @hide
+ */
+public class SatelliteEntitlementResponse {
+    private static final String TAG = "SatelliteEntitlementResponse";
+
+    /** Overall status of the SatMode entitlement, stating if the satellite service can be offered
+     * on the device, and if it can be activated or not by the user. */
+    private static final String ENTITLEMENT_STATUS_KEY = "EntitlementStatus";
+    /** List of allowed PLMNs where the service can be used. */
+    private static final String PLMN_ALLOWED_KEY = "PLMNAllowed";
+    /** List of barred PLMNs where the service can’t be used. */
+    private static final String PLMN_BARRED_KEY = "PLMNBarred";
+    /** allowed PLMN-ID where the service can be used or is barred. */
+    private static final String PLMN_KEY = "PLMN";
+    /** The data plan is of the metered or un-metered type. This value is optional. */
+    private static final String DATA_PLAN_TYPE_KEY = "DataPlanType";
+
+    @SatelliteEntitlementResult.SatelliteEntitlementStatus private int mEntitlementStatus;
+
+    /**
+     * <p> Available options are :
+     * "PLMNAllowed":[{ "PLMN": "XXXXXX", “DataPlanType”: "unmetered"},
+     * {"PLMN": "XXXXXX", “DataPlanType”: "metered"},
+     * {"PLMN": "XXXXXX"}]
+     */
+    private List<SatelliteNetworkInfo> mPlmnAllowedList;
+    /**
+     * <p> Available option is :
+     * "PLMNBarred":[{"PLMN": "XXXXXX"}, {"PLMN”:"XXXXXX"}]
+     */
+    private List<String> mPlmnBarredList;
+
+    public SatelliteEntitlementResponse(String response) {
+        mEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_DISABLED;
+        mPlmnAllowedList = new ArrayList<>();
+        mPlmnBarredList = new ArrayList<>();
+        parsingResponse(response);
+    }
+
+    /**
+     * Get the entitlement status for the satellite service
+     * @return The satellite entitlement status
+     */
+    public int getEntitlementStatus() {
+        return mEntitlementStatus;
+    }
+
+    /**
+     * Get the PLMNAllowed from the response
+     * @return The PLMNs Allowed list. PLMN and Data Plan Type(optional).
+     */
+    public List<SatelliteNetworkInfo> getPlmnAllowed() {
+        return mPlmnAllowedList.stream().map((info) -> new SatelliteNetworkInfo(info.mPlmn,
+                info.mDataPlanType)).collect(Collectors.toList());
+    }
+
+    /**
+     * Get the PLMNBarredList from the response
+     * @return The PLMNs Barred List
+     */
+    @VisibleForTesting
+    public List<String> getPlmnBarredList() {
+        return mPlmnBarredList.stream().map(String::new).collect(Collectors.toList());
+    }
+
+    private void parsingResponse(String response) {
+        JSONObject jsonAuthResponse = null;
+        try {
+            jsonAuthResponse = new JSONObject(response);
+            if (!jsonAuthResponse.has(ServiceEntitlement.APP_SATELLITE_ENTITLEMENT)) {
+                loge("parsingResponse failed with no app");
+                return;
+            }
+            JSONObject jsonToken = jsonAuthResponse.getJSONObject(
+                    ServiceEntitlement.APP_SATELLITE_ENTITLEMENT);
+            if (jsonToken.has(ENTITLEMENT_STATUS_KEY)) {
+                String entitlementStatus = jsonToken.getString(ENTITLEMENT_STATUS_KEY);
+                if (entitlementStatus == null) {
+                    loge("parsingResponse EntitlementStatus is null");
+                    return;
+                }
+                mEntitlementStatus = Integer.valueOf(entitlementStatus);
+            }
+            if (jsonToken.has(PLMN_ALLOWED_KEY)) {
+                JSONArray jsonArray = jsonToken.getJSONArray(PLMN_ALLOWED_KEY);
+                mPlmnAllowedList = new ArrayList<>();
+                for (int i = 0; i < jsonArray.length(); i++) {
+                    String dataPlanType = jsonArray.getJSONObject(i).has(DATA_PLAN_TYPE_KEY)
+                            ? jsonArray.getJSONObject(i).getString(DATA_PLAN_TYPE_KEY) : "";
+                    mPlmnAllowedList.add(new SatelliteNetworkInfo(
+                            jsonArray.getJSONObject(i).getString(PLMN_KEY), dataPlanType));
+                }
+            }
+            if (jsonToken.has(PLMN_BARRED_KEY)) {
+                mPlmnBarredList = new ArrayList<>();
+                JSONArray jsonArray = jsonToken.getJSONArray(PLMN_BARRED_KEY);
+                for (int i = 0; i < jsonArray.length(); i++) {
+                    mPlmnBarredList.add(jsonArray.getJSONObject(i).getString(PLMN_KEY));
+                }
+            }
+        } catch (JSONException e) {
+            loge("parsingResponse: failed JSONException", e);
+        } catch (NumberFormatException e) {
+            loge("parsingResponse: failed NumberFormatException", e);
+        }
+    }
+
+    private static void loge(String log) {
+        Log.e(TAG, log);
+    }
+
+    private static void loge(String log, Exception e) {
+        Log.e(TAG, log, e);
+    }
+}
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResult.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResult.java
new file mode 100644
index 0000000..3289232
--- /dev/null
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResult.java
@@ -0,0 +1,98 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+import android.annotation.IntDef;
+
+import com.android.internal.telephony.satellite.SatelliteNetworkInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * This class stores the result of the satellite entitlement query and passes them to
+ * SatelliteEntitlementController.
+ */
+public class SatelliteEntitlementResult {
+    /** SatMode allowed, but not yet provisioned and activated on the network. */
+    public static final int SATELLITE_ENTITLEMENT_STATUS_DISABLED = 0;
+    /** SatMode service allowed, provisioned and activated on the network. User can access the
+     * satellite service. */
+    public static final int SATELLITE_ENTITLEMENT_STATUS_ENABLED = 1;
+    /** SatMode cannot be offered for network or device. */
+    public static final int SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE = 2;
+    /** SatMode is being provisioned on the network. Not yet activated. */
+    public static final int SATELLITE_ENTITLEMENT_STATUS_PROVISIONING = 3;
+
+    @IntDef(prefix = {"SATELLITE_ENTITLEMENT_STATUS_"}, value = {
+            SATELLITE_ENTITLEMENT_STATUS_DISABLED,
+            SATELLITE_ENTITLEMENT_STATUS_ENABLED,
+            SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE,
+            SATELLITE_ENTITLEMENT_STATUS_PROVISIONING
+    })
+    public @interface SatelliteEntitlementStatus {}
+
+    private @SatelliteEntitlementStatus int mEntitlementStatus;
+    /**
+     * An SatelliteNetworkInfo list consisting of the PLMN and the DataPlanType in the PLMNAlowed
+     * item of the satellite configuration received from the entitlement server.
+     */
+    private List<SatelliteNetworkInfo> mAllowedSatelliteNetworkInfoList;
+
+    /**
+     * Store the result of the satellite entitlement response.
+     *
+     * @param entitlementStatus The entitlement status.
+     * @param allowedSatelliteNetworkInfoList The allowedSatelliteNetworkInfoList
+     */
+    public SatelliteEntitlementResult(@SatelliteEntitlementStatus int entitlementStatus,
+            List<SatelliteNetworkInfo> allowedSatelliteNetworkInfoList) {
+        mEntitlementStatus = entitlementStatus;
+        mAllowedSatelliteNetworkInfoList = allowedSatelliteNetworkInfoList;
+    }
+
+    /**
+     * Get the entitlement status.
+     *
+     * @return The entitlement status.
+     */
+    public @SatelliteEntitlementStatus int getEntitlementStatus() {
+        return mEntitlementStatus;
+    }
+
+    /**
+     * Get the plmn allowed list
+     *
+     * @return The plmn allowed list.
+     */
+    public List<String> getAllowedPLMNList() {
+        return mAllowedSatelliteNetworkInfoList.stream().map(info -> info.mPlmn).collect(
+                Collectors.toList());
+    }
+
+    /**
+     * Get the default SatelliteEntitlementResult. EntitlementStatus set to
+     * `SATELLITE_ENTITLEMENT_STATUS_DISABLED` and SatelliteNetworkInfo list set to empty.
+     *
+     * @return If there is no response, return default SatelliteEntitlementResult
+     */
+    public static SatelliteEntitlementResult getDefaultResult() {
+        return new SatelliteEntitlementResult(SATELLITE_ENTITLEMENT_STATUS_DISABLED,
+                new ArrayList<>());
+    }
+}
diff --git a/src/com/android/phone/security/SafetySourceReceiver.java b/src/com/android/phone/security/SafetySourceReceiver.java
new file mode 100644
index 0000000..76f8e72
--- /dev/null
+++ b/src/com/android/phone/security/SafetySourceReceiver.java
@@ -0,0 +1,84 @@
+/*
+ * 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.phone.security;
+
+import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
+import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.Flags;
+import com.android.phone.PhoneGlobals;
+import com.android.telephony.Rlog;
+
+public class SafetySourceReceiver extends BroadcastReceiver {
+    private static final String TAG = "TelephonySafetySourceReceiver";
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        // If none of the features that depend on this receiver are enabled, there's no reason
+        // to progress.
+        if (!Flags.enableIdentifierDisclosureTransparencyUnsolEvents()
+                || !Flags.enableModemCipherTransparencyUnsolEvents()) {
+            return;
+        }
+
+        String action = intent.getAction();
+        if (!ACTION_REFRESH_SAFETY_SOURCES.equals(action)) {
+            return;
+        }
+
+        String refreshBroadcastId =
+                intent.getStringExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID);
+        if (refreshBroadcastId == null) {
+            return;
+        }
+
+        if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                refreshSafetySources(refreshBroadcastId);
+            }
+        } else {
+            refreshSafetySources(refreshBroadcastId);
+        }
+    }
+
+    private void refreshSafetySources(String refreshBroadcastId) {
+        Phone phone = getDefaultPhone();
+        // It's possible that phones have not been created yet. Safety center may send a refresh
+        // broadcast very early on.
+        if (phone != null) {
+            phone.refreshSafetySources(refreshBroadcastId);
+        }
+
+    }
+
+    @VisibleForTesting
+    public Phone getDefaultPhone() {
+        try {
+            return PhoneGlobals.getPhone();
+        } catch (IllegalStateException e) {
+            Rlog.i(TAG, "Unable to get phone. Skipping safety source refresh: " + e.getMessage());
+        }
+        return null;
+    }
+}
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index f6cd0d1..f3158e6 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -104,8 +104,6 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.euicc.EuiccConnector;
-import com.android.internal.telephony.flags.FeatureFlags;
-import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.phone.R;
 
@@ -305,8 +303,6 @@
     private int mCellInfoRefreshRateIndex;
     private int mSelectedPhoneIndex;
 
-    private FeatureFlags mFeatureFlags;
-
     private final NetworkRequest mDefaultNetworkRequest = new NetworkRequest.Builder()
             .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
             .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -512,8 +508,6 @@
 
         log("Started onCreate");
 
-        mFeatureFlags = new FeatureFlagsImpl();
-
         mQueuedWork = new ThreadPoolExecutor(1, 1, RUNNABLE_TIMEOUT_MS, TimeUnit.MICROSECONDS,
                 new LinkedBlockingDeque<Runnable>());
         mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
@@ -1573,11 +1567,7 @@
     };
 
     private boolean isRadioOn() {
-        if (mFeatureFlags.radioInfoIsRadioOn()) {
-            return mTelephonyManager.getRadioPowerState() == TelephonyManager.RADIO_POWER_ON;
-        }
-        //FIXME: Replace with a TelephonyManager call
-        return mPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF;
+        return mTelephonyManager.getRadioPowerState() == TelephonyManager.RADIO_POWER_ON;
     }
 
     private void updateRadioPowerState() {
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index adb07f9..c00adef 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -31,7 +31,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.phone.ImsUtil;
 import com.android.phone.PhoneGlobals;
-import com.android.phone.common.R;
+import com.android.phone.R;
 
 public class DisconnectCauseUtil {
 
@@ -119,16 +119,20 @@
             FlagsAdapter featureFlags) {
         Context context = PhoneGlobals.getInstance();
 
-        return new DisconnectCause(
-                toTelecomDisconnectCauseCode(telephonyDisconnectCause, carrierConfig),
-                toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
-                        telephonyPreciseDisconnectCause, carrierConfig, featureFlags),
-                toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId),
-                toTelecomDisconnectReason(context, telephonyDisconnectCause, reason, phoneId),
-                toTelecomDisconnectCauseTone(telephonyDisconnectCause, carrierConfig, featureFlags),
-                telephonyDisconnectCause,
-                telephonyPreciseDisconnectCause,
-                imsReasonInfo);
+        return new DisconnectCause.Builder()
+                .setCode(toTelecomDisconnectCauseCode(telephonyDisconnectCause, carrierConfig))
+                .setLabel(toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
+                        telephonyPreciseDisconnectCause, carrierConfig, featureFlags))
+                .setDescription(toTelecomDisconnectCauseDescription(
+                        context, telephonyDisconnectCause, phoneId))
+                .setReason(toTelecomDisconnectReason(
+                        context, telephonyDisconnectCause, reason, phoneId))
+                .setTone(toTelecomDisconnectCauseTone(
+                        telephonyDisconnectCause, carrierConfig, featureFlags))
+                .setTelephonyDisconnectCause(telephonyDisconnectCause)
+                .setTelephonyPreciseDisconnectCause(telephonyPreciseDisconnectCause)
+                .setImsReasonInfo(imsReasonInfo)
+                .build();
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index ea29b77..a246a1c 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -64,6 +64,8 @@
 import com.android.internal.telephony.ExponentialBackoff;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SimultaneousCallingTracker;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
@@ -71,11 +73,15 @@
 import com.android.telephony.Rlog;
 
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 /**
  * Owns all data we have registered with Telecom including handling dynamic addition and
@@ -122,6 +128,7 @@
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
         private PhoneAccount mAccount;
+        private SimultaneousCallingTracker mSCT;
         private final PstnIncomingCallNotifier mIncomingCallNotifier;
         private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
         private boolean mIsEmergency;
@@ -132,6 +139,7 @@
         private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
         private ImsMmTelManager.CapabilityCallback mMmtelCapabilityCallback;
         private RegistrationManager.RegistrationCallback mImsRegistrationCallback;
+        private SimultaneousCallingTracker.Listener mSimultaneousCallingTrackerListener;
         private ImsMmTelManager mMmTelManager;
         private final boolean mIsTestAccount;
         private boolean mIsVideoCapable;
@@ -144,12 +152,18 @@
         private boolean mIsManageImsConferenceCallSupported;
         private boolean mIsUsingSimCallManager;
         private boolean mIsShowPreciseFailedCause;
+        private Set<Integer> mSimultaneousCallSupportedSubIds;
 
         AccountEntry(Phone phone, boolean isEmergency, boolean isTest) {
             mPhone = phone;
             mIsEmergency = isEmergency;
             mIsTestAccount = isTest;
             mIsAdhocConfCapable = mPhone.isImsRegistered();
+            if (Flags.simultaneousCallingIndications()) {
+                mSCT = SimultaneousCallingTracker.getInstance();
+                mSimultaneousCallSupportedSubIds =
+                        mSCT.getSubIdsSupportingSimultaneousCalling(mPhone.getSubId());
+            }
             mAccount = registerPstnPhoneAccount(isEmergency, isTest);
             Log.i(this, "Registered phoneAccount: %s with handle: %s",
                     mAccount, mAccount.getAccountHandle());
@@ -202,6 +216,21 @@
                 }
             };
             registerImsRegistrationCallback();
+
+            if (Flags.simultaneousCallingIndications()) {
+                //Register SimultaneousCallingTracker listener:
+                mSimultaneousCallingTrackerListener = new SimultaneousCallingTracker.Listener() {
+                    @Override
+                    public void onSimultaneousCallingSupportChanged(Map<Integer,
+                            Set<Integer>> simultaneousCallSubSupportMap) {
+                        updateSimultaneousCallSubSupportMap(simultaneousCallSubSupportMap);
+                    }
+                };
+                SimultaneousCallingTracker.getInstance()
+                        .addListener(mSimultaneousCallingTrackerListener);
+                Log.d(LOG_TAG, "Finished registering mSimultaneousCallingTrackerListener for "
+                        + "phoneId = " + mPhone.getPhoneId() + "; subId = " + mPhone.getSubId());
+            }
         }
 
         void teardown() {
@@ -216,6 +245,10 @@
                     mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
                 }
             }
+            if (Flags.simultaneousCallingIndications()) {
+                SimultaneousCallingTracker.getInstance()
+                        .removeListener(mSimultaneousCallingTrackerListener);
+            }
         }
 
         private void registerMmTelCapabilityCallback() {
@@ -465,6 +498,15 @@
             mIsUsingSimCallManager = isCarrierUsingSimCallManager();
             mIsShowPreciseFailedCause = isCarrierShowPreciseFailedCause();
 
+            // Set CAPABILITY_EMERGENCY_CALLS_ONLY flag if either
+            // - Carrier config overrides subscription is not voice capable, or
+            // - Resource config overrides it be emergency_calls_only
+            // TODO(b/316183370:): merge the two cases when clearing up flag
+            if (Flags.dataOnlyServiceAllowEmergencyCallOnly()) {
+                if (!isSubscriptionVoiceCapableByCarrierConfig()) {
+                    capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
+                }
+            }
             if (isEmergency && mContext.getResources().getBoolean(
                     R.bool.config_emergency_account_emergency_calls_only)) {
                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
@@ -504,7 +546,7 @@
                 Log.i(this, "Adding Merged Account with group: " + Rlog.pii(LOG_TAG, groupId));
             }
 
-            PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
+            PhoneAccount.Builder accountBuilder = PhoneAccount.builder(phoneAccountHandle, label)
                     .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, line1Number, null))
                     .setSubscriptionAddress(
                             Uri.fromParts(PhoneAccount.SCHEME_TEL, subNumber, null))
@@ -515,10 +557,19 @@
                     .setSupportedUriSchemes(Arrays.asList(
                             PhoneAccount.SCHEME_TEL, PhoneAccount.SCHEME_VOICEMAIL))
                     .setExtras(extras)
-                    .setGroupId(groupId)
-                    .build();
+                    .setGroupId(groupId);
 
-            return account;
+            if (Flags.simultaneousCallingIndications()) {
+                Set <PhoneAccountHandle> simultaneousCallingHandles =
+                        mSimultaneousCallSupportedSubIds.stream()
+                                .map(subscriptionId -> PhoneUtils.makePstnPhoneAccountHandleWithId(
+                                        String.valueOf(subscriptionId), userToRegister))
+                                .collect(Collectors.toSet());
+                accountBuilder.setSimultaneousCallingRestriction(simultaneousCallingHandles);
+            }
+
+
+            return accountBuilder.build();
         }
 
         public PhoneAccountHandle getPhoneAccountHandle() {
@@ -804,6 +855,21 @@
         }
 
         /**
+         * @return true if the subscription is voice capable by the carrier config.
+         */
+        private boolean isSubscriptionVoiceCapableByCarrierConfig() {
+            PersistableBundle b =
+                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) {
+                return true; // For any abnormal case, we assume subscription is voice capable
+            }
+            final int[] serviceCapabilities = b.getIntArray(
+                    CarrierConfigManager.KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY);
+            return Arrays.stream(serviceCapabilities).anyMatch(
+                    i -> i == SubscriptionManager.SERVICE_CAPABILITY_VOICE);
+        }
+
+        /**
          * Receives callback from {@link PstnPhoneCapabilitiesNotifier} when the video capabilities
          * have changed.
          *
@@ -824,6 +890,30 @@
             }
         }
 
+        public void updateSimultaneousCallSubSupportMap(Map<Integer,
+                Set<Integer>> simultaneousCallSubSupportMap) {
+            if (!Flags.simultaneousCallingIndications()) { return; }
+            //Check if the simultaneous call support subIds for this account have changed:
+            Set<Integer> updatedSimultaneousCallSupportSubIds = new HashSet<>(3);
+            updatedSimultaneousCallSupportSubIds.addAll(
+                    simultaneousCallSubSupportMap.get(mPhone.getSubId()));
+            if (!updatedSimultaneousCallSupportSubIds.equals(mSimultaneousCallSupportedSubIds)) {
+                //If necessary, update cache and re-register mAccount:
+                mSimultaneousCallSupportedSubIds = updatedSimultaneousCallSupportSubIds;
+                synchronized (mAccountsLock) {
+                    if (!mAccounts.contains(this)) {
+                        // Account has already been torn down, don't try to register it again.
+                        // This handles the case where teardown has already happened, and we got a
+                        // simultaneous calling support update that lost the race for the
+                        // mAccountsLock. In such a scenario by the time we get here, the original
+                        // phone account could have been torn down.
+                        return;
+                    }
+                    mAccount = registerPstnPhoneAccount(mIsEmergency, mIsTestAccount);
+                }
+            }
+        }
+
         public void updateAdhocConfCapability(boolean isAdhocConfCapable) {
             synchronized (mAccountsLock) {
                 if (!mAccounts.contains(this)) {
@@ -1233,7 +1323,18 @@
      */
     public static synchronized TelecomAccountRegistry getInstance(Context context) {
         if (sInstance == null && context != null) {
-            sInstance = new TelecomAccountRegistry(context);
+            if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
+                PackageManager pm = context.getPackageManager();
+                if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
+                        && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+                    sInstance = new TelecomAccountRegistry(context);
+                } else {
+                    Log.d(LOG_TAG, "Not initializing TelecomAccountRegistry: "
+                            + "missing telephony/calling feature(s)");
+                }
+            } else {
+                sInstance = new TelecomAccountRegistry(context);
+            }
         }
         return sInstance;
     }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index f96e6bd..7749a2c 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -83,6 +83,7 @@
 import com.android.internal.telephony.d2d.RtpTransport;
 import com.android.internal.telephony.d2d.Timeouts;
 import com.android.internal.telephony.d2d.TransportProtocol;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
@@ -963,7 +964,7 @@
     private Integer mEmergencyServiceCategory = null;
 
     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
-            String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
+            String callId, int callDirection) {
         setCallDirection(callDirection);
         setTelecomCallId(callId);
         if (originalConnection != null) {
@@ -1015,23 +1016,6 @@
         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
     }
 
-    /**
-     * Notifies this Connection of a request to disconnect a participant of the conference managed
-     * by the connection.
-     *
-     * @param endpoint the {@link Uri} of the participant to disconnect.
-     */
-    @Override
-    public void onDisconnectConferenceParticipant(Uri endpoint) {
-        Log.v(this, "onDisconnectConferenceParticipant %s", endpoint);
-
-        if (mOriginalConnection == null) {
-            return;
-        }
-
-        mOriginalConnection.onDisconnectConferenceParticipant(endpoint);
-    }
-
     @Override
     public void onSeparate() {
         Log.v(this, "onSeparate");
@@ -1304,12 +1288,22 @@
         originalConnection.sendRttModifyResponse(textStream);
     }
 
+    private boolean answeringDropsFgCalls() {
+        if (Flags.callExtraForNonHoldSupportedCarriers()) {
+            Bundle extras = getExtras();
+            if (extras != null) {
+                return extras.getBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL);
+            }
+        }
+        return false;
+    }
+
     public void performAnswer(int videoState) {
         Log.v(this, "performAnswer");
         if (isValidRingingCall() && getPhone() != null) {
             try {
                 mTelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                        getPhoneAccountHandle());
+                        getPhoneAccountHandle(), answeringDropsFgCalls());
                 getPhone().acceptCall(videoState);
             } catch (CallStateException e) {
                 Log.e(this, e, "Failed to accept call.");
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 0b0985b..ffcb269 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -37,7 +37,6 @@
 import android.os.Bundle;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
-import android.provider.DeviceConfig;
 import android.telecom.Conference;
 import android.telecom.Conferenceable;
 import android.telecom.Connection;
@@ -54,7 +53,7 @@
 import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.SelectionAttributes;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.RadioAccessFamily;
@@ -89,6 +88,7 @@
 import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.emergency.RadioOnHelper;
 import com.android.internal.telephony.emergency.RadioOnStateListener;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
@@ -140,8 +140,9 @@
     // Timeout before we terminate the outgoing DSDA call if HOLD did not complete in time on the
     // existing call.
     private static final int DEFAULT_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS = 2000;
-    private static final String KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG =
-            "is_domain_selection_compare_feature_enabled";
+
+    // Timeout to wait for the termination of incoming call before continue with the emergency call.
+    private static final int DEFAULT_REJECT_INCOMING_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds.
 
     // If configured, reject attempts to dial numbers matching this pattern.
     private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
@@ -230,7 +231,6 @@
     private DomainSelectionResolver mDomainSelectionResolver;
     private EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
     private TelephonyConnection mEmergencyConnection;
-    private String mEmergencyCallId = null;
     private Executor mDomainSelectionMainExecutor;
     private ImsManager mImsManager = null;
     private DomainSelectionConnection mDomainSelectionConnection;
@@ -575,8 +575,7 @@
                     }
                     // Update the domain in the case that it changes,for example during initial
                     // setup or when there was an srvcc or internal redial.
-                    mEmergencyStateTracker.onEmergencyCallDomainUpdated(
-                            origConn.getPhoneType(), c.getTelecomCallId());
+                    mEmergencyStateTracker.onEmergencyCallDomainUpdated(origConn.getPhoneType(), c);
                 }
 
                 @Override
@@ -589,8 +588,8 @@
                             + ", state=" + state);
                     if (c.getState() == Connection.STATE_ACTIVE) {
                         mEmergencyStateTracker.onEmergencyCallStateChanged(
-                                c.getOriginalConnection().getState(), c.getTelecomCallId());
-                        releaseEmergencyCallDomainSelection(false);
+                                c.getOriginalConnection().getState(), c);
+                        releaseEmergencyCallDomainSelection(false, true);
                     }
                 }
 
@@ -608,8 +607,8 @@
                         return;
                     }
                     Log.i(this, "onConnectionPropertiesChanged prop=" + connectionProperties);
-                    mEmergencyStateTracker.onEmergencyCallPropertiesChanged(connectionProperties,
-                            c.getTelecomCallId());
+                    mEmergencyStateTracker.onEmergencyCallPropertiesChanged(
+                            connectionProperties, c);
                 }
             };
 
@@ -641,6 +640,13 @@
                 }
             };
 
+    private void clearNormalCallDomainSelectionConnection() {
+        if (mDomainSelectionConnection != null) {
+            mDomainSelectionConnection.finishSelection();
+            mDomainSelectionConnection = null;
+        }
+    }
+
     /**
      * A listener for calls.
      */
@@ -653,17 +659,15 @@
                     if (c != null) {
                         switch(c.getState()) {
                             case Connection.STATE_ACTIVE: {
-                                Log.d(LOG_TAG, "Call State->ACTIVE."
-                                        + "Clearing DomainSelectionConnection");
-                                if (mDomainSelectionConnection != null) {
-                                    mDomainSelectionConnection.finishSelection();
-                                    mDomainSelectionConnection = null;
-                                }
+                                clearNormalCallDomainSelectionConnection();
                                 mNormalCallConnection = null;
                             }
                             break;
 
                             case Connection.STATE_DISCONNECTED: {
+                                // Clear connection if the call state changes from
+                                // DIALING -> DISCONNECTED without ACTIVE State.
+                                clearNormalCallDomainSelectionConnection();
                                 c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
                             }
                             break;
@@ -705,6 +709,20 @@
         }
     }
 
+    private static class OnDisconnectListener extends
+            com.android.internal.telephony.Connection.ListenerBase {
+        private final CompletableFuture<Boolean> mFuture;
+
+        OnDisconnectListener(CompletableFuture<Boolean> future) {
+            mFuture = future;
+        }
+
+        @Override
+        public void onDisconnect(int cause) {
+            mFuture.complete(true);
+        }
+    };
+
     private final DomainSelectionConnection.DomainSelectionConnectionCallback
             mEmergencyDomainSelectionConnectionCallback =
                     new DomainSelectionConnection.DomainSelectionConnectionCallback() {
@@ -728,9 +746,8 @@
                         Phone phone = mEmergencyCallDomainSelectionConnection.getPhone();
                         mEmergencyConnection.removeTelephonyConnectionListener(
                                 mEmergencyConnectionListener);
-                        releaseEmergencyCallDomainSelection(true);
-                        mEmergencyStateTracker.endCall(mEmergencyCallId);
-                        mEmergencyCallId = null;
+                        releaseEmergencyCallDomainSelection(true, false);
+                        mEmergencyStateTracker.endCall(c);
                         retryOutgoingOriginalConnection(c, phone, isPermanentFailure);
                         return;
                     }
@@ -1102,6 +1119,7 @@
         final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
                 /* Note: when not an emergency, handle can be null for unknown callers */
                 handle == null ? null : handle.getSchemeSpecificPart());
+        ImsPhone imsPhone = phone != null ? (ImsPhone) phone.getImsPhone() : null;
 
         boolean isPhoneWifiCallingEnabled = phone != null && phone.isWifiCallingEnabled();
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
@@ -1132,6 +1150,7 @@
             }
             int timeoutToOnTimeoutCallback = mDomainSelectionResolver.isDomainSelectionSupported()
                     ? TIMEOUT_TO_DYNAMIC_ROUTING_MS : 0;
+            final Phone phoneForEmergency = phone;
             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
@@ -1156,7 +1175,7 @@
                             && phone.getHalVersion(HAL_SERVICE_VOICE)
                             .less(RIL.RADIO_HAL_VERSION_1_4);
                     if (mDomainSelectionResolver.isDomainSelectionSupported()) {
-                        if (isEmergencyNumber) {
+                        if (isEmergencyNumber && phone == phoneForEmergency) {
                             // Since the domain selection service is enabled,
                             // dilaing normal routing emergency number only reaches here.
                             if (!isVoiceInService(phone, imsVoiceCapable)) {
@@ -1208,8 +1227,9 @@
             }
 
             if (!isEmergencyNumber) {
-                if (mSatelliteController.isSatelliteEnabled()
-                        || isCallDisallowedDueToSatellite(phone)) {
+                if ((mSatelliteController.isSatelliteEnabled()
+                        || isCallDisallowedDueToSatellite(phone))
+                        && (imsPhone == null || !imsPhone.canMakeWifiCall())) {
                     Log.d(this, "onCreateOutgoingConnection, cannot make call in satellite mode.");
                     return Connection.createFailedConnection(
                             mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -1249,27 +1269,9 @@
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         true, handle, phone);
 
-                CompletableFuture<Void> maybeHoldFuture = CompletableFuture.completedFuture(null);
-                if (mTelephonyManagerProxy.isConcurrentCallsPossible()
-                        && shouldHoldForEmergencyCall(phone)) {
-                    // If the PhoneAccountHandle was adjusted on building the TelephonyConnection,
-                    // the relevant PhoneAccountHandle will be updated in resultConnection.
-                    PhoneAccountHandle phoneAccountHandle =
-                            resultConnection.getPhoneAccountHandle() == null
-                            ? request.getAccountHandle() : resultConnection.getPhoneAccountHandle();
-                    Conferenceable c = maybeHoldCallsOnOtherSubs(phoneAccountHandle);
-                    if (c != null) {
-                        maybeHoldFuture = delayDialForOtherSubHold(phone, c, (success) -> {
-                            Log.i(this, "onCreateOutgoingConn emergency-"
-                                    + " delayDialForOtherSubHold success = " + success);
-                            if (!success) {
-                                // Terminates the existing call to make way for the emergency call.
-                                hangup(c, android.telephony.DisconnectCause
-                                        .OUTGOING_EMERGENCY_CALL_PLACED);
-                            }
-                        });
-                    }
-                }
+                CompletableFuture<Void> maybeHoldFuture =
+                        checkAndHoldCallsOnOtherSubsForEmergencyCall(request,
+                                resultConnection, phone);
                 Consumer<Boolean> ddsSwitchConsumer = (result) -> {
                     Log.i(this, "onCreateOutgoingConn emergency-"
                             + " delayDialForDdsSwitch result = " + result);
@@ -1281,6 +1283,32 @@
         }
     }
 
+    private CompletableFuture<Void> checkAndHoldCallsOnOtherSubsForEmergencyCall(
+            ConnectionRequest request, Connection resultConnection, Phone phone) {
+        CompletableFuture<Void> maybeHoldFuture = CompletableFuture.completedFuture(null);
+        if (mTelephonyManagerProxy.isConcurrentCallsPossible()
+                && shouldHoldForEmergencyCall(phone)) {
+            // If the PhoneAccountHandle was adjusted on building the TelephonyConnection,
+            // the relevant PhoneAccountHandle will be updated in resultConnection.
+            PhoneAccountHandle phoneAccountHandle =
+                    resultConnection.getPhoneAccountHandle() == null
+                    ? request.getAccountHandle() : resultConnection.getPhoneAccountHandle();
+            Conferenceable c = maybeHoldCallsOnOtherSubs(phoneAccountHandle);
+            if (c != null) {
+                maybeHoldFuture = delayDialForOtherSubHold(phone, c, (success) -> {
+                    Log.i(this, "checkAndHoldCallsOnOtherSubsForEmergencyCall"
+                            + " delayDialForOtherSubHold success = " + success);
+                    if (!success) {
+                        // Terminates the existing call to make way for the emergency call.
+                        hangup(c, android.telephony.DisconnectCause
+                                .OUTGOING_EMERGENCY_CALL_PLACED);
+                    }
+                });
+            }
+        }
+        return maybeHoldFuture;
+    }
+
     private Connection placeOutgoingConnection(ConnectionRequest request,
             Connection resultConnection, Phone phone) {
         // If there was a failure, the resulting connection will not be a TelephonyConnection,
@@ -2220,28 +2248,20 @@
             }
         } catch (CallStateException e) {
             Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
+            if (mDomainSelectionResolver.isDomainSelectionSupported()) {
+                // Notify EmergencyStateTracker and DomainSelector of the cancellation by exception
+                onLocalHangup(connection);
+            }
             connection.unregisterForCallEvents();
             handleCallStateException(e, connection, phone);
             return;
         }
         if (originalConnection == null) {
-            int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
-            // On GSM phones, null connection means that we dialed an MMI code
-            if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
-                    phone.isUtEnabled()) {
-                Log.d(this, "dialed MMI code");
-                int subId = phone.getSubId();
-                Log.d(this, "subId: "+subId);
-                telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
-                final Intent intent = new Intent(this, MMIDialogActivity.class);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                        Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                    SubscriptionManager.putSubscriptionIdExtra(intent, subId);
-                }
-                startActivity(intent);
-            }
             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
+
+            // On GSM phones, null connection means that we dialed an MMI code
+            int telephonyDisconnectCause = handleMmiCode(
+                    phone, android.telephony.DisconnectCause.OUTGOING_FAILURE);
             connection.setTelephonyConnectionDisconnected(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(telephonyDisconnectCause,
                             "Connection is null", phone.getPhoneId()));
@@ -2261,6 +2281,25 @@
         }
     }
 
+    private int handleMmiCode(Phone phone, int telephonyDisconnectCause) {
+        int disconnectCause = telephonyDisconnectCause;
+        if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM
+                || phone.isUtEnabled()) {
+            Log.d(this, "dialed MMI code");
+            int subId = phone.getSubId();
+            Log.d(this, "subId: " + subId);
+            disconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
+            final Intent intent = new Intent(this, MMIDialogActivity.class);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                SubscriptionManager.putSubscriptionIdExtra(intent, subId);
+            }
+            startActivity(intent);
+        }
+        return disconnectCause;
+    }
+
     private void handleOutgoingCallConnectionByCallDomainSelection(
             int domain, Phone phone, String number, int videoState) {
         Log.d(this, "Call Domain Selected : " + domain);
@@ -2270,14 +2309,6 @@
                 extras = new Bundle();
             }
             extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
-            // Add flag to bundle for comparing legacy and new domain selection results. When
-            // EXTRA_COMPARE_DOMAIN flag is true, legacy domain selection result is used for
-            // placing the call and if both the results are not same then bug report is generated.
-            DeviceConfig.Properties properties = //read all telephony properties
-                    DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY);
-            boolean compareDomainSelection =
-                    properties.getBoolean(KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG, false);
-            extras.putBoolean(PhoneConstants.EXTRA_COMPARE_DOMAIN, compareDomainSelection);
 
             if (phone != null) {
                 Log.v(LOG_TAG, "Call dialing. Domain: " + domain);
@@ -2290,6 +2321,24 @@
                                         .build(),
                                 mNormalCallConnection::registerForCallEvents);
 
+                if (connection == null) {
+                    Log.d(this, "placeOutgoingConnection, phone.dial returned null");
+
+                    // On GSM phones, null connection means that we dialed an MMI code
+                    int telephonyDisconnectCause = handleMmiCode(
+                            phone, android.telephony.DisconnectCause.OUTGOING_FAILURE);
+                    if (mNormalCallConnection.getState() != Connection.STATE_DISCONNECTED) {
+                        mNormalCallConnection.setTelephonyConnectionDisconnected(
+                                mDisconnectCauseFactory.toTelecomDisconnectCause(
+                                        telephonyDisconnectCause,
+                                        "Connection is null",
+                                        phone.getPhoneId()));
+                        mNormalCallConnection.close();
+                    }
+                    clearNormalCallDomainSelectionConnection();
+                    return;
+                }
+
                 mNormalCallConnection.setOriginalConnection(connection);
                 mNormalCallConnection.addTelephonyConnectionListener(mNormalCallConnectionListener);
                 return;
@@ -2313,10 +2362,7 @@
                             e.getMessage(), phone.getPhoneId()));
             mNormalCallConnection.close();
         }
-        if (mDomainSelectionConnection != null) {
-            mDomainSelectionConnection.finishSelection();
-            mDomainSelectionConnection = null;
-        }
+        clearNormalCallDomainSelectionConnection();
         mNormalCallConnection = null;
     }
 
@@ -2336,6 +2382,7 @@
         boolean isMmiCode = (dialPart.startsWith("*") || dialPart.startsWith("#"))
                 && dialPart.endsWith("#");
         boolean isSuppServiceCode = ImsPhoneMmiCode.isSuppServiceCodes(dialPart, phone);
+        boolean isPotentialUssdCode = isMmiCode && !isSuppServiceCode;
 
         // If the number is both an MMI code and a supplementary service code,
         // it shall be treated as UT. In this case, domain selection is not performed.
@@ -2344,6 +2391,10 @@
             return false;
         }
 
+        /* For USSD codes, connection is closed and MMIDialogActivity is started.
+           To avoid connection close and return false. isPotentialUssdCode is handled after
+            all condition checks. */
+
         // Check and select same domain as ongoing call on the same subscription (if exists)
         int activeCallDomain = getActiveCallDomain(phone.getSubId());
         if (activeCallDomain != NetworkRegistrationInfo.DOMAIN_UNKNOWN
@@ -2364,7 +2415,7 @@
         SelectionAttributes selectionAttributes =
                 new SelectionAttributes.Builder(phone.getPhoneId(), phone.getSubId(),
                         SELECTOR_TYPE_CALLING)
-                        .setNumber(number)
+                        .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null))
                         .setEmergency(false)
                         .setVideoCall(VideoProfile.isVideo(videoState))
                         .build();
@@ -2379,6 +2430,15 @@
         mNormalCallConnection = connection;
         future.thenAcceptAsync((domain) -> handleOutgoingCallConnectionByCallDomainSelection(
                 domain, phone, number, videoState), mDomainSelectionMainExecutor);
+
+        if (isPotentialUssdCode) {
+            Log.v(LOG_TAG, "PotentialUssdCode. Closing connection with DisconnectCause.DIALED_MMI");
+            connection.setTelephonyConnectionDisconnected(
+                    mDisconnectCauseFactory.toTelecomDisconnectCause(
+                            android.telephony.DisconnectCause.DIALED_MMI,
+                            "Dialing USSD", phone.getPhoneId()));
+            connection.close();
+        }
         return true;
     }
 
@@ -2395,6 +2455,32 @@
             Log.i(this, "placeEmergencyConnection");
 
             mIsEmergencyCallPending = true;
+            mEmergencyConnection = (TelephonyConnection) resultConnection;
+        }
+
+        CompletableFuture<Void> maybeHoldFuture =
+                checkAndHoldCallsOnOtherSubsForEmergencyCall(request, resultConnection, phone);
+        maybeHoldFuture.thenRun(() -> placeEmergencyConnectionInternal(resultConnection,
+                phone, request, numberToDial, isTestEmergencyNumber, needToTurnOnRadio));
+
+        // Non TelephonyConnection type instance means dialing failure.
+        return resultConnection;
+    }
+
+    @SuppressWarnings("FutureReturnValueIgnored")
+    private void placeEmergencyConnectionInternal(final Connection resultConnection,
+            final Phone phone, final ConnectionRequest request,
+            final String numberToDial, final boolean isTestEmergencyNumber,
+            final boolean needToTurnOnRadio) {
+
+        if (mEmergencyConnection == null) {
+            Log.i(this, "placeEmergencyConnectionInternal dialing canceled");
+            return;
+        }
+
+        if (resultConnection instanceof TelephonyConnection) {
+            Log.i(this, "placeEmergencyConnectionInternal");
+
             ((TelephonyConnection) resultConnection).addTelephonyConnectionListener(
                     mEmergencyConnectionListener);
 
@@ -2402,19 +2488,18 @@
                 mEmergencyStateTracker = EmergencyStateTracker.getInstance();
             }
 
-            mEmergencyCallId = resultConnection.getTelecomCallId();
             CompletableFuture<Integer> future = mEmergencyStateTracker.startEmergencyCall(
-                    phone, mEmergencyCallId, isTestEmergencyNumber);
+                    phone, resultConnection, isTestEmergencyNumber);
             future.thenAccept((result) -> {
                 Log.d(this, "startEmergencyCall-complete result=" + result);
-                if (mEmergencyCallId == null) {
+                if (mEmergencyConnection == null) {
                     Log.i(this, "startEmergencyCall-complete dialing canceled");
                     return;
                 }
                 if (result == android.telephony.DisconnectCause.NOT_DISCONNECTED) {
                     createEmergencyConnection(phone, (TelephonyConnection) resultConnection,
-                            numberToDial, request, needToTurnOnRadio,
-                            mEmergencyStateTracker.getEmergencyRegResult());
+                            numberToDial, isTestEmergencyNumber, request, needToTurnOnRadio,
+                            mEmergencyStateTracker.getEmergencyRegistrationResult());
                 } else {
                     mEmergencyConnection = null;
                     String reason = "Couldn't setup emergency call";
@@ -2427,18 +2512,15 @@
                     mIsEmergencyCallPending = false;
                 }
             });
-            mEmergencyConnection = (TelephonyConnection) resultConnection;
-            return resultConnection;
         }
-        Log.i(this, "placeEmergencyConnection returns null");
-        return null;
     }
 
     @SuppressWarnings("FutureReturnValueIgnored")
     private void createEmergencyConnection(final Phone phone,
             final TelephonyConnection resultConnection, final String number,
+            final boolean isTestEmergencyNumber,
             final ConnectionRequest request, boolean needToTurnOnRadio,
-            final EmergencyRegResult regResult) {
+            final EmergencyRegistrationResult regResult) {
         Log.i(this, "createEmergencyConnection");
 
         if (phone.getImsPhone() == null) {
@@ -2478,21 +2560,27 @@
         DomainSelectionService.SelectionAttributes attr =
                 EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                         phone.getPhoneId(), phone.getSubId(), needToTurnOnRadio,
-                        request.getTelecomCallId(), number, 0, null, regResult);
+                        request.getTelecomCallId(), number, isTestEmergencyNumber,
+                        0, null, regResult);
 
         CompletableFuture<Integer> future =
                 mEmergencyCallDomainSelectionConnection.createEmergencyConnection(
                         attr, mEmergencyDomainSelectionConnectionCallback);
         future.thenAcceptAsync((result) -> {
             Log.d(this, "createEmergencyConnection-complete result=" + result);
-            if (mEmergencyCallId == null) {
+            if (mEmergencyConnection == null) {
                 Log.i(this, "createEmergencyConnection-complete dialing canceled");
                 return;
             }
             Bundle extras = request.getExtras();
             extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, result);
-            placeOutgoingConnection(request, resultConnection, phone);
-            mIsEmergencyCallPending = false;
+            CompletableFuture<Void> rejectFuture = checkAndRejectIncomingCall(phone, (ret) -> {
+                if (!ret) {
+                    Log.i(this, "createEmergencyConnection reject incoming call failed");
+                }
+            });
+            rejectFuture.thenRun(() -> placeEmergencyConnectionOnSelectedDomain(request,
+                    resultConnection, phone));
         }, mDomainSelectionMainExecutor);
     }
 
@@ -2503,22 +2591,40 @@
         extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, NetworkRegistrationInfo.DOMAIN_CS);
         mDomainSelectionMainExecutor.execute(
                 () -> {
-                    if (mEmergencyCallId == null) {
+                    if (mEmergencyConnection == null) {
                         Log.i(this, "dialCsEmergencyCall dialing canceled");
                         return;
                     }
-                    placeOutgoingConnection(request, resultConnection, phone);
+                    CompletableFuture<Void> future = checkAndRejectIncomingCall(phone, (ret) -> {
+                        if (!ret) {
+                            Log.i(this, "dialCsEmergencyCall reject incoming call failed");
+                        }
+                    });
+                    future.thenRun(() -> placeEmergencyConnectionOnSelectedDomain(request,
+                            resultConnection, phone));
                 });
     }
 
-    private void releaseEmergencyCallDomainSelection(boolean cancel) {
+    private void placeEmergencyConnectionOnSelectedDomain(ConnectionRequest request,
+            TelephonyConnection resultConnection, Phone phone) {
+        if (mEmergencyConnection == null) {
+            Log.i(this, "placeEmergencyConnectionOnSelectedDomain dialing canceled");
+            return;
+        }
+        placeOutgoingConnection(request, resultConnection, phone);
+        mIsEmergencyCallPending = false;
+    }
+
+    private void releaseEmergencyCallDomainSelection(boolean cancel, boolean isActive) {
         if (mEmergencyCallDomainSelectionConnection != null) {
             if (cancel) mEmergencyCallDomainSelectionConnection.cancelSelection();
             else mEmergencyCallDomainSelectionConnection.finishSelection();
             mEmergencyCallDomainSelectionConnection = null;
         }
         mIsEmergencyCallPending = false;
-        mEmergencyConnection = null;
+        if (!isActive) {
+            mEmergencyConnection = null;
+        }
     }
 
     /**
@@ -2538,14 +2644,14 @@
         int callFailCause = c.getOriginalConnection().getPreciseDisconnectCause();
 
         Log.i(this, "maybeReselectDomain csCause=" +  callFailCause + ", psCause=" + reasonInfo);
-        if (TextUtils.equals(mEmergencyCallId, c.getTelecomCallId())) {
+        if (mEmergencyConnection == c) {
             if (mEmergencyCallDomainSelectionConnection != null) {
                 return maybeReselectDomainForEmergencyCall(c, callFailCause, reasonInfo);
             }
             Log.i(this, "maybeReselectDomain endCall()");
             c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
-            mEmergencyStateTracker.endCall(c.getTelecomCallId());
-            mEmergencyCallId = null;
+            releaseEmergencyCallDomainSelection(false, false);
+            mEmergencyStateTracker.endCall(c);
             return false;
         }
 
@@ -2557,10 +2663,7 @@
                             && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
                 // clear normal call domain selector
                 c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
-                if (mDomainSelectionConnection != null) {
-                    mDomainSelectionConnection.finishSelection();
-                    mDomainSelectionConnection = null;
-                }
+                clearNormalCallDomainSelectionConnection();
                 mNormalCallConnection = null;
 
                 onEmergencyRedial(c, c.getPhone().getDefaultPhone());
@@ -2586,7 +2689,7 @@
                     EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                             c.getPhone().getPhoneId(), c.getPhone().getSubId(), false,
                             c.getTelecomCallId(), c.getAddress().getSchemeSpecificPart(),
-                            callFailCause, reasonInfo, null);
+                            false, callFailCause, reasonInfo, null);
 
             CompletableFuture<Integer> future =
                     mEmergencyCallDomainSelectionConnection.reselectDomain(attr);
@@ -2595,7 +2698,7 @@
             if (future != null) {
                 future.thenAcceptAsync((result) -> {
                     Log.d(this, "reselectDomain-complete");
-                    if (mEmergencyCallId == null) {
+                    if (mEmergencyConnection == null) {
                         Log.i(this, "reselectDomain-complete dialing canceled");
                         return;
                     }
@@ -2607,9 +2710,8 @@
 
         Log.i(this, "maybeReselectDomainForEmergencyCall endCall()");
         c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
-        releaseEmergencyCallDomainSelection(true);
-        mEmergencyStateTracker.endCall(c.getTelecomCallId());
-        mEmergencyCallId = null;
+        releaseEmergencyCallDomainSelection(true, false);
+        mEmergencyStateTracker.endCall(c);
         return false;
     }
 
@@ -2756,20 +2858,35 @@
         }
 
         c.removeTelephonyConnectionListener(mTelephonyConnectionListener);
-        if (mDomainSelectionConnection != null) {
-            mDomainSelectionConnection.finishSelection();
-            mDomainSelectionConnection = null;
-        }
+        clearNormalCallDomainSelectionConnection();
         mNormalCallConnection = null;
         Log.d(LOG_TAG, "Reselect call domain not triggered.");
         return false;
     }
 
-    private void onEmergencyRedialOnDomain(TelephonyConnection connection,
+    private void onEmergencyRedialOnDomain(final TelephonyConnection connection,
             final Phone phone, @NetworkRegistrationInfo.Domain int domain) {
         Log.i(this, "onEmergencyRedialOnDomain phoneId=" + phone.getPhoneId()
                 + ", domain=" + DomainSelectionService.getDomainName(domain));
 
+        final Bundle extras = new Bundle();
+        extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
+
+        CompletableFuture<Void> future = checkAndRejectIncomingCall(phone, (ret) -> {
+            if (!ret) {
+                Log.i(this, "onEmergencyRedialOnDomain reject incoming call failed");
+            }
+        });
+        future.thenRun(() -> onEmergencyRedialOnDomainInternal(connection, phone, extras));
+    }
+
+    private void onEmergencyRedialOnDomainInternal(TelephonyConnection connection,
+            Phone phone, Bundle extras) {
+        if (mEmergencyConnection == null) {
+            Log.i(this, "onEmergencyRedialOnDomainInternal dialing canceled");
+            return;
+        }
+
         String number = connection.getAddress().getSchemeSpecificPart();
 
         // Indicates undetectable emergency number with DialArgs
@@ -2778,12 +2895,9 @@
         if (connection.getEmergencyServiceCategory() != null) {
             isEmergency = true;
             eccCategory = connection.getEmergencyServiceCategory();
-            Log.i(this, "onEmergencyRedialOnDomain eccCategory=" + eccCategory);
+            Log.i(this, "onEmergencyRedialOnDomainInternal eccCategory=" + eccCategory);
         }
 
-        Bundle extras = new Bundle();
-        extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
-
         com.android.internal.telephony.Connection originalConnection =
                 connection.getOriginalConnection();
         try {
@@ -2798,14 +2912,20 @@
                         connection::registerForCallEvents);
             }
         } catch (CallStateException e) {
-            Log.e(this, e, "onEmergencyRedialOnDomain, exception: " + e);
+            Log.e(this, e, "onEmergencyRedialOnDomainInternal, exception: " + e);
+            onLocalHangup(connection);
+            connection.unregisterForCallEvents();
+            handleCallStateException(e, connection, phone);
+            return;
         }
         if (originalConnection == null) {
-            Log.d(this, "onEmergencyRedialOnDomain, phone.dial returned null");
-            connection.setDisconnected(
+            Log.d(this, "onEmergencyRedialOnDomainInternal, phone.dial returned null");
+            onLocalHangup(connection);
+            connection.setTelephonyConnectionDisconnected(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
                                 android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                                 "unknown error"));
+            connection.close();
         } else {
             connection.setOriginalConnection(originalConnection);
         }
@@ -2826,12 +2946,12 @@
             mEmergencyStateTracker = EmergencyStateTracker.getInstance();
         }
 
-        mEmergencyCallId = c.getTelecomCallId();
+        mEmergencyConnection = c;
         CompletableFuture<Integer> future = mEmergencyStateTracker.startEmergencyCall(
-                phone, mEmergencyCallId, isTestEmergencyNumber);
+                phone, c, isTestEmergencyNumber);
         future.thenAccept((result) -> {
             Log.d(this, "onEmergencyRedial-complete result=" + result);
-            if (mEmergencyCallId == null) {
+            if (mEmergencyConnection == null) {
                 Log.i(this, "onEmergencyRedial-complete dialing canceled");
                 return;
             }
@@ -2852,15 +2972,13 @@
                 mEmergencyCallDomainSelectionConnection =
                         (EmergencyCallDomainSelectionConnection) selectConnection;
 
-                mEmergencyConnection = c;
-
                 DomainSelectionService.SelectionAttributes attr =
                         EmergencyCallDomainSelectionConnection.getSelectionAttributes(
                                 phone.getPhoneId(),
                                 phone.getSubId(), false,
                                 c.getTelecomCallId(),
-                                c.getAddress().getSchemeSpecificPart(),
-                                0, null, mEmergencyStateTracker.getEmergencyRegResult());
+                                c.getAddress().getSchemeSpecificPart(), isTestEmergencyNumber,
+                                0, null, mEmergencyStateTracker.getEmergencyRegistrationResult());
 
                 CompletableFuture<Integer> domainFuture =
                         mEmergencyCallDomainSelectionConnection.createEmergencyConnection(
@@ -2872,6 +2990,7 @@
                     mIsEmergencyCallPending = false;
                 }, mDomainSelectionMainExecutor);
             } else {
+                mEmergencyConnection = null;
                 c.setTelephonyConnectionDisconnected(
                         mDisconnectCauseFactory.toTelecomDisconnectCause(result, "unknown error"));
                 c.close();
@@ -2883,7 +3002,7 @@
     private void recreateEmergencyConnection(final TelephonyConnection connection,
             final Phone phone, final @NetworkRegistrationInfo.Domain int result) {
         Log.d(this, "recreateEmergencyConnection result=" + result);
-        if (mEmergencyCallId == null) {
+        if (mEmergencyConnection == null) {
             Log.i(this, "recreateEmergencyConnection dialing canceled");
             return;
         }
@@ -2934,15 +3053,6 @@
 
         Bundle extras = new Bundle();
         extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
-        // Add flag to bundle for comparing legacy and new domain selection results. When
-        // EXTRA_COMPARE_DOMAIN flag is true, legacy domain selection result is used for
-        // placing the call and if both the results are not same then bug report is generated.
-        DeviceConfig.Properties properties = //read all telephony properties
-                DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TELEPHONY);
-        boolean compareDomainSelection =
-                properties.getBoolean(KEY_DOMAIN_COMPARE_FEATURE_ENABLED_FLAG, false);
-        extras.putBoolean(PhoneConstants.EXTRA_COMPARE_DOMAIN, compareDomainSelection);
-
         com.android.internal.telephony.Connection originalConnection =
                 connection.getOriginalConnection();
         if (originalConnection instanceof ImsPhoneConnection) {
@@ -2977,16 +3087,25 @@
     }
 
     protected void onLocalHangup(TelephonyConnection c) {
-        if (TextUtils.equals(mEmergencyCallId, c.getTelecomCallId())) {
-            Log.i(this, "onLocalHangup " + mEmergencyCallId);
+        if (mEmergencyConnection == c) {
+            Log.i(this, "onLocalHangup " + c.getTelecomCallId());
             c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
-            releaseEmergencyCallDomainSelection(true);
-            mEmergencyStateTracker.endCall(c.getTelecomCallId());
-            mEmergencyCallId = null;
+            releaseEmergencyCallDomainSelection(true, false);
+            mEmergencyStateTracker.endCall(c);
         }
     }
 
     @VisibleForTesting
+    public TelephonyConnection getEmergencyConnection() {
+        return mEmergencyConnection;
+    }
+
+    @VisibleForTesting
+    public void setEmergencyConnection(TelephonyConnection c) {
+        mEmergencyConnection = c;
+    }
+
+    @VisibleForTesting
     public TelephonyConnection.TelephonyConnectionListener getEmergencyConnectionListener() {
         return mEmergencyConnectionListener;
     }
@@ -3354,6 +3473,52 @@
     }
 
     /**
+     * If needed, block until an incoming call is disconnected for outgoing emergency call,
+     * or timeout expires.
+     * @param phone The Phone to reject the incoming call
+     * @param completeConsumer The consumer to call once rejecting incoming call has been
+     *        completed. {@code true} result if the operation commpletes successfully, or
+     *        {@code false} if the operation timed out/failed.
+     */
+    private CompletableFuture<Void> checkAndRejectIncomingCall(Phone phone,
+            Consumer<Boolean> completeConsumer) {
+        if (phone == null) {
+            // Unexpected inputs
+            Log.i(this, "checkAndRejectIncomingCall phone is null");
+            completeConsumer.accept(false);
+            return CompletableFuture.completedFuture(null);
+        }
+
+        Call ringingCall = phone.getRingingCall();
+        if (ringingCall == null || !ringingCall.isRinging()) {
+            completeConsumer.accept(true);
+            return CompletableFuture.completedFuture(null);
+        }
+        Log.i(this, "checkAndRejectIncomingCall found a ringing call");
+
+        try {
+            ringingCall.hangup();
+            CompletableFuture<Boolean> future = new CompletableFuture<>();
+            com.android.internal.telephony.Connection cn = ringingCall.getLatestConnection();
+            cn.addListener(new OnDisconnectListener(future));
+            // A timeout that will complete the future to not block the outgoing call indefinitely.
+            CompletableFuture<Boolean> timeout = new CompletableFuture<>();
+            phone.getContext().getMainThreadHandler().postDelayed(
+                    () -> timeout.complete(false), DEFAULT_REJECT_INCOMING_CALL_TIMEOUT_MS);
+            // Ensure that the Consumer is completed on the main thread.
+            return future.acceptEitherAsync(timeout, completeConsumer,
+                    phone.getContext().getMainExecutor()).exceptionally((ex) -> {
+                        Log.w(this, "checkAndRejectIncomingCall - exceptionally= " + ex);
+                        return null;
+                    });
+        } catch (Exception e) {
+            Log.w(this, "checkAndRejectIncomingCall - exception= " + e.getMessage());
+            completeConsumer.accept(false);
+            return CompletableFuture.completedFuture(null);
+        }
+    }
+
+    /**
      * Get the Phone to use for an emergency call of the given emergency number address:
      *  a) If there are multiple Phones with the Subscriptions that support the emergency number
      *     address, and one of them is the default voice Phone, consider the default voice phone
@@ -3598,6 +3763,11 @@
     @VisibleForTesting
     public boolean isAvailableForEmergencyCalls(Phone phone,
             @EmergencyNumber.EmergencyCallRouting int routing) {
+        if (isCallDisallowedDueToSatellite(phone)) {
+            // Phone is connected to satellite due to which it is not preferred for emergency call.
+            return false;
+        }
+
         if (phone.getImsRegistrationTech() == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
             // When a Phone is registered to Cross-SIM calling, there must always be a Phone on the
             // other sub which is registered to cellular, so that must be selected.
@@ -3906,10 +4076,34 @@
         return origAccountHandle;
     }
 
+    /*
+     * Returns true if both existing connections on-device and the incoming connection support HOLD,
+     * false otherwise. Assumes that a TelephonyConference supports HOLD.
+     */
+    private boolean allCallsSupportHold(@NonNull TelephonyConnection incomingConnection) {
+        if (Flags.callExtraForNonHoldSupportedCarriers()) {
+            if (getAllConnections().stream()
+                    .filter(c ->
+                            // Exclude multiendpoint calls as they're not on this device.
+                            (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL)
+                                    == 0
+                                    && (c.getConnectionCapabilities()
+                                    & Connection.CAPABILITY_SUPPORT_HOLD) != 0).count() == 0) {
+                return false;
+            }
+            if ((incomingConnection.getConnectionCapabilities()
+                    & Connection.CAPABILITY_SUPPORT_HOLD) == 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
-     * For the passed in incoming {@link TelephonyConnection}, for non- dual active voice devices,
+     * For the passed in incoming {@link TelephonyConnection}, for non-dual active voice devices,
      * adds {@link Connection#EXTRA_ANSWERING_DROPS_FG_CALL} if there are ongoing calls on another
-     * subscription (ie phone account handle) than the one passed in.
+     * subscription (ie phone account handle) than the one passed in. For dual active voice devices,
+     * still sets the EXTRA if either subscription has connections that don't support hold.
      * @param connection The connection.
      * @param phoneAccountHandle The {@link PhoneAccountHandle} the incoming call originated on;
      *                           this is passed in because
@@ -3919,10 +4113,11 @@
      */
     public void maybeIndicateAnsweringWillDisconnect(@NonNull TelephonyConnection connection,
             @NonNull PhoneAccountHandle phoneAccountHandle) {
-        if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
-            return;
-        }
         if (isCallPresentOnOtherSub(phoneAccountHandle)) {
+            if (mTelephonyManagerProxy.isConcurrentCallsPossible()
+                    && allCallsSupportHold(connection)) {
+                return;
+            }
             Log.i(this, "maybeIndicateAnsweringWillDisconnect; answering call %s will cause a call "
                     + "on another subscription to drop.", connection.getTelecomCallId());
             Bundle extras = new Bundle();
@@ -3947,13 +4142,16 @@
 
     /**
      * Where there are ongoing calls on another subscription other than the one specified,
-     * disconnect these calls for non-DSDA devices. This is used where there is an incoming call on
-     * one sub, but there are ongoing calls on another sub which need to be disconnected.
+     * disconnect these calls. This is used where there is an incoming call on one sub, but there
+     * are ongoing calls on another sub which need to be disconnected.
      * @param incomingHandle The incoming {@link PhoneAccountHandle}.
+     * @param answeringDropsFgCall Whether for dual-SIM dual active devices, answering the incoming
+     *                            call should drop the second call.
      */
-    public void maybeDisconnectCallsOnOtherSubs(@NonNull PhoneAccountHandle incomingHandle) {
+    public void maybeDisconnectCallsOnOtherSubs(
+            @NonNull PhoneAccountHandle incomingHandle, boolean answeringDropsFgCall) {
         Log.i(this, "maybeDisconnectCallsOnOtherSubs: check for calls not on %s", incomingHandle);
-        maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle,
+        maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle, answeringDropsFgCall,
                 mTelephonyManagerProxy);
     }
 
@@ -3963,13 +4161,16 @@
      * the core functionality.
      * @param connections the calls to check.
      * @param incomingHandle the incoming handle.
+     * @param answeringDropsFgCall Whether for dual-SIM dual active devices, answering the incoming
+     *                            call should drop the second call.
      * @param telephonyManagerProxy the proxy to the {@link TelephonyManager} instance.
      */
     @VisibleForTesting
     public static void maybeDisconnectCallsOnOtherSubs(@NonNull Collection<Connection> connections,
             @NonNull PhoneAccountHandle incomingHandle,
+            boolean answeringDropsFgCall,
             TelephonyManagerProxy telephonyManagerProxy) {
-        if (telephonyManagerProxy.isConcurrentCallsPossible()) {
+        if (telephonyManagerProxy.isConcurrentCallsPossible() && !answeringDropsFgCall) {
             return;
         }
         connections.stream()
@@ -4241,6 +4442,10 @@
         }
 
         ServiceState serviceState = phone.getServiceState();
+        if (serviceState == null) {
+            return false;
+        }
+
         if (!serviceState.isUsingNonTerrestrialNetwork()) {
             // Device is not connected to satellite
             return false;
diff --git a/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
index 44904f4..d368d46 100644
--- a/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
+++ b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
@@ -29,9 +29,12 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.PreciseDisconnectCauses;
 import android.telephony.CarrierConfigManager;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
@@ -144,7 +147,7 @@
      * @param selector The instance of {@link EmergencyCallDomainSelector}.
      * @param callId The call identifier.
      * @param number The dialing number.
-     * @param inService Indiates that normal service is available.
+     * @param inService Indicates that normal service is available.
      * @param roaming Indicates that it's in roaming or non-domestic network.
      * @param modemCount The number of active modem count
      */
@@ -228,12 +231,26 @@
     }
 
     /**
+     * Returns whether there is another slot with which normal service is available.
+     *
+     * @return {@code true} if there is another slot with which normal service is available.
+     *         {@code false} otherwise.
+     */
+    public boolean isThereOtherSlotInService() {
+        return isThereOtherSlot(true);
+    }
+
+    /**
      * Returns whether there is another slot emergency capable.
      *
      * @return {@code true} if there is another slot emergency capable,
      *         {@code false} otherwise.
      */
     public boolean isThereOtherSlot() {
+        return isThereOtherSlot(false);
+    }
+
+    private boolean isThereOtherSlot(boolean networkRegisteredOnly) {
         logi("isThereOtherSlot modemCount=" + mModemCount);
         if (mModemCount < 2) return false;
 
@@ -254,7 +271,13 @@
             int subId = SubscriptionManager.getSubscriptionId(i);
             if (mEmergencyNumberHelper.isEmergencyNumber(subId, mNumber)) {
                 logi("isThereOtherSlot index=" + i + "(" + subId + "), found");
-                return true;
+                if (networkRegisteredOnly) {
+                    if (isNetworkRegistered(subId)) {
+                        return true;
+                    }
+                } else {
+                    return true;
+                }
             } else {
                 logi("isThereOtherSlot index=" + i + "(" + subId + "), not emergency number");
             }
@@ -263,6 +286,31 @@
         return false;
     }
 
+    private boolean isNetworkRegistered(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
+
+        TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
+        ServiceState ss = tm.getServiceState();
+        if (ss != null) {
+            NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo(
+                    NetworkRegistrationInfo.DOMAIN_PS,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            if (nri != null && nri.isNetworkRegistered()) {
+                // PS is IN_SERVICE state.
+                return true;
+            }
+            nri = ss.getNetworkRegistrationInfo(
+                    NetworkRegistrationInfo.DOMAIN_CS,
+                    AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+            if (nri != null && nri.isNetworkRegistered()) {
+                // CS is IN_SERVICE state.
+                return true;
+            }
+        }
+        logi("isNetworkRegistered subId=" + subId + " not network registered");
+        return false;
+    }
+
     /**
      * Caches the configuration.
      */
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index 3d6a4d1..962cbf1 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -45,6 +45,7 @@
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL;
 import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE;
 import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_SETTING_ENABLED;
 import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_VALID_EID;
@@ -54,6 +55,7 @@
 import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
 import static android.telephony.PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
 import static android.telephony.PreciseDisconnectCause.SERVICE_OPTION_NOT_AVAILABLE;
+import static android.telephony.TelephonyManager.DATA_CONNECTED;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -76,15 +78,14 @@
 import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.SelectionAttributes;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TransportSelectorCallback;
-import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ProvisioningManager;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -94,9 +95,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.function.IntFunction;
 
 /**
@@ -120,6 +119,7 @@
     private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
 
     private static List<String> sSimReadyAllowList;
+    private static List<String> sPreferSlotWithNormalServiceList;
 
     /**
      * Network callback used to determine whether Wi-Fi is connected or not.
@@ -159,9 +159,9 @@
     private @TransportType int mLastTransportType = TRANSPORT_TYPE_INVALID;
     private @DomainSelectionService.EmergencyScanType int mScanType;
     private @RadioAccessNetworkType List<Integer> mLastPreferredNetworks;
-    private boolean mIsTestEmergencyNumber;
 
     private CancellationSignal mCancelSignal;
+    private EmergencyRegistrationResult mLastRegResult;
 
     // Members for carrier configuration
     private @RadioAccessNetworkType int[] mImsRatsConfig;
@@ -182,6 +182,7 @@
     private boolean mRequiresImsRegistration;
     private boolean mRequiresVoLteEnabled;
     private boolean mLtePreferredAfterNrFailure;
+    private boolean mScanLimitedOnlyAfterVolteFailure;
 
     // Members for states
     private boolean mIsMonitoringConnectivity;
@@ -211,13 +212,15 @@
     private final PowerManager.WakeLock mPartialWakeLock;
     private final CrossSimRedialingController mCrossSimRedialingController;
     private final CarrierConfigHelper mCarrierConfigHelper;
+    private final EmergencyCallbackModeHelper mEcbmHelper;
 
     /** Constructor. */
     public EmergencyCallDomainSelector(Context context, int slotId, int subId,
             @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
             @NonNull DestroyListener destroyListener,
             @NonNull CrossSimRedialingController csrController,
-            @NonNull CarrierConfigHelper carrierConfigHelper) {
+            @NonNull CarrierConfigHelper carrierConfigHelper,
+            @NonNull EmergencyCallbackModeHelper ecbmHelper) {
         super(context, slotId, subId, looper, imsStateTracker, destroyListener, TAG);
 
         mImsStateTracker.addBarringInfoListener(this);
@@ -228,6 +231,7 @@
 
         mCrossSimRedialingController = csrController;
         mCarrierConfigHelper = carrierConfigHelper;
+        mEcbmHelper = ecbmHelper;
         acquireWakeLock();
     }
 
@@ -245,7 +249,7 @@
                 break;
 
             case MSG_NETWORK_SCAN_RESULT:
-                handleScanResult((EmergencyRegResult) msg.obj);
+                handleScanResult((EmergencyRegistrationResult) msg.obj);
                 break;
 
             case MSG_MAX_CELLULAR_TIMEOUT:
@@ -263,7 +267,7 @@
      *
      * @param result The scan result.
      */
-    private void handleScanResult(EmergencyRegResult result) {
+    private void handleScanResult(EmergencyRegistrationResult result) {
         logi("handleScanResult result=" + result);
 
         if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
@@ -282,7 +286,7 @@
                       && (mScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
                 mScanType = DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE;
                 mWwanSelectorCallback.onRequestEmergencyNetworkScan(
-                        mLastPreferredNetworks, mScanType, mCancelSignal,
+                        mLastPreferredNetworks, mScanType, false, mCancelSignal,
                         (regResult) -> {
                             logi("requestScan-onComplete");
                             sendMessage(obtainMessage(MSG_NETWORK_SCAN_RESULT, regResult));
@@ -294,9 +298,11 @@
             return;
         }
 
+        mLastRegResult = result;
         removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
         onWwanNetworkTypeSelected(getAccessNetworkType(result));
         mCancelSignal = null;
+        maybeModifyScanType(mLastNetworkType);
     }
 
     /**
@@ -305,7 +311,7 @@
      * @param result The result of network scan.
      * @return The selected network type.
      */
-    private @RadioAccessNetworkType int getAccessNetworkType(EmergencyRegResult result) {
+    private @RadioAccessNetworkType int getAccessNetworkType(EmergencyRegistrationResult result) {
         int accessNetworkType = result.getAccessNetwork();
         if (accessNetworkType != EUTRAN) return accessNetworkType;
 
@@ -323,12 +329,6 @@
     }
 
     @Override
-    public void cancelSelection() {
-        logi("cancelSelection");
-        finishSelection();
-    }
-
-    @Override
     public void reselectDomain(SelectionAttributes attr) {
         logi("reselectDomain attr=" + attr);
         mSelectionAttributes = attr;
@@ -338,12 +338,11 @@
     private void reselectDomain() {
         logi("reselectDomain tryCsWhenPsFails=" + mTryCsWhenPsFails);
 
-        int cause = mSelectionAttributes.getCsDisconnectCause();
+        int cause = getDisconnectCause();
         mCrossSimRedialingController.notifyCallFailure(cause);
 
-        // TODO(b/258112541) make EMERGENCY_PERM_FAILURE and EMERGENCY_TEMP_FAILURE public api
-        if (cause == EMERGENCY_PERM_FAILURE
-                || cause == EMERGENCY_TEMP_FAILURE) {
+        if ((cause == EMERGENCY_PERM_FAILURE || cause == EMERGENCY_TEMP_FAILURE)
+                && mCrossSimRedialingController.isThereOtherSlot()) {
             logi("reselectDomain should redial on the other subscription");
             terminateSelectionForCrossSimRedialing(cause == EMERGENCY_PERM_FAILURE);
             return;
@@ -355,11 +354,6 @@
             return;
         }
 
-        if (mIsTestEmergencyNumber) {
-            selectDomainForTestEmergencyNumber();
-            return;
-        }
-
         if (mTryCsWhenPsFails) {
             mTryCsWhenPsFails = false;
             // Initial state was CSFB available and dial PS failed.
@@ -403,8 +397,28 @@
         mDomainSelected = false;
     }
 
+    private int getDisconnectCause() {
+        int cause = mSelectionAttributes.getCsDisconnectCause();
+
+        ImsReasonInfo reasonInfo = mSelectionAttributes.getPsDisconnectCause();
+        if (reasonInfo != null) {
+            switch (reasonInfo.getCode()) {
+                case ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE:
+                    cause = EMERGENCY_TEMP_FAILURE;
+                    break;
+                case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE:
+                    cause = EMERGENCY_PERM_FAILURE;
+                    break;
+                default:
+                    break;
+            }
+        }
+        return cause;
+    }
+
     private void reselectDomainInternal() {
         post(() -> {
+            if (mDestroyed) return;
             requestScan(true, false, true);
             mDomainSelected = false;
         });
@@ -434,7 +448,7 @@
         logi("selectDomain attr=" + attr);
         mTransportSelectorCallback = cb;
         mSelectionAttributes = attr;
-        mIsTestEmergencyNumber = isTestEmergencyNumber(attr.getNumber());
+        mLastRegResult = mSelectionAttributes.getEmergencyRegistrationResult();
 
         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
         mModemCount = tm.getActiveModemCount();
@@ -490,7 +504,26 @@
      */
     private void updateCarrierConfiguration() {
         CarrierConfigManager configMgr = mContext.getSystemService(CarrierConfigManager.class);
-        PersistableBundle b = configMgr.getConfigForSubId(getSubId());
+        PersistableBundle b = configMgr.getConfigForSubId(getSubId(),
+                KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+                KEY_EMERGENCY_OVER_CS_ROAMING_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+                KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY,
+                KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY,
+                KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL,
+                KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT,
+                KEY_EMERGENCY_SCAN_TIMER_SEC_INT,
+                KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT,
+                KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT,
+                KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL,
+                KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT,
+                KEY_EMERGENCY_CALL_SETUP_TIMER_ON_CURRENT_NETWORK_SEC_INT,
+                KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL,
+                KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL,
+                KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL,
+                KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL,
+                KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY);
         if (b == null) {
             b = CarrierConfigManager.getDefaultConfig();
         }
@@ -521,6 +554,8 @@
         mRequiresVoLteEnabled = b.getBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL);
         mLtePreferredAfterNrFailure = b.getBoolean(
                 KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL);
+        mScanLimitedOnlyAfterVolteFailure = b.getBoolean(
+                KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL);
         String[] numbers = b.getStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY);
 
         if (mImsRatsConfig == null) mImsRatsConfig = new int[0];
@@ -556,6 +591,7 @@
                 + ", requiresImsReg=" + mRequiresImsRegistration
                 + ", requiresVoLteEnabled=" + mRequiresVoLteEnabled
                 + ", ltePreferredAfterNr=" + mLtePreferredAfterNrFailure
+                + ", scanLimitedOnly=" + mScanLimitedOnlyAfterVolteFailure
                 + ", cdmaPreferredNumbers=" + arrayToString(numbers));
 
         mCdmaPreferredNumbers = Arrays.asList(numbers);
@@ -583,26 +619,43 @@
      * Caches the resource configuration.
      */
     private void readResourceConfiguration() {
-        if (sSimReadyAllowList != null) return;
+        if (sSimReadyAllowList == null) {
+            sSimReadyAllowList = readResourceConfiguration(
+                    R.array.config_countries_require_sim_for_emergency);
+        }
+        logi("readResourceConfiguration simReadyCountries=" + sSimReadyAllowList);
+
+        if (sPreferSlotWithNormalServiceList == null) {
+            sPreferSlotWithNormalServiceList = readResourceConfiguration(
+                    R.array.config_countries_prefer_normal_service_capable_subscription);
+        }
+        logi("readResourceConfiguration preferNormalServiceCountries="
+                + sPreferSlotWithNormalServiceList);
+    }
+
+    private List<String> readResourceConfiguration(int id) {
+        logi("readResourceConfiguration id=" + id);
+
+        List<String> resource = null;
         try {
-            sSimReadyAllowList = Arrays.asList(mContext.getResources().getStringArray(
-                    R.array.config_countries_require_sim_for_emergency));
+            resource = Arrays.asList(mContext.getResources().getStringArray(id));
         } catch (Resources.NotFoundException nfe) {
             loge("readResourceConfiguration exception=" + nfe);
         } catch (NullPointerException npe) {
             loge("readResourceConfiguration exception=" + npe);
         } finally {
-            if (sSimReadyAllowList == null) {
-                sSimReadyAllowList = new ArrayList<String>();
+            if (resource == null) {
+                resource = new ArrayList<String>();
             }
         }
-        logi("readResourceConfiguration simReadyCountries=" + sSimReadyAllowList);
+        return resource;
     }
 
     /** For test purpose only */
     @VisibleForTesting
     public void clearResourceConfiguration() {
         sSimReadyAllowList = null;
+        sPreferSlotWithNormalServiceList = null;
     }
 
     private void selectDomain() {
@@ -624,13 +677,14 @@
         // Reset mDomainSelectionRequested to avoid redundant execution of selectDomain().
         mDomainSelectionRequested = false;
 
-        if (!allowEmergencyCalls(mSelectionAttributes.getEmergencyRegResult())) {
+        if (!allowEmergencyCalls(mSelectionAttributes.getEmergencyRegistrationResult())) {
             // Detected the country and found that emergency calls are not allowed with this slot.
             terminateSelectionPermanentlyForSlot();
             return;
         }
 
-        if (isWifiPreferred()) {
+        if (isWifiPreferred()
+                || isInEmergencyCallbackModeOnWlan()) {
             onWlanSelected();
             return;
         }
@@ -643,8 +697,12 @@
     }
 
     private void selectDomainFromInitialState() {
-        if (mIsTestEmergencyNumber) {
-            selectDomainForTestEmergencyNumber();
+        if (mDestroyed) return;
+
+        if (isInEmergencyCallbackModeOnPsWwan()) {
+            logi("selectDomain PS cellular connected in ECBM");
+            mPsNetworkType = EUTRAN;
+            onWwanNetworkTypeSelected(mPsNetworkType);
             return;
         }
 
@@ -652,13 +710,28 @@
         boolean psInService = isPsInService();
 
         if (!csInService && !psInService) {
-            mPsNetworkType = getSelectablePsNetworkType(false);
-            logi("selectDomain limited service ps=" + accessNetworkTypeToString(mPsNetworkType));
-            if (mPsNetworkType == UNKNOWN) {
-                requestScan(true);
-            } else {
-                onWwanNetworkTypeSelected(mPsNetworkType);
+            if (maybeRedialOnTheOtherSlotInNormalService()) {
+                return;
             }
+            mCsNetworkType = getSelectableCsNetworkType();
+            mPsNetworkType = getSelectablePsNetworkType(false);
+            logi("selectDomain limited service ps=" + accessNetworkTypeToString(mPsNetworkType)
+                    + ", cs=" + accessNetworkTypeToString(mCsNetworkType));
+            if (!isInRoaming()
+                    && (mPreferredNetworkScanType
+                            == CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE)) {
+                requestScan(true);
+                return;
+            }
+            // If NGRAN, request scan to trigger emergency registration.
+            if (mPsNetworkType == EUTRAN) {
+                onWwanNetworkTypeSelected(mPsNetworkType);
+            } else if (mCsNetworkType != UNKNOWN) {
+                onWwanNetworkTypeSelected(mCsNetworkType);
+            } else {
+                requestScan(true);
+            }
+            maybeModifyScanType(mLastNetworkType);
             return;
         }
 
@@ -675,7 +748,8 @@
         logi("selectDomain CS={" + csInService + ", " + accessNetworkTypeToString(mCsNetworkType)
                 + "}, PS={" + psInService + ", " + accessNetworkTypeToString(mPsNetworkType) + "}");
         if (csAvailable && psAvailable) {
-            if (mPreferImsWhenCallsOnCs || isImsRegisteredWithVoiceCapability()) {
+            if (mSelectionAttributes.isExitedFromAirplaneMode()
+                    || mPreferImsWhenCallsOnCs || isImsRegisteredWithVoiceCapability()) {
                 mTryCsWhenPsFails = true;
                 onWwanNetworkTypeSelected(mPsNetworkType);
             } else if (isDeactivatedSim()) {
@@ -686,7 +760,8 @@
             }
         } else if (psAvailable) {
             mTryEpsFallback = (mPsNetworkType == NGRAN) && isEpsFallbackAvailable();
-            if (!mRequiresImsRegistration || isImsRegisteredWithVoiceCapability()) {
+            if (mSelectionAttributes.isExitedFromAirplaneMode()
+                    || !mRequiresImsRegistration || isImsRegisteredWithVoiceCapability()) {
                 onWwanNetworkTypeSelected(mPsNetworkType);
             } else if (isDeactivatedSim()) {
                 // Deactivated SIM but PS is in service and supports emergency calls.
@@ -701,7 +776,8 @@
             onWwanNetworkTypeSelected(mCsNetworkType);
         } else {
             // PS is in service but not supports emergency calls.
-            if (mRequiresImsRegistration && !isImsRegisteredWithVoiceCapability()) {
+            if (!mSelectionAttributes.isExitedFromAirplaneMode()
+                    && mRequiresImsRegistration && !isImsRegisteredWithVoiceCapability()) {
                 // Carrier configuration requires IMS registration for emergency services over PS,
                 // but not registered. Try CS emergency call.
                 requestScan(true, true);
@@ -710,6 +786,7 @@
                 requestScan(true);
             }
         }
+        maybeModifyScanType(mLastNetworkType);
     }
 
     /**
@@ -744,20 +821,21 @@
 
         mCancelSignal = new CancellationSignal();
         // In case dialing over Wi-Fi has failed, do not the change the domain preference.
-        if (!wifiFailed) {
+        if (!wifiFailed || mLastPreferredNetworks == null) {
             mLastPreferredNetworks = getNextPreferredNetworks(csPreferred, mTryEpsFallback);
         }
         mTryEpsFallback = false;
 
         if (isInRoaming()
-                && (mPreferredNetworkScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
+                && (mPreferredNetworkScanType
+                        == CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE)) {
             // FULL_SERVICE only preference is available only when not in roaming.
             mScanType = DomainSelectionService.SCAN_TYPE_NO_PREFERENCE;
         }
 
         mIsScanRequested = true;
         mWwanSelectorCallback.onRequestEmergencyNetworkScan(
-                mLastPreferredNetworks, mScanType, mCancelSignal,
+                mLastPreferredNetworks, mScanType, false, mCancelSignal,
                 (result) -> {
                     logi("requestScan-onComplete");
                     sendMessage(obtainMessage(MSG_NETWORK_SCAN_RESULT, result));
@@ -834,10 +912,13 @@
         } else if (csPreferred || mLastNetworkType == EUTRAN || mLastNetworkType == NGRAN) {
             if (!csPreferred && mLastNetworkType == NGRAN && mLtePreferredAfterNrFailure) {
                 // LTE is preferred after dialing over NR failed.
-                List<Integer> imsRats = getImsNetworkTypeConfiguration();
-                imsRats.remove(Integer.valueOf(NGRAN));
-                preferredNetworks = generatePreferredNetworks(imsRats,
+                preferredNetworks = generatePreferredNetworks(getImsNetworkTypeConfiguration(),
                         getCsNetworkTypeConfiguration());
+                // Make NGRAN have the lowest priority
+                if (preferredNetworks.contains(NGRAN)) {
+                    preferredNetworks.remove(Integer.valueOf(NGRAN));
+                    preferredNetworks.add(NGRAN);
+                }
             } else  if (csPriority > NOT_SUPPORTED) {
                 // PS tried, generate the list with CS preferred.
                 preferredNetworks = generatePreferredNetworks(getCsNetworkTypeConfiguration(),
@@ -943,7 +1024,8 @@
      * @return {@code true} if CS is in service.
      */
     private boolean isCsInService() {
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
         if (regResult == null) return false;
 
         int regState = regResult.getRegState();
@@ -963,7 +1045,12 @@
      * @return The network type of the CS network.
      */
     private @RadioAccessNetworkType int getSelectableCsNetworkType() {
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        List<Integer> domains = getDomainPreference();
+        if (domains.indexOf(DOMAIN_CS) == NOT_SUPPORTED) {
+            return UNKNOWN;
+        }
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
         logi("getSelectableCsNetworkType regResult=" + regResult);
         if (regResult == null) return UNKNOWN;
 
@@ -988,7 +1075,8 @@
      * @return {@code true} if PS is in service.
      */
     private boolean isPsInService() {
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
         if (regResult == null) return false;
 
         int regState = regResult.getRegState();
@@ -1009,7 +1097,12 @@
      * @return The network type if the network supports emergency services over PS network.
      */
     private @RadioAccessNetworkType int getSelectablePsNetworkType(boolean inService) {
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        List<Integer> domains = getDomainPreference();
+        if (domains.indexOf(DOMAIN_PS_3GPP) == NOT_SUPPORTED) {
+            return UNKNOWN;
+        }
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
         logi("getSelectablePsNetworkType regResult=" + regResult);
         if (regResult == null) return UNKNOWN;
         if (mRequiresVoLteEnabled && !isAdvancedCallingSettingEnabled()) {
@@ -1039,7 +1132,8 @@
     }
 
     private boolean isEpsFallbackAvailable() {
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
         if (regResult == null) return false;
 
         List<Integer> ratList = getImsNetworkTypeConfiguration();
@@ -1175,7 +1269,8 @@
         }
 
         if (!mCdmaPreferredNumbers.isEmpty()) {
-            if (mCdmaPreferredNumbers.contains(mSelectionAttributes.getNumber())) {
+            String number = mSelectionAttributes.getAddress().getSchemeSpecificPart();
+            if (mCdmaPreferredNumbers.contains(number)) {
                 // The number will be dialed over CDMA.
                 ratList.clear();
                 ratList.add(new Integer(CDMA2000));
@@ -1206,12 +1301,12 @@
         tm = tm.createForSubscriptionId(getSubId());
         String netIso = tm.getNetworkCountryIso();
 
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        EmergencyRegistrationResult regResult = mLastRegResult;
         if (regResult != null) {
             if (regResult.getRegState() == REGISTRATION_STATE_HOME) return false;
             if (regResult.getRegState() == REGISTRATION_STATE_ROAMING) return true;
 
-            String iso = regResult.getIso();
+            String iso = regResult.getCountryIso();
             if (!TextUtils.isEmpty(iso)) netIso = iso;
         }
 
@@ -1360,14 +1455,14 @@
         }
     }
 
-    private boolean allowEmergencyCalls(EmergencyRegResult regResult) {
+    private boolean allowEmergencyCalls(EmergencyRegistrationResult regResult) {
         if (mModemCount < 2) return true;
         if (regResult == null) {
             loge("allowEmergencyCalls null regResult");
             return true;
         }
 
-        String iso = regResult.getIso();
+        String iso = regResult.getCountryIso();
         if (sSimReadyAllowList.contains(iso)) {
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
             int simState = tm.getSimState(getSlotId());
@@ -1383,6 +1478,20 @@
         return true;
     }
 
+    private boolean maybeRedialOnTheOtherSlotInNormalService() {
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
+        if (regResult == null) return false;
+
+        String iso = regResult.getCountryIso();
+        if (sPreferSlotWithNormalServiceList.contains(iso)
+                && mCrossSimRedialingController.isThereOtherSlotInService()) {
+            terminateSelectionForCrossSimRedialing(false);
+            return true;
+        }
+        return false;
+    }
+
     private void terminateSelectionPermanentlyForSlot() {
         logi("terminateSelectionPermanentlyForSlot");
         terminateSelection(true);
@@ -1397,11 +1506,6 @@
         mTransportSelectorCallback.onSelectionTerminated(permanent
                 ? DisconnectCause.EMERGENCY_PERM_FAILURE
                 : DisconnectCause.EMERGENCY_TEMP_FAILURE);
-
-        if (mIsScanRequested && mCancelSignal != null) {
-            mCancelSignal.cancel();
-            mCancelSignal = null;
-        }
     }
 
     /** Starts the cross stack timer. */
@@ -1411,7 +1515,8 @@
 
         if (mModemCount == 1) return;
 
-        EmergencyRegResult regResult = mSelectionAttributes.getEmergencyRegResult();
+        EmergencyRegistrationResult regResult =
+                mSelectionAttributes.getEmergencyRegistrationResult();
         if (regResult != null) {
             int regState = regResult.getRegState();
 
@@ -1423,8 +1528,9 @@
             inRoaming = (regState == REGISTRATION_STATE_ROAMING) || isInRoaming();
         }
 
+        String number = mSelectionAttributes.getAddress().getSchemeSpecificPart();
         mCrossSimRedialingController.startTimer(mContext, this, mSelectionAttributes.getCallId(),
-                mSelectionAttributes.getNumber(), inService, inRoaming, mModemCount);
+                number, inService, inRoaming, mModemCount);
     }
 
     /** Notifies that the cross stack redilaing timer has been expired. */
@@ -1439,6 +1545,15 @@
         terminateSelectionForCrossSimRedialing(false);
     }
 
+    private void maybeModifyScanType(int selectedNetworkType) {
+        if ((mPreferredNetworkScanType
+                != CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE)
+                && mScanLimitedOnlyAfterVolteFailure
+                && (selectedNetworkType == EUTRAN)) {
+            mScanType = DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE;
+        }
+    }
+
     private static String arrayToString(int[] intArray, IntFunction<String> func) {
         int length = intArray.length;
         StringBuilder sb = new StringBuilder("{");
@@ -1540,35 +1655,16 @@
         }
     }
 
-    private void selectDomainForTestEmergencyNumber() {
-        logi("selectDomainForTestEmergencyNumber");
-        if (isImsRegisteredWithVoiceCapability()) {
-            onWwanNetworkTypeSelected(EUTRAN);
-        } else {
-            onWwanNetworkTypeSelected(UTRAN);
-        }
+    private boolean isInEmergencyCallbackModeOnWlan() {
+        return mEcbmHelper.isInEmergencyCallbackMode(getSlotId())
+                && mEcbmHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WLAN
+                && mEcbmHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
     }
 
-    private boolean isTestEmergencyNumber(String number) {
-        number = PhoneNumberUtils.stripSeparators(number);
-        Map<Integer, List<EmergencyNumber>> list = new HashMap<>();
-        try {
-            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            list = tm.getEmergencyNumberList();
-        } catch (IllegalStateException ise) {
-            loge("isTestEmergencyNumber ise=" + ise);
-        }
-
-        for (Integer sub : list.keySet()) {
-            for (EmergencyNumber eNumber : list.get(sub)) {
-                if (number.equals(eNumber.getNumber())
-                        && eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST)) {
-                    logd("isTestEmergencyNumber: " + number + " is a test emergency number.");
-                    return true;
-                }
-            }
-        }
-        return false;
+    private boolean isInEmergencyCallbackModeOnPsWwan() {
+        return mEcbmHelper.isInEmergencyCallbackMode(getSlotId())
+                && mEcbmHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WWAN
+                && mEcbmHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
     }
 
     @Override
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java b/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java
new file mode 100644
index 0000000..cdf2225
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java
@@ -0,0 +1,235 @@
+/*
+ * 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.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
+import static android.telephony.SubscriptionManager.EXTRA_SLOT_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+import static android.telephony.TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_PHONE_IN_ECM_STATE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.util.ArrayMap;
+import android.util.Log;
+
+/** Helper class to cache emergency data connection state. */
+public class EmergencyCallbackModeHelper extends Handler {
+    private static final String TAG = "EmergencyCallbackModeHelper";
+    private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+
+    /**
+     * TelephonyCallback used to monitor ePDN state.
+     */
+    private static final class DataConnectionStateListener extends TelephonyCallback
+            implements TelephonyCallback.PreciseDataConnectionStateListener {
+
+        private final Handler mHandler;
+        private final TelephonyManager mTelephonyManager;
+        private final int mSubId;
+        private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        private int mState = TelephonyManager.DATA_UNKNOWN;
+
+        DataConnectionStateListener(Handler handler, TelephonyManager tm, int subId) {
+            mHandler = handler;
+            mTelephonyManager = tm;
+            mSubId = subId;
+        }
+
+        @Override
+        public void onPreciseDataConnectionStateChanged(
+                @NonNull PreciseDataConnectionState dataConnectionState) {
+            ApnSetting apnSetting = dataConnectionState.getApnSetting();
+            if ((apnSetting == null)
+                    || ((apnSetting.getApnTypeBitmask() & ApnSetting.TYPE_EMERGENCY) == 0)) {
+                return;
+            }
+            mTransportType = dataConnectionState.getTransportType();
+            mState = dataConnectionState.getState();
+            Log.i(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + mState
+                    + ", transport=" + mTransportType);
+        }
+
+        public void registerTelephonyCallback() {
+            TelephonyManager tm = mTelephonyManager.createForSubscriptionId(mSubId);
+            tm.registerTelephonyCallback(mHandler::post, this);
+        }
+
+        public void unregisterTelephonyCallback() {
+            mTelephonyManager.unregisterTelephonyCallback(this);
+        }
+
+        public int getSubId() {
+            return mSubId;
+        }
+
+        public int getTransportType() {
+            return mTransportType;
+        }
+
+        public int getState() {
+            return mState;
+        }
+    }
+
+    private final Context mContext;
+    private final TelephonyManager mTelephonyManager;
+    private final CarrierConfigManager mConfigManager;
+
+    private final ArrayMap<Integer, DataConnectionStateListener>
+            mDataConnectionStateListeners = new ArrayMap<>();
+
+    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+            (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigChanged(
+                    slotIndex, subId, carrierId);
+
+    /**
+     * Creates an instance.
+     *
+     * @param context The Context this is associated with.
+     * @param looper The Looper to run the EmergencyCallbackModeHelper.
+     */
+    public EmergencyCallbackModeHelper(@NonNull Context context, @NonNull Looper looper) {
+        super(looper);
+
+        mContext = context;
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mConfigManager.registerCarrierConfigChangeListener(this::post,
+                mCarrierConfigChangeListener);
+    }
+
+    /**
+     * Returns whether it is in emergency callback mode.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return true if it is in emergency callback mode.
+     */
+    public boolean isInEmergencyCallbackMode(int slotIndex) {
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+        if (listener == null) return false;
+
+        Intent intent = mContext.registerReceiver(null,
+                new IntentFilter(ACTION_EMERGENCY_CALLBACK_MODE_CHANGED));
+        if (intent != null
+                && ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(intent.getAction())) {
+            boolean inEcm = intent.getBooleanExtra(EXTRA_PHONE_IN_ECM_STATE, false);
+            int index = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
+            Log.i(TAG, "isInEmergencyCallbackMode inEcm=" + inEcm + ", slotIndex=" + index);
+            return inEcm && (slotIndex == index);
+        }
+        return false;
+    }
+
+    /**
+     * Returns the transport type of emergency data connection.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return the transport type of emergency data connection.
+     */
+    public int getTransportType(int slotIndex) {
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+        if (listener == null) return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        Log.i(TAG, "getTransportType " + listener.getTransportType());
+        return listener.getTransportType();
+    }
+
+    /**
+     * Returns the data connection state.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return the data connection state.
+     */
+    public int getDataConnectionState(int slotIndex) {
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+        if (listener == null) return TelephonyManager.DATA_UNKNOWN;
+        Log.i(TAG, "getDataConnectionState " + listener.getState());
+        return listener.getState();
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch(msg.what) {
+            default:
+                super.handleMessage(msg);
+                break;
+        }
+    }
+
+    private void onCarrierConfigChanged(int slotIndex, int subId, int carrierId) {
+        Log.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex
+                + ", subId=" + subId + ", carrierId=" + carrierId);
+
+        if (slotIndex < 0) {
+            return;
+        }
+
+        PersistableBundle b = mConfigManager.getConfigForSubId(subId,
+                KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
+
+        if (b.getBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL)) {
+            // ECBM supported
+            DataConnectionStateListener listener =
+                    mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+
+            // Remove stale listener.
+            if (listener != null && listener.getSubId() != subId) {
+                listener.unregisterTelephonyCallback();
+                listener = null;
+            }
+
+            if (listener == null) {
+                listener = new DataConnectionStateListener(this, mTelephonyManager, subId);
+                listener.registerTelephonyCallback();
+                mDataConnectionStateListeners.put(Integer.valueOf(slotIndex), listener);
+                Log.i(TAG, "onCarrierConfigChanged register callback");
+            }
+        } else {
+            // ECBM not supported
+            DataConnectionStateListener listener =
+                    mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+            if (listener != null) {
+                listener.unregisterTelephonyCallback();
+                mDataConnectionStateListeners.remove(Integer.valueOf(slotIndex));
+                Log.i(TAG, "onCarrierConfigChanged unregister callback");
+            }
+        }
+    }
+
+    /** Destroys the instance. */
+    public void destroy() {
+        if (DBG) Log.d(TAG, "destroy");
+        mConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+        mDataConnectionStateListeners.forEach((k, v) -> v.unregisterTelephonyCallback());
+    }
+}
diff --git a/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
index aef193b..7f28b04 100644
--- a/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelector.java
@@ -18,12 +18,17 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.CancellationSignal;
 import android.os.Looper;
+import android.os.Message;
 import android.os.PersistableBundle;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.BarringInfo;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.DomainSelectionService;
+import android.telephony.EmergencyRegistrationResult;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
@@ -31,11 +36,14 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.List;
+
 /**
  * Implements an emergency SMS domain selector for sending an emergency SMS.
  */
 public class EmergencySmsDomainSelector extends SmsDomainSelector implements
         ImsStateTracker.BarringInfoListener, ImsStateTracker.ServiceStateListener {
+    protected static final int EVENT_EMERGENCY_NETWORK_SCAN_RESULT = 201;
     /**
      * Stores the configuration value of
      * {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL}.
@@ -46,6 +54,8 @@
     private boolean mServiceStateReceived;
     private BarringInfo mBarringInfo;
     private boolean mBarringInfoReceived;
+    private boolean mEmergencyNetworkScanInProgress;
+    private CancellationSignal mEmergencyNetworkScanSignal;
 
     public EmergencySmsDomainSelector(Context context, int slotId, int subId,
             @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
@@ -68,6 +78,18 @@
     }
 
     @Override
+    public void handleMessage(@NonNull Message msg) {
+        switch (msg.what) {
+            case EVENT_EMERGENCY_NETWORK_SCAN_RESULT:
+                handleEmergencyNetworkScanResult((EmergencyRegistrationResult) msg.obj);
+                break;
+            default:
+                super.handleMessage(msg);
+                break;
+        }
+    }
+
+    @Override
     public void finishSelection() {
         super.finishSelection();
         mServiceStateReceived = false;
@@ -75,6 +97,12 @@
         mBarringInfoReceived = false;
         mBarringInfo = null;
         mEmergencySmsOverImsSupportedByConfig = null;
+
+        mEmergencyNetworkScanInProgress = false;
+        if (mEmergencyNetworkScanSignal != null) {
+            mEmergencyNetworkScanSignal.cancel();
+            mEmergencyNetworkScanSignal = null;
+        }
     }
 
     @Override
@@ -111,7 +139,7 @@
              * when {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL} is set
              * to true.
              */
-            if (isEmergencySmsOverImsSupportedIfLteLimitedOrInService()) {
+            if (isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService()) {
                 /**
                  * Emergency SMS should be supported via emergency PDN.
                  * If this condition is false, then need to fallback to CS network
@@ -139,60 +167,132 @@
             return;
         }
 
+        if (mEmergencyNetworkScanInProgress) {
+            logi("Emergency network scan is in progress.");
+            return;
+        }
+
         logi("selectDomain: " + mImsStateTracker.imsStateToString());
 
         if (isSmsOverImsAvailable()) {
-            boolean isEmergencySmsOverImsSupportedIfLteLimitedOrInService =
-                    isEmergencySmsOverImsSupportedIfLteLimitedOrInService();
+            boolean isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService =
+                    isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService();
 
             if (mImsStateTracker.isImsRegisteredOverWlan()) {
                 /**
                  * When {@link CarrierConfigManager#KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL}
-                 * is set to true, the emergency SMS supports on the LTE network using the
+                 * is set to true, the emergency SMS supports on the LTE/NR network using the
                  * emergency PDN. As of now, since the emergency SMS doesn't use the emergency PDN
                  * over WLAN, the domain selector reports the domain as WLAN only if
-                 * {@code isEmergencySmsOverImsSupportedIfLteLimitedOrInService} is set to false
+                 * {@code isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService} is set to false
                  * and IMS is registered over WLAN.
                  * Otherwise, the domain selector reports the domain as WWAN.
                  */
-                if (!isEmergencySmsOverImsSupportedIfLteLimitedOrInService) {
+                if (!isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService) {
                     notifyWlanSelected(false);
                     return;
                 }
 
                 logi("DomainSelected: WLAN >> WWAN");
             }
-            notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS,
-                    isEmergencySmsOverImsSupportedIfLteLimitedOrInService);
+
+            /**
+             * The request of emergency network scan triggers the modem to request the emergency
+             * service fallback because NR network doesn't support the emergency service.
+             */
+            if (isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService
+                    && isNrEmergencyServiceFallbackRequired()) {
+                requestEmergencyNetworkScan(List.of(AccessNetworkType.EUTRAN));
+            } else {
+                notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_PS,
+                        isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService);
+            }
         } else {
             notifyWwanSelected(NetworkRegistrationInfo.DOMAIN_CS, false);
         }
     }
 
+    private void requestEmergencyNetworkScan(List<Integer> preferredNetworks) {
+        mEmergencyNetworkScanInProgress = true;
+
+        if (mWwanSelectorCallback == null) {
+            mTransportSelectorCallback.onWwanSelected((callback) -> {
+                mWwanSelectorCallback = callback;
+                requestEmergencyNetworkScanInternal(preferredNetworks);
+            });
+        } else {
+            requestEmergencyNetworkScanInternal(preferredNetworks);
+        }
+    }
+
+    private void requestEmergencyNetworkScanInternal(List<Integer> preferredNetworks) {
+        logi("requestEmergencyNetworkScan: preferredNetworks=" + preferredNetworks);
+        mEmergencyNetworkScanSignal = new CancellationSignal();
+        mWwanSelectorCallback.onRequestEmergencyNetworkScan(
+                preferredNetworks,
+                DomainSelectionService.SCAN_TYPE_FULL_SERVICE, false,
+                mEmergencyNetworkScanSignal,
+                (regResult) -> {
+                    logi("requestEmergencyNetworkScan-onComplete");
+                    obtainMessage(EVENT_EMERGENCY_NETWORK_SCAN_RESULT, regResult).sendToTarget();
+                });
+    }
+
+    /**
+     * Handles the emergency network scan result.
+     *
+     * This triggers the emergency service fallback to modem when the emergency service is not
+     * supported but the emergency service fallback is supported in the current network.
+     *
+     * @param regResult The emergency registration result that is triggered
+     *                  by the emergency network scan.
+     */
+    private void handleEmergencyNetworkScanResult(EmergencyRegistrationResult regResult) {
+        logi("handleEmergencyNetworkScanResult: " + regResult);
+
+        mEmergencyNetworkScanInProgress = false;
+        mEmergencyNetworkScanSignal = null;
+
+        int accessNetworkType = regResult.getAccessNetwork();
+        int domain = NetworkRegistrationInfo.DOMAIN_CS;
+
+        if (accessNetworkType == AccessNetworkType.NGRAN) {
+            domain = NetworkRegistrationInfo.DOMAIN_PS;
+        } else if (accessNetworkType == AccessNetworkType.EUTRAN) {
+            if (regResult.getDomain() == NetworkRegistrationInfo.DOMAIN_CS) {
+                logi("PS emergency service is not supported in LTE network.");
+            } else {
+                domain = NetworkRegistrationInfo.DOMAIN_PS;
+            }
+        }
+
+        notifyWwanSelected(domain, (domain == NetworkRegistrationInfo.DOMAIN_PS));
+    }
+
     /**
      * Checks if the emergency SMS messages over IMS is available according to the carrier
      * configuration and the current network states.
      */
     private boolean isImsEmergencySmsAvailable() {
-        boolean isEmergencySmsOverImsSupportedIfLteLimitedOrInService =
-                isEmergencySmsOverImsSupportedIfLteLimitedOrInService();
+        boolean isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService =
+                isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService();
         boolean networkAvailable = isNetworkAvailableForImsEmergencySms();
 
         logi("isImsEmergencySmsAvailable: "
-                + "emergencySmsOverIms=" + isEmergencySmsOverImsSupportedIfLteLimitedOrInService
+                + "emergencySmsOverIms=" + isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService
                 + ", mmTelFeatureAvailable=" + mImsStateTracker.isMmTelFeatureAvailable()
                 + ", networkAvailable=" + networkAvailable);
 
-        return isEmergencySmsOverImsSupportedIfLteLimitedOrInService
+        return isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService
                 && mImsStateTracker.isMmTelFeatureAvailable()
                 && networkAvailable;
     }
 
     /**
-     * Checks if sending emergency SMS messages over IMS is supported when in LTE/limited LTE
-     * (Emergency only) service mode from the carrier configuration.
+     * Checks if sending emergency SMS messages over IMS is supported when in the network(LTE/NR)
+     * normal/limited(Emergency only) service mode from the carrier configuration.
      */
-    private boolean isEmergencySmsOverImsSupportedIfLteLimitedOrInService() {
+    private boolean isEmergencySmsOverImsSupportedIfNetworkLimitedOrInService() {
         if (mEmergencySmsOverImsSupportedByConfig == null) {
             CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class);
 
@@ -257,7 +357,8 @@
      */
     private boolean isNetworkAvailableForImsEmergencySms() {
         return isLteEmergencyAvailableInService()
-                || isLteEmergencyAvailableInLimitedService();
+                || isLteEmergencyAvailableInLimitedService()
+                || isNrEmergencyAvailable();
     }
 
     /**
@@ -280,6 +381,22 @@
     }
 
     /**
+     * Checks if the emergency service fallback is supported by the network.
+     *
+     * @return {@code true} if the emergency service fallback is supported by the network,
+     *         {@code false} otherwise.
+     */
+    private boolean isEmergencyServiceFallbackSupported(@NonNull NetworkRegistrationInfo regInfo) {
+        final DataSpecificRegistrationInfo dsRegInfo = regInfo.getDataSpecificInfo();
+        if (dsRegInfo != null) {
+            final VopsSupportInfo vopsSupportInfo = dsRegInfo.getVopsSupportInfo();
+            return vopsSupportInfo != null
+                    && vopsSupportInfo.isEmergencyServiceFallbackSupported();
+        }
+        return false;
+    }
+
+    /**
      * Checks if the emergency service is allowed (not barred) by the network.
      *
      * This checks if SystemInformationBlockType2 includes the ac-BarringInfo and
@@ -297,4 +414,45 @@
                 mBarringInfo.getBarringServiceInfo(BarringInfo.BARRING_SERVICE_TYPE_EMERGENCY);
         return !bsi.isBarred();
     }
+
+    /**
+     * Checks if the emergency service fallback is available in the NR network
+     * because the emergency service is not supported.
+     */
+    private boolean isNrEmergencyServiceFallbackRequired() {
+        if (mServiceState == null) {
+            return false;
+        }
+
+        final NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (regInfo != null
+                && regInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_NR
+                && regInfo.isRegistered()) {
+            return !isEmergencyServiceSupported(regInfo)
+                    && isEmergencyServiceFallbackSupported(regInfo);
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the emergency service is available in the NR network.
+     */
+    private boolean isNrEmergencyAvailable() {
+        if (mServiceState == null) {
+            return false;
+        }
+
+        final NetworkRegistrationInfo regInfo = mServiceState.getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        if (regInfo != null
+                && regInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_NR
+                && regInfo.isRegistered()) {
+            return isEmergencyServiceSupported(regInfo)
+                    || isEmergencyServiceFallbackSupported(regInfo);
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
index f85dabe..31a1cc2 100644
--- a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -34,6 +34,8 @@
 import android.telephony.TransportSelectorCallback;
 import android.telephony.ims.ImsReasonInfo;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Implements domain selector for outgoing non-emergency calls.
  */
@@ -42,8 +44,15 @@
 
     private static final String LOG_TAG = "NCDS";
 
-    private boolean mStopDomainSelection = true;
-    private ServiceState mServiceState;
+    @VisibleForTesting
+    protected enum SelectorState {
+        ACTIVE,
+        INACTIVE,
+        DESTROYED
+    };
+
+    protected SelectorState mSelectorState = SelectorState.INACTIVE;
+    protected ServiceState mServiceState;
     private boolean mImsRegStateReceived;
     private boolean mMmTelCapabilitiesReceived;
     private boolean mReselectDomain;
@@ -66,9 +75,10 @@
     public void selectDomain(SelectionAttributes attributes, TransportSelectorCallback callback) {
         mSelectionAttributes = attributes;
         mTransportSelectorCallback = callback;
-        mStopDomainSelection = false;
+        mSelectorState = SelectorState.ACTIVE;
 
         if (callback == null) {
+            mSelectorState = SelectorState.INACTIVE;
             loge("Invalid params: TransportSelectorCallback is null");
             return;
         }
@@ -79,7 +89,7 @@
             return;
         }
 
-        int subId = attributes.getSubId();
+        int subId = attributes.getSubscriptionId();
         boolean validSubscriptionId = SubscriptionManager.isValidSubscriptionId(subId);
         if (attributes.getSelectorType() != SELECTOR_TYPE_CALLING || attributes.isEmergency()
                 || !validSubscriptionId) {
@@ -95,6 +105,7 @@
             logd("NormalCallDomainSelection triggered. Sub-id:" + subId);
             post(() -> selectDomain());
         } else {
+            mSelectorState = SelectorState.INACTIVE;
             loge("Subscription-ids doesn't match. This instance is associated with sub-id:"
                     + getSubId() + ", requested sub-id:" + subId);
             // TODO: Throw anamoly here. This condition should never occur.
@@ -111,27 +122,44 @@
     @Override
     public synchronized void finishSelection() {
         logd("finishSelection");
-        mStopDomainSelection = true;
-        mImsStateTracker.removeServiceStateListener(this);
-        mImsStateTracker.removeImsStateListener(this);
-        mSelectionAttributes = null;
-        mTransportSelectorCallback = null;
+        if (mSelectorState == SelectorState.ACTIVE) {
+            // This is cancel selection case.
+            cancelSelection();
+            return;
+        }
+
+        if (mSelectorState != SelectorState.DESTROYED) {
+            mImsStateTracker.removeServiceStateListener(this);
+            mImsStateTracker.removeImsStateListener(this);
+            mSelectionAttributes = null;
+            mTransportSelectorCallback = null;
+            destroy();
+        }
     }
 
     @Override
     public void destroy() {
-        finishSelection();
-        super.destroy();
+        logd("destroy");
+        switch (mSelectorState) {
+            case INACTIVE:
+                mSelectorState = SelectorState.DESTROYED;
+                super.destroy();
+                break;
+
+            case ACTIVE:
+                loge("destroy is called when selector state is in ACTIVE state");
+                cancelSelection();
+                break;
+
+            case DESTROYED:
+                super.destroy();
+                break;
+        }
     }
 
-    /**
-     * Cancel an ongoing selection operation. It is up to the DomainSelectionService
-     * to clean up all ongoing operations with the framework.
-     */
-    @Override
     public void cancelSelection() {
         logd("cancelSelection");
-        mStopDomainSelection = true;
+        mSelectorState = SelectorState.INACTIVE;
         mReselectDomain = false;
         if (mTransportSelectorCallback != null) {
             mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.OUTGOING_CANCELED);
@@ -170,7 +198,7 @@
 
     private void notifyPsSelected() {
         logd("notifyPsSelected");
-        mStopDomainSelection = true;
+        mSelectorState = SelectorState.INACTIVE;
         if (mImsStateTracker.isImsRegisteredOverWlan()) {
             logd("WLAN selected");
             mTransportSelectorCallback.onWlanSelected(false);
@@ -198,7 +226,7 @@
 
     private void notifyCsSelected() {
         logd("notifyCsSelected");
-        mStopDomainSelection = true;
+        mSelectorState = SelectorState.INACTIVE;
         if (mWwanSelectorCallback == null) {
             mTransportSelectorCallback.onWwanSelected((callback) -> {
                 mWwanSelectorCallback = callback;
@@ -220,7 +248,7 @@
     }
 
     private void notifySelectionTerminated(@DisconnectCauses int cause) {
-        mStopDomainSelection = true;
+        mSelectorState = SelectorState.INACTIVE;
         if (mTransportSelectorCallback != null) {
             mTransportSelectorCallback.onSelectionTerminated(cause);
             finishSelection();
@@ -238,7 +266,7 @@
 
         PersistableBundle config = null;
         if (configManager != null) {
-            config = configManager.getConfigForSubId(mSelectionAttributes.getSubId(),
+            config = configManager.getConfigForSubId(mSelectionAttributes.getSubscriptionId(),
                     new String[] {CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL});
         }
 
@@ -266,7 +294,7 @@
 
         PersistableBundle config = null;
         if (configManager != null) {
-            config = configManager.getConfigForSubId(mSelectionAttributes.getSubId(),
+            config = configManager.getConfigForSubId(mSelectionAttributes.getSubscriptionId(),
                     new String[] {CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL});
         }
 
@@ -284,7 +312,7 @@
     }
 
     private synchronized void selectDomain() {
-        if (mStopDomainSelection || mSelectionAttributes == null
+        if (mSelectorState != SelectorState.ACTIVE || mSelectionAttributes == null
                 || mTransportSelectorCallback == null) {
             logd("Domain Selection is stopped.");
             return;
@@ -382,7 +410,8 @@
         // Handle voice call.
         if (mImsStateTracker.isImsVoiceCapable()) {
             logd("IMS is voice capable");
-            if (PhoneNumberUtils.isWpsCallNumber(mSelectionAttributes.getNumber())) {
+            String number = mSelectionAttributes.getAddress().getSchemeSpecificPart();
+            if (PhoneNumberUtils.isWpsCallNumber(number)) {
                 handleWpsCall();
             } else {
                 notifyPsSelected();
@@ -398,4 +427,9 @@
             }
         }
     }
+
+    @VisibleForTesting
+    public SelectorState getSelectorState() {
+        return mSelectorState;
+    }
 }
diff --git a/src/com/android/services/telephony/domainselection/SmsDomainSelector.java b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
index 95b04e2..4e41e43 100644
--- a/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/SmsDomainSelector.java
@@ -71,12 +71,6 @@
     }
 
     @Override
-    public void cancelSelection() {
-        logi("cancelSelection");
-        finishSelection();
-    }
-
-    @Override
     public void reselectDomain(@NonNull SelectionAttributes attr) {
         if (isDomainSelectionRequested()) {
             // The domain selection is already requested,
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
index 66894f7..d79a260 100644
--- a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -74,7 +73,8 @@
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
                 @NonNull CrossSimRedialingController crossSimRedialingController,
-                @NonNull CarrierConfigHelper carrierConfigHelper);
+                @NonNull CarrierConfigHelper carrierConfigHelper,
+                @NonNull EmergencyCallbackModeHelper emergencyCallbackModeHelper);
     }
 
     private static final class DefaultDomainSelectorFactory implements DomainSelectorFactory {
@@ -84,7 +84,8 @@
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
                 @NonNull CrossSimRedialingController crossSimRedialingController,
-                @NonNull CarrierConfigHelper carrierConfigHelper) {
+                @NonNull CarrierConfigHelper carrierConfigHelper,
+                @NonNull EmergencyCallbackModeHelper emergencyCallbackModeHelper) {
             DomainSelectorBase selector = null;
 
             logi("create-DomainSelector: slotId=" + slotId + ", subId=" + subId
@@ -96,7 +97,7 @@
                     if (isEmergency) {
                         selector = new EmergencyCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener, crossSimRedialingController,
-                                carrierConfigHelper);
+                                carrierConfigHelper, emergencyCallbackModeHelper);
                     } else {
                         selector = new NormalCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener);
@@ -192,7 +193,7 @@
 
     // Persistent Logging
     private static final LocalLog sEventLog = new LocalLog(20);
-    private final Context mContext;
+    private Context mContext;
     // Map of slotId -> ImsStateTracker
     private final SparseArray<ImsStateTracker> mImsStateTrackers = new SparseArray<>(2);
     private final List<DomainSelectorContainer> mDomainSelectorContainers = new ArrayList<>();
@@ -201,22 +202,31 @@
     private Handler mServiceHandler;
     private CrossSimRedialingController mCrossSimRedialingController;
     private CarrierConfigHelper mCarrierConfigHelper;
+    private EmergencyCallbackModeHelper mEmergencyCallbackModeHelper;
 
-    public TelephonyDomainSelectionService(Context context) {
-        this(context, ImsStateTracker::new, new DefaultDomainSelectorFactory(), null);
+    /** Default constructor. */
+    public TelephonyDomainSelectionService() {
+        this(ImsStateTracker::new, new DefaultDomainSelectorFactory(), null, null);
     }
 
     @VisibleForTesting
-    public TelephonyDomainSelectionService(Context context,
+    protected TelephonyDomainSelectionService(
             @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
             @NonNull DomainSelectorFactory domainSelectorFactory,
-            @Nullable CarrierConfigHelper carrierConfigHelper) {
-        mContext = context;
+            @Nullable CarrierConfigHelper carrierConfigHelper,
+            @Nullable EmergencyCallbackModeHelper ecbmHelper) {
         mImsStateTrackerFactory = imsStateTrackerFactory;
         mDomainSelectorFactory = domainSelectorFactory;
+        mCarrierConfigHelper = carrierConfigHelper;
+    }
+
+    @Override
+    public void onCreate() {
+        logd("onCreate");
+        mContext = getApplicationContext();
 
         // Create a worker thread for this domain selection service.
-        getExecutor();
+        getCreateExecutor();
 
         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
         int activeModemCount = (tm != null) ? tm.getActiveModemCount() : 1;
@@ -231,9 +241,13 @@
             loge("Adding OnSubscriptionChangedListener failed");
         }
 
-        mCrossSimRedialingController = new CrossSimRedialingController(context, getLooper());
-        mCarrierConfigHelper = (carrierConfigHelper != null)
-                ? carrierConfigHelper : new CarrierConfigHelper(context, getLooper());
+        mCrossSimRedialingController = new CrossSimRedialingController(mContext, getLooper());
+        if (mCarrierConfigHelper == null) {
+            mCarrierConfigHelper = new CarrierConfigHelper(mContext, getLooper());
+        }
+        if (mEmergencyCallbackModeHelper == null) {
+            mEmergencyCallbackModeHelper = new EmergencyCallbackModeHelper(mContext, getLooper());
+        }
 
         logi("TelephonyDomainSelectionService created");
     }
@@ -282,6 +296,11 @@
             mCarrierConfigHelper = null;
         }
 
+        if (mEmergencyCallbackModeHelper != null) {
+            mEmergencyCallbackModeHelper.destroy();
+            mEmergencyCallbackModeHelper = null;
+        }
+
         if (mServiceHandler != null) {
             mServiceHandler.getLooper().quit();
             mServiceHandler = null;
@@ -297,14 +316,14 @@
     @Override
     public void onDomainSelection(@NonNull SelectionAttributes attr,
             @NonNull TransportSelectorCallback callback) {
-        final int slotId = attr.getSlotId();
-        final int subId = attr.getSubId();
+        final int slotId = attr.getSlotIndex();
+        final int subId = attr.getSubscriptionId();
         final int selectorType = attr.getSelectorType();
         final boolean isEmergency = attr.isEmergency();
         ImsStateTracker ist = getImsStateTracker(slotId);
         DomainSelectorBase selector = mDomainSelectorFactory.create(mContext, slotId, subId,
                 selectorType, isEmergency, getLooper(), ist, mDestroyListener,
-                mCrossSimRedialingController, mCarrierConfigHelper);
+                mCrossSimRedialingController, mCarrierConfigHelper, mEmergencyCallbackModeHelper);
 
         if (selector != null) {
             // Ensures that ImsStateTracker is started before selecting the domain if not started
@@ -313,15 +332,21 @@
             addDomainSelector(slotId, selectorType, isEmergency, selector);
         } else {
             loge("No proper domain selector: " + selectorTypeToString(selectorType));
-            callback.onSelectionTerminated(DisconnectCause.ERROR_UNSPECIFIED);
+            // Executed through the service handler to ensure that the callbacks are not called
+            // directly in this execution flow.
+            mServiceHandler.post(() ->
+                    callback.onSelectionTerminated(DisconnectCause.ERROR_UNSPECIFIED));
             return;
         }
 
-        // Notify the caller that the domain selector is created.
-        callback.onCreated(selector);
-
-        // Performs the domain selection.
-        selector.selectDomain(attr, callback);
+        // Executed through the service handler to ensure that the callbacks are not called
+        // directly in this execution flow.
+        mServiceHandler.post(() ->  {
+            // Notify the caller that the domain selector is created.
+            callback.onCreated(selector);
+            // Performs the domain selection.
+            selector.selectDomain(attr, callback);
+        });
     }
 
     /**
@@ -359,8 +384,15 @@
     /**
      *  Returns an Executor used to execute methods called remotely by the framework.
      */
-    @SuppressLint("OnNameExpected")
     @Override
+    public @NonNull Executor getCreateExecutor() {
+        return getExecutor();
+    }
+
+    /**
+     *  Returns an Executor used to execute methods called remotely by the framework.
+     */
+    @VisibleForTesting
     public @NonNull Executor getExecutor() {
         if (mServiceHandler == null) {
             HandlerThread handlerThread = new HandlerThread(TAG);
@@ -480,7 +512,6 @@
         switch (selectorType) {
             case SELECTOR_TYPE_CALLING: return "CALLING";
             case SELECTOR_TYPE_SMS: return "SMS";
-            case SELECTOR_TYPE_UT: return "UT";
             default: return Integer.toString(selectorType);
         }
     }
diff --git a/testapps/GbaTestApp/res/values-eu/strings.xml b/testapps/GbaTestApp/res/values-eu/strings.xml
index 6774d99..c192a56 100644
--- a/testapps/GbaTestApp/res/values-eu/strings.xml
+++ b/testapps/GbaTestApp/res/values-eu/strings.xml
@@ -20,10 +20,10 @@
     <string name="request_naf_url" msgid="4487793541217737042">"Sareko aplikazioaren funtzioaren (NAF) URLa"</string>
     <string name="request_force_bootstrapping" msgid="206043602616214325">"Bootstrapping-a erabiltzera behartu nahi duzu?"</string>
     <string name="request_org" msgid="8416693445448308975">"Erakundearen kodea"</string>
-    <string name="request_security_protocol" msgid="1444164827561010482">"UA segurtasun-protokoloaren IDa"</string>
+    <string name="request_security_protocol" msgid="1444164827561010482">"UA segurtasun-protokoloaren identifikatzailea"</string>
     <string name="request_tls_cipher_suite" msgid="6659854717595308404">"TLS Cipher Suite ID"</string>
     <string name="response_success" msgid="2469204471244527663">"Burutu da GBA autentifikazioa?"</string>
-    <string name="response_fail_reason" msgid="3401426967253202496">"Hutsegitearen arrazoiaren IDa"</string>
+    <string name="response_fail_reason" msgid="3401426967253202496">"Hutsegitearen arrazoiaren identifikatzailea"</string>
     <string name="response_key" msgid="8839847772051686309">"GBA-ko gakoa (CK + IK)"</string>
     <string name="response_btid" msgid="2550216722679350756">"Bootstrapping Transaction Identifier (B-TID)"</string>
     <string name="sample_naf" msgid="255371174145881001">"3GPP-bootstrapping@naf1.operator.com"</string>
diff --git a/testapps/GbaTestApp/res/values-mr/strings.xml b/testapps/GbaTestApp/res/values-mr/strings.xml
index 75aaf5f..f8e8992 100644
--- a/testapps/GbaTestApp/res/values-mr/strings.xml
+++ b/testapps/GbaTestApp/res/values-mr/strings.xml
@@ -3,9 +3,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="2922839697457005451">"GbaTestApp"</string>
     <string name="label_settings" msgid="8030871890526865502">"सेटिंग्ज"</string>
-    <string name="label_carrier" msgid="1470455313066422804">"वाहक कॉंफिगरेशन"</string>
-    <string name="label_service" msgid="2668963955237345578">"सेवा कॉंफिगरेशन"</string>
-    <string name="label_test" msgid="8425079572898571918">"चाचणी कॉंफिगरेशन"</string>
+    <string name="label_carrier" msgid="1470455313066422804">"वाहकाचे कॉन्फिगरेशन"</string>
+    <string name="label_service" msgid="2668963955237345578">"सेवेचे कॉन्फिगरेशन"</string>
+    <string name="label_test" msgid="8425079572898571918">"चाचणीचे कॉन्फिगरेशन"</string>
     <string name="button_name_running" msgid="4557363091224858010">"रन करत आहे"</string>
     <string name="button_name_exit" msgid="8025683733431538975">"बाहेर पडा"</string>
     <string name="label_test_result" msgid="892984695972956196">"चाचणी परिणाम"</string>
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 35a0822..3ec9b69 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -56,6 +56,7 @@
         <activity android:name=".ContactListActivity" />
         <activity android:name=".ProvisioningActivity" />
         <activity android:name=".FileUploadActivity" />
+        <activity android:name=".carrierLock.CarrieLockModeListActivity" />
 
         <provider
             android:name=".util.ChatProvider"
@@ -117,6 +118,10 @@
             </intent-filter>
         </service>
 
+        <provider
+            android:name=".carrierLock.CarrierLockProvider"
+            android:authorities="com.sample.lockProvider"
+            android:exported="true" />
     </application>
 
 </manifest>
diff --git a/testapps/TestRcsApp/TestApp/lint-baseline.xml b/testapps/TestRcsApp/TestApp/lint-baseline.xml
index 8971388..872a626 100644
--- a/testapps/TestRcsApp/TestApp/lint-baseline.xml
+++ b/testapps/TestRcsApp/TestApp/lint-baseline.xml
@@ -1,26 +1,444 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
-        errorLine1="            telephonyManager.bootstrapAuthenticationRequest(mUiccType,"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionMessageCallback`"
+        errorLine1="            new DelegateConnectionMessageCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
-            line="130"
-            column="30"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="87"
+            column="17"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
-        errorLine1="            UaSecurityProtocolIdentifier spId = builder.build();"
-        errorLine2="                                                        ~~~~~">
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
+        errorLine1="            new DelegateConnectionStateCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="117"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
+        errorLine1='                        stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
+        errorLine2="                                                             ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="148"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
+        errorLine1="                                featureTagState.getState());"
+        errorLine2="                                                ~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="149"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+        errorLine1="                    Set&lt;String&gt; registeredFt = registrationState.getRegisteredFeatureTags();"
+        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="151"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
+        errorLine1="            mSipDelegateManager = imsManager.getSipDelegateManager(mDefaultSmsSubId);"
+        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="220"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
+        errorLine1="                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
+        errorLine2="                                        ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="231"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
+        errorLine1="                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="231"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+        errorLine1="                mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
+        errorLine2="                                    ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="247"
+            column="37"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+        errorLine1="            mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="322"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1='                + "mVersion=" + config.getVersion()'
+        errorLine2="                                       ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="332"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+        errorLine1='                + ", \n\tmTransportType=" + config.getTransportType()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="333"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1='                + ", \n\tmLocalIpAddr=" + config.getLocalAddress()'
+        errorLine2="                                                 ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="334"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1='                + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="335"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
+        errorLine1='                + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
+        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="336"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
+        errorLine1='                + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="337"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1='                + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="338"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+        errorLine1='                + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="339"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
+        errorLine1='                + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="340"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+        errorLine1='                + ", \n\tmHomeDomain=" + config.getHomeDomain()'
+        errorLine2="                                                ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="341"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+        errorLine1='                + ", \n\tmImei=" + config.getImei()'
+        errorLine2="                                          ~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="342"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
+        errorLine1='                + ", \n\tmGruu=" + config.getPublicGruuUri()'
+        errorLine2="                                          ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="343"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
+        errorLine1='                + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="344"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
+        errorLine1='                + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="345"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+        errorLine1='                + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
+        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="346"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
+        errorLine1='                + ", \n\tmPathHeader=" + config.getSipPathHeader()'
+        errorLine2="                                                ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="347"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+        errorLine1='                + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="348"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+        errorLine1='                + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="349"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+        errorLine1='                + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
+        errorLine2="                                                ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="350"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+        errorLine1='                + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
+        errorLine2="                                                 ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="351"
+            column="50"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
+        errorLine1='                + ", \n\tmCniHeader=" + config.getSipCniHeader()'
+        errorLine2="                                               ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="352"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+        errorLine1='                + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
+        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="353"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+        errorLine1='                + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
+        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="354"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
+        errorLine1="                + &quot;, \n\tmNatConfiguration=&quot; + config.getNatSocketAddress() + '}';"
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            line="355"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+        errorLine1="            new RcsProvisioningCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="89"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+        errorLine1="                mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="220"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+        errorLine1="                mProvisioningManager.registerRcsProvisioningCallback(getMainExecutor(), mCallback);"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="221"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+        errorLine1="        return new RcsClientConfiguration("
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="231"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
+            line="348"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
+        errorLine1="                    new UaSecurityProtocolIdentifier.Builder();"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
-            line="129"
-            column="57"/>
+            line="120"
+            column="21"/>
     </issue>
 
     <issue
@@ -58,134 +476,68 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
-        errorLine1="                    Set&lt;String&gt; registeredFt = registrationState.getRegisteredFeatureTags();"
-        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
+        errorLine1="            UaSecurityProtocolIdentifier spId = builder.build();"
+        errorLine2="                                                        ~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="151"
-            column="66"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="129"
+            column="57"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
-        errorLine1='                        stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
-        errorLine2="                                                             ~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
+        errorLine1="            telephonyManager.bootstrapAuthenticationRequest(mUiccType,"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="148"
-            column="62"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="130"
+            column="30"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
-        errorLine1="                                featureTagState.getState());"
-        errorLine2="                                                ~~~~~~~~">
+        message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
+        errorLine1="                    new BootstrapAuthenticationCallback() {"
+        errorLine2="                    ^">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="149"
-            column="49"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="135"
+            column="21"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
-        errorLine1="            mSipDelegateManager = imsManager.getSipDelegateManager(mDefaultSmsSubId);"
-        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~">
+        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
+        errorLine1="                    new BootstrapAuthenticationCallback() {"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="220"
-            column="46"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
+            line="135"
+            column="25"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
-        errorLine1="                    boolean capable = mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+        errorLine1="            new RcsProvisioningCallback() {"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="204"
-            column="60"/>
+            line="80"
+            column="17"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
-        errorLine1="            mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+        errorLine1="        return new RcsClientConfiguration("
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="166"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
-        errorLine1="                    mProvisioningManager.registerRcsProvisioningCallback(mExecutorService,"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="181"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
-        errorLine1="                mProvisioningManager.registerRcsProvisioningCallback(getMainExecutor(), mCallback);"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
-            line="221"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
-        errorLine1="                    mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="180"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
-        errorLine1="                mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
-            line="220"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
-        errorLine1="                mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="195"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
-        errorLine1="            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
-            line="348"
-            column="34"/>
+            line="106"
+            column="16"/>
     </issue>
 
     <issue
@@ -201,122 +553,122 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getContactUri`"
-        errorLine1="                b.append(t.getContactUri());"
-        errorLine2="                           ~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+        errorLine1="            mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="220"
-            column="28"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="166"
+            column="34"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
-        errorLine1="                            t.getServiceCapabilities();"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+        errorLine1="                    mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());"
+        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="227"
-            column="31"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="180"
+            column="42"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
-        errorLine1="                if (t.getServiceCapabilities() != null) {"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+        errorLine1="                    mProvisioningManager.registerRcsProvisioningCallback(mExecutorService,"
+        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="225"
-            column="23"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="181"
+            column="42"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceId`"
-        errorLine1="                b.append(t.getServiceId());"
-        errorLine2="                           ~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="                mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="222"
-            column="28"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="195"
+            column="38"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceVersion`"
-        errorLine1="                b.append(t.getServiceVersion());"
-        errorLine2="                           ~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+        errorLine1="                    boolean capable = mProvisioningManager.isRcsVolteSingleRegistrationCapable();"
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="224"
-            column="28"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
+            line="204"
+            column="60"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getSupportedDuplexModes`"
-        errorLine1="                    b.append(servCaps.getSupportedDuplexModes());"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="233"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getUnsupportedDuplexModes`"
-        errorLine1="                    b.append(servCaps.getUnsupportedDuplexModes());"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="235"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isAudioCapable`"
-        errorLine1="                    b.append(servCaps.isAudioCapable());"
-        errorLine2="                                      ~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="229"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isVideoCapable`"
-        errorLine1="                    b.append(servCaps.isVideoCapable());"
-        errorLine2="                                      ~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="231"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityMechanism`"
-        errorLine1="        if (c.getCapabilityMechanism() == RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE) {"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="216"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityTuples`"
-        errorLine1="            for (RcsContactPresenceTuple t : c.getCapabilityTuples()) {"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestCapabilities`"
+        errorLine1="                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, getMainExecutor(),"
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="218"
+            line="95"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
+        errorLine1="                        new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                        ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="96"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
+        errorLine1="                        new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="96"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestAvailability`"
+        errorLine1="                mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="135"
+            column="48"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
+        errorLine1="                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                                           ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="136"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
+        errorLine1="                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
+        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="136"
             column="48"/>
     </issue>
 
@@ -355,475 +707,123 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestAvailability`"
-        errorLine1="                mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityMechanism`"
+        errorLine1="        if (c.getCapabilityMechanism() == RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE) {"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="216"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactUceCapability#getCapabilityTuples`"
+        errorLine1="            for (RcsContactPresenceTuple t : c.getCapabilityTuples()) {"
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="135"
+            line="218"
             column="48"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter#requestCapabilities`"
-        errorLine1="                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, getMainExecutor(),"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getContactUri`"
+        errorLine1="                b.append(t.getContactUri());"
+        errorLine2="                           ~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="95"
-            column="48"/>
+            line="220"
+            column="28"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
-        errorLine1='                + ", \n\tmHomeDomain=" + config.getHomeDomain()'
-        errorLine2="                                                ~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceId`"
+        errorLine1="                b.append(t.getServiceId());"
+        errorLine2="                           ~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="341"
-            column="49"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="222"
+            column="28"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
-        errorLine1='                + ", \n\tmImei=" + config.getImei()'
-        errorLine2="                                          ~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceVersion`"
+        errorLine1="                b.append(t.getServiceVersion());"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="342"
-            column="43"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="224"
+            column="28"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
-        errorLine1='                + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
+        errorLine1="                if (t.getServiceCapabilities() != null) {"
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="354"
-            column="57"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="225"
+            column="23"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
-        errorLine1='                + ", \n\tmLocalIpAddr=" + config.getLocalAddress()'
-        errorLine2="                                                 ~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple#getServiceCapabilities`"
+        errorLine1="                            t.getServiceCapabilities();"
+        errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="334"
-            column="50"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="227"
+            column="31"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
-        errorLine1='                + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isAudioCapable`"
+        errorLine1="                    b.append(servCaps.isAudioCapable());"
+        errorLine2="                                      ~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="338"
-            column="56"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
+            line="229"
+            column="39"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
-        errorLine1="                + &quot;, \n\tmNatConfiguration=&quot; + config.getNatSocketAddress() + '}';"
-        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#isVideoCapable`"
+        errorLine1="                    b.append(servCaps.isVideoCapable());"
+        errorLine2="                                      ~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="355"
-            column="55"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
-        errorLine1='                + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="340"
-            column="60"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
-        errorLine1='                + ", \n\tmGruu=" + config.getPublicGruuUri()'
-        errorLine2="                                          ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="343"
-            column="43"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
-        errorLine1='                + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
-        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="339"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
-        errorLine1='                + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="353"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
-        errorLine1='                + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="344"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
-        errorLine1='                + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
-        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="345"
-            column="51"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
-        errorLine1='                + ", \n\tmCniHeader=" + config.getSipCniHeader()'
-        errorLine2="                                               ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="352"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
-        errorLine1='                + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
-        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="349"
-            column="55"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
-        errorLine1='                + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
-        errorLine2="                                                ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="350"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
-        errorLine1='                + ", \n\tmPathHeader=" + config.getSipPathHeader()'
-        errorLine2="                                                ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="347"
-            column="49"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
-        errorLine1='                + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
-        errorLine2="                                                 ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="351"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
-        errorLine1='                + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="335"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
-        errorLine1='                + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="346"
-            column="57"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
-        errorLine1='                + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="348"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
-        errorLine1='                + ", \n\tmTransportType=" + config.getTransportType()'
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="333"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
-        errorLine1='                + "mVersion=" + config.getVersion()'
-        errorLine2="                                       ~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="332"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
-        errorLine1='                + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="336"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
-        errorLine1='                + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="337"
-            column="60"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
-        errorLine1="                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
-        errorLine2="                                        ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
+            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
             line="231"
-            column="41"/>
+            column="39"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
-        errorLine1="                mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="247"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
-        errorLine1="            mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="322"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
-        errorLine1="                    new UaSecurityProtocolIdentifier.Builder();"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
-            line="120"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
-        errorLine1="                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),"
-        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="231"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
-        errorLine1="        return new RcsClientConfiguration("
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
-            line="231"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
-        errorLine1="        return new RcsClientConfiguration("
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="106"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
-        errorLine1="                    new BootstrapAuthenticationCallback() {"
-        errorLine2="                    ^">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
-            line="135"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
-        errorLine1="                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
-        errorLine2="                                           ^">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getSupportedDuplexModes`"
+        errorLine1="                    b.append(servCaps.getSupportedDuplexModes());"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="136"
-            column="44"/>
+            line="233"
+            column="39"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Cast to `CapabilitiesCallback` requires API level 31 (current min is 30)"
-        errorLine1="                        new RcsUceAdapter.CapabilitiesCallback() {"
-        errorLine2="                        ^">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities#getUnsupportedDuplexModes`"
+        errorLine1="                    b.append(servCaps.getUnsupportedDuplexModes());"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="96"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
-        errorLine1="                    new BootstrapAuthenticationCallback() {"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java"
-            line="135"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
-        errorLine1="            new RcsProvisioningCallback() {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java"
-            line="89"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
-        errorLine1="            new RcsProvisioningCallback() {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java"
-            line="80"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
-        errorLine1="                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="136"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.RcsUceAdapter.CapabilitiesCallback`"
-        errorLine1="                        new RcsUceAdapter.CapabilitiesCallback() {"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java"
-            line="96"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionMessageCallback`"
-        errorLine1="            new DelegateConnectionMessageCallback() {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="87"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
-        errorLine1="            new DelegateConnectionStateCallback() {"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java"
-            line="117"
-            column="17"/>
+            line="235"
+            column="39"/>
     </issue>
 
 </issues>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/res/layout/CarrierLockListLayout.xml b/testapps/TestRcsApp/TestApp/res/layout/CarrierLockListLayout.xml
new file mode 100644
index 0000000..f07c65c
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/CarrierLockListLayout.xml
@@ -0,0 +1,77 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/noLockMode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockMode"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToVZW"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_VZW"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToATT"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_ATT"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToTMO"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_TMO"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToKOODOS"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_KOODOS"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToTELUS"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_TELUS"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
index 939feb0..ebf5508 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
@@ -74,6 +74,14 @@
             android:textAlignment="center"
             android:textAllCaps="false" />
 
+        <Button
+            android:id="@+id/setCarrierLockMode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/setCarrierLockMode"
+            android:textAlignment="center"
+            android:textAllCaps="false"/>
+
         <TextView
             android:id="@+id/version_info"
             android:layout_width="match_parent"
diff --git a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
index f52b70d..b017139 100644
--- a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
@@ -72,6 +72,8 @@
     <string name="browse">Browse</string>
     <string name="upload">Upload</string>
     <string name="upload_file_gba">Upload File with GBA</string>
+    <string name="setCarrierLockMode">CarrierLock</string>
+
     <string name="invalid_parameters">Invalid Parameters</string>
     <string name="server">Server:</string>
     <string name="file_name">File Name:</string>
@@ -79,6 +81,13 @@
     <string name="file_empty">File is empty</string>
     <string name="version_info">Version: %s</string>
 
+    <string name="no_LockMode">NoLock/ UnLocked</string>
+    <string name="no_LockTo_VZW">Lock to Verizon</string>
+    <string name="no_LockTo_ATT">Lock to ATT</string>
+    <string name="no_LockTo_TMO">Lock to TMO</string>
+    <string name="no_LockTo_KOODOS">Lock to KOODO</string>
+    <string name="no_LockTo_TELUS">Lock to TELUS</string>
+
     <string-array name="rcs_profile">
         <item>UP_1.0</item>
         <item>UP_2.3</item>
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
index 89c5268..5d2db73 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
@@ -29,6 +29,8 @@
 
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.google.android.sample.rcsclient.carrierLock.CarrieLockModeListActivity;
+
 /** An activity to show function list. */
 public class MainActivity extends AppCompatActivity {
     private static final String TAG = "TestRcsApp.MainActivity";
@@ -39,6 +41,7 @@
     private Button mMessageClientButton;
     private Button mFileUploadButton;
     private TextView mVersionInfo;
+    private Button mCarrierLockModeListBtn;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -56,6 +59,7 @@
         mGbaButton = (Button) this.findViewById(R.id.gba);
         mFileUploadButton = findViewById(R.id.uploadFile);
         mVersionInfo = this.findViewById(R.id.version_info);
+        mCarrierLockModeListBtn = findViewById(R.id.setCarrierLockMode);
         mProvisionButton.setOnClickListener(view -> {
             Intent intent = new Intent(this, ProvisioningActivity.class);
             MainActivity.this.startActivity(intent);
@@ -90,6 +94,11 @@
                     appVersionName);
             mVersionInfo.setText(version);
         }
+
+        mCarrierLockModeListBtn.setOnClickListener(view -> {
+            Intent intent = new Intent(this, CarrieLockModeListActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
     }
 
     @Override
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrieLockModeListActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrieLockModeListActivity.java
new file mode 100644
index 0000000..6547aeb
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrieLockModeListActivity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.sample.rcsclient.carrierLock;
+
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.sample.rcsclient.R;
+
+public class CarrieLockModeListActivity extends AppCompatActivity {
+
+    private final CarrierLockProvider mCarrierLockProvider = new CarrierLockProvider();
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.CarrierLockListLayout);
+
+        Button noLockModeBtn = this.findViewById(R.id.noLockMode);
+        assert noLockModeBtn != null;
+        noLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.UNLOCKED);
+            Toast.makeText(this, "Lock mode set to UNLOCKED", Toast.LENGTH_LONG).show();
+        });
+
+        Button vzwLockModeBtn = this.findViewById(R.id.lockToVZW);
+        assert vzwLockModeBtn != null;
+        vzwLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_VZW);
+            Toast.makeText(this, "Lock mode set to VZW", Toast.LENGTH_LONG).show();
+        });
+
+        Button attLockModeBtn = this.findViewById(R.id.lockToATT);
+        assert attLockModeBtn != null;
+        attLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_ATT);
+            Toast.makeText(this, "Lock mode set to ATT", Toast.LENGTH_LONG).show();
+        });
+
+        Button tmoLockModeBtn = this.findViewById(R.id.lockToTMO);
+        assert tmoLockModeBtn != null;
+        tmoLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_TMO);
+            Toast.makeText(this, "Lock mode set to TMO", Toast.LENGTH_LONG).show();
+        });
+
+        Button koodoLockModeBtn = this.findViewById(R.id.lockToKOODOS);
+        assert koodoLockModeBtn != null;
+        koodoLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_KOODO);
+            Toast.makeText(this, "Lock mode set to KOODO", Toast.LENGTH_LONG).show();
+        });
+
+        Button telusLockModeBtn = this.findViewById(R.id.lockToTELUS);
+        assert telusLockModeBtn != null;
+        telusLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_TELUS);
+            Toast.makeText(this, "Lock mode set to TELUS", Toast.LENGTH_LONG).show();
+        });
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierLockProvider.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierLockProvider.java
new file mode 100644
index 0000000..8fa3cd6
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierLockProvider.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.sample.rcsclient.carrierLock;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+public class CarrierLockProvider extends ContentProvider {
+
+    public static final String AUTHORITY = "com.sample.lockProvider";
+    public static final String TAG = "TestCarrierLockProvider";
+
+    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/carrierLock");
+    // content://com.sample.lockProvider/carrierLock
+
+    private static CarrierRestriction mLockMode = CarrierRestriction.UNLOCKED;
+    private static final ArrayList<Integer> mCarrierIds = new ArrayList<>();
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Bundle call(String method, String args, Bundle extras) {
+        Bundle result = new Bundle();
+        Log.d(TAG, "call query STARTED on method = " + method);
+        switch (method) {
+            case "getCarrierRestrictionStatus":
+                try {
+                    if (mLockMode == CarrierRestriction.UNLOCKED) {
+                        result.putInt("restriction_status", 0); // Unlocked
+                    } else {
+                        result.putInt("restriction_status", 2); // Locked/Restricted
+                    }
+                    mCarrierIds.clear();
+                    Log.d(TAG, "Query come : Lock mode set to " + mLockMode);
+                    switch (mLockMode) {
+                        case UNLOCKED:
+                            // Do Nothing
+                            break;
+                        case LOCK_TO_VZW:
+                            mCarrierIds.add(1839);
+                            break;
+                        case LOCK_TO_ATT:
+                            mCarrierIds.add(1187);
+                            mCarrierIds.add(10021);
+                            mCarrierIds.add(2119);
+                            mCarrierIds.add(2120);
+                            mCarrierIds.add(1779);
+                            mCarrierIds.add(10028);
+                            break;
+                        case LOCK_TO_TMO:
+                            mCarrierIds.add(1);
+                            break;
+                        case LOCK_TO_KOODO:
+                            mCarrierIds.add(2020);
+                            break;
+                        case LOCK_TO_TELUS:
+                            mCarrierIds.add(1404);
+                            break;
+                        default:
+                            // Nothing
+                    }
+                    StringJoiner joiner = new StringJoiner(", ");
+                    if (!mCarrierIds.isEmpty()) {
+                        result.putIntegerArrayList("allowed_carrier_ids", mCarrierIds);
+                        for (Integer num : mCarrierIds) {
+                            joiner.add(num.toString());
+                        }
+                        result.putString("PrintableCarrierIds", joiner.toString());
+                        Log.d(TAG, "Locked to carrierIds = " + joiner.toString());
+                    } else {
+                        result.putString("allowed_carrier_ids", "");
+                        result.putString("PrintableCarrierIds", "");
+                    }
+
+                } catch (Exception e) {
+                    Log.e(TAG, " call :: query :: exception = " + e.getMessage());
+                }
+                return result;
+
+            case "getList:":
+                String list = String.valueOf(
+                        mCarrierIds.size());
+                result.putString("carrierList", list);
+                return result;
+            default:
+                return null;
+        }
+    }
+
+    private void updateLockValue(int lockValue) {
+        Log.d(TAG, "updateLockValue through ADB to = " + lockValue);
+        switch (lockValue) {
+            case 1:
+                mLockMode = CarrierRestriction.LOCK_TO_VZW;
+                break;
+            case 2:
+                mLockMode = CarrierRestriction.LOCK_TO_ATT;
+                break;
+            case 3:
+                mLockMode = CarrierRestriction.LOCK_TO_TMO;
+                break;
+            case 4:
+                mLockMode = CarrierRestriction.LOCK_TO_KOODO;
+                break;
+            case 5:
+                mLockMode = CarrierRestriction.LOCK_TO_TELUS;
+                break;
+            default:
+                mLockMode = CarrierRestriction.UNLOCKED;
+                break;
+        }
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        Log.d(TAG, "CarrierLockProvider Query");
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "vnd.android.cursor.dir/vnd." + AUTHORITY + ".books";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        Log.d(TAG, "CarrierLockProvider insert START");
+        assert values != null;
+        int newValue = values.getAsInteger("newValue");
+        updateLockValue(newValue);
+        return CONTENT_URI;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    public void setLockMode(CarrierRestriction lockMode) {
+        mLockMode = lockMode;
+        Log.d(TAG, "Setting lockMode to " + mLockMode);
+    }
+}
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierRestriction.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierRestriction.java
new file mode 100644
index 0000000..34f9e7b
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierRestriction.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.sample.rcsclient.carrierLock;
+
+public enum CarrierRestriction {
+    UNLOCKED,
+    LOCK_TO_VZW,
+    LOCK_TO_ATT,
+    LOCK_TO_TMO,
+    LOCK_TO_KOODO,
+    LOCK_TO_TELUS
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml b/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
index e0c7c3e..b2110a3 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/lint-baseline.xml
@@ -1,48 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.0.0-dev" type="baseline" dependencies="true" variant="all" version="8.0.0-dev">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.net.ConnectivityManager#registerQosCallback`"
-        errorLine1="            connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
-            line="118"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.net.ConnectivityManager#unregisterQosCallback`"
-        errorLine1="        connectivityManager.unregisterQosCallback(qosCallback);"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
-            line="181"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
-        errorLine1="        telephonyManager.bootstrapAuthenticationRequest("
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
+        errorLine1="                new UaSecurityProtocolIdentifier.Builder();"
+        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
-            line="97"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
-        errorLine1="        UaSecurityProtocolIdentifier spId = builder.build();"
-        errorLine2="                                                    ~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
-            line="79"
-            column="53"/>
+            line="55"
+            column="17"/>
     </issue>
 
     <issue
@@ -91,673 +58,57 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
-        errorLine1="                                .getRegisteredFeatureTags()"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.gba.UaSecurityProtocolIdentifier.Builder#build`"
+        errorLine1="        UaSecurityProtocolIdentifier spId = builder.build();"
+        errorLine2="                                                    ~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="139"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
-        errorLine1="            Set&lt;String&gt; registeredFt = registrationState.getRegisteredFeatureTags();"
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="223"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
-        errorLine1='                stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
-        errorLine2="                                                     ~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="220"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
-        errorLine1="                        featureTagState.getState());"
-        errorLine2="                                        ~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="221"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
-        errorLine1="        this.sipDelegateManager = imsManager.getSipDelegateManager(subscriptionId);"
-        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="77"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
-        errorLine1="        return provisioningManager.isRcsVolteSingleRegistrationCapable();"
-        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="166"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
-        errorLine1="            provisioningManager.registerRcsProvisioningCallback(executorService, callback);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="147"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
-        errorLine1="        provisioningManager.setRcsClientConfiguration(clientConfiguration);"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="111"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#triggerRcsReconfiguration`"
-        errorLine1="        provisioningManager.triggerRcsReconfiguration();"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="176"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
-        errorLine1="            provisioningManager.unregisterRcsProvisioningCallback(callback);"
-        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="158"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
-        errorLine1='                    + ", \n\tmHomeDomain=" + config.getHomeDomain()'
-        errorLine2="                                                    ~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="246"
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="79"
             column="53"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
-        errorLine1="            return mConfiguration.getHomeDomain();"
-        errorLine2="                                  ~~~~~~~~~~~~~">
+        message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
+        errorLine1="                new TelephonyManager.BootstrapAuthenticationCallback() {"
+        errorLine2="                ^">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="317"
-            column="35"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="81"
+            column="17"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
-        errorLine1='                    + ", \n\tmImei=" + config.getImei()'
-        errorLine2="                                              ~~~~~~~">
+        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
+        errorLine1="                new TelephonyManager.BootstrapAuthenticationCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="247"
-            column="47"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="81"
+            column="21"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
-        errorLine1="            return mConfiguration.getImei();"
-        errorLine2="                                  ~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#bootstrapAuthenticationRequest`"
+        errorLine1="        telephonyManager.bootstrapAuthenticationRequest("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="357"
-            column="35"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
+            line="97"
+            column="26"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
-        errorLine1='                    + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
+        errorLine1="        return new SipMessage(startLine, headers.toString(), rawContent);"
+        errorLine2="               ~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="259"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
-        errorLine1="            SipDelegateConfiguration.IpSecConfiguration c = mConfiguration.getIpSecConfiguration();"
-        errorLine2="                                                                           ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="333"
-            column="76"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
-        errorLine1='                    + ", \n\tmLocalAddr=" + config.getLocalAddress()'
-        errorLine2="                                                   ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="239"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
-        errorLine1="            return mConfiguration.getLocalAddress().getAddress().getHostAddress();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="296"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
-        errorLine1="            return mConfiguration.getLocalAddress().getPort();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="301"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
-        errorLine1='                    + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="243"
-            column="60"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
-        errorLine1="                    ? mConfiguration.getMaxUdpPayloadSizeBytes() : 1500;"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="378"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
-        errorLine1="            return mConfiguration.getMaxUdpPayloadSizeBytes() &gt; 0"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="377"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
-        errorLine1="                    + &quot;, \n\tmNatConfiguration=&quot; + config.getNatSocketAddress() + '}';"
-        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="260"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
-        errorLine1='                    + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
-        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="245"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
-        errorLine1='                    + ", \n\tmGruu=" + config.getPublicGruuUri()'
-        errorLine2="                                              ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="248"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
-        errorLine1='                    + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
-        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="244"
-            column="63"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
-        errorLine1="            return mConfiguration.getPublicUserIdentifier();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="312"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
-        errorLine1='                    + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
-        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="258"
-            column="62"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
-        errorLine1="            String associatedUris = mConfiguration.getSipAssociatedUriHeader();"
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="322"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
-        errorLine1='                    + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="249"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
-        errorLine1='                    + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
-        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="250"
-            column="55"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
-        errorLine1='                    + ", \n\tmCniHeader=" + config.getSipCniHeader()'
-        errorLine2="                                                   ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="257"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
-        errorLine1='                    + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
-        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="254"
-            column="59"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
-        errorLine1="            return mConfiguration.getSipContactUserParameter();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="352"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
-        errorLine1='                    + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
-        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="255"
-            column="53"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
-        errorLine1="            return mConfiguration.getSipPaniHeader();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="362"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
-        errorLine1='                    + ", \n\tmPathHeader=" + config.getSipPathHeader()'
-        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="252"
-            column="53"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
-        errorLine1='                    + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
-        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="256"
-            column="54"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
-        errorLine1="            return mConfiguration.getSipPlaniHeader();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="367"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
-        errorLine1='                    + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="240"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
-        errorLine1="            return mConfiguration.getSipServerAddress().getAddress().getHostAddress();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="286"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
-        errorLine1="            return mConfiguration.getSipServerAddress().getPort();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="291"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
-        errorLine1='                    + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
-        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="251"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
-        errorLine1="                    mConfiguration.getSipServiceRouteHeader();"
-        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="343"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
-        errorLine1='                    + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
-        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="253"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
-        errorLine1="            return mConfiguration.getSipUserAgentHeader();"
-        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="372"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
-        errorLine1='                    + ", \n\tmTransportType=" + config.getTransportType()'
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="238"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
-        errorLine1="            int sipTransport = mConfiguration.getTransportType();"
-        errorLine2="                                              ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="306"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
-        errorLine1="                                        + registeredSipConfig.getVersion());"
-        errorLine2="                                                              ~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="127"
-            column="63"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
-        errorLine1='                    + "mVersion=" + config.getVersion()'
-        errorLine2="                                           ~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="237"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
-        errorLine1="            return mConfiguration.getVersion();"
-        errorLine2="                                  ~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="281"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
-        errorLine1='                    + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
-        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="241"
-            column="66"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
-        errorLine1='                    + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
-        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="242"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#getSipSecurityVerifyHeader`"
-        errorLine1="            return c.getSipSecurityVerifyHeader();"
-        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="337"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConnection#sendMessage`"
-        errorLine1="            sipDelegateConnection.sendMessage(MessageConverter.toPlatformMessage(message),"
-        errorLine2="                                  ~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="271"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
-        errorLine1="                controller.sipDelegateManager.createSipDelegate("
-        errorLine2="                                              ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="205"
-            column="47"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
-        errorLine1="            sipDelegateManager.destroySipDelegate(context.sipDelegateConnection,"
-        errorLine2="                               ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="92"
-            column="32"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getContent`"
-        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
-        errorLine2="                                                                           ~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="395"
-            column="76"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
-        errorLine1='                        + message.getHeaderSection().substring(0, 10) + "-&gt;"'
-        errorLine2="                                  ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="392"
-            column="35"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
-        errorLine1="            String headers = message.getHeaderSection();"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="387"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getStartLine`"
-        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
-        errorLine2="                                          ~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="395"
-            column="43"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
+            line="72"
+            column="16"/>
     </issue>
 
     <issue
@@ -784,94 +135,6 @@
 
     <issue
         id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.net.QosSocketInfo`"
-        errorLine1="            connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
-        errorLine2="                                                    ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
-            line="118"
-            column="53"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.gba.UaSecurityProtocolIdentifier.Builder`"
-        errorLine1="                new UaSecurityProtocolIdentifier.Builder();"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
-            line="55"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
-        errorLine1="            DelegateRequest request = new DelegateRequest(imsService.getFeatureTags());"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="203"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
-        errorLine1="        return new RcsClientConfiguration("
-        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="74"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
-        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
-        errorLine2="                   ~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
-            line="395"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
-        errorLine1="        return new SipMessage(startLine, headers.toString(), rawContent);"
-        errorLine2="               ~~~~~~~~~~~~~~">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java"
-            line="72"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Cast to `BootstrapAuthenticationCallback` requires API level 31 (current min is 30)"
-        errorLine1="                new TelephonyManager.BootstrapAuthenticationCallback() {"
-        errorLine2="                ^">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
-            line="81"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Cast to `RcsProvisioningCallback` requires API level 31 (current min is 30)"
-        errorLine1="                new RcsProvisioningCallback() {"
-        errorLine2="                ^">
-        <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="114"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="NewApi"
         message="Class requires API level 31 (current min is 30): `android.net.QosCallback`"
         errorLine1="    private final QosCallback qosCallback = new QosCallback() {"
         errorLine2="                                                ~~~~~~~~~~~">
@@ -883,24 +146,68 @@
 
     <issue
         id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyManager.BootstrapAuthenticationCallback`"
-        errorLine1="                new TelephonyManager.BootstrapAuthenticationCallback() {"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `new android.net.QosSocketInfo`"
+        errorLine1="            connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),"
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java"
-            line="81"
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java"
+            line="118"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ImsManager#getSipDelegateManager`"
+        errorLine1="        this.sipDelegateManager = imsManager.getSipDelegateManager(subscriptionId);"
+        errorLine2="                                             ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="77"
+            column="46"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#destroySipDelegate`"
+        errorLine1="            sipDelegateManager.destroySipDelegate(context.sipDelegateConnection,"
+        errorLine2="                               ~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="92"
+            column="32"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
+        errorLine1="                new DelegateConnectionStateCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="114"
             column="21"/>
     </issue>
 
     <issue
         id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
-        errorLine1="                new RcsProvisioningCallback() {"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1="                                        + registeredSipConfig.getVersion());"
+        errorLine2="                                                              ~~~~~~~~~~">
         <location
-            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
-            line="114"
-            column="21"/>
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="127"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+        errorLine1="                                .getRegisteredFeatureTags()"
+        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="139"
+            column="34"/>
     </issue>
 
     <issue
@@ -916,13 +223,684 @@
 
     <issue
         id="NewApi"
-        message="Class requires API level 31 (current min is 30): `android.telephony.ims.stub.DelegateConnectionStateCallback`"
-        errorLine1="                new DelegateConnectionStateCallback() {"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.DelegateRequest`"
+        errorLine1="            DelegateRequest request = new DelegateRequest(imsService.getFeatureTags());"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="203"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateManager#createSipDelegate`"
+        errorLine1="                controller.sipDelegateManager.createSipDelegate("
+        errorLine2="                                              ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="205"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getFeatureTag`"
+        errorLine1='                stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append('
+        errorLine2="                                                     ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="220"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.FeatureTagState#getState`"
+        errorLine1="                        featureTagState.getState());"
+        errorLine2="                                        ~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="221"
+            column="41"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.DelegateRegistrationState#getRegisteredFeatureTags`"
+        errorLine1="            Set&lt;String&gt; registeredFt = registrationState.getRegisteredFeatureTags();"
+        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="223"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1='                    + "mVersion=" + config.getVersion()'
+        errorLine2="                                           ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="237"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+        errorLine1='                    + ", \n\tmTransportType=" + config.getTransportType()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="238"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1='                    + ", \n\tmLocalAddr=" + config.getLocalAddress()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="239"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1='                    + ", \n\tmSipServerAddr=" + config.getSipServerAddress()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="240"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipCompactFormEnabled`"
+        errorLine1='                    + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()'
+        errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="241"
+            column="66"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#isSipKeepaliveEnabled`"
+        errorLine1='                    + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()'
+        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="242"
+            column="64"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1='                    + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()'
+        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="243"
+            column="60"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+        errorLine1='                    + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()'
+        errorLine2="                                                              ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="244"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPrivateUserIdentifier`"
+        errorLine1='                    + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()'
+        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="245"
+            column="64"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+        errorLine1='                    + ", \n\tmHomeDomain=" + config.getHomeDomain()'
+        errorLine2="                                                    ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="246"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+        errorLine1='                    + ", \n\tmImei=" + config.getImei()'
+        errorLine2="                                              ~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="247"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicGruuUri`"
+        errorLine1='                    + ", \n\tmGruu=" + config.getPublicGruuUri()'
+        errorLine2="                                              ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="248"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationHeader`"
+        errorLine1='                    + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()'
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="249"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAuthenticationNonce`"
+        errorLine1='                    + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()'
+        errorLine2="                                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="250"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+        errorLine1='                    + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()'
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="251"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPathHeader`"
+        errorLine1='                    + ", \n\tmPathHeader=" + config.getSipPathHeader()'
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="252"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+        errorLine1='                    + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()'
+        errorLine2="                                                         ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="253"
+            column="58"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+        errorLine1='                    + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()'
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="254"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+        errorLine1='                    + ", \n\tmPaniHeader=" + config.getSipPaniHeader()'
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="255"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+        errorLine1='                    + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()'
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="256"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipCniHeader`"
+        errorLine1='                    + ", \n\tmCniHeader=" + config.getSipCniHeader()'
+        errorLine2="                                                   ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="257"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+        errorLine1='                    + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()'
+        errorLine2="                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="258"
+            column="62"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+        errorLine1='                    + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()'
+        errorLine2="                                                            ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="259"
+            column="61"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getNatSocketAddress`"
+        errorLine1="                    + &quot;, \n\tmNatConfiguration=&quot; + config.getNatSocketAddress() + '}';"
+        errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="260"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConnection#sendMessage`"
+        errorLine1="            sipDelegateConnection.sendMessage(MessageConverter.toPlatformMessage(message),"
+        errorLine2="                                  ~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="271"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getVersion`"
+        errorLine1="            return mConfiguration.getVersion();"
+        errorLine2="                                  ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="281"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1="            return mConfiguration.getSipServerAddress().getAddress().getHostAddress();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="286"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServerAddress`"
+        errorLine1="            return mConfiguration.getSipServerAddress().getPort();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="291"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1="            return mConfiguration.getLocalAddress().getAddress().getHostAddress();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="296"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getLocalAddress`"
+        errorLine1="            return mConfiguration.getLocalAddress().getPort();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="301"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getTransportType`"
+        errorLine1="            int sipTransport = mConfiguration.getTransportType();"
+        errorLine2="                                              ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="306"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getPublicUserIdentifier`"
+        errorLine1="            return mConfiguration.getPublicUserIdentifier();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="312"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getHomeDomain`"
+        errorLine1="            return mConfiguration.getHomeDomain();"
+        errorLine2="                                  ~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="317"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipAssociatedUriHeader`"
+        errorLine1="            String associatedUris = mConfiguration.getSipAssociatedUriHeader();"
+        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="322"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getIpSecConfiguration`"
+        errorLine1="            SipDelegateConfiguration.IpSecConfiguration c = mConfiguration.getIpSecConfiguration();"
+        errorLine2="                                                                           ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="333"
+            column="76"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration.IpSecConfiguration#getSipSecurityVerifyHeader`"
+        errorLine1="            return c.getSipSecurityVerifyHeader();"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="337"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipServiceRouteHeader`"
+        errorLine1="                    mConfiguration.getSipServiceRouteHeader();"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="343"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipContactUserParameter`"
+        errorLine1="            return mConfiguration.getSipContactUserParameter();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="352"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getImei`"
+        errorLine1="            return mConfiguration.getImei();"
+        errorLine2="                                  ~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="357"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPaniHeader`"
+        errorLine1="            return mConfiguration.getSipPaniHeader();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="362"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipPlaniHeader`"
+        errorLine1="            return mConfiguration.getSipPlaniHeader();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="367"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getSipUserAgentHeader`"
+        errorLine1="            return mConfiguration.getSipUserAgentHeader();"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="372"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1="            return mConfiguration.getMaxUdpPayloadSizeBytes() &gt; 0"
+        errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="377"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipDelegateConfiguration#getMaxUdpPayloadSizeBytes`"
+        errorLine1="                    ? mConfiguration.getMaxUdpPayloadSizeBytes() : 1500;"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="378"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
+        errorLine1="            String headers = message.getHeaderSection();"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="387"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getHeaderSection`"
+        errorLine1='                        + message.getHeaderSection().substring(0, 10) + "-&gt;"'
+        errorLine2="                                  ~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="392"
+            column="35"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getContent`"
+        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
+        errorLine2="                                                                           ~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="395"
+            column="76"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.SipMessage#getStartLine`"
+        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
+        errorLine2="                                          ~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="395"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.SipMessage`"
+        errorLine1="            return new SipMessage(message.getStartLine(), headers, message.getContent());"
+        errorLine2="                   ~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java"
+            line="395"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `new android.telephony.ims.RcsClientConfiguration`"
+        errorLine1="        return new RcsClientConfiguration("
+        errorLine2="               ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="74"
+            column="16"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#setRcsClientConfiguration`"
+        errorLine1="        provisioningManager.setRcsClientConfiguration(clientConfiguration);"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="111"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast to `RcsProvisioningCallback` requires API level 31 (current min is 30)"
+        errorLine1="                new RcsProvisioningCallback() {"
+        errorLine2="                ^">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="114"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager.RcsProvisioningCallback`"
+        errorLine1="                new RcsProvisioningCallback() {"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
             line="114"
             column="21"/>
     </issue>
 
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#registerRcsProvisioningCallback`"
+        errorLine1="            provisioningManager.registerRcsProvisioningCallback(executorService, callback);"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="147"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#unregisterRcsProvisioningCallback`"
+        errorLine1="            provisioningManager.unregisterRcsProvisioningCallback(callback);"
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="158"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#isRcsVolteSingleRegistrationCapable`"
+        errorLine1="        return provisioningManager.isRcsVolteSingleRegistrationCapable();"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="166"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 30): `android.telephony.ims.ProvisioningManager#triggerRcsReconfiguration`"
+        errorLine1="        provisioningManager.triggerRcsReconfiguration();"
+        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java"
+            line="176"
+            column="29"/>
+    </issue>
+
 </issues>
\ No newline at end of file
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
index 40e3c69..6a79412 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteControl.xml
@@ -15,7 +15,7 @@
   ~ 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"
@@ -84,6 +84,42 @@
             android:paddingRight="4dp"
             android:text="@string/requestTimeForNextSatelliteVisibility"/>
          <Button
+            android:id="@+id/removeUserRestrictReason"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/removeUserRestrictReason"/>
+         <Button
+            android:id="@+id/addUserRestrictReason"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/addUserRestrictReason"/>
+         <Button
+            android:id="@+id/getSatellitePlmn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/getSatellitePlmn"/>
+         <Button
+            android:id="@+id/getAllSatellitePlmn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/getAllSatellitePlmn"/>
+         <Button
+            android:id="@+id/isSatelliteEnabledForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/isSatelliteEnabledForCarrier"/>
+         <Button
+            android:id="@+id/isRequestIsSatelliteEnabledForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/isRequestIsSatelliteEnabledForCarrier"/>
+         <Button
             android:id="@+id/Back"
             android:onClick="Back"
             android:textColor="@android:color/holo_blue_dark"
@@ -102,4 +138,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 c136ce7..7f2f026 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_TestSatelliteWrapper.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_TestSatelliteWrapper.xml
@@ -14,91 +14,157 @@
   ~ See the License for the specific language governing permissions and
   ~ 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:layout_height="match_parent">
 
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        android:layout_weight="0"
-        android:textColor="@android:color/holo_blue_dark"
-        android:textSize="20dp"
-        android:text="Satellite Wrapper Test"/>
-    <Button
-        android:id="@+id/requestNtnSignalStrength"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingRight="4dp"
-        android:text="@string/requestNtnSignalStrength"/>
-    <Button
-        android:id="@+id/registerForNtnSignalStrengthChanged"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingRight="4dp"
-        android:text="@string/registerForNtnSignalStrengthChanged"/>
-    <Button
-        android:id="@+id/unregisterForNtnSignalStrengthChanged"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingRight="4dp"
-        android:text="@string/unregisterForNtnSignalStrengthChanged"/>
-    <Button
-        android:id="@+id/isOnlyNonTerrestrialNetworkSubscription"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingRight="4dp"
-        android:text="@string/isOnlyNonTerrestrialNetworkSubscription"/>
-    <Button
-        android:id="@+id/registerForSatelliteCapabilitiesChanged"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingRight="4dp"
-        android:text="@string/registerForSatelliteCapabilitiesChanged"/>
-    <Button
-        android:id="@+id/unregisterForSatelliteCapabilitiesChanged"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingRight="4dp"
-        android:text="@string/unregisterForSatelliteCapabilitiesChanged"/>
     <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="horizontal">
-         <Button
-            android:id="@+id/Back"
-            android:onClick="Back"
+        android:orientation="vertical"
+        android:gravity="center"
+        android:paddingStart="4dp">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="0dp"
+            android:layout_weight="0"
             android:textColor="@android:color/holo_blue_dark"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:paddingRight="4dp"
-            android:text="@string/Back"/>
+            android:textSize="20dp"
+            android:text="Satellite Wrapper Test"/>
         <Button
-            android:id="@+id/ClearLog"
-            android:onClick="ClearLog"
-            android:textColor="@android:color/holo_blue_dark"
-            android:layout_marginTop="10dp"
-            android:layout_marginBottom="10dp"
-            android:layout_width="0dp"
+            android:id="@+id/requestNtnSignalStrength"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_weight="1"
             android:paddingRight="4dp"
-            android:text="@string/ClearLog"/>
+            android:text="@string/requestNtnSignalStrength"/>
+        <Button
+            android:id="@+id/registerForNtnSignalStrengthChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/registerForNtnSignalStrengthChanged"/>
+        <Button
+            android:id="@+id/unregisterForNtnSignalStrengthChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/unregisterForNtnSignalStrengthChanged"/>
+        <Button
+            android:id="@+id/isOnlyNonTerrestrialNetworkSubscription"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/isOnlyNonTerrestrialNetworkSubscription"/>
+        <Button
+            android:id="@+id/registerForSatelliteCapabilitiesChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/registerForSatelliteCapabilitiesChanged"/>
+        <Button
+            android:id="@+id/unregisterForSatelliteCapabilitiesChanged"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/unregisterForSatelliteCapabilitiesChanged"/>
+        <Button
+            android:id="@+id/isNonTerrestrialNetwork"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/isNonTerrestrialNetwork"/>
+        <Button
+            android:id="@+id/getAvailableServices"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/getAvailableServices"/>
+        <Button
+            android:id="@+id/isUsingNonTerrestrialNetwork"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/isUsingNonTerrestrialNetwork"/>
+        <Button
+            android:id="@+id/requestAttachEnabledForCarrier_enable"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestAttachEnabledForCarrier_enable"/>
+        <Button
+            android:id="@+id/requestAttachEnabledForCarrier_disable"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestAttachEnabledForCarrier_disable"/>
+        <Button
+            android:id="@+id/requestIsAttachEnabledForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/requestIsAttachEnabledForCarrier"/>
+        <Button
+            android:id="@+id/addAttachRestrictionForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/addAttachRestrictionForCarrier"/>
+        <Button
+            android:id="@+id/removeAttachRestrictionForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/removeAttachRestrictionForCarrier"/>
+        <Button
+            android:id="@+id/getAttachRestrictionReasonsForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/getAttachRestrictionReasonsForCarrier"/>
+        <Button
+            android:id="@+id/getSatellitePlmnsForCarrier"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/getSatellitePlmnsForCarrier"/>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+             <Button
+                android:id="@+id/Back"
+                android:onClick="Back"
+                android:textColor="@android:color/holo_blue_dark"
+                android:layout_marginTop="10dp"
+                android:layout_marginBottom="10dp"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:paddingRight="4dp"
+                android:text="@string/Back"/>
+            <Button
+                android:id="@+id/ClearLog"
+                android:onClick="ClearLog"
+                android:textColor="@android:color/holo_blue_dark"
+                android:layout_marginTop="10dp"
+                android:layout_marginBottom="10dp"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:paddingRight="4dp"
+                android:text="@string/ClearLog"/>
+        </LinearLayout>
+        <ListView
+            android:id="@+id/logListView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:capitalize="characters"
+            android:textColor="@android:color/holo_blue_light"
+            android:layout_centerVertical="true"
+            android:textSize="8dp" />
     </LinearLayout>
-    <ListView
-        android:id="@+id/logListView"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:capitalize="characters"
-        android:textColor="@android:color/holo_blue_light"
-        android:layout_centerVertical="true"
-        android:textSize="8dp" />
-</LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
index 8ebe5f3..20f5ca8 100644
--- a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
@@ -64,6 +64,23 @@
     <string name="isOnlyNonTerrestrialNetworkSubscription">isOnlyNonTerrestrialNetworkSubscription</string>
     <string name="registerForSatelliteCapabilitiesChanged">registerForSatelliteCapabilitiesChanged</string>
     <string name="unregisterForSatelliteCapabilitiesChanged">unregisterForSatelliteCapabilitiesChanged</string>
+    <string name="isNonTerrestrialNetwork">isNonTerrestrialNetwork</string>
+    <string name="getAvailableServices">getAvailableServices</string>
+    <string name="isUsingNonTerrestrialNetwork">isUsingNonTerrestrialNetwork</string>
+    <string name="requestAttachEnabledForCarrier_enable">requestAttachEnabledForCarrier_enable</string>
+    <string name="requestAttachEnabledForCarrier_disable">requestAttachEnabledForCarrier_disable</string>
+    <string name="requestIsAttachEnabledForCarrier">requestIsAttachEnabledForCarrier</string>
+    <string name="addAttachRestrictionForCarrier">addAttachRestrictionForCarrier</string>
+    <string name="removeAttachRestrictionForCarrier">removeAttachRestrictionForCarrier</string>
+    <string name="getAttachRestrictionReasonsForCarrier">getAttachRestrictionReasonsForCarrier</string>
+    <string name="getSatellitePlmnsForCarrier">getSatellitePlmnsForCarrier</string>
+
+    <string name="removeUserRestrictReason">removeUserRestrictReason</string>
+    <string name="addUserRestrictReason">addUserRestrictReason</string>
+    <string name="getSatellitePlmn">getSatellitePlmn</string>
+    <string name="getAllSatellitePlmn">getAllSatellitePlmn</string>
+    <string name="isSatelliteEnabledForCarrier">isSatelliteEnabledForCarrier</string>
+    <string name="isRequestIsSatelliteEnabledForCarrier">isRequestIsSatelliteEnabledForCarrier</string>
 
     <string name="Back">Back</string>
     <string name="ClearLog">Clear Log</string>
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java
index 97f676f..9ea1b44 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Datagram.java
@@ -29,11 +29,12 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.telephony.satellite.EnableRequestAttributes;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteDatagramCallback;
 import android.telephony.satellite.SatelliteManager;
-import android.telephony.satellite.SatelliteStateCallback;
+import android.telephony.satellite.SatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.stub.SatelliteResult;
 import android.util.Log;
@@ -61,7 +62,7 @@
 
     private SatelliteManager mSatelliteManager;
     private SatelliteDatagramCallbackTestApp mDatagramCallback;
-    private SatelliteStateCallbackTestApp mStateCallback;
+    private SatelliteModemStateCallbackTestApp mStateCallback;
     private SatelliteTransmissionUpdateCallbackTestApp mCallback;
     private android.telephony.satellite.stub.SatelliteDatagram mReceivedDatagram;
 
@@ -75,32 +76,32 @@
         super.onCreate(savedInstanceState);
         mSatelliteManager = getSystemService(SatelliteManager.class);
         mDatagramCallback = new SatelliteDatagramCallbackTestApp();
-        mStateCallback = new SatelliteStateCallbackTestApp();
+        mStateCallback = new SatelliteModemStateCallbackTestApp();
         mCallback = new SatelliteTransmissionUpdateCallbackTestApp();
 
         mReceivedDatagram = new android.telephony.satellite.stub.SatelliteDatagram();
 
         setContentView(R.layout.activity_Datagram);
         findViewById(R.id.startSatelliteTransmissionUpdates)
-                .setOnClickListener(this::startSatelliteTransmissionUpdatesApp);
+                .setOnClickListener(this::startTransmissionUpdatesApp);
         findViewById(R.id.stopSatelliteTransmissionUpdates)
-                .setOnClickListener(this::stopSatelliteTransmissionUpdatesApp);
+                .setOnClickListener(this::stopTransmissionUpdatesApp);
         findViewById(R.id.pollPendingSatelliteDatagrams)
-                .setOnClickListener(this::pollPendingSatelliteDatagramsApp);
+                .setOnClickListener(this::pollPendingDatagramsApp);
         findViewById(R.id.sendSatelliteDatagram)
-                .setOnClickListener(this::sendSatelliteDatagramApp);
+                .setOnClickListener(this::sendDatagramApp);
         findViewById(R.id.registerForSatelliteDatagram)
-                .setOnClickListener(this::registerForSatelliteDatagramApp);
+                .setOnClickListener(this::registerForIncomingDatagramApp);
         findViewById(R.id.unregisterForSatelliteDatagram)
-                .setOnClickListener(this::unregisterForSatelliteDatagramApp);
+                .setOnClickListener(this::unregisterForIncomingDatagramApp);
         findViewById(R.id.showDatagramSendStateTransition)
                 .setOnClickListener(this::showDatagramSendStateTransitionApp);
         findViewById(R.id.showDatagramReceiveStateTransition)
                 .setOnClickListener(this::showDatagramReceiveStateTransitionApp);
         findViewById(R.id.registerForSatelliteModemStateChanged)
-                .setOnClickListener(this::registerForSatelliteModemStateChangedApp);
+                .setOnClickListener(this::registerForModemStateChangedApp);
         findViewById(R.id.unregisterForSatelliteModemStateChanged)
-                .setOnClickListener(this::unregisterForSatelliteModemStateChangedApp);
+                .setOnClickListener(this::unregisterForModemStateChangedApp);
         findViewById(R.id.showSatelliteModemStateTransition)
                 .setOnClickListener(this::showSatelliteModemStateTransitionApp);
 
@@ -121,7 +122,7 @@
         }
     }
 
-    protected class SatelliteStateCallbackTestApp implements SatelliteStateCallback {
+    protected class SatelliteModemStateCallbackTestApp implements SatelliteModemStateCallback {
         @Override
         public void onSatelliteModemStateChanged(int state) {
             mModemState = state;
@@ -166,10 +167,12 @@
         }
     }
 
-    private void startSatelliteTransmissionUpdatesApp(View view) {
+    private void startTransmissionUpdatesApp(View view) {
         TextView textView = findViewById(R.id.text_id);
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, error::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, error::offer);
         TextView showErrorStatusTextView = findViewById(R.id.showErrorStatus);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -186,7 +189,7 @@
             return;
         }
         error.clear();
-        mSatelliteManager.startSatelliteTransmissionUpdates(Runnable::run, error::offer, mCallback);
+        mSatelliteManager.startTransmissionUpdates(Runnable::run, error::offer, mCallback);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
@@ -202,10 +205,10 @@
         }
     }
 
-    private void stopSatelliteTransmissionUpdatesApp(View view) {
+    private void stopTransmissionUpdatesApp(View view) {
         TextView textView = findViewById(R.id.text_id);
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.stopSatelliteTransmissionUpdates(mCallback, Runnable::run, error::offer);
+        mSatelliteManager.stopTransmissionUpdates(mCallback, Runnable::run, error::offer);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
@@ -220,7 +223,7 @@
             textView.setText("stopSatelliteTransmissionUpdates exception caught =" + e);
         }
     }
-    private void pollPendingSatelliteDatagramsApp(View view) {
+    private void pollPendingDatagramsApp(View view) {
         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
         TextView showErrorStatusTextView = findViewById(R.id.showErrorStatus);
         TextView textView = findViewById(R.id.text_id);
@@ -228,7 +231,9 @@
         if (SatelliteTestApp.getTestSatelliteService() != null) {
             SatelliteTestApp.getTestSatelliteService().sendOnPendingDatagrams();
         }
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
@@ -246,61 +251,61 @@
             showErrorStatusTextView.setText("Enable SatelliteService exception caught = " + e);
             return;
         }
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
                 textView.setText("Timed out for poll message");
             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
-                textView.setText("Failed to pollPendingSatelliteDatagrams with error = "
+                textView.setText("Failed to pollPendingDatagrams with error = "
                         + SatelliteErrorUtils.mapError(value));
             } else {
-                textView.setText("pollPendingSatelliteDatagrams is successful");
+                textView.setText("pollPendingDatagrams is successful");
             }
         } catch (InterruptedException e) {
-            textView.setText("pollPendingSatelliteDatagrams exception caught =" + e);
+            textView.setText("pollPendingDatagrams exception caught =" + e);
         }
     }
 
-    private void sendSatelliteDatagramApp(View view) {
+    private void sendDatagramApp(View view) {
         TextView textView = findViewById(R.id.text_id);
         mSatelliteManager.setDeviceAlignedWithSatellite(true);
         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
         String mText = "This is a test datagram message";
         SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
-                textView.setText("Timed out for sendSatelliteDatagram");
+                textView.setText("Timed out for sendDatagram");
             } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
-                textView.setText("Failed to sendSatelliteDatagram with error = "
+                textView.setText("Failed to sendDatagram with error = "
                         + SatelliteErrorUtils.mapError(value));
             } else {
-                textView.setText("sendSatelliteDatagram is successful");
+                textView.setText("sendDatagram is successful");
             }
         } catch (InterruptedException e) {
-            textView.setText("sendSatelliteDatagram exception caught =" + e);
+            textView.setText("sendDatagram exception caught =" + e);
         }
     }
 
-    private void registerForSatelliteDatagramApp(View view) {
-        int result = mSatelliteManager.registerForSatelliteDatagram(Runnable::run,
+    private void registerForIncomingDatagramApp(View view) {
+        int result = mSatelliteManager.registerForIncomingDatagram(Runnable::run,
                 mDatagramCallback);
         TextView textView = findViewById(R.id.text_id);
         if (result == 0) {
-            textView.setText("registerForSatelliteDatagram is successful");
+            textView.setText("registerForIncomingDatagram is successful");
         } else {
-            textView.setText("Status for registerForSatelliteDatagram : "
+            textView.setText("Status for registerForIncomingDatagram : "
                     + SatelliteErrorUtils.mapError(result));
         }
     }
 
-    private void unregisterForSatelliteDatagramApp(View view) {
-        mSatelliteManager.unregisterForSatelliteDatagram(mDatagramCallback);
+    private void unregisterForIncomingDatagramApp(View view) {
+        mSatelliteManager.unregisterForIncomingDatagram(mDatagramCallback);
         TextView textView = findViewById(R.id.text_id);
-        textView.setText("unregisterForSatelliteDatagram is successful");
+        textView.setText("unregisterForIncomingDatagram is successful");
     }
 
     private void showDatagramSendStateTransitionApp(View view) {
@@ -315,22 +320,22 @@
                 + mShowDatagramReceiveStateTransition);
     }
 
-    private void registerForSatelliteModemStateChangedApp(View view) {
-        int result = mSatelliteManager.registerForSatelliteModemStateChanged(Runnable::run,
+    private void registerForModemStateChangedApp(View view) {
+        int result = mSatelliteManager.registerForModemStateChanged(Runnable::run,
                 mStateCallback);
         TextView textView = findViewById(R.id.text_id);
         if (result == 0) {
-            textView.setText("registerForSatelliteModemStateChanged is successful");
+            textView.setText("registerForModemStateChanged is successful");
         } else {
-            textView.setText("Status for registerForSatelliteModemStateChanged : "
+            textView.setText("Status for registerForModemStateChanged : "
                     + SatelliteErrorUtils.mapError(result));
         }
     }
 
-    private void unregisterForSatelliteModemStateChangedApp(View view) {
-        mSatelliteManager.unregisterForSatelliteModemStateChanged(mStateCallback);
+    private void unregisterForModemStateChangedApp(View view) {
+        mSatelliteManager.unregisterForModemStateChanged(mStateCallback);
         TextView textView = findViewById(R.id.text_id);
-        textView.setText("unregisterForSatelliteModemStateChanged is successful");
+        textView.setText("unregisterForModemStateChanged is successful");
     }
 
     private void showSatelliteModemStateTransitionApp(View view) {
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl
index 2c320c8..0a32432 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/ILocalSatelliteListener.aidl
@@ -64,4 +64,10 @@
      * enableCellularModemWhileSatelliteModeIsOn from Telephony.
      */
     void onEnableCellularModemWhileSatelliteModeIsOn(in boolean enable);
+
+    /**
+     * Indicates that MockSatelliteService has just received the request
+     * setSatellitePlmn from Telephony.
+     */
+    void onSetSatellitePlmn();
 }
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/MultipleSendReceive.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/MultipleSendReceive.java
index 3c0b2fd..723f690 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/MultipleSendReceive.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/MultipleSendReceive.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
+import android.telephony.satellite.EnableRequestAttributes;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.util.Log;
@@ -66,20 +67,22 @@
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 4);
         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, resultListener::offer);
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 3);
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 2);
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 1);
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 0);
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
             TextView textView = findViewById(R.id.text_id);
@@ -100,15 +103,15 @@
         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
         String mText = "This is a test datagram message";
         SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(1000, TimeUnit.MILLISECONDS);
@@ -127,27 +130,29 @@
     private void multipleSendReceiveSatelliteDatagramApp(View view) {
         mSatelliteManager.setDeviceAlignedWithSatellite(true);
         LinkedBlockingQueue<Integer> resultListener = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, resultListener::offer);
         String mText = "This is a test datagram message";
         SatelliteDatagram datagram = new SatelliteDatagram(mText.getBytes());
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 4);
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 3);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 2);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 1);
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, resultListener::offer);
         SatelliteTestApp.getTestSatelliteService().sendOnSatelliteDatagramReceived(
                 mReceivedDatagram, 0);
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 940435e..20c5ef5 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/Provisioning.java
@@ -55,15 +55,15 @@
 
         setContentView(R.layout.activity_Provisioning);
         findViewById(R.id.provisionSatelliteService)
-                .setOnClickListener(this::provisionSatelliteServiceApp);
+                .setOnClickListener(this::provisionServiceApp);
         findViewById(R.id.deprovisionSatelliteService)
-                .setOnClickListener(this::deprovisionSatelliteServiceApp);
+                .setOnClickListener(this::deprovisionServiceApp);
         findViewById(R.id.requestIsSatelliteProvisioned)
-                .setOnClickListener(this::requestIsSatelliteProvisionedApp);
+                .setOnClickListener(this::requestIsProvisionedApp);
         findViewById(R.id.registerForSatelliteProvisionStateChanged)
-                .setOnClickListener(this::registerForSatelliteProvisionStateChangedApp);
+                .setOnClickListener(this::registerForProvisionStateChangedApp);
         findViewById(R.id.unregisterForSatelliteProvisionStateChanged)
-                .setOnClickListener(this::unregisterForSatelliteProvisionStateChangedApp);
+                .setOnClickListener(this::unregisterForProvisionStateChangedApp);
         findViewById(R.id.showCurrentSatelliteProvisionState)
                 .setOnClickListener(this::showCurrentSatelliteProvisionStateApp);
         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
@@ -84,13 +84,13 @@
         }
     }
 
-    private void provisionSatelliteServiceApp(View view) {
+    private void provisionServiceApp(View view) {
         TextView textView = findViewById(R.id.text_id);
         CancellationSignal cancellationSignal = new CancellationSignal();
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
         String mText = "This is test provision data.";
         byte[] testProvisionData = mText.getBytes();
-        mSatelliteManager.provisionSatelliteService("SATELLITE_TOKEN", testProvisionData,
+        mSatelliteManager.provisionService("SATELLITE_TOKEN", testProvisionData,
                 cancellationSignal, Runnable::run, error::offer);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -107,10 +107,10 @@
         }
     }
 
-    private void deprovisionSatelliteServiceApp(View view) {
+    private void deprovisionServiceApp(View view) {
         TextView textView = findViewById(R.id.text_id);
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.deprovisionSatelliteService("SATELLITE_TOKEN", Runnable::run,
+        mSatelliteManager.deprovisionService("SATELLITE_TOKEN", Runnable::run,
                 error::offer);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -127,7 +127,7 @@
         }
     }
 
-    private void requestIsSatelliteProvisionedApp(View view) {
+    private void requestIsProvisionedApp(View view) {
         final AtomicReference<Boolean> enabled = new AtomicReference<>();
         final AtomicReference<Integer> errorCode = new AtomicReference<>();
         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> mReceiver =
@@ -148,19 +148,19 @@
                         + SatelliteErrorUtils.mapError(errorCode.get()));
             }
         };
-        mSatelliteManager.requestIsSatelliteProvisioned(Runnable::run, mReceiver);
+        mSatelliteManager.requestIsProvisioned(Runnable::run, mReceiver);
     }
 
-    private void registerForSatelliteProvisionStateChangedApp(View view) {
-        int result = mSatelliteManager.registerForSatelliteProvisionStateChanged(Runnable::run,
+    private void registerForProvisionStateChangedApp(View view) {
+        int result = mSatelliteManager.registerForProvisionStateChanged(Runnable::run,
                 mCallback);
         TextView textView = findViewById(R.id.text_id);
         textView.setText("Status for registerForSatelliteProvisionStateChanged : "
                 + SatelliteErrorUtils.mapError(result));
     }
 
-    private void unregisterForSatelliteProvisionStateChangedApp(View view) {
-        mSatelliteManager.unregisterForSatelliteProvisionStateChanged(mCallback);
+    private void unregisterForProvisionStateChangedApp(View view) {
+        mSatelliteManager.unregisterForProvisionStateChanged(mCallback);
         TextView textView = findViewById(R.id.text_id);
         textView.setText("unregisterForSatelliteProvisionStateChanged is successful");
     }
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 4e339a3..dd7b825 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteControl.java
@@ -20,6 +20,9 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.OutcomeReceiver;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.satellite.EnableRequestAttributes;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.stub.SatelliteResult;
@@ -28,6 +31,7 @@
 import android.widget.TextView;
 
 import java.time.Duration;
+import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
@@ -40,11 +44,13 @@
     private static final long TIMEOUT = 3000;
 
     private SatelliteManager mSatelliteManager;
+    private SubscriptionManager mSubscriptionManager;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mSatelliteManager = getSystemService(SatelliteManager.class);
+        mSubscriptionManager = getSystemService(SubscriptionManager.class);
 
         setContentView(R.layout.activity_SatelliteControl);
         findViewById(R.id.enableSatellite)
@@ -52,18 +58,29 @@
         findViewById(R.id.disableSatellite)
                 .setOnClickListener(this::disableSatelliteApp);
         findViewById(R.id.requestIsSatelliteEnabled)
-                .setOnClickListener(this::requestIsSatelliteEnabledApp);
+                .setOnClickListener(this::requestIsEnabledApp);
         findViewById(R.id.requestIsDemoModeEnabled)
                 .setOnClickListener(this::requestIsDemoModeEnabledApp);
         findViewById(R.id.requestIsSatelliteSupported)
-                .setOnClickListener(this::requestIsSatelliteSupportedApp);
+                .setOnClickListener(this::requestIsSupportedApp);
         findViewById(R.id.requestSatelliteCapabilities)
-                .setOnClickListener(this::requestSatelliteCapabilitiesApp);
+                .setOnClickListener(this::requestCapabilitiesApp);
         findViewById(R.id.requestIsSatelliteCommunicationAllowedForCurrentLocation)
-                .setOnClickListener(
-                this::requestIsSatelliteCommunicationAllowedForCurrentLocationApp);
+                .setOnClickListener(this::requestIsCommunicationAllowedForCurrentLocationApp);
         findViewById(R.id.requestTimeForNextSatelliteVisibility)
                 .setOnClickListener(this::requestTimeForNextSatelliteVisibilityApp);
+        findViewById(R.id.removeUserRestrictReason)
+                .setOnClickListener(this::removeUserRestrictReasonApp);
+        findViewById(R.id.addUserRestrictReason)
+                .setOnClickListener(this::addUserRestrictReasonApp);
+        findViewById(R.id.getSatellitePlmn)
+                .setOnClickListener(this::getSatellitePlmnApp);
+        findViewById(R.id.getAllSatellitePlmn)
+                .setOnClickListener(this::getAllSatellitePlmnApp);
+        findViewById(R.id.isSatelliteEnabledForCarrier)
+                .setOnClickListener(this::isSatelliteEnabledForCarrierApp);
+        findViewById(R.id.isRequestIsSatelliteEnabledForCarrier)
+                .setOnClickListener(this::isRequestIsSatelliteEnabledForCarrierApp);
         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -74,7 +91,9 @@
 
     private void enableSatelliteApp(View view) {
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, error::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, error::offer);
         TextView textView = findViewById(R.id.text_id);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -93,7 +112,8 @@
 
     private void disableSatelliteApp(View view) {
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
-        mSatelliteManager.requestSatelliteEnabled(false, true, Runnable::run, error::offer);
+        mSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(false).build(),
+                Runnable::run, error::offer);
         TextView textView = findViewById(R.id.text_id);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -110,7 +130,7 @@
         }
     }
 
-    private void requestIsSatelliteEnabledApp(View view) {
+    private void requestIsEnabledApp(View view) {
         final AtomicReference<Boolean> enabled = new AtomicReference<>();
         final AtomicReference<Integer> errorCode = new AtomicReference<>();
         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
@@ -120,9 +140,9 @@
                 enabled.set(result);
                 TextView textView = findViewById(R.id.text_id);
                 if (enabled.get()) {
-                    textView.setText("requestIsSatelliteEnabled is true");
+                    textView.setText("requestIsEnabled is true");
                 } else {
-                    textView.setText("Status for requestIsSatelliteEnabled result : "
+                    textView.setText("Status for requestIsEnabled result : "
                             + enabled.get());
                 }
             }
@@ -131,11 +151,11 @@
             public void onError(SatelliteManager.SatelliteException exception) {
                 errorCode.set(exception.getErrorCode());
                 TextView textView = findViewById(R.id.text_id);
-                textView.setText("Status for requestIsSatelliteEnabled error : "
+                textView.setText("Status for requestIsEnabled error : "
                         + SatelliteErrorUtils.mapError(errorCode.get()));
             }
         };
-        mSatelliteManager.requestIsSatelliteEnabled(Runnable::run, receiver);
+        mSatelliteManager.requestIsEnabled(Runnable::run, receiver);
     }
 
     private void requestIsDemoModeEnabledApp(View view) {
@@ -166,7 +186,7 @@
         mSatelliteManager.requestIsDemoModeEnabled(Runnable::run, receiver);
     }
 
-    private void requestIsSatelliteSupportedApp(View view) {
+    private void requestIsSupportedApp(View view) {
         final AtomicReference<Boolean> enabled = new AtomicReference<>();
         final AtomicReference<Integer> errorCode = new AtomicReference<>();
         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
@@ -176,9 +196,9 @@
                 enabled.set(result);
                 TextView textView = findViewById(R.id.text_id);
                 if (enabled.get()) {
-                    textView.setText("requestIsSatelliteSupported is true");
+                    textView.setText("requestIsSupported is true");
                 } else {
-                    textView.setText("Status for requestIsSatelliteSupported result : "
+                    textView.setText("Status for requestIsSupported result : "
                             + enabled.get());
                 }
             }
@@ -187,14 +207,14 @@
             public void onError(SatelliteManager.SatelliteException exception) {
                 errorCode.set(exception.getErrorCode());
                 TextView textView = findViewById(R.id.text_id);
-                textView.setText("Status for requestIsSatelliteSupported error : "
+                textView.setText("Status for requestIsSupported error : "
                         + SatelliteErrorUtils.mapError(errorCode.get()));
             }
         };
-        mSatelliteManager.requestIsSatelliteSupported(Runnable::run, receiver);
+        mSatelliteManager.requestIsSupported(Runnable::run, receiver);
     }
 
-    private void requestSatelliteCapabilitiesApp(View view) {
+    private void requestCapabilitiesApp(View view) {
         final AtomicReference<SatelliteCapabilities> capabilities = new AtomicReference<>();
         final AtomicReference<Integer> errorCode = new AtomicReference<>();
         OutcomeReceiver<SatelliteCapabilities, SatelliteManager.SatelliteException> receiver =
@@ -203,7 +223,7 @@
             public void onResult(SatelliteCapabilities result) {
                 capabilities.set(result);
                 TextView textView = findViewById(R.id.text_id);
-                textView.setText("Status for requestSatelliteCapabilities result: "
+                textView.setText("Status for requestCapabilities result: "
                         + capabilities.get());
             }
 
@@ -211,17 +231,17 @@
             public void onError(SatelliteManager.SatelliteException exception) {
                 errorCode.set(exception.getErrorCode());
                 TextView textView = findViewById(R.id.text_id);
-                textView.setText("Status for requestSatelliteCapabilities error : "
+                textView.setText("Status for requestCapabilities error : "
                         + SatelliteErrorUtils.mapError(errorCode.get()));
             }
         };
-        mSatelliteManager.requestSatelliteCapabilities(Runnable::run, receiver);
+        mSatelliteManager.requestCapabilities(Runnable::run, receiver);
     }
 
-    private void requestIsSatelliteCommunicationAllowedForCurrentLocationApp(View view) {
+    private void requestIsCommunicationAllowedForCurrentLocationApp(View view) {
         final AtomicReference<Boolean> enabled = new AtomicReference<>();
         final AtomicReference<Integer> errorCode = new AtomicReference<>();
-        String display = "requestIsSatelliteCommunicationAllowedForCurrentLocation";
+        String display = "requestIsCommunicationAllowedForCurrentLocation";
         OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> receiver =
                 new OutcomeReceiver<>() {
             @Override
@@ -244,7 +264,7 @@
                         + SatelliteErrorUtils.mapError(errorCode.get()));
             }
         };
-        mSatelliteManager.requestIsSatelliteCommunicationAllowedForCurrentLocation(Runnable::run,
+        mSatelliteManager.requestIsCommunicationAllowedForCurrentLocation(Runnable::run,
                 receiver);
     }
 
@@ -271,4 +291,87 @@
         };
         mSatelliteManager.requestTimeForNextSatelliteVisibility(Runnable::run, receiver);
     }
+
+    private void removeUserRestrictReasonApp(View view) {
+        TextView textView = findViewById(R.id.text_id);
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        List<SubscriptionInfo> infoList = mSubscriptionManager.getAvailableSubscriptionInfoList();
+        List<Integer> subIdList = infoList.stream()
+                .map(SubscriptionInfo::getSubscriptionId)
+                .toList();
+        for (int subId : subIdList) {
+            mSatelliteManager.removeAttachRestrictionForCarrier(subId,
+                    SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER,
+                    Runnable::run, error::offer);
+        }
+
+        try {
+            Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
+            if (value == null) {
+                textView.setText("Timed out to removeAttachRestrictionForCarrier");
+            } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+                textView.setText("Failed to removeAttachRestrictionForCarrier with error = "
+                        + SatelliteErrorUtils.mapError(value));
+            } else {
+                textView.setText(subIdList == null || subIdList.isEmpty() ? "no active subId list" :
+                        "removeAttachRestrictionForCarrier for all subIdList=" + subIdList);
+            }
+        } catch (InterruptedException e) {
+            textView.setText("removeAttachRestrictionForCarrier exception caught =" + e);
+        }
+    }
+
+    private void addUserRestrictReasonApp(View view) {
+        TextView textView = findViewById(R.id.text_id);
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        List<SubscriptionInfo> infoList = mSubscriptionManager.getAvailableSubscriptionInfoList();
+        List<Integer> subIdList = infoList.stream()
+                .map(SubscriptionInfo::getSubscriptionId)
+                .toList();
+        for (int subId : subIdList) {
+            mSatelliteManager.addAttachRestrictionForCarrier(subId,
+                    SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER,
+                    Runnable::run, error::offer);
+        }
+
+        try {
+            Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
+            if (value == null) {
+                textView.setText("Timed out to addAttachRestrictionForCarrier");
+            } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+                textView.setText("Failed to addAttachRestrictionForCarrier with error = "
+                        + SatelliteErrorUtils.mapError(value));
+            } else {
+                textView.setText(subIdList == null || subIdList.isEmpty() ? "no active subId list" :
+                        "addAttachRestrictionForCarrier for all subIdList=" + subIdList);
+            }
+        } catch (InterruptedException e) {
+            textView.setText("addAttachRestrictionForCarrier exception caught =" + e);
+        }
+    }
+
+    private void getSatellitePlmnApp(View view) {
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("[SatelliteService] getSatellitePlmnApp = "
+                + SatelliteTestApp.getTestSatelliteService().getCarrierPlmnList());
+    }
+
+    private void getAllSatellitePlmnApp(View view) {
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("[SatelliteService] getAllSatellitePlmnApp = "
+                + SatelliteTestApp.getTestSatelliteService().getAllSatellitePlmnList());
+    }
+
+    private void isSatelliteEnabledForCarrierApp(View view) {
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("[SatelliteService] isSatelliteEnabledForCarrier= "
+                + SatelliteTestApp.getTestSatelliteService().isSatelliteEnabledForCarrier());
+    }
+
+    private void isRequestIsSatelliteEnabledForCarrierApp(View view) {
+        TextView textView = findViewById(R.id.text_id);
+        textView.setText("[SatelliteService] isRequestIsSatelliteEnabledForCarrier= "
+                + SatelliteTestApp.getTestSatelliteService()
+                .isRequestIsSatelliteEnabledForCarrier());
+    }
 }
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java
index ffdabdf..ef0c85c 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteErrorUtils.java
@@ -16,7 +16,31 @@
 
 package com.android.phone.testapps.satellitetestapp;
 
-import android.telephony.satellite.stub.SatelliteResult;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ACCESS_BARRED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_BUSY;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NETWORK_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NETWORK_TIMEOUT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_AUTHORIZED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_NO_RESOURCES;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVER_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_MODEM_STATE;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS;
+
 import android.util.Log;
 
 /**
@@ -31,44 +55,54 @@
      */
     public static String mapError(int error) {
         switch (error) {
-            case SatelliteResult.SATELLITE_RESULT_SUCCESS:
+            case SATELLITE_RESULT_SUCCESS:
                 return "SATELLITE_RESULT_SUCCESS";
-            case SatelliteResult.SATELLITE_RESULT_ERROR:
+            case SATELLITE_RESULT_ERROR:
                 return "SATELLITE_RESULT_ERROR";
-            case SatelliteResult.SATELLITE_RESULT_SERVER_ERROR:
+            case SATELLITE_RESULT_SERVER_ERROR:
                 return "SATELLITE_RESULT_SERVER_ERROR";
-            case SatelliteResult.SATELLITE_RESULT_SERVICE_ERROR:
+            case SATELLITE_RESULT_SERVICE_ERROR:
                 return "SATELLITE_RESULT_SERVICE_ERROR";
-            case SatelliteResult.SATELLITE_RESULT_MODEM_ERROR:
+            case SATELLITE_RESULT_MODEM_ERROR:
                 return "SATELLITE_RESULT_MODEM_ERROR";
-            case SatelliteResult.SATELLITE_RESULT_NETWORK_ERROR:
+            case SATELLITE_RESULT_NETWORK_ERROR:
                 return "SATELLITE_RESULT_NETWORK_ERROR";
-            case SatelliteResult.SATELLITE_RESULT_INVALID_MODEM_STATE:
+            case SATELLITE_RESULT_INVALID_TELEPHONY_STATE:
+                return "SATELLITE_RESULT_INVALID_TELEPHONY_STATE";
+            case SATELLITE_RESULT_INVALID_MODEM_STATE:
                 return "SATELLITE_RESULT_INVALID_MODEM_STATE";
-            case SatelliteResult.SATELLITE_RESULT_INVALID_ARGUMENTS:
+            case SATELLITE_RESULT_INVALID_ARGUMENTS:
                 return "SATELLITE_RESULT_INVALID_ARGUMENTS";
-            case SatelliteResult.SATELLITE_RESULT_REQUEST_FAILED:
+            case SATELLITE_RESULT_REQUEST_FAILED:
                 return "SATELLITE_RESULT_REQUEST_FAILED";
-            case SatelliteResult.SATELLITE_RESULT_RADIO_NOT_AVAILABLE:
+            case SATELLITE_RESULT_RADIO_NOT_AVAILABLE:
                 return "SATELLITE_RESULT_RADIO_NOT_AVAILABLE";
-            case SatelliteResult.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED:
+            case SATELLITE_RESULT_REQUEST_NOT_SUPPORTED:
                 return "SATELLITE_RESULT_REQUEST_NOT_SUPPORTED";
-            case SatelliteResult.SATELLITE_RESULT_NO_RESOURCES:
+            case SATELLITE_RESULT_NO_RESOURCES:
                 return "SATELLITE_RESULT_NO_RESOURCES";
-            case SatelliteResult.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED:
+            case SATELLITE_RESULT_SERVICE_NOT_PROVISIONED:
                 return "SATELLITE_RESULT_SERVICE_NOT_PROVISIONED";
-            case SatelliteResult.SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS:
+            case SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS:
                 return "SATELLITE_RESULT_SERVICE_PROVISION_IN_PROGRESS";
-            case SatelliteResult.SATELLITE_RESULT_REQUEST_ABORTED:
+            case SATELLITE_RESULT_REQUEST_ABORTED:
                 return "SATELLITE_RESULT_REQUEST_ABORTED";
-            case SatelliteResult.SATELLITE_RESULT_ACCESS_BARRED:
+            case SATELLITE_RESULT_ACCESS_BARRED:
                 return "SATELLITE_RESULT_ACCESS_BARRED";
-            case SatelliteResult.SATELLITE_RESULT_NETWORK_TIMEOUT:
+            case SATELLITE_RESULT_NETWORK_TIMEOUT:
                 return "SATELLITE_RESULT_NETWORK_TIMEOUT";
-            case SatelliteResult.SATELLITE_RESULT_NOT_REACHABLE:
+            case SATELLITE_RESULT_NOT_REACHABLE:
                 return "SATELLITE_RESULT_NOT_REACHABLE";
-            case SatelliteResult.SATELLITE_RESULT_NOT_AUTHORIZED:
+            case SATELLITE_RESULT_NOT_AUTHORIZED:
                 return "SATELLITE_RESULT_NOT_AUTHORIZED";
+            case SATELLITE_RESULT_NOT_SUPPORTED:
+                return "SATELLITE_RESULT_NOT_SUPPORTED";
+            case SATELLITE_RESULT_REQUEST_IN_PROGRESS:
+                return "SATELLITE_RESULT_REQUEST_IN_PROGRESS";
+            case SATELLITE_RESULT_MODEM_BUSY:
+                return "SATELLITE_RESULT_MODEM_BUSY";
+            case SATELLITE_RESULT_ILLEGAL_STATE:
+                return "SATELLITE_RESULT_ILLEGAL_STATE";
         }
         Log.d(TAG, "Received invalid satellite service error: " + error);
         return "SATELLITE_RESULT_SERVICE_ERROR";
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 ced9a06..c8ee5fa 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
@@ -138,6 +138,11 @@
                 public void onEnableCellularModemWhileSatelliteModeIsOn(boolean enable) {
                     Log.d(TAG, "onEnableCellularModemWhileSatelliteModeIsOn");
                 }
+
+                @Override
+                public void onSetSatellitePlmn() {
+                    Log.d(TAG, "onSetSatellitePlmn");
+                }
             };
 
     private class TestSatelliteServiceConnection implements ServiceConnection {
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 a64aa5d..ab7b1c4 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SendReceive.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SendReceive.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.OutcomeReceiver;
+import android.telephony.satellite.EnableRequestAttributes;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
@@ -124,7 +125,7 @@
 
         SatelliteDatagram datagram = new SatelliteDatagram(mMessageInput.getBytes());
         //Sending Message
-        mSatelliteManager.sendSatelliteDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
+        mSatelliteManager.sendDatagram(SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE,
                 datagram, true, Runnable::run, error::offer);
         TextView messageStatusTextView = findViewById(R.id.messageStatus);
         try {
@@ -139,7 +140,7 @@
                         + mEnterMessage.getText().toString());
             }
         } catch (InterruptedException e) {
-            messageStatusTextView.setText("sendSatelliteDatagram exception caught = " + e);
+            messageStatusTextView.setText("sendDatagram exception caught = " + e);
         }
     }
 
@@ -148,16 +149,18 @@
         byte[] testProvisionData = mMessageOutput.getBytes();
         setupForTransferringDatagram(testProvisionData);
 
-        int result = mSatelliteManager.registerForSatelliteDatagram(Runnable::run, mCallback);
+        int result = mSatelliteManager.registerForIncomingDatagram(Runnable::run, mCallback);
         TextView showErrorStatusTextView = findViewById(R.id.showErrorStatus);
         if (result != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
-            showErrorStatusTextView.setText("Status for registerForSatelliteDatagram : "
+            showErrorStatusTextView.setText("Status for registerForIncomingDatagram : "
                     + SatelliteErrorUtils.mapError(result));
         }
         if (SatelliteTestApp.getTestSatelliteService() != null) {
             SatelliteTestApp.getTestSatelliteService().sendOnPendingDatagrams();
         }
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, resultListener::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
@@ -173,7 +176,7 @@
             return;
         }
 
-        mSatelliteManager.pollPendingSatelliteDatagrams(Runnable::run, resultListener::offer);
+        mSatelliteManager.pollPendingDatagrams(Runnable::run, resultListener::offer);
         try {
             Integer value = resultListener.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
@@ -185,7 +188,7 @@
                 mMessageStatusTextView.setText("Successfully polled pending messages");
             }
         } catch (InterruptedException e) {
-            mMessageStatusTextView.setText("pollPendingSatelliteDatagrams exception caught = " + e);
+            mMessageStatusTextView.setText("pollPendingDatagrams exception caught = " + e);
         }
     }
 
@@ -195,7 +198,7 @@
         LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
 
         //Provisioning
-        mSatelliteManager.provisionSatelliteService("SATELLITE_TOKEN", provisionData,
+        mSatelliteManager.provisionService("SATELLITE_TOKEN", provisionData,
                 cancellationSignal, Runnable::run, error::offer);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -233,12 +236,14 @@
                                 + SatelliteErrorUtils.mapError(errorCode.get()));
                     }
                 };
-        mSatelliteManager.requestSatelliteCapabilities(Runnable::run, receiver);
+        mSatelliteManager.requestCapabilities(Runnable::run, receiver);
 
         //Satellite Position
         SatelliteTransmissionUpdateCallbackTestApp callback =
                 new SatelliteTransmissionUpdateCallbackTestApp();
-        mSatelliteManager.requestSatelliteEnabled(true, true, Runnable::run, error::offer);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true).setDemoMode(true).build(),
+                Runnable::run, error::offer);
         try {
             Integer value = error.poll(TIMEOUT, TimeUnit.MILLISECONDS);
             if (value == null) {
@@ -254,7 +259,7 @@
         }
         error.clear();
 
-        mSatelliteManager.startSatelliteTransmissionUpdates(Runnable::run, error::offer, callback);
+        mSatelliteManager.startTransmissionUpdates(Runnable::run, error::offer, callback);
         // Position update
         android.telephony.satellite.stub.PointingInfo pointingInfo =
                 new android.telephony.satellite.stub.PointingInfo();
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 929a2e5..af37611 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteService.java
@@ -41,7 +41,9 @@
 import com.android.internal.util.FunctionalUtils;
 import com.android.telephony.Rlog;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -94,6 +96,10 @@
     private boolean mIsSupported;
     private int mModemState;
     private boolean mIsCellularModemEnabledMode;
+    private List<String> mCarrierPlmnList = new ArrayList<>();
+    private List<String> mAllPlmnList = new ArrayList<>();
+    private boolean mIsSatelliteEnabledForCarrier;
+    private boolean mIsRequestIsSatelliteEnabledForCarrier;
 
     /**
      * Create TestSatelliteService using the Executor specified for methods being called from
@@ -109,6 +115,8 @@
         mIsSupported = true;
         mModemState = SatelliteModemState.SATELLITE_MODEM_STATE_OFF;
         mIsCellularModemEnabledMode = false;
+        mIsSatelliteEnabledForCarrier = false;
+        mIsRequestIsSatelliteEnabledForCarrier = false;
     }
 
     /**
@@ -330,14 +338,14 @@
         if (mLocalListener != null) {
             runWithExecutor(() -> mLocalListener.onPollPendingSatelliteDatagrams());
         } else {
-            loge("pollPendingSatelliteDatagrams: mLocalListener is null");
+            loge("pollPendingDatagrams: mLocalListener is null");
         }
     }
 
     @Override
     public void sendSatelliteDatagram(@NonNull SatelliteDatagram datagram, boolean isEmergency,
             @NonNull IIntegerConsumer errorCallback) {
-        logd("sendSatelliteDatagram: mErrorCode=" + mErrorCode);
+        logd("sendDatagram: mErrorCode=" + mErrorCode);
         if (mErrorCode != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
             runWithExecutor(() -> errorCallback.accept(mErrorCode));
         } else {
@@ -347,7 +355,7 @@
         if (mLocalListener != null) {
             runWithExecutor(() -> mLocalListener.onSendSatelliteDatagram(datagram, isEmergency));
         } else {
-            loge("sendSatelliteDatagram: mLocalListener is null");
+            loge("sendDatagram: mLocalListener is null");
         }
     }
 
@@ -365,7 +373,7 @@
     @Override
     public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
             @NonNull IIntegerConsumer errorCallback, @NonNull IBooleanConsumer callback) {
-        logd("requestIsSatelliteCommunicationAllowedForCurrentLocation: mErrorCode=" + mErrorCode);
+        logd("requestIsCommunicationAllowedForCurrentLocation: mErrorCode=" + mErrorCode);
         if (mErrorCode != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
             runWithExecutor(() -> errorCallback.accept(mErrorCode));
             return;
@@ -389,6 +397,55 @@
         runWithExecutor(() -> callback.accept(SATELLITE_ALWAYS_VISIBLE));
     }
 
+    @Override
+    public void setSatellitePlmn(int simLogicalSlotIndex, List<String> carrierPlmnList,
+            List<String> allSatellitePlmnList, IIntegerConsumer resultCallback) {
+        logd("setSatellitePlmn: simLogicalSlotIndex=" + simLogicalSlotIndex + " , carrierPlmnList="
+                + carrierPlmnList + " , allSatellitePlmnList=" + allSatellitePlmnList);
+        if (mErrorCode != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+            runWithExecutor(() -> resultCallback.accept(mErrorCode));
+            return;
+        }
+        runWithExecutor(() -> resultCallback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS));
+
+        mCarrierPlmnList = carrierPlmnList;
+        mAllPlmnList = allSatellitePlmnList;
+
+        if (mLocalListener != null) {
+            runWithExecutor(() -> mLocalListener.onSetSatellitePlmn());
+        } else {
+            loge("setSatellitePlmn: mLocalListener is null");
+        }
+    }
+
+    @Override
+    public void setSatelliteEnabledForCarrier(int simLogicalSlotIndex, boolean satelliteEnabled,
+            IIntegerConsumer callback) {
+        logd("setSatelliteEnabledForCarrier: simLogicalSlotIndex=" + simLogicalSlotIndex
+                + ", satelliteEnabled=" + satelliteEnabled);
+        if (mErrorCode != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+            runWithExecutor(() -> callback.accept(mErrorCode));
+            return;
+        }
+
+        mIsSatelliteEnabledForCarrier = satelliteEnabled;
+        runWithExecutor(() -> callback.accept(SatelliteResult.SATELLITE_RESULT_SUCCESS));
+    }
+
+    @Override
+    public void requestIsSatelliteEnabledForCarrier(int simLogicalSlotIndex,
+            IIntegerConsumer resultCallback, IBooleanConsumer callback) {
+        logd("requestIsSatelliteEnabledForCarrier: simLogicalSlotIndex=" + simLogicalSlotIndex);
+        if (mErrorCode != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+            runWithExecutor(() -> resultCallback.accept(mErrorCode));
+            mIsRequestIsSatelliteEnabledForCarrier = false;
+            return;
+        }
+
+        runWithExecutor(() -> callback.accept(mIsSatelliteEnabledForCarrier));
+        mIsRequestIsSatelliteEnabledForCarrier = true;
+    }
+
     public void setLocalSatelliteListener(@NonNull ILocalSatelliteListener listener) {
         logd("setLocalSatelliteListener: listener=" + listener);
         mLocalListener = listener;
@@ -508,6 +565,22 @@
         }
     }
 
+    public List<String> getCarrierPlmnList() {
+        return mCarrierPlmnList;
+    }
+
+    public List<String> getAllSatellitePlmnList() {
+        return mAllPlmnList;
+    }
+
+    public boolean isSatelliteEnabledForCarrier() {
+        return mIsSatelliteEnabledForCarrier;
+    }
+
+    public boolean isRequestIsSatelliteEnabledForCarrier() {
+        return mIsRequestIsSatelliteEnabledForCarrier;
+    }
+
     /**
      * Log the message to the radio buffer with {@code DEBUG} priority.
      *
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 792c984..4f0679d 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
@@ -23,7 +23,6 @@
 import android.os.OutcomeReceiver;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.wrapper.NtnSignalStrengthCallbackWrapper;
 import android.telephony.satellite.wrapper.NtnSignalStrengthWrapper;
 import android.telephony.satellite.wrapper.SatelliteCapabilitiesCallbackWrapper;
@@ -37,11 +36,12 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-
 /**
  * Activity related to SatelliteControl APIs for satellite.
  */
@@ -55,6 +55,7 @@
     private NtnSignalStrengthCallback mNtnSignalStrengthCallback = null;
     private SatelliteCapabilitiesCallbackWrapper mSatelliteCapabilitiesCallback;
     private SubscriptionManager mSubscriptionManager;
+    private int mSubId;
 
     private ListView mLogListView;
 
@@ -63,6 +64,7 @@
         super.onCreate(savedInstanceState);
         mSatelliteManagerWrapper = SatelliteManagerWrapper.getInstance(this);
         mSubscriptionManager = getSystemService(SubscriptionManager.class);
+        mSubId = getActiveSubId();
 
         setContentView(R.layout.activity_TestSatelliteWrapper);
         findViewById(R.id.requestNtnSignalStrength)
@@ -74,9 +76,29 @@
         findViewById(R.id.isOnlyNonTerrestrialNetworkSubscription)
                 .setOnClickListener(this::isOnlyNonTerrestrialNetworkSubscription);
         findViewById(R.id.registerForSatelliteCapabilitiesChanged)
-                .setOnClickListener(this::registerForSatelliteCapabilitiesChanged);
+                .setOnClickListener(this::registerForCapabilitiesChanged);
         findViewById(R.id.unregisterForSatelliteCapabilitiesChanged)
-                .setOnClickListener(this::unregisterForSatelliteCapabilitiesChanged);
+                .setOnClickListener(this::unregisterForCapabilitiesChanged);
+        findViewById(R.id.isNonTerrestrialNetwork)
+                .setOnClickListener(this::isNonTerrestrialNetwork);
+        findViewById(R.id.getAvailableServices)
+                .setOnClickListener(this::getAvailableServices);
+        findViewById(R.id.isUsingNonTerrestrialNetwork)
+                .setOnClickListener(this::isUsingNonTerrestrialNetwork);
+        findViewById(R.id.requestAttachEnabledForCarrier_enable)
+                .setOnClickListener(this::requestAttachEnabledForCarrier_enable);
+        findViewById(R.id.requestAttachEnabledForCarrier_disable)
+                .setOnClickListener(this::requestAttachEnabledForCarrier_disable);
+        findViewById(R.id.requestIsAttachEnabledForCarrier)
+                .setOnClickListener(this::requestIsAttachEnabledForCarrier);
+        findViewById(R.id.addAttachRestrictionForCarrier)
+                .setOnClickListener(this::addAttachRestrictionForCarrier);
+        findViewById(R.id.removeAttachRestrictionForCarrier)
+                .setOnClickListener(this::removeAttachRestrictionForCarrier);
+        findViewById(R.id.getAttachRestrictionReasonsForCarrier)
+                .setOnClickListener(this::getAttachRestrictionReasonsForCarrier);
+        findViewById(R.id.getSatellitePlmnsForCarrier)
+                .setOnClickListener(this::getSatellitePlmnsForCarrier);
         findViewById(R.id.Back).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -109,21 +131,24 @@
 
         if (mSatelliteManagerWrapper != null) {
             if (mNtnSignalStrengthCallback != null) {
-                Log.d(TAG, "unregisterForNtnSignalStrengthChanged()");
+                logd("unregisterForNtnSignalStrengthChanged()");
                 mSatelliteManagerWrapper.unregisterForNtnSignalStrengthChanged(
                         mNtnSignalStrengthCallback);
             }
             if (mSatelliteCapabilitiesCallback != null) {
-                Log.d(TAG, "unregisterForSatelliteCapabilitiesChanged()");
-                mSatelliteManagerWrapper.unregisterForSatelliteCapabilitiesChanged(
+                logd("unregisterForCapabilitiesChanged()");
+                mSatelliteManagerWrapper.unregisterForCapabilitiesChanged(
                         mSatelliteCapabilitiesCallback);
             }
         }
+        mSubscriptionManager = null;
+        mSatelliteManagerWrapper = null;
+        mExecutor.shutdown();
     }
 
     private void requestNtnSignalStrength(View view) {
         addLogMessage("requestNtnSignalStrength");
-        Log.d(TAG, "requestNtnSignalStrength");
+        logd("requestNtnSignalStrength");
         OutcomeReceiver<NtnSignalStrengthWrapper,
                 SatelliteManagerWrapper.SatelliteExceptionWrapper> receiver =
                 new OutcomeReceiver<>() {
@@ -140,7 +165,7 @@
                         if (exception != null) {
                             String onError = "requestNtnSignalStrength exception: "
                                     + translateResultCodeToString(exception.getErrorCode());
-                            Log.d(TAG, onError);
+                            logd(onError);
                             addLogMessage(onError);
                         }
                     }
@@ -148,18 +173,18 @@
 
         try {
             mSatelliteManagerWrapper.requestNtnSignalStrength(mExecutor, receiver);
-        } catch (SecurityException | IllegalStateException ex) {
+        } catch (SecurityException ex) {
             String errorMessage = "requestNtnSignalStrength: " + ex.getMessage();
-            Log.d(TAG, errorMessage);
+            logd(errorMessage);
             addLogMessage(errorMessage);
         }
     }
 
     private void registerForNtnSignalStrengthChanged(View view) {
         addLogMessage("registerForNtnSignalStrengthChanged");
-        Log.d(TAG, "registerForNtnSignalStrengthChanged()");
+        logd("registerForNtnSignalStrengthChanged()");
         if (mNtnSignalStrengthCallback == null) {
-            Log.d(TAG, "create new NtnSignalStrengthCallback instance.");
+            logd("create new NtnSignalStrengthCallback instance.");
             mNtnSignalStrengthCallback = new NtnSignalStrengthCallback();
         }
 
@@ -167,24 +192,16 @@
             mSatelliteManagerWrapper.registerForNtnSignalStrengthChanged(mExecutor,
                     mNtnSignalStrengthCallback);
         } catch (Exception ex) {
-            String errorMessage;
-            if (ex instanceof SatelliteManager.SatelliteException) {
-                errorMessage =
-                        "registerForNtnSignalStrengthChanged: " + translateResultCodeToString(
-                                ((SatelliteManager.SatelliteException) ex).getErrorCode());
-            } else {
-                errorMessage = "registerForNtnSignalStrengthChanged: " + ex.getMessage();
-            }
-            Log.d(TAG, errorMessage);
+            String errorMessage = "registerForNtnSignalStrengthChanged: " + ex.getMessage();
+            logd(errorMessage);
             addLogMessage(errorMessage);
             mNtnSignalStrengthCallback = null;
-
         }
     }
 
     private void unregisterForNtnSignalStrengthChanged(View view) {
         addLogMessage("unregisterForNtnSignalStrengthChanged");
-        Log.d(TAG, "unregisterForNtnSignalStrengthChanged()");
+        logd("unregisterForNtnSignalStrengthChanged()");
         if (mNtnSignalStrengthCallback != null) {
             mSatelliteManagerWrapper.unregisterForNtnSignalStrengthChanged(
                     mNtnSignalStrengthCallback);
@@ -197,7 +214,7 @@
 
     private void isOnlyNonTerrestrialNetworkSubscription(View view) {
         addLogMessage("isOnlyNonTerrestrialNetworkSubscription");
-        Log.d(TAG, "isOnlyNonTerrestrialNetworkSubscription()");
+        logd("isOnlyNonTerrestrialNetworkSubscription()");
         List<SubscriptionInfo> infoList = mSubscriptionManager.getAvailableSubscriptionInfoList();
         List<Integer> subIdList = infoList.stream()
                 .map(SubscriptionInfo::getSubscriptionId)
@@ -222,34 +239,34 @@
         }
     }
 
-    private void registerForSatelliteCapabilitiesChanged(View view) {
-        addLogMessage("registerForSatelliteCapabilitiesChanged");
-        Log.d(TAG, "registerForSatelliteCapabilitiesChanged()");
+    private void registerForCapabilitiesChanged(View view) {
+        addLogMessage("registerForCapabilitiesChanged");
+        logd("registerForCapabilitiesChanged()");
         if (mSatelliteCapabilitiesCallback == null) {
             mSatelliteCapabilitiesCallback =
                     SatelliteCapabilities -> {
                         String message = "Received SatelliteCapabillities : "
                                 + SatelliteCapabilities;
-                        Log.d(TAG, message);
+                        logd(message);
                         runOnUiThread(() -> addLogMessage(message));
                     };
         }
 
-        int result = mSatelliteManagerWrapper.registerForSatelliteCapabilitiesChanged(mExecutor,
+        int result = mSatelliteManagerWrapper.registerForCapabilitiesChanged(mExecutor,
                 mSatelliteCapabilitiesCallback);
         if (result != SatelliteManagerWrapper.SATELLITE_RESULT_SUCCESS) {
             String onError = translateResultCodeToString(result);
-            Log.d(TAG, onError);
+            logd(onError);
             addLogMessage(onError);
             mSatelliteCapabilitiesCallback = null;
         }
     }
 
-    private void unregisterForSatelliteCapabilitiesChanged(View view) {
-        addLogMessage("unregisterForSatelliteCapabilitiesChanged");
-        Log.d(TAG, "unregisterForSatelliteCapabilitiesChanged()");
+    private void unregisterForCapabilitiesChanged(View view) {
+        addLogMessage("unregisterForCapabilitiesChanged");
+        logd("unregisterForCapabilitiesChanged()");
         if (mSatelliteCapabilitiesCallback != null) {
-            mSatelliteManagerWrapper.unregisterForSatelliteCapabilitiesChanged(
+            mSatelliteManagerWrapper.unregisterForCapabilitiesChanged(
                     mSatelliteCapabilitiesCallback);
             mSatelliteCapabilitiesCallback = null;
             addLogMessage("mSatelliteCapabilitiesCallback was unregistered");
@@ -263,11 +280,238 @@
         public void onNtnSignalStrengthChanged(
                 @NonNull NtnSignalStrengthWrapper ntnSignalStrength) {
             String message = "Received NTN SignalStrength : " + ntnSignalStrength.getLevel();
-            Log.d(TAG, message);
+            logd(message);
             runOnUiThread(() -> addLogMessage(message));
         }
     }
 
+    private void isNonTerrestrialNetwork(View view) {
+        boolean isNonTerrestrialNetwork = mSatelliteManagerWrapper.isNonTerrestrialNetwork(mSubId);
+        addLogMessage("isNonTerrestrialNetwork=" + isNonTerrestrialNetwork);
+        logd("isNonTerrestrialNetwork=" + isNonTerrestrialNetwork);
+    }
+
+    private void getAvailableServices(View view) {
+        List<Integer> as = mSatelliteManagerWrapper.getAvailableServices(mSubId);
+        String availableServices = as.stream().map(Object::toString).collect(
+                Collectors.joining(", "));
+        addLogMessage("getAvailableServices=" + availableServices);
+        logd("getAvailableServices=" + availableServices);
+    }
+
+    private void isUsingNonTerrestrialNetwork(View view) {
+        boolean isUsingNonTerrestrialNetwork =
+                mSatelliteManagerWrapper.isUsingNonTerrestrialNetwork(mSubId);
+        addLogMessage("isUsingNonTerrestrialNetwork=" + isUsingNonTerrestrialNetwork);
+        logd("isUsingNonTerrestrialNetwork=" + isUsingNonTerrestrialNetwork);
+    }
+
+    private void requestAttachEnabledForCarrier_enable(View view) {
+        addLogMessage("requestAttachEnabledForCarrier");
+        logd("requestAttachEnabledForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("requestAttachEnabledForCarrier: Subscription ID is invalid");
+            logd("requestAttachEnabledForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        Consumer<Integer> callback = result -> {
+            runOnUiThread(() -> addLogMessage("requestAttachEnabledForCarrier result: " + result));
+            logd("requestAttachEnabledForCarrier result: " + result);
+        };
+
+        try {
+            mSatelliteManagerWrapper.requestAttachEnabledForCarrier(mSubId, true, mExecutor,
+                    callback);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "requestAttachEnabledForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void requestAttachEnabledForCarrier_disable(View view) {
+        addLogMessage("requestAttachEnabledForCarrier");
+        logd("requestAttachEnabledForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("requestAttachEnabledForCarrier: Subscription ID is invalid");
+            logd("requestAttachEnabledForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        Consumer<Integer> callback = result -> {
+            runOnUiThread(() -> addLogMessage("requestAttachEnabledForCarrier result: " + result));
+            logd("requestAttachEnabledForCarrier result: " + result);
+        };
+
+        try {
+            mSatelliteManagerWrapper.requestAttachEnabledForCarrier(mSubId, false, mExecutor,
+                    callback);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "requestAttachEnabledForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void requestIsAttachEnabledForCarrier(View view) {
+        logd("requestIsAttachEnabledForCarrier");
+        addLogMessage("requestIsAttachEnabledForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("requestIsAttachEnabledForCarrier: Subscription ID is invalid");
+            logd("requestIsAttachEnabledForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        OutcomeReceiver<Boolean,
+                SatelliteManagerWrapper.SatelliteExceptionWrapper> receiver =
+                new OutcomeReceiver<>() {
+                    @Override
+                    public void onResult(Boolean result) {
+                        logd("requestIsAttachEnabledForCarrier: onResult=" + result);
+                        addLogMessage("requestIsAttachEnabledForCarrier: onResult=" + result);
+                    }
+
+                    @Override
+                    public void onError(
+                            SatelliteManagerWrapper.SatelliteExceptionWrapper exception) {
+                        if (exception != null) {
+                            String onError = "requestIsAttachEnabledForCarrier exception: "
+                                    + translateResultCodeToString(exception.getErrorCode());
+                            logd(onError);
+                            addLogMessage(onError);
+                        }
+                    }
+                };
+
+        try {
+            mSatelliteManagerWrapper.requestIsAttachEnabledForCarrier(mSubId, mExecutor, receiver);
+        } catch (SecurityException | IllegalStateException | IllegalArgumentException ex) {
+            String errorMessage = "requestIsAttachEnabledForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void addAttachRestrictionForCarrier(View view) {
+        addLogMessage("addAttachRestrictionForCarrier");
+        logd("addAttachRestrictionForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("addAttachRestrictionForCarrier: Subscription ID is invalid");
+            logd("addAttachRestrictionForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        int reason = SatelliteManagerWrapper.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+
+        Consumer<Integer> callback = result -> {
+            runOnUiThread(() -> addLogMessage("addAttachRestrictionForCarrier result: " + result));
+            logd("addAttachRestrictionForCarrier result: " + result);
+        };
+
+        try {
+            mSatelliteManagerWrapper.addAttachRestrictionForCarrier(mSubId, reason, mExecutor,
+                    callback);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "addAttachRestrictionForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void removeAttachRestrictionForCarrier(View view) {
+        addLogMessage("removeAttachRestrictionForCarrier");
+        logd("removeAttachRestrictionForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("removeAttachRestrictionForCarrier: Subscription ID is invalid");
+            logd("removeAttachRestrictionForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        int reason = SatelliteManagerWrapper.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
+
+        Consumer<Integer> callback = result -> {
+            runOnUiThread(
+                    () -> addLogMessage("removeAttachRestrictionForCarrier result: " + result));
+            logd("removeAttachRestrictionForCarrier result: " + result);
+        };
+
+        try {
+            mSatelliteManagerWrapper.removeAttachRestrictionForCarrier(mSubId, reason, mExecutor,
+                    callback);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "removeAttachRestrictionForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void getAttachRestrictionReasonsForCarrier(View view) {
+        addLogMessage("getAttachRestrictionReasonsForCarrier");
+        logd("getAttachRestrictionReasonsForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("getAttachRestrictionReasonsForCarrier: Subscription ID is invalid");
+            logd("getAttachRestrictionReasonsForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        try {
+            Set<Integer> reasons = mSatelliteManagerWrapper.getAttachRestrictionReasonsForCarrier(
+                    mSubId);
+            String stringReasons = reasons.stream().map(Object::toString).collect(
+                    Collectors.joining(", "));
+            logd("getAttachRestrictionReasonsForCarrier=" + stringReasons);
+            addLogMessage("getAttachRestrictionReasonsForCarrier=" + stringReasons);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "getAttachRestrictionReasonsForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private void getSatellitePlmnsForCarrier(View view) {
+        addLogMessage("getSatellitePlmnsForCarrier");
+        logd("getSatellitePlmnsForCarrier");
+
+        if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            addLogMessage("getSatellitePlmnsForCarrier: Subscription ID is invalid");
+            logd("getSatellitePlmnsForCarrier: Subscription ID is invalid");
+            return;
+        }
+
+        try {
+            List<String> reasons = mSatelliteManagerWrapper.getSatellitePlmnsForCarrier(
+                    mSubId);
+            String stringReasons = reasons.stream().collect(Collectors.joining(", "));
+            logd("getSatellitePlmnsForCarrier=" + stringReasons);
+            addLogMessage("getSatellitePlmnsForCarrier=" + stringReasons);
+        } catch (SecurityException | IllegalArgumentException ex) {
+            String errorMessage = "getSatellitePlmnsForCarrier: " + ex.getMessage();
+            logd(errorMessage);
+            addLogMessage(errorMessage);
+        }
+    }
+
+    private int getActiveSubId() {
+        int subId;
+        List<SubscriptionInfo> subscriptionInfoList =
+                mSubscriptionManager.getActiveSubscriptionInfoList();
+
+        if (subscriptionInfoList != null && subscriptionInfoList.size() > 0) {
+            subId = subscriptionInfoList.get(0).getSubscriptionId();
+        } else {
+            subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+        logd("getActiveSubId() returns " + subId);
+        return subId;
+    }
+
     private String translateResultCodeToString(
             @SatelliteManagerWrapper.SatelliteResult int result) {
         switch (result) {
@@ -317,6 +561,8 @@
                 return "SATELLITE_RESULT_REQUEST_IN_PROGRESS";
             case SatelliteManagerWrapper.SATELLITE_RESULT_MODEM_BUSY:
                 return "SATELLITE_RESULT_MODEM_BUSY";
+            case SatelliteManagerWrapper.SATELLITE_RESULT_ILLEGAL_STATE:
+                return "SATELLITE_RESULT_ILLEGAL_STATE";
             default:
                 return "INVALID CODE: " + result;
         }
@@ -327,4 +573,10 @@
         mAdapter.notifyDataSetChanged();
         mLogListView.setSelection(mAdapter.getCount() - 1);
     }
+
+    private static void logd(String message) {
+        if (message != null) {
+            Log.d(TAG, message);
+        }
+    }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index 3015f76..6914839 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -50,10 +50,10 @@
         "testables",
         "platform-compat-test-rules",
         "flag-junit",
+        "telephony_flags_core_java_lib",
         "satellite-s2storage-rw",
         "satellite-s2storage-testutils",
         "s2-geometry-library-java",
-        "telephony-satellite",
     ],
 
     test_suites: [
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
index bd2e4f7..bda2313 100644
--- a/tests/src/com/android/phone/CarrierConfigLoaderTest.java
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -21,6 +21,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -30,6 +31,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
@@ -56,12 +58,17 @@
 
 import com.android.TelephonyTestBase;
 import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -76,10 +83,14 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class CarrierConfigLoaderTest extends TelephonyTestBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
+    private static final String TAG = CarrierConfigLoaderTest.class.getSimpleName();
     private static final int DEFAULT_PHONE_ID = 0;
     private static final int DEFAULT_SUB_ID = SubscriptionManager.getDefaultSubscriptionId();
     private static final String PLATFORM_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
+    private static final String PLATFORM_CARRIER_CONFIG_FEATURE = "com.android.carrierconfig";
     private static final long PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE = 1;
     private static final String CARRIER_CONFIG_EXAMPLE_KEY =
             CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT;
@@ -92,6 +103,7 @@
     @Mock SubscriptionManagerService mSubscriptionManagerService;
     @Mock SharedPreferences mSharedPreferences;
     @Mock TelephonyRegistryManager mTelephonyRegistryManager;
+    @Mock FeatureFlags mFeatureFlags;
 
     private TelephonyManager mTelephonyManager;
     private CarrierConfigLoader mCarrierConfigLoader;
@@ -118,6 +130,8 @@
         doReturn(Build.FINGERPRINT).when(mSharedPreferences).getString(eq("build_fingerprint"),
                 any());
         doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(new String[]{TAG}).when(mPackageManager).getPackagesForUid(anyInt());
+
         doReturn(mResources).when(mContext).getResources();
         doReturn(InstrumentationRegistry.getTargetContext().getFilesDir()).when(
                 mContext).getFilesDir();
@@ -141,7 +155,8 @@
         mHandlerThread.start();
 
         mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
-        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper());
+        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper(),
+                mFeatureFlags);
         mHandler = mCarrierConfigLoader.getHandler();
 
         // Clear all configs to have the same starting point.
@@ -412,6 +427,39 @@
         assertThat(dumpContent).doesNotContain("Permission Denial:");
     }
 
+    @Test
+    @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
+    public void testGetConfigForSubIdWithFeature_withTelephonyFeatureMapping() throws Exception {
+        doNothing().when(mContext).enforcePermission(
+                eq(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE),
+                anyInt(), anyInt(), anyString());
+
+        // Replace field to set SDK version of vendor partition to Android V
+        int vendorApiLevel = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        replaceInstance(CarrierConfigLoader.class, "mVendorApiLevel", mCarrierConfigLoader,
+                vendorApiLevel);
+
+        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        doReturn(false).when(mPackageManager).hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
+
+        // Not defined required feature, expect UnsupportedOperationException
+        assertThrows(UnsupportedOperationException.class,
+                () -> mCarrierConfigLoader.getConfigForSubIdWithFeature(DEFAULT_SUB_ID,
+                        PLATFORM_CARRIER_CONFIG_PACKAGE, PLATFORM_CARRIER_CONFIG_FEATURE));
+
+        doReturn(true).when(mPackageManager).hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
+
+        // Defined required feature, not expect UnsupportedOperationException
+        try {
+            mCarrierConfigLoader.getConfigForSubIdWithFeature(DEFAULT_SUB_ID,
+                    PLATFORM_CARRIER_CONFIG_PACKAGE, PLATFORM_CARRIER_CONFIG_FEATURE);
+        } catch (UnsupportedOperationException e) {
+            fail("not expected UnsupportedOperationException");
+        }
+    }
+
     private static PersistableBundle getTestConfig() {
         PersistableBundle config = new PersistableBundle();
         config.putInt(CARRIER_CONFIG_EXAMPLE_KEY, CARRIER_CONFIG_EXAMPLE_VALUE);
diff --git a/tests/src/com/android/phone/DiagnosticDataCollectorTest.java b/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
index e0d89bc..983d135 100644
--- a/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
+++ b/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.os.DropBoxManager;
+import android.os.SystemClock;
 import android.telephony.TelephonyManager;
 
 import org.junit.After;
@@ -94,30 +95,37 @@
 
     @Test
     public void testPersistForTelecomDumpsys() throws IOException, InterruptedException {
-        TelephonyManager.EmergencyCallDiagnosticParams dp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        dp.setTelecomDumpSysCollection(true);
-        mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_telecom");
+        TelephonyManager.EmergencyCallDiagnosticData.Builder callDiagnosticBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticData.Builder();
+        TelephonyManager.EmergencyCallDiagnosticData ecdData =
+                callDiagnosticBuilder.setTelecomDumpsysCollectionEnabled(true).build();
+        mDiagnosticDataCollector.persistEmergencyDianosticData(
+                mConfig, ecdData, "test_tag_telecom");
 
         verifyCmdAndDropboxTag(TELECOM_DUMPSYS_COMMAND, "test_tag_telecom", false);
     }
 
     @Test
     public void testPersistForTelephonyDumpsys() throws IOException, InterruptedException {
-        TelephonyManager.EmergencyCallDiagnosticParams dp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        dp.setTelephonyDumpSysCollection(true);
-        mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_telephony");
+        TelephonyManager.EmergencyCallDiagnosticData.Builder callDiagnosticBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticData.Builder();
+        TelephonyManager.EmergencyCallDiagnosticData ecdData =
+                callDiagnosticBuilder.setTelephonyDumpsysCollectionEnabled(true).build();
+        mDiagnosticDataCollector.persistEmergencyDianosticData(
+                mConfig, ecdData, "test_tag_telephony");
 
         verifyCmdAndDropboxTag(TELEPHONY_DUMPSYS_COMMAND, "test_tag_telephony", false);
     }
 
     @Test
     public void testPersistForLogcat() throws IOException, InterruptedException {
-        TelephonyManager.EmergencyCallDiagnosticParams dp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        dp.setLogcatCollection(true, System.currentTimeMillis());
-        mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_logcat");
+        TelephonyManager.EmergencyCallDiagnosticData.Builder callDiagnosticBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticData.Builder();
+        TelephonyManager.EmergencyCallDiagnosticData ecdData =
+                callDiagnosticBuilder.setLogcatCollectionStartTimeMillis(
+                        SystemClock.elapsedRealtime()).build();
+        mDiagnosticDataCollector.persistEmergencyDianosticData(
+                mConfig, ecdData, "test_tag_logcat");
 
         verifyCmdAndDropboxTag(LOGCAT_BINARY, "test_tag_logcat", true);
     }
diff --git a/tests/src/com/android/phone/ImsProvisioningControllerTest.java b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
index db83cca..e12be53 100644
--- a/tests/src/com/android/phone/ImsProvisioningControllerTest.java
+++ b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
@@ -68,10 +68,11 @@
 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.ims.FeatureConnector;
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
diff --git a/tests/src/com/android/phone/ImsProvisioningLoaderTest.java b/tests/src/com/android/phone/ImsProvisioningLoaderTest.java
index 61cab1d..207e454 100644
--- a/tests/src/com/android/phone/ImsProvisioningLoaderTest.java
+++ b/tests/src/com/android/phone/ImsProvisioningLoaderTest.java
@@ -28,10 +28,10 @@
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
index 2bd87be..c86502b 100644
--- a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
+++ b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
@@ -48,10 +48,11 @@
 import android.os.Looper;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyRegistryManager;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.TelephonyTestBase;
 import com.android.ims.FeatureConnector;
 import com.android.ims.ImsManager;
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index 150703d..04fce84 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -20,6 +20,7 @@
 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.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
@@ -31,9 +32,12 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.os.Build;
 import android.permission.flags.Flags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.RadioAccessFamily;
@@ -49,12 +53,17 @@
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.util.Locale;
 
 /**
@@ -62,9 +71,14 @@
  */
 @RunWith(AndroidJUnit4.class)
 public class PhoneInterfaceManagerTest extends TelephonyTestBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private PhoneInterfaceManager mPhoneInterfaceManager;
     private SharedPreferences mSharedPreferences;
     private IIntegerConsumer mIIntegerConsumer;
+    private static final String sDebugPackageName =
+            PhoneInterfaceManagerTest.class.getPackageName();
 
     @Mock
     PhoneGlobals mPhoneGlobals;
@@ -72,7 +86,8 @@
     Phone mPhone;
     @Mock
     FeatureFlags mFeatureFlags;
-
+    @Mock
+    PackageManager mPackageManager;
     @Mock
     private SubscriptionManagerService mSubscriptionManagerService;
 
@@ -82,6 +97,8 @@
     @UiThreadTest
     public void setUp() throws Exception {
         super.setUp();
+        doReturn(sDebugPackageName).when(mPhoneGlobals).getOpPackageName();
+
         // 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
@@ -92,7 +109,15 @@
         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
+        mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
+        doReturn(false).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        mPhoneInterfaceManager.setPackageManager(mPackageManager);
+        doReturn(true).when(mPackageManager).hasSystemFeature(anyString());
     }
 
     @Test
@@ -269,6 +294,108 @@
                 mPhoneInterfaceManager).getDefaultPhone();
     }
 
+    @Test
+    public void setNullCipherNotificationsEnabled_allReqsMet_successfullyEnabled() {
+        setModemSupportsNullCipherNotification(true);
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        assertFalse(mSharedPreferences.contains(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED));
+
+        mPhoneInterfaceManager.setNullCipherNotificationsEnabled(true);
+
+        assertTrue(
+                mSharedPreferences.getBoolean(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED, false));
+    }
+
+    @Test
+    public void setNullCipherNotificationsEnabled_allReqsMet_successfullyDisabled() {
+        setModemSupportsNullCipherNotification(true);
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        assertFalse(mSharedPreferences.contains(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED));
+
+        mPhoneInterfaceManager.setNullCipherNotificationsEnabled(false);
+
+        assertFalse(
+                mSharedPreferences.getBoolean(Phone.PREF_NULL_CIPHER_NOTIFICATIONS_ENABLED, true));
+    }
+
+    @Test
+    public void setNullCipherNotificationsEnabled_lackingNecessaryHal_throwsException() {
+        setModemSupportsNullCipherNotification(true);
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+        doReturn(102).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mPhoneInterfaceManager.setNullCipherNotificationsEnabled(true));
+    }
+
+    @Test
+    public void setNullCipherNotificationsEnabled_lackingModemSupport_throwsException() {
+        setModemSupportsNullCipherNotification(false);
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mPhoneInterfaceManager.setNullCipherNotificationsEnabled(true));
+    }
+
+    @Test
+    public void setNullCipherNotificationsEnabled_lackingPermissions_throwsException() {
+        setModemSupportsNullCipherNotification(true);
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        doThrow(SecurityException.class).when(mPhoneInterfaceManager).enforceModifyPermission();
+
+        assertThrows(SecurityException.class, () ->
+                mPhoneInterfaceManager.setNullCipherNotificationsEnabled(true));
+    }
+
+    @Test
+    public void isNullCipherNotificationsEnabled_allReqsMet_returnsTrue() {
+        setModemSupportsNullCipherNotification(true);
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        doNothing().when(mPhoneInterfaceManager).enforceReadPrivilegedPermission(anyString());
+        doReturn(true).when(mPhone).getNullCipherNotificationsPreferenceEnabled();
+
+        assertTrue(mPhoneInterfaceManager.isNullCipherNotificationsEnabled());
+    }
+
+    @Test
+    public void isNullCipherNotificationsEnabled_lackingNecessaryHal_throwsException() {
+        setModemSupportsNullCipherNotification(true);
+        doReturn(102).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        doNothing().when(mPhoneInterfaceManager).enforceReadPrivilegedPermission(anyString());
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                mPhoneInterfaceManager.isNullCipherNotificationsEnabled());
+    }
+
+    @Test
+    public void isNullCipherNotificationsEnabled_lackingModemSupport_throwsException() {
+        setModemSupportsNullCipherNotification(false);
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        doNothing().when(mPhoneInterfaceManager).enforceReadPrivilegedPermission(anyString());
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                mPhoneInterfaceManager.isNullCipherNotificationsEnabled());
+    }
+
+    @Test
+    public void isNullCipherNotificationsEnabled_lackingPermissions_throwsException() {
+        setModemSupportsNullCipherNotification(true);
+        doReturn(202).when(mPhoneInterfaceManager).getHalVersion(anyInt());
+        doThrow(SecurityException.class).when(
+                mPhoneInterfaceManager).enforceReadPrivilegedPermission(anyString());
+
+        assertThrows(SecurityException.class, () ->
+                mPhoneInterfaceManager.isNullCipherNotificationsEnabled());
+    }
+
+    private void setModemSupportsNullCipherNotification(boolean enable) {
+        doReturn(enable).when(mPhone).isNullCipherNotificationSupported();
+        doReturn(mPhone).when(mPhoneInterfaceManager).getDefaultPhone();
+    }
+
     /**
      * Verify getCarrierRestrictionStatus throws exception for invalid caller package name.
      */
@@ -359,4 +486,62 @@
         }
         assertEquals("Expected error to be empty, was " + error, error, "");
     }
+
+    @Test
+    @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
+    public void testWithTelephonyFeatureAndCompatChanges() throws Exception {
+        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+        try {
+            // FEATURE_TELEPHONY_CALLING
+            mPhoneInterfaceManager.handlePinMmiForSubscriber(1, "123456789");
+
+            // FEATURE_TELEPHONY_RADIO_ACCESS
+            mPhoneInterfaceManager.toggleRadioOnOffForSubscriber(1);
+        } catch (Exception e) {
+            fail("Not expect exception " + e.getMessage());
+        }
+    }
+
+    @Test
+    @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
+    public void testWithoutTelephonyFeatureAndCompatChanges() throws Exception {
+        // Replace field to set SDK version of vendor partition to Android V
+        int vendorApiLevel = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        replaceInstance(PhoneInterfaceManager.class, "mVendorApiLevel", mPhoneInterfaceManager,
+                vendorApiLevel);
+
+        // telephony features is not defined, expect UnsupportedOperationException.
+        doReturn(false).when(mPackageManager).hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_CALLING);
+        doReturn(false).when(mPackageManager).hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS);
+        mPhoneInterfaceManager.setPackageManager(mPackageManager);
+        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
+        mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
+        doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
+
+        assertThrows(UnsupportedOperationException.class,
+                () -> mPhoneInterfaceManager.handlePinMmiForSubscriber(1, "123456789"));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mPhoneInterfaceManager.toggleRadioOnOffForSubscriber(1));
+    }
+
+    @Test
+    public void testGetCurrentPackageNameWithNoKnownPackage() throws Exception {
+        Field field = PhoneInterfaceManager.class.getDeclaredField("mApp");
+        field.setAccessible(true);
+        Field modifiersField = Field.class.getDeclaredField("accessFlags");
+        modifiersField.setAccessible(true);
+        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+        field.set(mPhoneInterfaceManager, mPhoneGlobals);
+
+        doReturn(mPackageManager).when(mPhoneGlobals).getPackageManager();
+        doReturn(null).when(mPackageManager).getPackagesForUid(anyInt());
+
+        String packageName = mPhoneInterfaceManager.getCurrentPackageName();
+        assertEquals(null, packageName);
+    }
 }
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index 57f9f6b..fe13d56 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -61,10 +61,11 @@
 import android.telephony.ims.aidl.IRcsConfigCallback;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.ims.FeatureConnector;
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.telephony.ITelephony;
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
index 4bbde79..1d2ca74 100644
--- a/tests/src/com/android/phone/ServiceStateProviderTest.java
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -64,9 +64,9 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import libcore.junit.util.compat.CoreCompatChangeRule;
 
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
index 84c233a..16a256d 100644
--- a/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
+++ b/tests/src/com/android/phone/satellite/accesscontrol/S2RangeSatelliteOnDeviceAccessControllerTest.java
@@ -80,13 +80,14 @@
         SatelliteOnDeviceAccessController accessController = null;
         try {
             accessController = SatelliteOnDeviceAccessController.create(mFile);
+            int s2Level = accessController.getS2Level();
 
             // Verify an edge cell of range 1 not in the output file
             S2CellId s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 999));
             S2LatLng s2LatLng = s2CellId.toLatLng();
             SatelliteOnDeviceAccessController.LocationToken locationToken =
-                    accessController.createLocationTokenForLatLng(
-                            s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                    SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                            s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
             boolean isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
@@ -96,8 +97,8 @@
                 s2LatLng = s2CellId.toLatLng();
 
                 // Lookup using location token
-                locationToken = accessController.createLocationTokenForLatLng(
-                                s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                                s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
                 isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
                 assertTrue(isAllowed == isAllowedList);
             }
@@ -105,8 +106,8 @@
             // Verify the middle cell not in the output file
             s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 2000));
             s2LatLng = s2CellId.toLatLng();
-            locationToken = accessController.createLocationTokenForLatLng(
-                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
@@ -114,8 +115,8 @@
             for (int suffix = 2001; suffix < 3000; suffix++) {
                 s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, suffix));
                 s2LatLng = s2CellId.toLatLng();
-                locationToken = accessController.createLocationTokenForLatLng(
-                        s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                        s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
                 isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
                 assertTrue(isAllowed == isAllowedList);
             }
@@ -123,16 +124,16 @@
             // Verify an edge cell of range 2 not in the output file
             s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1000, 3000));
             s2LatLng = s2CellId.toLatLng();
-            locationToken = accessController.createLocationTokenForLatLng(
-                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
             // Verify an edge cell of range 3 not in the output file
             s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, 999));
             s2LatLng = s2CellId.toLatLng();
-            locationToken = accessController.createLocationTokenForLatLng(
-                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
 
@@ -140,8 +141,8 @@
             for (int suffix = 1000; suffix < 2000; suffix++) {
                 s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, suffix));
                 s2LatLng = s2CellId.toLatLng();
-                locationToken = accessController.createLocationTokenForLatLng(
-                        s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+                locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                        s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
                 isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
                 assertTrue(isAllowed == isAllowedList);
             }
@@ -149,8 +150,8 @@
             // Verify an edge cell of range 3 not in the output file
             s2CellId = new S2CellId(TestUtils.createCellId(fileFormat, 1, 1001, 2000));
             s2LatLng = s2CellId.toLatLng();
-            locationToken = accessController.createLocationTokenForLatLng(
-                    s2LatLng.latDegrees(), s2LatLng.lngDegrees());
+            locationToken = SatelliteOnDeviceAccessController.createLocationTokenForLatLng(
+                    s2LatLng.latDegrees(), s2LatLng.lngDegrees(), s2Level);
             isAllowed = accessController.isSatCommunicationAllowedAtLocation(locationToken);
             assertTrue(isAllowed != isAllowedList);
         } catch (Exception ex) {
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
new file mode 100644
index 0000000..e6f70aa
--- /dev/null
+++ b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.satellite.accesscontrol;
+
+import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_ERROR;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.telecom.TelecomManager;
+import android.testing.TestableLooper;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyCountryDetector;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.SatelliteConfig;
+import com.android.internal.telephony.satellite.SatelliteConfigParser;
+import com.android.internal.telephony.satellite.SatelliteController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/** Unit test for {@link SatelliteAccessController} */
+@RunWith(AndroidJUnit4.class)
+public class SatelliteAccessControllerTest {
+    private static final String TAG = "SatelliteAccessControllerTest";
+    private static final String[] TEST_SATELLITE_COUNTRY_CODES = {"US", "CA", "UK"};
+    private static final String TEST_SATELLITE_S2_FILE = "sat_s2_file.dat";
+    private static final boolean TEST_SATELLITE_ALLOW = true;
+    private static final int TEST_LOCATION_FRESH_DURATION_SECONDS = 10;
+    private static final long TEST_LOCATION_FRESH_DURATION_NANOS =
+            TimeUnit.SECONDS.toNanos(TEST_LOCATION_FRESH_DURATION_SECONDS);
+    private static final long TIMEOUT = 500;
+    private static final List<String> EMPTY_STRING_LIST = new ArrayList<>();
+    private static final List<String> LOCATION_PROVIDERS =
+            listOf(LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER);
+    private static final int SUB_ID = 0;
+
+    @Mock
+    private LocationManager mMockLocationManager;
+    @Mock
+    private TelecomManager mMockTelecomManager;
+    @Mock
+    private TelephonyCountryDetector mMockCountryDetector;
+    @Mock
+    private SatelliteController mMockSatelliteController;
+    @Mock
+    private Context mMockContext;
+    @Mock private Phone mMockPhone;
+    @Mock private Phone mMockPhone2;
+    @Mock private FeatureFlags mMockFeatureFlags;
+    @Mock private Resources mMockResources;
+    @Mock private SatelliteOnDeviceAccessController mMockSatelliteOnDeviceAccessController;
+    @Mock Location mMockLocation0;
+    @Mock Location mMockLocation1;
+    @Mock File mMockSatS2File;
+
+    private Looper mLooper;
+    private TestableLooper mTestableLooper;
+    private Phone[] mPhones;
+    private TestSatelliteAccessController mSatelliteAccessControllerUT;
+    @Captor
+    private ArgumentCaptor<CancellationSignal> mLocationRequestCancellationSignalCaptor;
+    @Captor
+    private ArgumentCaptor<Consumer<Location>> mLocationRequestConsumerCaptor;
+    @Captor
+    private ArgumentCaptor<ResultReceiver> mResultReceiverFromSatelliteControllerCaptor;
+    private boolean mQueriedSatelliteAllowed = false;
+    private int mQueriedSatelliteAllowedResultCode = SATELLITE_RESULT_SUCCESS;
+    private Semaphore mSatelliteAllowedSemaphore = new Semaphore(0);
+    private ResultReceiver mSatelliteAllowedReceiver = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mQueriedSatelliteAllowedResultCode = resultCode;
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                    mQueriedSatelliteAllowed = resultData.getBoolean(
+                            KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                } else {
+                    logd("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                    mQueriedSatelliteAllowed = false;
+                }
+            } else {
+                logd("mSatelliteAllowedReceiver: resultCode=" + resultCode);
+                mQueriedSatelliteAllowed = false;
+            }
+            try {
+                mSatelliteAllowedSemaphore.release();
+            } catch (Exception ex) {
+                fail("mSatelliteAllowedReceiver: Got exception in releasing semaphore, ex=" + ex);
+            }
+        }
+    };
+
+    private boolean mQueriedSatelliteAllowed2 = false;
+    private int mQueriedSatelliteAllowedResultCode2 = SATELLITE_RESULT_SUCCESS;
+    private Semaphore mSatelliteAllowedSemaphore2 = new Semaphore(0);
+    private ResultReceiver mSatelliteAllowedReceiver2 = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mQueriedSatelliteAllowedResultCode2 = resultCode;
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                    mQueriedSatelliteAllowed2 = resultData.getBoolean(
+                            KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                } else {
+                    logd("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                    mQueriedSatelliteAllowed2 = false;
+                }
+            } else {
+                logd("mSatelliteAllowedReceiver2: resultCode=" + resultCode);
+                mQueriedSatelliteAllowed2 = false;
+            }
+            try {
+                mSatelliteAllowedSemaphore2.release();
+            } catch (Exception ex) {
+                fail("mSatelliteAllowedReceiver2: Got exception in releasing semaphore, ex=" + ex);
+            }
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        logd("setUp");
+        MockitoAnnotations.initMocks(this);
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        HandlerThread handlerThread = new HandlerThread("SatelliteAccessControllerTest");
+        handlerThread.start();
+        mLooper = handlerThread.getLooper();
+        mTestableLooper = new TestableLooper(mLooper);
+        when(mMockContext.getSystemServiceName(LocationManager.class)).thenReturn(
+                Context.LOCATION_SERVICE);
+        when(mMockContext.getSystemServiceName(TelecomManager.class)).thenReturn(
+                Context.TELECOM_SERVICE);
+        when(mMockContext.getSystemService(LocationManager.class)).thenReturn(
+                mMockLocationManager);
+        when(mMockContext.getSystemService(TelecomManager.class)).thenReturn(
+                mMockTelecomManager);
+        mPhones = new Phone[] {mMockPhone, mMockPhone2};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        replaceInstance(SatelliteController.class, "sInstance", null,
+                mMockSatelliteController);
+        replaceInstance(TelephonyCountryDetector.class, "sInstance", null,
+                mMockCountryDetector);
+        when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockResources.getStringArray(
+                com.android.internal.R.array.config_oem_enabled_satellite_country_codes))
+                .thenReturn(TEST_SATELLITE_COUNTRY_CODES);
+        when(mMockResources.getBoolean(
+                com.android.internal.R.bool.config_oem_enabled_satellite_access_allow))
+                .thenReturn(TEST_SATELLITE_ALLOW);
+        when(mMockResources.getString(
+                com.android.internal.R.string.config_oem_enabled_satellite_s2cell_file))
+                .thenReturn(TEST_SATELLITE_S2_FILE);
+        when(mMockResources.getInteger(com.android.internal.R.integer
+                .config_oem_enabled_satellite_location_fresh_duration))
+                .thenReturn(TEST_LOCATION_FRESH_DURATION_SECONDS);
+        doNothing().when(mMockSatelliteController)
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                        anyInt(), any(ResultReceiver.class));
+
+        when(mMockLocationManager.getProviders(true)).thenReturn(LOCATION_PROVIDERS);
+        when(mMockLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER))
+                .thenReturn(mMockLocation0);
+        when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
+                .thenReturn(mMockLocation1);
+        when(mMockLocation0.getLatitude()).thenReturn(0.0);
+        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);
+
+        mSatelliteAccessControllerUT = new TestSatelliteAccessController(mMockContext,
+                mMockFeatureFlags, mLooper, mMockLocationManager, mMockTelecomManager,
+                mMockSatelliteOnDeviceAccessController, mMockSatS2File);
+        mTestableLooper.processAllMessages();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        logd("tearDown");
+        if (mTestableLooper != null) {
+            mTestableLooper.destroy();
+            mTestableLooper = null;
+        }
+
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    public void testGetInstance() {
+        SatelliteAccessController inst1 =
+                SatelliteAccessController.getOrCreateInstance(mMockContext, mMockFeatureFlags);
+        SatelliteAccessController inst2 =
+                SatelliteAccessController.getOrCreateInstance(mMockContext, mMockFeatureFlags);
+        assertEquals(inst1, inst2);
+    }
+
+    @Test
+    public void testRequestIsSatelliteCommunicationAllowedForCurrentLocation() throws Exception {
+        // OEM-enabled satellite is not supported
+        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, mQueriedSatelliteAllowedResultCode);
+
+        // OEM-enabled satellite is supported, but SatelliteController returns error for the query
+        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+
+        clearInvocations(mMockSatelliteController);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver2);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController, never())
+                .requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                        anyInt(), any(ResultReceiver.class));
+
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_ERROR, null);
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore2, 1));
+        assertEquals(SATELLITE_RESULT_ERROR, mQueriedSatelliteAllowedResultCode);
+        assertEquals(SATELLITE_RESULT_ERROR, mQueriedSatelliteAllowedResultCode2);
+        assertFalse(mQueriedSatelliteAllowed);
+        assertFalse(mQueriedSatelliteAllowed2);
+
+        // SatelliteController returns success result but the result bundle does not have
+        // KEY_SATELLITE_COMMUNICATION_ALLOWED
+        clearAllInvocations();
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, null);
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertFalse(mQueriedSatelliteAllowed);
+
+        // SatelliteController returns disallowed result
+        clearAllInvocations();
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, false);
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertFalse(mQueriedSatelliteAllowed);
+
+        // SatelliteController returns allowed result. Network country codes are available, but one
+        // country code is not in the allowed list
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(listOf("US", "IN"));
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertFalse(mQueriedSatelliteAllowed);
+
+        // SatelliteController returns allowed result. Network country codes are available, and all
+        // country codes are in the allowed list
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(listOf("US", "CA"));
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertTrue(mQueriedSatelliteAllowed);
+
+        // SatelliteController returns allowed result. Network country codes are not available.
+        // TelecomManager.isInEmergencyCall() returns true. On-device access controller will be
+        // used. Last known location is available and fresh.
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST);
+        when(mMockTelecomManager.isInEmergencyCall()).thenReturn(true);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
+        when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(2L);
+        when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        assertTrue(
+                mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted());
+        verify(mMockSatelliteOnDeviceAccessController).isSatCommunicationAllowedAtLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class));
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertTrue(mQueriedSatelliteAllowed);
+
+        // Move time forward and verify resources are cleaned up
+        clearAllInvocations();
+        mTestableLooper.moveTimeForward(mSatelliteAccessControllerUT
+                .getKeepOnDeviceAccessControllerResourcesTimeoutMillis());
+        mTestableLooper.processAllMessages();
+        assertFalse(
+                mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted());
+        assertTrue(mSatelliteAccessControllerUT.isSatelliteOnDeviceAccessControllerReset());
+        verify(mMockSatelliteOnDeviceAccessController).close();
+
+        // Restore SatelliteOnDeviceAccessController for next verification
+        mSatelliteAccessControllerUT.setSatelliteOnDeviceAccessController(
+                mMockSatelliteOnDeviceAccessController);
+
+        // SatelliteController returns allowed result. Network country codes are not available.
+        // TelecomManager.isInEmergencyCall() returns false. Phone0 is in ECM. On-device access
+        // controller will be used. Last known location is not fresh.
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST);
+        when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false);
+        when(mMockPhone.isInEcm()).thenReturn(true);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
+        when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L);
+        when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        assertFalse(
+                mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted());
+        verify(mMockLocationManager).getCurrentLocation(eq(LocationManager.GPS_PROVIDER),
+                any(LocationRequest.class), mLocationRequestCancellationSignalCaptor.capture(),
+                any(Executor.class), mLocationRequestConsumerCaptor.capture());
+        assertTrue(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted());
+        sendLocationRequestResult(mMockLocation0);
+        assertFalse(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted());
+        // The LocationToken should be already in the cache
+        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class));
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertTrue(mQueriedSatelliteAllowed);
+
+        // Timed out to wait for current location. No cached country codes.
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST);
+        when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false);
+        when(mMockPhone.isInEcm()).thenReturn(true);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
+        when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L);
+        when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L);
+        when(mMockCountryDetector.getCachedLocationCountryIsoInfo()).thenReturn(new Pair<>("", 0L));
+        when(mMockCountryDetector.getCachedNetworkCountryIsoInfo()).thenReturn(new HashMap<>());
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        assertFalse(
+                mSatelliteAccessControllerUT.isKeepOnDeviceAccessControllerResourcesTimerStarted());
+        verify(mMockLocationManager).getCurrentLocation(anyString(), any(LocationRequest.class),
+                any(CancellationSignal.class), any(Executor.class), any(Consumer.class));
+        assertTrue(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted());
+        // Timed out
+        mTestableLooper.moveTimeForward(
+                mSatelliteAccessControllerUT.getWaitForCurrentLocationTimeoutMillis());
+        mTestableLooper.processAllMessages();
+        assertFalse(mSatelliteAccessControllerUT.isWaitForCurrentLocationTimerStarted());
+        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class));
+        verifyCountryDetectorApisCalled();
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS,
+                mQueriedSatelliteAllowedResultCode);
+        assertFalse(mQueriedSatelliteAllowed);
+
+        // SatelliteController returns allowed result. Network country codes are not available.
+        // TelecomManager.isInEmergencyCall() returns false. No phone is in ECM. Last known location
+        // is not fresh. Cached country codes should be used for verifying satellite allow. No
+        // cached country codes are available.
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST);
+        when(mMockCountryDetector.getCachedLocationCountryIsoInfo()).thenReturn(new Pair<>("", 0L));
+        when(mMockCountryDetector.getCachedNetworkCountryIsoInfo()).thenReturn(new HashMap<>());
+        when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false);
+        when(mMockPhone.isInEcm()).thenReturn(false);
+        when(mMockPhone2.isInEcm()).thenReturn(false);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
+        when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L);
+        when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        verify(mMockLocationManager, never()).getCurrentLocation(anyString(),
+                any(LocationRequest.class), any(CancellationSignal.class), any(Executor.class),
+                any(Consumer.class));
+        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class));
+        verifyCountryDetectorApisCalled();
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertFalse(mQueriedSatelliteAllowed);
+
+        // SatelliteController returns allowed result. Network country codes are not available.
+        // TelecomManager.isInEmergencyCall() returns false. No phone is in ECM. Last known location
+        // is not fresh. Cached country codes should be used for verifying satellite allow. Cached
+        // country codes are available.
+        clearAllInvocations();
+        when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST);
+        when(mMockCountryDetector.getCachedLocationCountryIsoInfo())
+                .thenReturn(new Pair<>("US", 5L));
+        Map<String, Long> cachedNetworkCountryCodes = new HashMap<>();
+        cachedNetworkCountryCodes.put("UK", 1L);
+        cachedNetworkCountryCodes.put("US", 3L);
+        when(mMockCountryDetector.getCachedNetworkCountryIsoInfo())
+                .thenReturn(cachedNetworkCountryCodes);
+        when(mMockTelecomManager.isInEmergencyCall()).thenReturn(false);
+        when(mMockPhone.isInEcm()).thenReturn(false);
+        when(mMockPhone2.isInEcm()).thenReturn(false);
+        mSatelliteAccessControllerUT.elapsedRealtimeNanos = TEST_LOCATION_FRESH_DURATION_NANOS + 1;
+        when(mMockLocation0.getElapsedRealtimeNanos()).thenReturn(0L);
+        when(mMockLocation1.getElapsedRealtimeNanos()).thenReturn(0L);
+        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
+                SUB_ID, mSatelliteAllowedReceiver);
+        mTestableLooper.processAllMessages();
+        verify(mMockSatelliteController).requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                anyInt(), mResultReceiverFromSatelliteControllerCaptor.capture());
+        sendSatelliteAllowResultFromSatelliteController(SATELLITE_RESULT_SUCCESS, true);
+        verify(mMockLocationManager, never()).getCurrentLocation(anyString(),
+                any(LocationRequest.class), any(CancellationSignal.class), any(Executor.class),
+                any(Consumer.class));
+        verify(mMockSatelliteOnDeviceAccessController, never()).isSatCommunicationAllowedAtLocation(
+                any(SatelliteOnDeviceAccessController.LocationToken.class));
+        verifyCountryDetectorApisCalled();
+        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
+                mSatelliteAllowedSemaphore, 1));
+        assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedSatelliteAllowedResultCode);
+        assertTrue(mQueriedSatelliteAllowed);
+    }
+
+    @Test
+    public void testUpdateSatelliteConfigData() {
+        // Verify the case when the configParser is not exist.
+        SatelliteConfigParser spyConfigParserNull =
+                spy(new SatelliteConfigParser((byte[]) null));
+        doReturn(spyConfigParserNull).when(mMockSatelliteController).getSatelliteConfigParser();
+        mSatelliteAccessControllerUT.updateSatelliteConfigData(mMockContext);
+
+        assertNull(spyConfigParserNull.getConfig());
+
+        // Verify the case when the configParser is exist but empty.
+        SatelliteConfigParser spyConfigParserEmpty =
+                spy(new SatelliteConfigParser("test".getBytes()));
+        doReturn(spyConfigParserEmpty).when(mMockSatelliteController).getSatelliteConfigParser();
+        mSatelliteAccessControllerUT.updateSatelliteConfigData(mMockContext);
+
+        assertNull(spyConfigParserEmpty.getConfig());
+
+        // Verify the case when the configParser is exist and valid data
+        SatelliteConfig mockSatelliteConfig = mock(SatelliteConfig.class);
+        final String filePath = "/data/user_de/0/com.android.phone/app_satellite/s2_cell_file";
+        Path targetSatS2FilePath = Paths.get(filePath);
+        doReturn(false).when(mockSatelliteConfig).isFileExist(any());
+        doReturn(targetSatS2FilePath).when(mockSatelliteConfig)
+                .copySatS2FileToPhoneDirectory(any(), any());
+        doReturn(Arrays.asList("US")).when(mockSatelliteConfig).getDeviceSatelliteCountryCodes();
+        doReturn(false).when(mockSatelliteConfig).isSatelliteDataForAllowedRegion();
+        doReturn(targetSatS2FilePath).when(mockSatelliteConfig).getSatelliteS2CellFile(any());
+        doReturn(mockSatelliteConfig).when(mMockSatelliteController).getSatelliteConfig();
+
+        mSatelliteAccessControllerUT.updateSatelliteConfigData(mMockContext);
+        verify(mockSatelliteConfig, times(0)).getDeviceSatelliteCountryCodes();
+        verify(mockSatelliteConfig, times(0)).isSatelliteDataForAllowedRegion();
+        verify(mockSatelliteConfig, times(2)).getSatelliteS2CellFile(any());
+    }
+
+    private void clearAllInvocations() {
+        clearInvocations(mMockSatelliteController);
+        clearInvocations(mMockSatelliteOnDeviceAccessController);
+        clearInvocations(mMockLocationManager);
+        clearInvocations(mMockCountryDetector);
+    }
+
+    private void verifyCountryDetectorApisCalled() {
+        verify(mMockCountryDetector).getCurrentNetworkCountryIso();
+        verify(mMockCountryDetector).getCachedLocationCountryIsoInfo();
+        verify(mMockCountryDetector).getCachedLocationCountryIsoInfo();
+    }
+
+    private boolean waitForRequestIsSatelliteAllowedForCurrentLocationResult(Semaphore semaphore,
+            int expectedNumberOfEvents) {
+        for (int i = 0; i < expectedNumberOfEvents; i++) {
+            try {
+                if (!semaphore.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS)) {
+                    logd("Timeout to receive "
+                            + "requestIsCommunicationAllowedForCurrentLocation()"
+                            + " callback");
+                    return false;
+                }
+            } catch (Exception ex) {
+                logd("waitForRequestIsSatelliteSupportedResult: Got exception=" + ex);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void sendSatelliteAllowResultFromSatelliteController(
+            int resultCode, Boolean satelliteAllowed) {
+        Bundle bundle = null;
+        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+            bundle = new Bundle();
+            if (satelliteAllowed != null) {
+                bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, satelliteAllowed);
+            }
+        }
+        mResultReceiverFromSatelliteControllerCaptor.getValue().send(resultCode, bundle);
+        mTestableLooper.processAllMessages();
+    }
+
+    private void sendLocationRequestResult(Location location) {
+        mLocationRequestConsumerCaptor.getValue().accept(location);
+        mTestableLooper.processAllMessages();
+    }
+
+    @SafeVarargs
+    private static <E> List<E> listOf(E... values) {
+        return Arrays.asList(values);
+    }
+
+    private static void logd(String message) {
+        Log.d(TAG, message);
+    }
+
+    private static void replaceInstance(final Class c,
+            final String instanceName, final Object obj, final Object newValue) throws Exception {
+        Field field = c.getDeclaredField(instanceName);
+        field.setAccessible(true);
+        field.set(obj, newValue);
+    }
+
+    private static class TestSatelliteAccessController extends SatelliteAccessController {
+        public long elapsedRealtimeNanos = 0;
+
+        /**
+         * Create a SatelliteAccessController instance.
+         *
+         * @param context                           The context associated with the
+         *                                          {@link SatelliteAccessController} instance.
+         * @param featureFlags                      The FeatureFlags that are supported.
+         * @param looper                            The Looper to run the SatelliteAccessController
+         *                                          on.
+         * @param locationManager                   The LocationManager for querying current
+         *                                          location of the
+         *                                          device.
+         * @param satelliteOnDeviceAccessController The on-device satellite access controller
+         *                                          instance.
+         */
+        protected TestSatelliteAccessController(Context context, FeatureFlags featureFlags,
+                Looper looper, LocationManager locationManager, TelecomManager telecomManager,
+                SatelliteOnDeviceAccessController satelliteOnDeviceAccessController,
+                File s2CellFile) {
+            super(context, featureFlags, looper, locationManager, telecomManager,
+                    satelliteOnDeviceAccessController, s2CellFile);
+        }
+
+        @Override
+        protected long getElapsedRealtimeNanos() {
+            return elapsedRealtimeNanos;
+        }
+
+        public boolean isKeepOnDeviceAccessControllerResourcesTimerStarted() {
+            return hasMessages(EVENT_KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT);
+        }
+
+        public boolean isSatelliteOnDeviceAccessControllerReset() {
+            synchronized (mLock) {
+                return (mSatelliteOnDeviceAccessController == null);
+            }
+        }
+
+        public void setSatelliteOnDeviceAccessController(
+                @Nullable SatelliteOnDeviceAccessController accessController) {
+            synchronized (mLock) {
+                mSatelliteOnDeviceAccessController = accessController;
+            }
+        }
+
+        public long getKeepOnDeviceAccessControllerResourcesTimeoutMillis() {
+            return KEEP_ON_DEVICE_ACCESS_CONTROLLER_RESOURCES_TIMEOUT_MILLIS;
+        }
+
+        public long getWaitForCurrentLocationTimeoutMillis() {
+            return WAIT_FOR_CURRENT_LOCATION_TIMEOUT_MILLIS;
+        }
+
+        public boolean isWaitForCurrentLocationTimerStarted() {
+            return hasMessages(EVENT_WAIT_FOR_CURRENT_LOCATION_TIMEOUT);
+        }
+    }
+}
diff --git a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java
new file mode 100644
index 0000000..f096e0d
--- /dev/null
+++ b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+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 com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE;
+import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_PROVISIONING;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyVararg;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.libraries.entitlement.ServiceEntitlement;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SatelliteEntitlementApiTest {
+    private static final String TEST_URL = "https://test.url";
+    private static final List<String> TEST_PLMN_ALLOWED = Arrays.asList("31026", "302820");
+    @Mock
+    Context mContext;
+    @Mock
+    ServiceEntitlement mServiceEntitlement;
+    @Mock
+    CarrierConfigManager mCarrierConfigManager;
+    @Mock
+    TelephonyManager mTelephonyManager;
+    private PersistableBundle mCarrierConfigBundle;
+    private SatelliteEntitlementApi mSatelliteEntitlementAPI;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(Context.CARRIER_CONFIG_SERVICE).when(mContext).getSystemServiceName(
+                CarrierConfigManager.class);
+        doReturn(mCarrierConfigManager).when(mContext).getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        mCarrierConfigBundle = new PersistableBundle();
+        doReturn(mCarrierConfigBundle)
+                .when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());
+        doReturn(Context.TELEPHONY_SERVICE).when(mContext).getSystemServiceName(
+                TelephonyManager.class);
+        doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+
+        mSatelliteEntitlementAPI = new SatelliteEntitlementApi(mContext, mCarrierConfigBundle,
+                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+    }
+
+    @Test
+    public void testCheckEntitlementStatus() throws Exception {
+        mCarrierConfigBundle.putString(
+                CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
+                TEST_URL);
+        Field fieldServiceEntitlement = SatelliteEntitlementApi.class.getDeclaredField(
+                "mServiceEntitlement");
+        fieldServiceEntitlement.setAccessible(true);
+        fieldServiceEntitlement.set(mSatelliteEntitlementAPI, mServiceEntitlement);
+
+        // Get the EntitlementStatus to DISABLED
+        int expectedEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_DISABLED;
+        doReturn(getResponse(SATELLITE_ENTITLEMENT_STATUS_DISABLED))
+                .when(mServiceEntitlement)
+                .queryEntitlementStatus(eq(ServiceEntitlement.APP_SATELLITE_ENTITLEMENT), any());
+        SatelliteEntitlementResult result =
+                mSatelliteEntitlementAPI.checkEntitlementStatus();
+        assertNotNull(result);
+        assertEquals(expectedEntitlementStatus, result.getEntitlementStatus());
+        assertTrue(result.getAllowedPLMNList().size() == 0);
+
+        // Get the EntitlementStatus to ENABLED
+        expectedEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_ENABLED;
+        doReturn(getResponse(SATELLITE_ENTITLEMENT_STATUS_ENABLED))
+                .when(mServiceEntitlement)
+                .queryEntitlementStatus(eq(ServiceEntitlement.APP_SATELLITE_ENTITLEMENT), any());
+        result = mSatelliteEntitlementAPI.checkEntitlementStatus();
+        assertNotNull(result);
+        assertEquals(expectedEntitlementStatus, result.getEntitlementStatus());
+        assertEquals(TEST_PLMN_ALLOWED, result.getAllowedPLMNList());
+
+        // Get the EntitlementStatus to INCOMPATIBLE
+        expectedEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE;
+        doReturn(getResponse(SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE))
+                .when(mServiceEntitlement)
+                .queryEntitlementStatus(eq(ServiceEntitlement.APP_SATELLITE_ENTITLEMENT), any());
+        result = mSatelliteEntitlementAPI.checkEntitlementStatus();
+        assertNotNull(result);
+        assertEquals(expectedEntitlementStatus, result.getEntitlementStatus());
+        assertTrue(result.getAllowedPLMNList().size() == 0);
+
+        // Get the EntitlementStatus to PROVISIONING
+        expectedEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_PROVISIONING;
+        doReturn(getResponse(SATELLITE_ENTITLEMENT_STATUS_PROVISIONING))
+                .when(mServiceEntitlement)
+                .queryEntitlementStatus(eq(ServiceEntitlement.APP_SATELLITE_ENTITLEMENT), any());
+        result = mSatelliteEntitlementAPI.checkEntitlementStatus();
+        assertNotNull(result);
+        assertEquals(expectedEntitlementStatus, result.getEntitlementStatus());
+        assertTrue(result.getAllowedPLMNList().size() == 0);
+    }
+
+    private String getResponse(int entitlementStatus) {
+        return "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{"
+                + "\"EntitlementStatus\":\"" + entitlementStatus + "\""
+                + getPLMNListOrEmpty(entitlementStatus)
+                + "}}";
+    }
+
+    private String getPLMNListOrEmpty(int entitlementStatus) {
+        return entitlementStatus == SATELLITE_ENTITLEMENT_STATUS_ENABLED ? ","
+                + "\"PLMNAllowed\":[{\"PLMN\":\"31026\",\"DataPlanType\":\"unmetered\"},"
+                + "{\"PLMN\":\"302820\",\"DataPlanType\":\"metered\"}]" : "";
+    }
+}
diff --git a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java
new file mode 100644
index 0000000..18a0284
--- /dev/null
+++ b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementControllerTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_ENABLED;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyVararg;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+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.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.satellite.SatelliteController;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
+
+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;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class SatelliteEntitlementControllerTest extends TelephonyTestBase {
+    private static final String TAG = "SatelliteEntitlementControllerTest";
+    private static final int SUB_ID = 0;
+    private static final int SUB_ID_2 = 1;
+    private static final int[] ACTIVE_SUB_ID = {SUB_ID};
+    private static final int DEFAULT_QUERY_REFRESH_DAY = 30;
+    private static final List<String> PLMN_ALLOWED_LIST = Arrays.asList("31026", "302820");
+    @Mock
+    CarrierConfigManager mCarrierConfigManager;
+    @Mock
+    ConnectivityManager mConnectivityManager;
+    @Mock Network mNetwork;
+    @Mock TelephonyManager mTelephonyManager;
+    @Mock SubscriptionManagerService mMockSubscriptionManagerService;
+    @Mock SatelliteEntitlementApi mSatelliteEntitlementApi;
+    @Mock SatelliteEntitlementResult mSatelliteEntitlementResult;
+    @Mock SatelliteController mSatelliteController;
+    @Mock ExponentialBackoff mExponentialBackoff;
+    private PersistableBundle mCarrierConfigBundle;
+    private TestSatelliteEntitlementController mSatelliteEntitlementController;
+    private Handler mHandler;
+    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);
+
+        HandlerThread handlerThread = new HandlerThread("SatelliteEntitlementController");
+        handlerThread.start();
+        mHandler = new Handler(handlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+            }
+        };
+        mTestableLooper = new TestableLooper(mHandler.getLooper());
+        doReturn(Context.TELEPHONY_SERVICE).when(mContext).getSystemServiceName(
+                TelephonyManager.class);
+        doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+        doReturn(Context.CARRIER_CONFIG_SERVICE).when(mContext).getSystemServiceName(
+                CarrierConfigManager.class);
+        doReturn(mCarrierConfigManager).when(mContext).getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        doAnswer(invocation -> {
+            Executor executor = invocation.getArgument(0);
+            CarrierConfigManager.CarrierConfigChangeListener listener = invocation.getArgument(1);
+            mCarrierConfigChangedListenerList.add(new Pair<>(executor, listener));
+            return null;
+        }).when(mCarrierConfigManager).registerCarrierConfigChangeListener(
+                any(Executor.class),
+                any(CarrierConfigManager.CarrierConfigChangeListener.class));
+        mCarrierConfigBundle = new PersistableBundle();
+        mCarrierConfigBundle.putInt(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT,
+                DEFAULT_QUERY_REFRESH_DAY);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        doReturn(mCarrierConfigBundle)
+                .when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyVararg());
+        doReturn(Context.CONNECTIVITY_SERVICE).when(mContext).getSystemServiceName(
+                ConnectivityManager.class);
+        doReturn(mConnectivityManager).when(mContext).getSystemService(
+                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);
+        doReturn(mSatelliteEntitlementResult).when(
+                mSatelliteEntitlementApi).checkEntitlementStatus();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testIsQueryAvailable() throws Exception {
+        doReturn(ACTIVE_SUB_ID).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
+
+        // Verify don't start the query when KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL is false.
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi, never()).checkEntitlementStatus();
+        verify(mSatelliteController, never()).onSatelliteEntitlementStatusUpdated(anyInt(),
+                anyBoolean(), anyList(), any());
+
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        // Verify don't start the query when ExponentialBackoff is in progressed.
+        replaceInstance(SatelliteEntitlementController.class, "mExponentialBackoffPerSub",
+                mSatelliteEntitlementController, Map.of(SUB_ID, mExponentialBackoff));
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi, never()).checkEntitlementStatus();
+        verify(mSatelliteController, never()).onSatelliteEntitlementStatusUpdated(anyInt(),
+                anyBoolean(), anyList(), any());
+
+        replaceInstance(SatelliteEntitlementController.class, "mExponentialBackoffPerSub",
+                mSatelliteEntitlementController, new HashMap<>());
+        // Verify don't start the query when Internet is disconnected.
+        doReturn(ACTIVE_SUB_ID).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
+        setInternetConnected(false);
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi, never()).checkEntitlementStatus();
+        verify(mSatelliteController, never()).onSatelliteEntitlementStatusUpdated(anyInt(),
+                anyBoolean(), anyList(), any());
+
+        setInternetConnected(true);
+        // Verify don't start the query when last query refresh time is not expired.
+        setLastQueryTime(System.currentTimeMillis());
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi, never()).checkEntitlementStatus();
+        verify(mSatelliteController, never()).onSatelliteEntitlementStatusUpdated(anyInt(),
+                anyBoolean(), anyList(), any());
+
+        // Verify start the query when isQueryAvailable return true
+        setLastQueryTime(0L);
+        doReturn(mSatelliteEntitlementResult).when(
+                mSatelliteEntitlementApi).checkEntitlementStatus();
+        setSatelliteEntitlementResult(SATELLITE_ENTITLEMENT_STATUS_ENABLED, PLMN_ALLOWED_LIST);
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi).checkEntitlementStatus();
+        verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(anyInt(),
+                anyBoolean(), anyList(), any());
+    }
+
+    @Test
+    public void testCheckSatelliteEntitlementStatus() throws Exception {
+        setIsQueryAvailableTrue();
+        // Verify don't call the checkSatelliteEntitlementStatus when getActiveSubIdList is empty.
+        doReturn(new int[]{}).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi, never()).checkEntitlementStatus();
+        // Verify don't call the updateSatelliteEntitlementStatus.
+        verify(mSatelliteController, never()).onSatelliteEntitlementStatusUpdated(anyInt(),
+                anyBoolean(), anyList(), any());
+
+        // Verify call the checkSatelliteEntitlementStatus with invalid response.
+        setIsQueryAvailableTrue();
+        doReturn(mSatelliteEntitlementResult).when(
+                mSatelliteEntitlementApi).checkEntitlementStatus();
+        replaceInstance(SatelliteEntitlementController.class,
+                "mSatelliteEntitlementResultPerSub", mSatelliteEntitlementController,
+                new HashMap<>());
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi).checkEntitlementStatus();
+        // Verify call the updateSatelliteEntitlementStatus with satellite service is disabled
+        // and empty PLMNAllowed
+        verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(eq(SUB_ID),
+                eq(false), eq(new ArrayList<>()), any());
+
+        // Verify call the checkSatelliteEntitlementStatus with the subscribed result.
+        clearInvocationsForMock();
+        setIsQueryAvailableTrue();
+        doReturn(mSatelliteEntitlementResult).when(
+                mSatelliteEntitlementApi).checkEntitlementStatus();
+        setSatelliteEntitlementResult(SATELLITE_ENTITLEMENT_STATUS_ENABLED, PLMN_ALLOWED_LIST);
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi).checkEntitlementStatus();
+        // Verify call the updateSatelliteEntitlementStatus with satellite service is enable and
+        // availablePLMNAllowedList
+        verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(eq(SUB_ID), eq(true),
+                eq(PLMN_ALLOWED_LIST), any());
+
+        // Change subId and verify call the updateSatelliteEntitlementStatus with  satellite
+        // service is enable and availablePLMNAllowedList
+        clearInvocationsForMock();
+        doReturn(new int[]{SUB_ID_2}).when(mMockSubscriptionManagerService).getActiveSubIdList(
+                true);
+        mSatelliteEntitlementController.handleCmdStartQueryEntitlement();
+
+        verify(mSatelliteEntitlementApi).checkEntitlementStatus();
+        verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(eq(SUB_ID_2), eq(true),
+                eq(PLMN_ALLOWED_LIST), any());
+    }
+
+    @Test
+    public void testCheckSatelliteEntitlementStatusWhenInternetConnected() throws Exception {
+        Field fieldNetworkCallback = SatelliteEntitlementController.class.getDeclaredField(
+                "mNetworkCallback");
+        fieldNetworkCallback.setAccessible(true);
+        ConnectivityManager.NetworkCallback networkCallback =
+                (ConnectivityManager.NetworkCallback) fieldNetworkCallback.get(
+                        mSatelliteEntitlementController);
+        Network mockNetwork = mock(Network.class);
+
+        // Verify the called the checkSatelliteEntitlementStatus when Internet is connected.
+        setInternetConnected(true);
+        setLastQueryTime(0L);
+        setSatelliteEntitlementResult(SATELLITE_ENTITLEMENT_STATUS_ENABLED, PLMN_ALLOWED_LIST);
+
+        networkCallback.onAvailable(mockNetwork);
+        mTestableLooper.processAllMessages();
+
+        verify(mSatelliteEntitlementApi).checkEntitlementStatus();
+        // Verify call the updateSatelliteEntitlementStatus with satellite service is available.
+        verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(eq(SUB_ID), eq(true),
+                eq(PLMN_ALLOWED_LIST), any());
+    }
+
+    @Test
+    public void testCheckSatelliteEntitlementStatusWhenCarrierConfigChanged() throws Exception {
+        // Verify the called the checkSatelliteEntitlementStatus when CarrierConfigChanged
+        // occurred and Internet is connected.
+        setInternetConnected(true);
+        setLastQueryTime(0L);
+        setSatelliteEntitlementResult(SATELLITE_ENTITLEMENT_STATUS_ENABLED, PLMN_ALLOWED_LIST);
+        triggerCarrierConfigChanged();
+
+        verify(mSatelliteEntitlementApi).checkEntitlementStatus();
+        // Verify call the updateSatelliteEntitlementStatus with satellite service is available.
+        verify(mSatelliteController).onSatelliteEntitlementStatusUpdated(eq(SUB_ID), eq(true),
+                eq(PLMN_ALLOWED_LIST), any());
+    }
+
+    private void triggerCarrierConfigChanged() {
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        mTestableLooper.processAllMessages();
+    }
+
+    private void clearInvocationsForMock() {
+        clearInvocations(mSatelliteEntitlementApi);
+        clearInvocations(mSatelliteController);
+    }
+
+    private void setIsQueryAvailableTrue() throws Exception {
+        doReturn(ACTIVE_SUB_ID).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
+        mCarrierConfigBundle.putBoolean(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, true);
+        replaceInstance(SatelliteEntitlementController.class, "mRetryCountPerSub",
+                mSatelliteEntitlementController, new HashMap<>());
+        setInternetConnected(true);
+        setLastQueryTime(0L);
+        replaceInstance(SatelliteEntitlementController.class,
+                "mSatelliteEntitlementResultPerSub", mSatelliteEntitlementController,
+                new HashMap<>());
+    }
+
+    private void setInternetConnected(boolean connected) {
+        NetworkCapabilities networkCapabilities = new NetworkCapabilities.Builder().build();
+
+        if (connected) {
+            networkCapabilities = new NetworkCapabilities.Builder()
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                    .setTransportInfo(mock(WifiInfo.class))
+                    .build();
+        }
+        doReturn(networkCapabilities).when(mConnectivityManager).getNetworkCapabilities(mNetwork);
+    }
+
+    private void setSatelliteEntitlementResult(int entitlementStatus,
+            List<String> plmnAllowedList) {
+        doReturn(entitlementStatus).when(mSatelliteEntitlementResult).getEntitlementStatus();
+        doReturn(plmnAllowedList).when(mSatelliteEntitlementResult).getAllowedPLMNList();
+    }
+
+    private void setLastQueryTime(Long lastQueryTime) throws Exception {
+        Map<Integer, Long> lastQueryTimePerSub = new HashMap<>();
+        replaceInstance(SatelliteEntitlementController.class, "mLastQueryTimePerSub",
+                mSatelliteEntitlementController, lastQueryTimePerSub);
+        lastQueryTimePerSub.put(SUB_ID, lastQueryTime);
+    }
+
+    public static class TestSatelliteEntitlementController extends SatelliteEntitlementController {
+        private SatelliteEntitlementApi mInjectSatelliteEntitlementApi;
+
+        TestSatelliteEntitlementController(@NonNull Context context, @NonNull Looper looper,
+                SatelliteEntitlementApi api) {
+            super(context, looper);
+            mInjectSatelliteEntitlementApi = api;
+        }
+
+        @Override
+        public SatelliteEntitlementApi getSatelliteEntitlementApi(int subId) {
+            logd("getSatelliteEntitlementApi");
+            return mInjectSatelliteEntitlementApi;
+        }
+    }
+
+    private static void logd(String log) {
+        Log.d(TAG, log);
+    }
+}
diff --git a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java
new file mode 100644
index 0000000..45e2a71
--- /dev/null
+++ b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.phone.satellite.entitlement;
+
+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 com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE;
+import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_PROVISIONING;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.satellite.SatelliteNetworkInfo;
+import com.android.libraries.entitlement.ServiceEntitlement;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SatelliteEntitlementResponseTest {
+    private static final String TEST_OTHER_APP_ID = "ap201x";
+    private static final List<SatelliteNetworkInfo> TEST_PLMN_DATA_PLAN_TYPE_LIST = Arrays.asList(
+            new SatelliteNetworkInfo("31026", "unmetered"),
+            new SatelliteNetworkInfo("302820", "metered"));
+    private static final List<String> TEST_PLMN_BARRED_LIST = Arrays.asList("31017", "302020");
+    private static final String RESPONSE_WITHOUT_SATELLITE_APP_ID =
+            "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                    + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                    + TEST_OTHER_APP_ID + "\":{"
+                    + "\"EntitlementStatus\":\"" + SATELLITE_ENTITLEMENT_STATUS_ENABLED + "\"}}";
+    private static final String RESPONSE_WITHOUT_ENTITLEMENT_STATUS =
+            "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                    + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                    + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{}}";
+
+    @Test
+    public void testGetSatelliteEntitlementResponse() throws Exception {
+        // Received the body with satellite service enabled.
+        SatelliteEntitlementResponse response = new SatelliteEntitlementResponse(
+                getResponse(SATELLITE_ENTITLEMENT_STATUS_ENABLED));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(0).mPlmn,
+                response.getPlmnAllowed().get(0).mPlmn);
+        assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(0).mDataPlanType,
+                response.getPlmnAllowed().get(0).mDataPlanType);
+        assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(1).mPlmn,
+                response.getPlmnAllowed().get(1).mPlmn);
+        assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(1).mDataPlanType,
+                response.getPlmnAllowed().get(1).mDataPlanType);
+        assertEquals(TEST_PLMN_BARRED_LIST, response.getPlmnBarredList());
+
+        // Received the empty body.
+        response = new SatelliteEntitlementResponse("");
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_DISABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body without satellite app id.
+        response = new SatelliteEntitlementResponse(RESPONSE_WITHOUT_SATELLITE_APP_ID);
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_DISABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body without EntitlementStatus.
+        response = new SatelliteEntitlementResponse(RESPONSE_WITHOUT_ENTITLEMENT_STATUS);
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_DISABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body with an entitlementStatus value of DISABLED.
+        response = new SatelliteEntitlementResponse(
+                getResponse(SATELLITE_ENTITLEMENT_STATUS_DISABLED));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_DISABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body with an entitlementStatus value of INCOMPATIBLE.
+        response = new SatelliteEntitlementResponse(
+                getResponse(SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_INCOMPATIBLE, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body with an entitlementStatus value of PROVISIONING.
+        response = new SatelliteEntitlementResponse(
+                getResponse(SATELLITE_ENTITLEMENT_STATUS_PROVISIONING));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_PROVISIONING, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+    }
+
+    private String getResponse(int entitlementStatus) {
+        return "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{"
+                + "\"EntitlementStatus\":\"" + entitlementStatus + "\""
+                + getPLMNListOrEmpty(entitlementStatus)
+                + "}}";
+    }
+
+    private String getPLMNListOrEmpty(int entitlementStatus) {
+        return entitlementStatus == SATELLITE_ENTITLEMENT_STATUS_ENABLED ? ","
+                + "\"PLMNAllowed\":[{\"PLMN\":\"31026\",\"DataPlanType\":\"unmetered\"},"
+                + "{\"PLMN\":\"302820\",\"DataPlanType\":\"metered\"}],"
+                + "\"PLMNBarred\":[{\"PLMN\":\"31017\"},"
+                + "{\"PLMN\":\"302020\"}]" : "";
+    }
+}
diff --git a/tests/src/com/android/phone/security/SafetySourceReceiverTest.java b/tests/src/com/android/phone/security/SafetySourceReceiverTest.java
new file mode 100644
index 0000000..305e698
--- /dev/null
+++ b/tests/src/com/android/phone/security/SafetySourceReceiverTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.phone.security;
+
+import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
+import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class SafetySourceReceiverTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    Context mContext;
+
+    SafetySourceReceiver mSafetySourceReceiver;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        SafetySourceReceiver receiver = new SafetySourceReceiver();
+        mSafetySourceReceiver = spy(receiver);
+
+        when(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+    }
+
+    @Test
+    public void testOnReceive() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENFORCE_TELEPHONY_FEATURE_MAPPING_FOR_PUBLIC_APIS);
+        Phone mockPhone = mock(Phone.class);
+        when(mSafetySourceReceiver.getDefaultPhone()).thenReturn(mockPhone);
+
+        Intent intent = new Intent(ACTION_REFRESH_SAFETY_SOURCES);
+        intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, "aBroadcastId");
+        mSafetySourceReceiver.onReceive(mContext, intent);
+
+        verify(mockPhone, times(1)).refreshSafetySources("aBroadcastId");
+    }
+
+    @Test
+    public void testOnReceive_featureFlagsOff() {
+        mSetFlagsRule.disableFlags(
+                Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENFORCE_TELEPHONY_FEATURE_MAPPING_FOR_PUBLIC_APIS);
+
+        Intent intent = new Intent(ACTION_REFRESH_SAFETY_SOURCES);
+        intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, "aBroadcastId");
+        mSafetySourceReceiver.onReceive(mContext, intent);
+
+        verify(mSafetySourceReceiver, never()).getDefaultPhone();
+    }
+
+    @Test
+    public void testOnReceive_phoneNotReadyYet() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENFORCE_TELEPHONY_FEATURE_MAPPING_FOR_PUBLIC_APIS);
+        when(mSafetySourceReceiver.getDefaultPhone()).thenReturn(null);
+
+        Intent intent = new Intent(ACTION_REFRESH_SAFETY_SOURCES);
+        intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, "aBroadcastId");
+
+        // this call succeeding without a NPE means this test has passed. There are no observable
+        // side effects to a null Phone, because all side effects happen on the Phone instance.
+        mSafetySourceReceiver.onReceive(mContext, intent);
+    }
+
+    @Test
+    public void testOnReceive_noTelephonyFeature() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENABLE_MODEM_CIPHER_TRANSPARENCY_UNSOL_EVENTS,
+                Flags.FLAG_ENFORCE_TELEPHONY_FEATURE_MAPPING_FOR_PUBLIC_APIS);
+
+        when(mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
+
+        Phone mockPhone = mock(Phone.class);
+        when(mSafetySourceReceiver.getDefaultPhone()).thenReturn(mockPhone);
+
+        Intent intent = new Intent(ACTION_REFRESH_SAFETY_SOURCES);
+        intent.putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, "aBroadcastId");
+        mSafetySourceReceiver.onReceive(mContext, intent);
+
+        verify(mockPhone, never()).refreshSafetySources(any());
+    }
+}
diff --git a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
index 16a5cdb..e5f7fd3 100644
--- a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
+++ b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
@@ -41,7 +41,7 @@
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
-import com.android.phone.common.R;
+import com.android.phone.R;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
index a9207e6..b1572f1 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceControllerTest.java
@@ -27,7 +27,8 @@
 import android.content.ComponentName;
 import android.os.Looper;
 import android.telecom.PhoneAccountHandle;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index e2a199b..ca16bc7 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -40,7 +40,8 @@
 import android.telecom.StatusHints;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import com.android.ims.internal.ConferenceParticipant;
 
diff --git a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
index b7fe988..c7080b4 100644
--- a/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConferenceControllerTest.java
@@ -28,7 +28,8 @@
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.PhoneAccount;
-import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index f0a5220..e791d3c 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -46,6 +46,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -80,9 +81,9 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
@@ -308,7 +309,7 @@
                 anyString(), anyInt());
         doReturn(CompletableFuture.completedFuture(NOT_DISCONNECTED))
                 .when(mEmergencyStateTracker)
-                .startEmergencyCall(any(), anyString(), eq(false));
+                .startEmergencyCall(any(), any(), eq(false));
         replaceInstance(TelephonyConnectionService.class,
                 "mDomainSelectionMainExecutor", mTestConnectionService, getExecutor());
         doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
@@ -319,10 +320,15 @@
         mBinderStub = (IConnectionService.Stub) mTestConnectionService.onBind(null);
         mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
         mSetFlagsRule.enableFlags(Flags.FLAG_DO_NOT_OVERRIDE_PRECISE_LABEL);
+        mSetFlagsRule.enableFlags(Flags.FLAG_CALL_EXTRA_FOR_NON_HOLD_SUPPORTED_CARRIERS);
     }
 
     @After
     public void tearDown() throws Exception {
+        if (mTestConnectionService != null
+                && mTestConnectionService.getEmergencyConnection() != null) {
+            mTestConnectionService.onLocalHangup(mTestConnectionService.getEmergencyConnection());
+        }
         mTestConnectionService = null;
         super.tearDown();
     }
@@ -1816,7 +1822,7 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB1_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB1_HANDLE, false, mTelephonyManagerProxy);
         // Would've preferred to use mockito, but can't mock out TelephonyConnection/Connection
         // easily.
         assertFalse(tc1.wasDisconnected);
@@ -1829,7 +1835,7 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, true);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         // Other call is an emergency call, so don't disconnect it.
         assertFalse(tc1.wasDisconnected);
     }
@@ -1842,7 +1848,7 @@
                 android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         // Other call is an external call, so don't disconnect it.
         assertFalse(tc1.wasDisconnected);
     }
@@ -1854,7 +1860,7 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         assertTrue(tc1.wasDisconnected);
     }
 
@@ -1868,14 +1874,14 @@
         tcs.add(tc1);
         tcs.add(tc2);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         assertTrue(tc1.wasDisconnected);
         assertTrue(tc2.wasDisconnected);
     }
 
     /**
      * Verifies that DSDA or virtual DSDA-enabled devices can support active non-emergency calls on
-     * separate subs.
+     * separate subs, when the extra EXTRA_ANSWERING_DROPS_FG_CALL is not set on the incoming call.
      */
     @Test
     @SmallTest
@@ -1886,10 +1892,26 @@
         SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
         tcs.add(tc1);
         TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
-                tcs, SUB2_HANDLE, mTelephonyManagerProxy);
+                tcs, SUB2_HANDLE, false, mTelephonyManagerProxy);
         assertFalse(tc1.wasDisconnected);
     }
 
+    /**
+     * Verifies that DSDA or virtual DSDA-enabled devices will disconnect the existing call when the
+     * call extra EXTRA_ANSWERING_DROPS_FG_CALL is set on the incoming call on a different sub.
+     */
+    @Test
+    @SmallTest
+    public void testDisconnectDifferentSubForVirtualDsdaDevice_ifCallExtraSet() {
+        when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tcs.add(tc1);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+                tcs, SUB2_HANDLE, true, mTelephonyManagerProxy);
+        assertTrue(tc1.wasDisconnected);
+    }
 
     /**
      * For calls on the same sub, the Dialer implements the 'swap' functionality to perform hold and
@@ -1972,6 +1994,27 @@
     }
 
     /**
+     * For DSDA devices with AP domain selection service enabled, placing an outgoing call
+     * on a 2nd sub will hold the existing ACTIVE connection on the first sub.
+     */
+    @Test
+    @SmallTest
+    public void testHoldOnOtherSubForVirtualDsdaDeviceWithDomainSelectionEnabled() {
+        when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tc1.setTelephonyConnectionActive();
+        tcs.add(tc1);
+
+        Conferenceable c = TelephonyConnectionService.maybeHoldCallsOnOtherSubs(
+                tcs, new ArrayList<>(), SUB2_HANDLE, mTelephonyManagerProxy);
+        assertTrue(c.equals(tc1));
+        assertTrue(tc1.wasHeld);
+    }
+
+    /**
      * For DSDA devices, if the existing connection was already held, placing an outgoing call on a
      * 2nd sub will not attempt to hold the existing connection on the first sub.
      */
@@ -2124,13 +2167,22 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
@@ -2154,13 +2206,22 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
@@ -2187,14 +2248,23 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mEmergencyStateTracker, times(1))
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mDomainSelectionResolver, times(0))
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyCallDomainSelectionConnection, times(0))
                 .createEmergencyConnection(any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
@@ -2222,8 +2292,7 @@
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
-        Connection nc = Mockito.mock(Connection.class);
-        doReturn(nc).when(mPhone0).dial(anyString(), any(), any());
+        doReturn(mInternalConnection).when(mPhone0).dial(anyString(), any(), any());
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
         DialArgs dialArgs = argsCaptor.getValue();
@@ -2251,8 +2320,131 @@
 
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
-        Connection nc = Mockito.mock(Connection.class);
-        doReturn(nc).when(mPhone0).dial(anyString(), any(), any());
+        doReturn(mInternalConnection).when(mPhone0).dial(anyString(), any(), any());
+
+        verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+        DialArgs dialArgs = argsCaptor.getValue();
+        assertNotNull("DialArgs param is null", dialArgs);
+        assertNotNull("intentExtras is null", dialArgs.intentExtras);
+        assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+        assertEquals(selectedDomain,
+                dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+    }
+
+    @Test
+    public void testDomainSelectionRedialFailedWithException() throws Exception {
+        setupForCallTest();
+
+        int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+        int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+        int selectedDomain = DOMAIN_CS;
+
+        TestTelephonyConnection c = setupForReDialForDomainSelection(
+                mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+        CallStateException cse = new CallStateException(CallStateException.ERROR_CALLING_DISABLED,
+                "Calling disabled via ro.telephony.disable-call property");
+        doThrow(cse).when(mPhone0).dial(anyString(), any(), any());
+
+        assertTrue(mTestConnectionService.maybeReselectDomain(c, null, true,
+                android.telephony.DisconnectCause.NOT_VALID));
+        verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+        verify(mEmergencyCallDomainSelectionConnection).cancelSelection();
+        verify(mEmergencyStateTracker).endCall(any());
+    }
+
+    @Test
+    public void testDomainSelectionRejectIncoming() throws Exception {
+        setupForCallTest();
+
+        int selectedDomain = DOMAIN_CS;
+
+        setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+        doReturn(mInternalConnection2).when(mCall).getLatestConnection();
+        doReturn(true).when(mCall).isRinging();
+        doReturn(mCall).when(mPhone0).getRingingCall();
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
+        verify(mDomainSelectionResolver)
+                .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
+        verify(mEmergencyStateTracker)
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
+        verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
+        ArgumentCaptor<Connection.Listener> listenerCaptor =
+                ArgumentCaptor.forClass(Connection.Listener.class);
+
+        verify(mInternalConnection2).addListener(listenerCaptor.capture());
+        verify(mCall).hangup();
+        verify(mPhone0, never()).dial(anyString(), any(), any());
+
+        Connection.Listener listener = listenerCaptor.getValue();
+
+        assertNotNull(listener);
+
+        listener.onDisconnect(0);
+
+        verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
+
+        ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
+
+        verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
+
+        DialArgs dialArgs = argsCaptor.getValue();
+
+        assertNotNull("DialArgs param is null", dialArgs);
+        assertNotNull("intentExtras is null", dialArgs.intentExtras);
+        assertTrue(dialArgs.intentExtras.containsKey(PhoneConstants.EXTRA_DIAL_DOMAIN));
+        assertEquals(selectedDomain,
+                dialArgs.intentExtras.getInt(PhoneConstants.EXTRA_DIAL_DOMAIN, -1));
+    }
+
+    @Test
+    public void testDomainSelectionRedialRejectIncoming() throws Exception {
+        setupForCallTest();
+
+        int preciseDisconnectCause = com.android.internal.telephony.CallFailCause.ERROR_UNSPECIFIED;
+        int disconnectCause = android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
+        int selectedDomain = DOMAIN_CS;
+
+        TestTelephonyConnection c = setupForReDialForDomainSelection(
+                mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
+
+        doReturn(mInternalConnection2).when(mCall).getLatestConnection();
+        doReturn(true).when(mCall).isRinging();
+        doReturn(mCall).when(mPhone0).getRingingCall();
+
+        assertTrue(mTestConnectionService.maybeReselectDomain(c, null, true,
+                android.telephony.DisconnectCause.NOT_VALID));
+        verify(mEmergencyCallDomainSelectionConnection).reselectDomain(any());
+
+        ArgumentCaptor<Connection.Listener> listenerCaptor =
+                ArgumentCaptor.forClass(Connection.Listener.class);
+
+        verify(mInternalConnection2).addListener(listenerCaptor.capture());
+        verify(mCall).hangup();
+        verify(mPhone0, never()).dial(anyString(), any(), any());
+
+        Connection.Listener listener = listenerCaptor.getValue();
+
+        assertNotNull(listener);
+
+        listener.onDisconnect(0);
+
+        ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
         DialArgs dialArgs = argsCaptor.getValue();
@@ -2324,13 +2516,22 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
@@ -2385,132 +2586,83 @@
     @Test
     public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_InService()
             throws Exception {
-        setupForCallTest();
-
-        doReturn(false).when(mPhone0).isRadioOn();
-        ServiceState ss = new ServiceState();
-        ss.setState(ServiceState.STATE_POWER_OFF);
-        when(mPhone0.getServiceState()).thenReturn(ss);
-        when(mSST.getServiceState()).thenReturn(ss);
-
-        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
-
-        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
-                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
-                Collections.emptyList(),
-                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
-
-        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
-        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
-        doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
-                anyString());
-
         when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
-
-        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
-                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
-                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+        Phone testPhone = setupConnectionServiceInApmForDomainSelection(true);
 
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        when(testPhone.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
 
         assertFalse(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
 
         when(mSST.isRadioOn()).thenReturn(true);
 
         assertFalse(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+        Phone otherPhone = makeTestPhone(1, ServiceState.STATE_OUT_OF_SERVICE, false);
+        when(otherPhone.getServiceState()).thenReturn(ss);
+
+        // Radio on is OK for other phone
+        assertTrue(callback.getValue()
+                .isOkToCall(otherPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
 
         ss.setState(ServiceState.STATE_IN_SERVICE);
 
         assertTrue(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
+
+        mConnection.setDisconnected(null);
     }
 
     @Test
     public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_Timeout()
             throws Exception {
-        setupForCallTest();
-
-        doReturn(false).when(mPhone0).isRadioOn();
-        ServiceState ss = new ServiceState();
-        ss.setState(ServiceState.STATE_POWER_OFF);
-        when(mPhone0.getServiceState()).thenReturn(ss);
-        when(mSST.getServiceState()).thenReturn(ss);
-
-        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
-
-        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
-                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
-                Collections.emptyList(),
-                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
-
-        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
-        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
-        doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
-                anyString());
-
         when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
-
-        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
-                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
-                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+        Phone testPhone = setupConnectionServiceInApmForDomainSelection(true);
 
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
+        ServiceState ss = new ServiceState();
+        ss.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        when(testPhone.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
         when(mSST.isRadioOn()).thenReturn(true);
 
         assertFalse(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
         assertTrue(callback.getValue()
-                .onTimeout(mPhone0, ServiceState.STATE_OUT_OF_SERVICE, false));
+                .onTimeout(testPhone, ServiceState.STATE_OUT_OF_SERVICE, false));
+
+        mConnection.setDisconnected(null);
     }
 
     @Test
     public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_CombinedAttach()
             throws Exception {
-        setupForCallTest();
-
-        doReturn(false).when(mPhone0).isRadioOn();
-        ServiceState ss = new ServiceState();
-        ss.setState(ServiceState.STATE_POWER_OFF);
-        when(mPhone0.getServiceState()).thenReturn(ss);
-        when(mSST.getServiceState()).thenReturn(ss);
-
-        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
-
-        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
-                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
-                Collections.emptyList(),
-                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
-
-        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
-        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
-        doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
-                anyString());
-
         when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
-
-        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
-                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
-                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+        Phone testPhone = setupConnectionServiceInApmForDomainSelection(true);
 
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
-        when(mSST.isRadioOn()).thenReturn(true);
+        ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_IN_SERVICE);
+        when(testPhone.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+        when(mSST.isRadioOn()).thenReturn(true);
 
         DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
                 .setLteAttachResultType(DataSpecificRegistrationInfo.LTE_ATTACH_TYPE_COMBINED)
@@ -2526,46 +2678,27 @@
         ss.addNetworkRegistrationInfo(nri);
 
         assertTrue(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
+
+        mConnection.setDisconnected(null);
     }
 
     @Test
     public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_PsOnly()
             throws Exception {
-        setupForCallTest();
-
-        doReturn(false).when(mPhone0).isRadioOn();
-        ServiceState ss = new ServiceState();
-        ss.setState(ServiceState.STATE_POWER_OFF);
-        when(mPhone0.getServiceState()).thenReturn(ss);
-        when(mSST.getServiceState()).thenReturn(ss);
-
-        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
-
-        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
-                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
-                Collections.emptyList(),
-                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
-
-        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
-        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
-        doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
-                anyString());
-
         when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
-
-        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
-                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
-                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+        Phone testPhone = setupConnectionServiceInApmForDomainSelection(true);
 
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
-        when(mSST.isRadioOn()).thenReturn(true);
+        ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_IN_SERVICE);
+        when(testPhone.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+        when(mSST.isRadioOn()).thenReturn(true);
 
         DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
                 .build();
@@ -2580,48 +2713,29 @@
         ss.addNetworkRegistrationInfo(nri);
 
         assertFalse(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
         assertTrue(callback.getValue()
-                .onTimeout(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+                .onTimeout(testPhone, ServiceState.STATE_IN_SERVICE, false));
+
+        mConnection.setDisconnected(null);
     }
 
     @Test
     public void testDomainSelectionNormalRoutingEmergencyNumber_exitingApm_PsOnly_ImsRegistered()
             throws Exception {
-        setupForCallTest();
-
-        doReturn(false).when(mPhone0).isRadioOn();
-        ServiceState ss = new ServiceState();
-        ss.setState(ServiceState.STATE_POWER_OFF);
-        when(mPhone0.getServiceState()).thenReturn(ss);
-        when(mSST.getServiceState()).thenReturn(ss);
-
-        setupForDialForDomainSelection(mPhone0, DOMAIN_CS, false);
-
-        EmergencyNumber emergencyNumber = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "", "",
-                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
-                Collections.emptyList(),
-                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
-                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
-
-        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
-        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
-        doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
-                anyString());
-
         when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
-
-        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
-                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
-                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+        Phone testPhone = setupConnectionServiceInApmForDomainSelection(true);
 
         ArgumentCaptor<RadioOnStateListener.Callback> callback =
                 ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
         verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
-                any(), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
+                eq(testPhone), eq(false), eq(TIMEOUT_TO_DYNAMIC_ROUTING_MS));
 
-        when(mSST.isRadioOn()).thenReturn(true);
+        ServiceState ss = new ServiceState();
         ss.setState(ServiceState.STATE_IN_SERVICE);
+        when(testPhone.getServiceState()).thenReturn(ss);
+        when(mSST.getServiceState()).thenReturn(ss);
+        when(mSST.isRadioOn()).thenReturn(true);
 
         DataSpecificRegistrationInfo dsri = new DataSpecificRegistrationInfo.Builder(3)
                 .build();
@@ -2636,9 +2750,11 @@
         ss.addNetworkRegistrationInfo(nri);
 
         assertFalse(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, false));
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
         assertTrue(callback.getValue()
-                .isOkToCall(mPhone0, ServiceState.STATE_IN_SERVICE, true));
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, true));
+
+        mConnection.setDisconnected(null);
     }
 
     @Test
@@ -2652,6 +2768,7 @@
 
         setupForDialForDomainSelection(mPhone0, selectedDomain, true);
         doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
+        doReturn(mInternalConnection).when(mPhone0).dial(anyString(), any(), any());
 
         TestTelephonyConnection c = setupForReDialForDomainSelection(
                 mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
@@ -2662,13 +2779,22 @@
         assertTrue(mTestConnectionService.maybeReselectDomain(c, reasonInfo, true,
                 android.telephony.DisconnectCause.NOT_VALID));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
@@ -2693,6 +2819,7 @@
 
         setupForDialForDomainSelection(mPhone0, selectedDomain, true);
         doReturn(mPhone0).when(mImsPhone).getDefaultPhone();
+        doReturn(mInternalConnection).when(mPhone0).dial(anyString(), any(), any());
 
         TestTelephonyConnection c = setupForReDialForDomainSelection(
                 mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
@@ -2703,13 +2830,22 @@
         assertTrue(mTestConnectionService.maybeReselectDomain(c, reasonInfo, true,
                 android.telephony.DisconnectCause.NOT_VALID));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         ArgumentCaptor<DialArgs> argsCaptor = ArgumentCaptor.forClass(DialArgs.class);
 
         verify(mPhone0).dial(anyString(), argsCaptor.capture(), any());
@@ -2766,7 +2902,7 @@
 
         DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
 
-        assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+        assertEquals(mPhone1.getPhoneId(), attr.getSlotIndex());
     }
 
     @Test
@@ -2812,7 +2948,7 @@
 
         DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
 
-        assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+        assertEquals(mPhone1.getPhoneId(), attr.getSlotIndex());
     }
 
     @Test
@@ -2831,6 +2967,10 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        android.telecom.Connection c = mTestConnectionService.getEmergencyConnection();
+
+        assertNotNull(c);
+
         ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
                 ArgumentCaptor.forClass(
                         DomainSelectionConnection.DomainSelectionConnectionCallback.class);
@@ -2846,7 +2986,29 @@
         callback.onSelectionTerminated(ERROR_UNSPECIFIED);
 
         verify(mEmergencyCallDomainSelectionConnection).cancelSelection();
-        verify(mEmergencyStateTracker).endCall(eq(TELECOM_CALL_ID1));
+        verify(mEmergencyStateTracker).endCall(eq(c));
+    }
+
+    @Test
+    public void testDomainSelectionDialFailedByException() throws Exception {
+        setupForCallTest();
+
+        int selectedDomain = DOMAIN_CS;
+
+        setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+
+        CallStateException cse = new CallStateException(CallStateException.ERROR_CALLING_DISABLED,
+                "Calling disabled via ro.telephony.disable-call property");
+        doThrow(cse).when(mPhone0).dial(anyString(), any(), any());
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        verify(mEmergencyStateTracker)
+                .startEmergencyCall(any(), any(), anyBoolean());
+        verify(mEmergencyCallDomainSelectionConnection).cancelSelection();
+        verify(mEmergencyStateTracker).endCall(any());
     }
 
     @Test
@@ -2859,26 +3021,23 @@
 
         CompletableFuture<Integer> future = new CompletableFuture<>();
         doReturn(future).when(mEmergencyStateTracker)
-                .startEmergencyCall(any(), anyString(), eq(false));
+                .startEmergencyCall(any(), any(), eq(false));
 
         mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
-
-        TelephonyConnection c = new TestTelephonyConnection();
-        c.setTelecomCallId(TELECOM_CALL_ID1);
+                .startEmergencyCall(eq(mPhone0), any(), eq(false));
 
         // dialing is canceled
-        mTestConnectionService.onLocalHangup(c);
+        mTestConnectionService.onLocalHangup(mTestConnectionService.getEmergencyConnection());
 
         // startEmergencyCall has completed
         future.complete(NOT_DISCONNECTED);
 
         // verify that createEmergencyConnection is discarded
-        verify(mEmergencyCallDomainSelectionConnection, times(0))
+        verify(mEmergencyCallDomainSelectionConnection, never())
                 .createEmergencyConnection(any(), any());
     }
 
@@ -2900,17 +3059,14 @@
 
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
 
-        TelephonyConnection c = new TestTelephonyConnection();
-        c.setTelecomCallId(TELECOM_CALL_ID1);
-
         // dialing is canceled
-        mTestConnectionService.onLocalHangup(c);
+        mTestConnectionService.onLocalHangup(mTestConnectionService.getEmergencyConnection());
 
         // domain selection has completed
         future.complete(selectedDomain);
 
         // verify that dialing is discarded
-        verify(mPhone0, times(0)).dial(anyString(), any(), any());
+        verify(mPhone0, never()).dial(anyString(), any(), any());
     }
 
     @Test
@@ -2923,7 +3079,6 @@
 
         TestTelephonyConnection c = setupForReDialForDomainSelection(
                 mPhone0, selectedDomain, preciseDisconnectCause, disconnectCause, true);
-        c.setTelecomCallId(TELECOM_CALL_ID1);
 
         CompletableFuture<Integer> future = new CompletableFuture<>();
         doReturn(future).when(mEmergencyCallDomainSelectionConnection)
@@ -2960,20 +3115,28 @@
                 mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
         c.setEmergencyServiceCategory(eccCategory);
         c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
-        c.setTelecomCallId(TELECOM_CALL_ID1);
 
         CompletableFuture<Integer> future = new CompletableFuture<>();
         doReturn(future).when(mEmergencyStateTracker)
-                .startEmergencyCall(any(), anyString(), eq(false));
+                .startEmergencyCall(any(), any(), eq(false));
 
         ImsReasonInfo reasonInfo = new ImsReasonInfo(CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null);
         assertTrue(mTestConnectionService.maybeReselectDomain(c, reasonInfo, true,
                 android.telephony.DisconnectCause.NOT_VALID));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         // dialing is canceled
         mTestConnectionService.onLocalHangup(c);
 
@@ -3002,7 +3165,6 @@
                 mImsPhone, selectedDomain, preciseDisconnectCause, disconnectCause, false);
         c.setEmergencyServiceCategory(eccCategory);
         c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
-        c.setTelecomCallId(TELECOM_CALL_ID1);
 
         CompletableFuture<Integer> future = new CompletableFuture<>();
         doReturn(future).when(mEmergencyCallDomainSelectionConnection)
@@ -3021,7 +3183,7 @@
         future.complete(selectedDomain);
 
         // verify that dialing is discarded
-        verify(mPhone0, times(0)).dial(anyString(), any(), any());
+        verify(mPhone0, never()).dial(anyString(), any(), any());
     }
 
     @Test
@@ -3036,15 +3198,25 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mSatelliteSOSMessageRecommender).onEmergencyCallStarted(any());
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
         verify(mPhone0).dial(anyString(), any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         TestTelephonyConnection c = new TestTelephonyConnection();
+        mTestConnectionService.setEmergencyConnection(c);
         c.setTelecomCallId(TELECOM_CALL_ID1);
         c.setIsImsConnection(true);
         Connection orgConn = c.getOriginalConnection();
@@ -3058,10 +3230,10 @@
         connectionListener.onOriginalConnectionConfigured(c);
 
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallDomainUpdated(
-                eq(PhoneConstants.PHONE_TYPE_IMS), eq(TELECOM_CALL_ID1));
+                eq(PhoneConstants.PHONE_TYPE_IMS), eq(c));
 
         verify(mEmergencyStateTracker, times(0)).onEmergencyCallStateChanged(
-                any(), eq(TELECOM_CALL_ID1));
+                any(), eq(c));
         verify(mSatelliteSOSMessageRecommender, times(0))
                 .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
 
@@ -3072,7 +3244,7 @@
 
         // ACTIVE sate is notified
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
-                eq(Call.State.ACTIVE), eq(TELECOM_CALL_ID1));
+                eq(Call.State.ACTIVE), eq(c));
         verify(mSatelliteSOSMessageRecommender, times(1))
                 .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1),
                         eq(android.telecom.Connection.STATE_ACTIVE));
@@ -3085,7 +3257,7 @@
 
         // state change not notified any more after CONNECTED once
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
-                any(), eq(TELECOM_CALL_ID1));
+                any(), eq(c));
         verify(mSatelliteSOSMessageRecommender, times(1))
                 .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
 
@@ -3097,7 +3269,7 @@
 
         // state change not notified any more after CONNECTED once
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
-                any(), eq(TELECOM_CALL_ID1));
+                any(), eq(c));
         verify(mSatelliteSOSMessageRecommender, times(1))
                 .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
 
@@ -3109,7 +3281,7 @@
 
          // domain change notified
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallDomainUpdated(
-                eq(PhoneConstants.PHONE_TYPE_GSM), eq(TELECOM_CALL_ID1));
+                eq(PhoneConstants.PHONE_TYPE_GSM), eq(c));
 
         // state change to DISCONNECTED
         c.setDisconnected(null);
@@ -3119,7 +3291,7 @@
 
         // state change not notified
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallStateChanged(
-                any(), eq(TELECOM_CALL_ID1));
+                any(), eq(c));
         verify(mSatelliteSOSMessageRecommender, times(1))
                 .onEmergencyCallConnectionStateChanged(eq(TELECOM_CALL_ID1), anyInt());
     }
@@ -3136,15 +3308,24 @@
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
                         TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
 
+        ArgumentCaptor<android.telecom.Connection> connectionCaptor =
+                ArgumentCaptor.forClass(android.telecom.Connection.class);
+
         verify(mDomainSelectionResolver)
                 .getDomainSelectionConnection(eq(mPhone0), eq(SELECTOR_TYPE_CALLING), eq(true));
         verify(mEmergencyStateTracker)
-                .startEmergencyCall(eq(mPhone0), eq(TELECOM_CALL_ID1), eq(false));
+                .startEmergencyCall(eq(mPhone0), connectionCaptor.capture(), eq(false));
         verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
         verify(mPhone0).dial(anyString(), any(), any());
 
+        android.telecom.Connection tc = connectionCaptor.getValue();
+
+        assertNotNull(tc);
+        assertEquals(TELECOM_CALL_ID1, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+
         TestTelephonyConnection c = new TestTelephonyConnection();
-        c.setTelecomCallId(TELECOM_CALL_ID1);
+        mTestConnectionService.setEmergencyConnection(c);
         c.setIsImsConnection(true);
         Connection orgConn = c.getOriginalConnection();
         doReturn(PhoneConstants.PHONE_TYPE_IMS).when(orgConn).getPhoneType();
@@ -3156,18 +3337,18 @@
         connectionListener.onConnectionPropertiesChanged(c, PROPERTY_WIFI);
 
         verify(mEmergencyStateTracker, times(0)).onEmergencyCallPropertiesChanged(
-                anyInt(), anyString());
+                anyInt(), any());
 
         doReturn(Call.State.ACTIVE).when(orgConn).getState();
         connectionListener.onConnectionPropertiesChanged(c, PROPERTY_WIFI);
 
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallPropertiesChanged(
-                eq(PROPERTY_WIFI), eq(TELECOM_CALL_ID1));
+                eq(PROPERTY_WIFI), eq(c));
 
         connectionListener.onConnectionPropertiesChanged(c, 0);
 
         verify(mEmergencyStateTracker, times(1)).onEmergencyCallPropertiesChanged(
-                eq(0), eq(TELECOM_CALL_ID1));
+                eq(0), eq(c));
     }
 
     @Test
@@ -3276,6 +3457,7 @@
     public void testNormalCallSatelliteEnabled() {
         setupForCallTest();
         doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+
         mConnection = mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
                 createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
         DisconnectCause disconnectCause = mConnection.getDisconnectCause();
@@ -3336,6 +3518,30 @@
     }
 
     @Test
+    public void testNormalCallUsingNonTerrestrialNetwork_canMakeWifiCall() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        setupForCallTest();
+        // Call is not supported while using satellite
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+                .build();
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        when(mPhone0.getServiceState()).thenReturn(ss);
+        // Wi-Fi call is possible
+        doReturn(true).when(mImsPhone).canMakeWifiCall();
+        when(mPhone0.getImsPhone()).thenReturn(mImsPhone);
+
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1, "1234", TELECOM_CALL_ID1));
+        DisconnectCause disconnectCause = mConnection.getDisconnectCause();
+        assertNotEquals(android.telephony.DisconnectCause.SATELLITE_ENABLED,
+                disconnectCause.getTelephonyDisconnectCause());
+    }
+
+    @Test
     public void testIsAvailableForEmergencyCallsNotForCrossSim() {
         Phone mockPhone = Mockito.mock(Phone.class);
         when(mockPhone.getImsRegistrationTech()).thenReturn(
@@ -3349,6 +3555,52 @@
     }
 
     @Test
+    public void testIsAvailableForEmergencyCallsUsingNonTerrestrialNetwork_enableFlag() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        Phone mockPhone = Mockito.mock(Phone.class);
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+                .build();
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        ss.setEmergencyOnly(true);
+        ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone.getServiceState()).thenReturn(ss);
+
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+    }
+
+    @Test
+    public void testIsAvailableForEmergencyCallsUsingNonTerrestrialNetwork_disableFlag() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        Phone mockPhone = Mockito.mock(Phone.class);
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_VOICE))
+                .build();
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        ss.setEmergencyOnly(true);
+        ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone.getServiceState()).thenReturn(ss);
+
+        assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+        assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+    }
+
+    @Test
     public void testIsAvailableForEmergencyCallsForEmergencyRoutingInEmergencyOnly() {
         ServiceState mockService = Mockito.mock(ServiceState.class);
         when(mockService.isEmergencyOnly()).thenReturn(true);
@@ -3503,9 +3755,54 @@
         doReturn(mImsPhone).when(mockPhone).getImsPhone();
     }
 
+    private Phone setupConnectionServiceInApmForDomainSelection(boolean normalRouting) {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+                false /*isEmergencyOnly*/);
+        Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+                false /*isEmergencyOnly*/);
+        doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
+        doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
+        List<Phone> phones = new ArrayList<>(2);
+        doReturn(false).when(testPhone0).isRadioOn();
+        doReturn(false).when(testPhone1).isRadioOn();
+        phones.add(testPhone0);
+        phones.add(testPhone1);
+        setPhones(phones);
+        setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+        setupDeviceConfig(testPhone0, testPhone1, 0);
+        setupForDialForDomainSelection(testPhone0, DOMAIN_CS, false);
+
+        EmergencyNumber emergencyNumber;
+        if (normalRouting) {
+            emergencyNumber = new EmergencyNumber(TEST_ADDRESS.getSchemeSpecificPart(), "", "",
+                    EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                    Collections.emptyList(),
+                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                    EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        } else {
+            emergencyNumber = setupEmergencyNumber(TEST_ADDRESS);
+        }
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+        doReturn(emergencyNumber).when(mEmergencyNumberTracker).getEmergencyNumber(anyString());
+        doReturn(Arrays.asList(emergencyNumber)).when(mEmergencyNumberTracker).getEmergencyNumbers(
+                anyString());
+        doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(
+                PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", mConnection);
+
+        return testPhone0;
+    }
+
     private TestTelephonyConnection setupForReDialForDomainSelection(
             Phone mockPhone, int domain, int preciseDisconnectCause,
             int disconnectCause, boolean fromEmergency) throws Exception {
+        TestTelephonyConnection c = new TestTelephonyConnection();
         try {
             if (fromEmergency) {
                 doReturn(CompletableFuture.completedFuture(domain))
@@ -3514,8 +3811,8 @@
                 replaceInstance(TelephonyConnectionService.class,
                         "mEmergencyCallDomainSelectionConnection",
                         mTestConnectionService, mEmergencyCallDomainSelectionConnection);
-                replaceInstance(TelephonyConnectionService.class, "mEmergencyCallId",
-                        mTestConnectionService, TELECOM_CALL_ID1);
+                replaceInstance(TelephonyConnectionService.class, "mEmergencyConnection",
+                        mTestConnectionService, c);
             } else {
                 doReturn(CompletableFuture.completedFuture(domain))
                         .when(mNormalCallDomainSelectionConnection).reselectDomain(any());
@@ -3529,7 +3826,6 @@
 
         doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
 
-        TestTelephonyConnection c = new TestTelephonyConnection();
         c.setTelecomCallId(TELECOM_CALL_ID1);
         c.setMockPhone(mockPhone);
         c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
diff --git a/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java b/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
index 5d4fe17..8f51dab 100644
--- a/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
@@ -32,7 +32,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -68,11 +67,11 @@
     private static final int SUB_1 = 1;
     private static final int TEST_SIM_CARRIER_ID = 1911;
 
-    @Mock private Context mContext;
     @Mock private SharedPreferences mSharedPreferences;
     @Mock private SharedPreferences.Editor mEditor;
     @Mock private Resources mResources;
 
+    private Context mContext;
     private HandlerThread mHandlerThread;
     private TestableLooper mLooper;
     private CarrierConfigHelper mCarrierConfigHelper;
diff --git a/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java b/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java
index 74c3311..872238c 100644
--- a/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/DomainSelectorBaseTest.java
@@ -26,8 +26,8 @@
 import android.os.Looper;
 import android.telephony.DomainSelectionService.SelectionAttributes;
 import android.telephony.TransportSelectorCallback;
-import android.test.suitebuilder.annotation.SmallTest;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TestContext;
@@ -52,11 +52,6 @@
         }
 
         @Override
-        public void cancelSelection() {
-            // No operations.
-        }
-
-        @Override
         public void reselectDomain(@NonNull SelectionAttributes attr) {
             // No operations.
         }
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index eab40f9..0735c3f 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.services.telephony.domainselection;
 
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
@@ -40,6 +42,7 @@
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL;
 import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE;
 import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE;
 import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_NO_PREFERENCE;
@@ -51,8 +54,11 @@
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
 import static android.telephony.PreciseDisconnectCause.SERVICE_OPTION_NOT_AVAILABLE;
+import static android.telephony.TelephonyManager.DATA_CONNECTED;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED;
 
 import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_MAX_CELLULAR_TIMEOUT;
 import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_NETWORK_SCAN_TIMEOUT;
@@ -65,10 +71,12 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyVararg;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -79,6 +87,8 @@
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkRequest;
+import android.net.Uri;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IPowerManager;
@@ -86,6 +96,7 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.telecom.PhoneAccount;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.BarringInfo;
 import android.telephony.CarrierConfigManager;
@@ -93,7 +104,7 @@
 import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.SelectionAttributes;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PreciseDisconnectCause;
 import android.telephony.SubscriptionManager;
@@ -103,16 +114,18 @@
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.ProvisioningManager;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 import android.util.Log;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
+
 import com.android.TestContext;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -130,6 +143,7 @@
 
     private static final int SLOT_0 = 0;
     private static final int SLOT_0_SUB_ID = 1;
+    private static final Uri TEST_URI = Uri.fromParts(PhoneAccount.SCHEME_TEL, "911", null);
 
     @Mock private CarrierConfigManager mCarrierConfigManager;
     @Mock private ConnectivityManager mConnectivityManager;
@@ -142,6 +156,7 @@
     @Mock private ProvisioningManager mProvisioningManager;
     @Mock private CrossSimRedialingController mCsrdCtrl;
     @Mock private CarrierConfigHelper mCarrierConfigHelper;
+    @Mock private EmergencyCallbackModeHelper mEcbmHelper;
     @Mock private Resources mResources;
 
     private Context mContext;
@@ -153,7 +168,7 @@
     private @AccessNetworkConstants.RadioAccessNetworkType List<Integer> mAccessNetwork;
     private PowerManager mPowerManager;
     private ConnectivityManager.NetworkCallback mNetworkCallback;
-    private Consumer<EmergencyRegResult> mResultConsumer;
+    private Consumer<EmergencyRegistrationResult> mResultConsumer;
 
     @Before
     public void setUp() throws Exception {
@@ -220,7 +235,7 @@
         when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
 
         mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt()))
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg()))
             .thenReturn(getDefaultPersistableBundle());
 
         mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
@@ -244,7 +259,6 @@
         doReturn(mProvisioningManager).when(imsManager).getProvisioningManager(anyInt());
         doReturn(null).when(mProvisioningManager).getProvisioningStringValue(anyInt());
 
-        when(mTransportSelectorCallback.onWwanSelected()).thenReturn(mWwanSelectorCallback);
         doAnswer(new Answer<Void>() {
             @Override
             public Void answer(InvocationOnMock invocation) throws Throwable {
@@ -259,13 +273,16 @@
             @Override
             public Void answer(InvocationOnMock invocation) throws Throwable {
                 mAccessNetwork = (List<Integer>) invocation.getArguments()[0];
-                mResultConsumer = (Consumer<EmergencyRegResult>) invocation.getArguments()[3];
+                mResultConsumer =
+                        (Consumer<EmergencyRegistrationResult>) invocation.getArguments()[4];
                 return null;
             }
         }).when(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
 
         when(mResources.getStringArray(anyInt())).thenReturn(null);
+
+        doReturn(false).when(mCsrdCtrl).isThereOtherSlot();
     }
 
     @After
@@ -287,16 +304,77 @@
         createSelector(SLOT_0_SUB_ID);
 
         verify(mWwanSelectorCallback, times(0)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
         verify(mWwanSelectorCallback, times(0)).onDomainSelected(anyInt(), eq(true));
     }
 
     @Test
+    public void testDestroyed() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        mDomainSelector.destroy();
+        mDomainSelector.handleMessage(mDomainSelector.obtainMessage(Integer.MAX_VALUE));
+        unsolBarringInfoChanged(false);
+
+        verify(mTransportSelectorCallback, never()).onWwanSelected(any());
+    }
+
+    @Test
+    public void testDomainPreferenceConfigurationError() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        int[] domainPreference = new int[] {
+                CarrierConfigManager.ImsEmergency.DOMAIN_PS_NON_3GPP,
+                };
+        bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
+                UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyScanCsPreferred();
+    }
+
+    @Test
+    public void testNullEmergencyRegistrationResult() throws Exception {
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, null);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyScanPsPreferred();
+    }
+
+    @Test
     public void testNoRedundantDomainSelectionFromInitialState() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -321,12 +399,13 @@
                 CarrierConfigManager.ImsEmergency.DOMAIN_CS
                 };
         bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -347,7 +426,7 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -360,7 +439,7 @@
 
         verify(mTransportSelectorCallback, times(1)).onWwanSelected(any());
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
     }
 
     @Test
@@ -373,7 +452,7 @@
         doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
         doReturn(new String[] {"jp"}).when(mResources).getStringArray(anyInt());
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "", "jp");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -392,7 +471,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -409,7 +489,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -426,7 +507,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -449,7 +531,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -468,9 +551,10 @@
         //Extended service request failed
         SelectionAttributes.Builder builder =
                 new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
                 .setCsDisconnectCause(SERVICE_OPTION_NOT_AVAILABLE)
                 .setEmergency(true)
-                .setEmergencyRegResult(regResult);
+                .setEmergencyRegistrationResult(regResult);
         attr = builder.build();
         mDomainSelector.reselectDomain(attr);
         processAllMessages();
@@ -479,11 +563,65 @@
     }
 
     @Test
+    public void testDefaultCombinedImsRegisteredSelectPsThenNotExtendedServiceRequestFails()
+            throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+
+        SelectionAttributes.Builder builder =
+                new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult);
+        attr = builder.build();
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyScanPsPreferred();
+    }
+
+    @Test
+    public void testDefaultCombinedImsNotRegisteredDeactivatedSimSelectPs() throws Exception {
+        doReturn(SIM_ACTIVATION_STATE_DEACTIVATED).when(mTelephonyManager).getDataActivationState();
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
     public void testDefaultCombinedImsNotRegisteredSelectCs() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -496,16 +634,69 @@
     }
 
     @Test
-    public void testNoCsCombinedImsNotRegisteredSelectPs() throws Exception {
+    public void testAirplaneDefaultCombinedImsNotRegisteredSelectPs() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = new SelectionAttributes.Builder(
+                        SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult)
+                .setExitedFromAirplaneMode(true)
+                .build();
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testAirplaneRequiresRegCombinedImsNotRegisteredSelectPs() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
-        bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
-                new int[0]);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        bundle.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = new SelectionAttributes.Builder(
+                        SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult)
+                .setExitedFromAirplaneMode(true)
+                .build();
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testNoCsCombinedImsNotRegisteredSelectPs() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY,
+                new int[0]);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -522,7 +713,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -539,7 +731,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -556,7 +749,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -573,7 +767,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -590,7 +785,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -607,7 +803,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -624,7 +821,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -641,7 +839,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -658,7 +857,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -675,7 +875,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -692,7 +893,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -709,7 +911,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -726,7 +929,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -743,7 +947,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -756,11 +961,96 @@
     }
 
     @Test
+    public void testNotSupportPsEmergency() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        int[] domainPreference = new int[] {
+                CarrierConfigManager.ImsEmergency.DOMAIN_CS
+                };
+        bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertEquals(2, mAccessNetwork.size());
+        assertEquals(UTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(GERAN, (int) mAccessNetwork.get(1));
+    }
+
+    @Test
+    public void testNotSupportPsCombinedImsRegisteredSelectCs() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        int[] domainPreference = new int[] {
+                CarrierConfigManager.ImsEmergency.DOMAIN_CS
+                };
+        bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testNotSupportCsCombinedImsNotRegisteredSelectPs() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        int[] domainPreference = new int[] {
+                CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP
+                };
+        bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
     public void testDefaultEpsImsRegisteredBarredScanPsPreferred() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -777,7 +1067,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -794,7 +1085,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -811,7 +1103,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -828,7 +1121,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -845,7 +1139,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -862,7 +1157,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -879,7 +1175,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -896,7 +1193,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -913,7 +1211,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -930,7 +1229,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -947,7 +1247,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -964,7 +1265,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -981,7 +1283,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -998,7 +1301,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1015,7 +1319,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1032,7 +1337,7 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -1047,12 +1352,13 @@
     public void testVoLteOnEpsImsNotRegisteredSelectPs() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, true);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1069,7 +1375,7 @@
     public void testVoLteOffEpsImsNotRegisteredScanCsPreferred() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL, true);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         // Disable VoLTE.
         when(mMmTelManager.isAdvancedCallingSettingEnabled()).thenReturn(false);
@@ -1077,7 +1383,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1094,12 +1401,13 @@
     public void testRequiresRegEpsImsNotRegisteredScanCsPreferred() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, true);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1112,15 +1420,64 @@
     }
 
     @Test
+    public void testRequiresRegEpsImsNotRegisteredDeactivatedSimSelectPs() throws Exception {
+        doReturn(SIM_ACTIVATION_STATE_DEACTIVATED).when(mTelephonyManager).getDataActivationState();
+
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testRequiresRegEpsImsNotRegisteredEmcNotSupportedScanCsPreferred()
+            throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putBoolean(KEY_EMERGENCY_REQUIRES_IMS_REGISTRATION_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS,
+                true, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyScanCsPreferred();
+    }
+
+    @Test
     public void testDefaultEpsImsRegisteredBarredScanTimeoutWifi() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
+        mResultConsumer = null;
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1141,6 +1498,14 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
         verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
+
+        assertNotNull(mResultConsumer);
+
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        // Ignore the stale result
+        verify(mWwanSelectorCallback, never()).onDomainSelected(anyInt(), anyBoolean());
     }
 
     @Test
@@ -1149,12 +1514,13 @@
                 TelephonyManager.SIM_STATE_PIN_REQUIRED);
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1173,12 +1539,13 @@
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_SETTING_ENABLED);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1212,12 +1579,13 @@
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT, VOWIFI_REQUIRES_VALID_EID);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1250,7 +1618,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1283,7 +1652,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_PS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1321,7 +1691,8 @@
         doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
         doReturn(new String[] {"jp"}).when(mResources).getStringArray(anyInt());
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "", "jp");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -1335,6 +1706,37 @@
     }
 
     @Test
+    public void testDualSimInvalidSubscriptionAfterScan() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+                .when(mTelephonyManager).getSimState(anyInt());
+        doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
+        doReturn(new String[] {"jp"}).when(mResources).getStringArray(anyInt());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        assertNotNull(mResultConsumer);
+
+        regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "jp");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(1))
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+    }
+
+    @Test
     public void testDualSimInvalidSubscriptionButNoOtherSlot() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
@@ -1344,7 +1746,8 @@
         doReturn(false).when(mCsrdCtrl).isThereOtherSlot();
         doReturn(new String[] {"jp"}).when(mResources).getStringArray(anyInt());
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "", "jp");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -1359,10 +1762,59 @@
     }
 
     @Test
+    public void testDualSimNormalServiceOnTheOtherSubscription() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(true).when(mCsrdCtrl).isThereOtherSlotInService();
+        doReturn(new String[] {"in"}).when(mResources).getStringArray(anyInt());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "in");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(1))
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
+    @Test
     public void testEutranWithCsDomainOnly() throws Exception {
         setupForHandleScanResult();
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                DOMAIN_CS, false, false, 0, 0, "", "");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testEutranWithPsDomainOnly() throws Exception {
+        setupForHandleScanResult();
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                DOMAIN_PS, false, false, 0, 0, "", "");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testUtran() throws Exception {
+        setupForHandleScanResult();
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 DOMAIN_CS, false, false, 0, 0, "", "");
         mResultConsumer.accept(regResult);
         processAllMessages();
@@ -1374,13 +1826,14 @@
     public void testFullService() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         mResultConsumer = null;
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -1390,28 +1843,53 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), eq(false), any(), any());
         assertNotNull(mResultConsumer);
 
         mResultConsumer.accept(regResult);
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(2)).onRequestEmergencyNetworkScan(
-                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), eq(false), any(), any());
     }
 
     @Test
-    public void testFullServiceThenLimtedService() throws Exception {
+    public void testFullServiceInRoaming() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(true);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_ROAMING,
+                0, true, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_NO_PREFERENCE),
+                anyBoolean(), any(), any());
+    }
+
+    @Test
+    public void testFullServiceThenLimitedService() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT,
                 SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         mResultConsumer = null;
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -1421,14 +1899,15 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), eq(false), any(), any());
         assertNotNull(mResultConsumer);
 
         mResultConsumer.accept(regResult);
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), eq(DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE), any(), any());
+                any(), eq(DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE),
+                eq(false), any(), any());
     }
 
     @Test
@@ -1680,12 +2159,43 @@
     }
 
     @Test
+    public void testScanLimitedOnlyAfterVoLteFailure() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putBoolean(KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL,
+                true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback).onDomainSelected(eq(DOMAIN_PS), anyBoolean());
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE),
+                anyBoolean(), any(), any());
+    }
+
+    @Test
     public void testStartCrossStackTimer() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -1699,16 +2209,6 @@
     }
 
     @Test
-    public void testStopCrossStackTimerOnCancel() throws Exception {
-        createSelector(SLOT_0_SUB_ID);
-        unsolBarringInfoChanged(false);
-
-        mDomainSelector.cancelSelection();
-
-        verify(mCsrdCtrl).stopTimer();
-    }
-
-    @Test
     public void testStopCrossStackTimerOnFinish() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
@@ -1722,8 +2222,10 @@
     public void testCrossStackTimerTempFailure() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
+        doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1735,8 +2237,9 @@
         verifyCsDialed();
 
         attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
                 .setEmergency(true)
-                .setEmergencyRegResult(regResult)
+                .setEmergencyRegistrationResult(regResult)
                 .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE)
                 .build();
 
@@ -1744,14 +2247,18 @@
         processAllMessages();
 
         verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE));
+        verify(mTransportSelectorCallback)
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
     }
 
     @Test
-    public void testCrossStackTimerPermFailure() throws Exception {
+    public void testCrossStackTimerTempFailureNoValidSubscription() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
+        doReturn(false).when(mCsrdCtrl).isThereOtherSlot();
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1763,8 +2270,42 @@
         verifyCsDialed();
 
         attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
                 .setEmergency(true)
-                .setEmergencyRegResult(regResult)
+                .setEmergencyRegistrationResult(regResult)
+                .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE)
+                .build();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE));
+        verify(mTransportSelectorCallback, never())
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+    }
+
+    @Test
+    public void testCrossStackTimerPermFailure() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult)
                 .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE)
                 .build();
 
@@ -1772,6 +2313,41 @@
         processAllMessages();
 
         verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE));
+        verify(mTransportSelectorCallback)
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+    }
+
+    @Test
+    public void testCrossStackTimerPermFailureNoValidSubscription() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(false).when(mCsrdCtrl).isThereOtherSlot();
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        attr = new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult)
+                .setCsDisconnectCause(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE)
+                .build();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mCsrdCtrl).notifyCallFailure(eq(PreciseDisconnectCause.EMERGENCY_PERM_FAILURE));
+        verify(mTransportSelectorCallback, never())
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
     }
 
     @Test
@@ -1779,20 +2355,31 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
         processAllMessages();
 
         bindImsServiceUnregistered();
+        processAllMessages();
 
-        verifyScanPsPreferred();
+        ArgumentCaptor<CancellationSignal> cancelCaptor =
+                ArgumentCaptor.forClass(CancellationSignal.class);
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_NO_PREFERENCE),
+                anyBoolean(), cancelCaptor.capture(), any());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
 
         mDomainSelector.notifyCrossStackTimerExpired();
 
         verify(mTransportSelectorCallback)
                 .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_TEMP_FAILURE));
+
+        CancellationSignal cancelSignal = cancelCaptor.getValue();
+        assertNotNull(cancelSignal);
+        assertFalse(cancelSignal.isCanceled());
     }
 
     @Test
@@ -1800,7 +2387,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1828,7 +2416,7 @@
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         setupForHandleScanResult();
 
@@ -1849,13 +2437,35 @@
     }
 
     @Test
+    public void testMaxCellularTimeoutWifiNotAvailable() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+        bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        setupForHandleScanResult();
+
+        assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+        assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+
+        // Max cellular timer expired
+        mDomainSelector.removeMessages(MSG_MAX_CELLULAR_TIMEOUT);
+        mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
+
+        assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+    }
+
+    @Test
     public void testSimLockNoMaxCellularTimeout() throws Exception {
         when(mTelephonyManager.getSimState(anyInt())).thenReturn(
                 TelephonyManager.SIM_STATE_PIN_REQUIRED);
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         setupForHandleScanResult();
 
@@ -1868,7 +2478,7 @@
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         setupForHandleScanResult();
 
@@ -1893,12 +2503,13 @@
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 5);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -1915,6 +2526,8 @@
         mDomainSelector.reselectDomain(attr);
         processAllMessages();
 
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
         assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
         assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
 
@@ -1929,10 +2542,92 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
         processAllMessages();
 
+        assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+
         mDomainSelector.reselectDomain(attr);
         processAllMessages();
 
+        verify(mWwanSelectorCallback, times(2)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+    }
+
+    @Test
+    public void testMaxCellularTimeoutWhileDialingOnCellularWhileDialing() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
+        bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 5);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+
+        assertFalse(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
         assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+        mResultConsumer = null;
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertTrue(mDomainSelector.hasMessages(MSG_NETWORK_SCAN_TIMEOUT));
+        assertTrue(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+        assertNotNull(mResultConsumer);
+
+        // Scan result received and redialing on cellular
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        // Wi-Fi is connected.
+        mNetworkCallback.onAvailable(null);
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+        // Max cellular timer expired
+        mDomainSelector.removeMessages(MSG_MAX_CELLULAR_TIMEOUT);
+        mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
+        processAllMessages();
+
+        assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+
+        // Waiting for reselectDomain since there is a dialing on going.
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+
+        // Wi-Fi is disconnected.
+        mNetworkCallback.onUnavailable();
+        processAllMessages();
+
+        mResultConsumer = null;
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(0)).onWlanSelected(anyBoolean());
+        verify(mWwanSelectorCallback, times(2)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+
+        // Wi-Fi is re-connected.
+        mNetworkCallback.onAvailable(null);
+        processAllMessages();
+
+        mResultConsumer = null;
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
         verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
     }
 
@@ -1941,7 +2636,7 @@
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         setupForHandleScanResult();
 
@@ -1967,7 +2662,7 @@
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         bundle.putInt(KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT, 20);
         bundle.putInt(KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT, 2);
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         setupForHandleScanResult();
 
@@ -1986,7 +2681,8 @@
         verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
         assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
 
-        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_HOME,
                 NetworkRegistrationInfo.DOMAIN_CS,
                 true, true, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
@@ -2013,7 +2709,7 @@
         when(mTelephonyManager.getSimState(anyInt())).thenReturn(
                 TelephonyManager.SIM_STATE_PIN_REQUIRED);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -2023,7 +2719,7 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
         assertEquals(4, mAccessNetwork.size());
         assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
         assertEquals(NGRAN, (int) mAccessNetwork.get(1));
@@ -2039,7 +2735,7 @@
         when(mTelephonyManager.getSimState(anyInt())).thenReturn(
                 TelephonyManager.SIM_STATE_PIN_REQUIRED);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -2049,7 +2745,7 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
         assertEquals(4, mAccessNetwork.size());
         assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
         assertEquals(UTRAN, (int) mAccessNetwork.get(1));
@@ -2062,7 +2758,7 @@
         createSelector(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         unsolBarringInfoChanged(false);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID, regResult);
@@ -2073,7 +2769,7 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
         assertEquals(4, mAccessNetwork.size());
         assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
         assertEquals(UTRAN, (int) mAccessNetwork.get(1));
@@ -2089,7 +2785,7 @@
         // The last valid subscription supported NR.
         doReturn(true).when(mCarrierConfigHelper).isVoNrEmergencySupported(eq(SLOT_0));
 
-        EmergencyRegResult regResult = getEmergencyRegResult(
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(
                 UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID, regResult);
@@ -2100,7 +2796,7 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
         assertEquals(4, mAccessNetwork.size());
         assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
         assertEquals(NGRAN, (int) mAccessNetwork.get(1));
@@ -2108,16 +2804,368 @@
         assertEquals(GERAN, (int) mAccessNetwork.get(3));
     }
 
+    @Test
+    public void testDefaultLimitedServiceEutran() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testDefaultLimitedServiceScanTypeFullService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyScanPreferred(DomainSelectionService.SCAN_TYPE_FULL_SERVICE, EUTRAN);
+    }
+
+    @Test
+    public void testScanLtePreferredAfterNgranFailure() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                new int[] { NGRAN, EUTRAN });
+        bundle.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS, true, false, 1, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(UTRAN, (int) mAccessNetwork.get(1));
+        assertEquals(GERAN, (int) mAccessNetwork.get(2));
+        assertEquals(NGRAN, (int) mAccessNetwork.get(3));
+    }
+
+    @Test
+    public void testDefaultLimitedServiceNgran() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyScanPsPreferred();
+    }
+
+    @Test
+    public void testTestEmergencyNumberOverCs() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID,
+                true /*isTestEmergencyNumber*/, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testTestEmergencyNumberOverPs() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID,
+                true /*isTestEmergencyNumber*/, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testTestEmergencyNumberScanRequest() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID,
+                true /*isTestEmergencyNumber*/, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService(true);
+        processAllMessages();
+
+        verifyScanPsPreferred();
+    }
+
+    @Test
+    public void testLimitedServiceDialCs() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UTRAN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testWhileInEcbmOnWwan() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WWAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Consumer<WwanSelectorCallback> consumer =
+                        (Consumer<WwanSelectorCallback>) invocation.getArguments()[0];
+                consumer.accept(mWwanSelectorCallback);
+                return null;
+            }
+        }).when(mTransportSelectorCallback).onWwanSelected(any());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+        verify(mWwanSelectorCallback).onDomainSelected(eq(DOMAIN_PS), eq(true));
+    }
+
+    @Test
+    public void testWhileInEcbmOnWlanConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback, never()).onWwanSelected(any());
+    }
+
+    @Test
+    public void testWhileInEcbmOnWlanNotConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+    }
+
+    @Test
+    public void testNotInEcbmOnWlanConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(false).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+    }
+
+    @Test
+    public void testNotInEcbmOnWwanConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(false).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Consumer<WwanSelectorCallback> consumer =
+                        (Consumer<WwanSelectorCallback>) invocation.getArguments()[0];
+                consumer.accept(mWwanSelectorCallback);
+                return null;
+            }
+        }).when(mTransportSelectorCallback).onWwanSelected(any());
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+        verify(mWwanSelectorCallback, never()).onDomainSelected(anyInt(), anyBoolean());
+        verify(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+    }
+
+    @Test
+    public void testIsInRoaming() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        int[] domainPreference = new int[] {
+                CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+                };
+        bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_INT_ARRAY, domainPreference);
+        int[] domainPreferenceRoam = new int[] {
+                CarrierConfigManager.ImsEmergency.DOMAIN_PS_3GPP,
+                CarrierConfigManager.ImsEmergency.DOMAIN_CS,
+                };
+        bundle.putIntArray(KEY_EMERGENCY_DOMAIN_PREFERENCE_ROAMING_INT_ARRAY, domainPreferenceRoam);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn("").when(mTelephonyManager).getNetworkCountryIso();
+        doReturn("us").when(mTelephonyManager).getSimCountryIso();
+
+        mResultConsumer = null;
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(true);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
+                REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), eq(false), any(), any());
+        assertEquals(1, mAccessNetwork.size());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
+        assertNotNull(mResultConsumer);
+
+        regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "zz");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(2)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), eq(false), any(), any());
+        assertEquals(3, mAccessNetwork.size());
+        assertEquals(UTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(GERAN, (int) mAccessNetwork.get(1));
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(2));
+    }
+
     private void setupForScanListTest(PersistableBundle bundle) throws Exception {
         setupForScanListTest(bundle, false);
     }
 
     private void setupForScanListTest(PersistableBundle bundle, boolean psFailed) throws Exception {
-        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "");
         if (psFailed) {
             regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
@@ -2164,7 +3212,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
-        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_UNKNOWN,
                 0, false, false, 0, 0, "", "");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
@@ -2174,14 +3223,14 @@
         processAllMessages();
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), anyInt(), any(), any());
+                any(), anyInt(), anyBoolean(), any(), any());
         assertNotNull(mResultConsumer);
     }
 
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
-                mImsStateTracker, mDestroyListener, mCsrdCtrl, mCarrierConfigHelper);
+                mImsStateTracker, mDestroyListener, mCsrdCtrl, mCarrierConfigHelper, mEcbmHelper);
         mDomainSelector.clearResourceConfiguration();
         replaceInstance(DomainSelectorBase.class,
                 "mWwanSelectorCallback", mDomainSelector, mWwanSelectorCallback);
@@ -2208,7 +3257,7 @@
     private void verifyScanPreferred(int scanType, int expectedPreferredAccessNetwork) {
         processAllMessages();
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
-                any(), eq(scanType), any(), any());
+                any(), eq(scanType), anyBoolean(), any(), any());
         assertEquals(expectedPreferredAccessNetwork, (int) mAccessNetwork.get(0));
     }
 
@@ -2240,7 +3289,7 @@
         mDomainSelector.onImsMmTelCapabilitiesChanged();
     }
 
-    private static EmergencyRegResult getEmergencyRegResult(
+    private static EmergencyRegistrationResult getEmergencyRegResult(
             @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
             @NetworkRegistrationInfo.RegistrationState int regState,
             @NetworkRegistrationInfo.Domain int domain,
@@ -2250,13 +3299,13 @@
                 isEmcBearerSupported, emc, emf, mcc, mnc, "");
     }
 
-    private static EmergencyRegResult getEmergencyRegResult(
+    private static EmergencyRegistrationResult getEmergencyRegResult(
             @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
             @NetworkRegistrationInfo.RegistrationState int regState,
             @NetworkRegistrationInfo.Domain int domain,
             boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
             @NonNull String mcc, @NonNull String mnc, @NonNull String iso) {
-        return new EmergencyRegResult(accessNetwork, regState,
+        return new EmergencyRegistrationResult(accessNetwork, regState,
                 domain, isVopsSupported, isEmcBearerSupported,
                 emc, emf, mcc, mnc, iso);
     }
@@ -2350,12 +3399,19 @@
         return bundle;
     }
 
-    public static SelectionAttributes getSelectionAttributes(int slotId, int subId,
-            EmergencyRegResult regResult) {
+    private static SelectionAttributes getSelectionAttributes(int slotId, int subId,
+            EmergencyRegistrationResult regResult) {
+        return getSelectionAttributes(slotId, subId, false, regResult);
+    }
+
+    private static SelectionAttributes getSelectionAttributes(int slotId, int subId,
+            boolean isTestEmergencyNumber, EmergencyRegistrationResult regResult) {
         SelectionAttributes.Builder builder =
                 new SelectionAttributes.Builder(slotId, subId, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
                 .setEmergency(true)
-                .setEmergencyRegResult(regResult);
+                .setTestEmergencyNumber(isTestEmergencyNumber)
+                .setEmergencyRegistrationResult(regResult);
         return builder.build();
     }
 
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java
new file mode 100644
index 0000000..9a4e0d8
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for EmergencyCallbackModeHelper
+ */
+public class EmergencyCallbackModeHelperTest {
+    private static final String TAG = "EmergencyCallbackModeHelperTest";
+
+    private static final int SLOT_0 = 0;
+    private static final int SLOT_1 = 1;
+    private static final int SUB_1 = 1;
+    private static final int SUB_2 = 2;
+
+    private Context mContext;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+    private EmergencyCallbackModeHelper mEcbmHelper;
+    private CarrierConfigManager mCarrierConfigManager;
+    private TelephonyManager mTelephonyManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = new TestContext() {
+            private Intent mIntent;
+
+            @Override
+            public String getSystemServiceName(Class<?> serviceClass) {
+                if (serviceClass == TelephonyManager.class) {
+                    return Context.TELEPHONY_SERVICE;
+                } else if (serviceClass == CarrierConfigManager.class) {
+                    return Context.CARRIER_CONFIG_SERVICE;
+                }
+                return super.getSystemServiceName(serviceClass);
+            }
+
+            @Override
+            public String getOpPackageName() {
+                return "";
+            }
+
+            @Override
+            public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+                return mIntent;
+            }
+
+            @Override
+            public void sendStickyBroadcast(Intent intent) {
+                mIntent = intent;
+            }
+        };
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mHandlerThread = new HandlerThread("EmergencyCallbackModeHelperTest");
+        mHandlerThread.start();
+
+        try {
+            mLooper = new TestableLooper(mHandlerThread.getLooper());
+        } catch (Exception e) {
+            logd("Unable to create looper from handler.");
+        }
+
+        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+
+        mEcbmHelper = new EmergencyCallbackModeHelper(mContext, mHandlerThread.getLooper());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mEcbmHelper != null) {
+            mEcbmHelper.destroy();
+            mEcbmHelper = null;
+        }
+
+        if (mLooper != null) {
+            mLooper.destroy();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    public void testInit() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+        ArgumentCaptor<Executor> executorCaptor = ArgumentCaptor.forClass(Executor.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(executorCaptor.capture(),
+                callbackCaptor.capture());
+        assertNotNull(executorCaptor.getValue());
+        assertNotNull(callbackCaptor.getValue());
+    }
+
+    @Test
+    public void testEmergencyCallbackModeNotSupported() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM not supported
+        PersistableBundle b = getPersistableBundle(false);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        // No TelephonyCallback registered
+        verify(mTelephonyManager, never()).registerTelephonyCallback(any(), any());
+    }
+
+    @Test
+    public void testEmergencyCallbackModeSupported() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback registered
+        verify(mTelephonyManager).registerTelephonyCallback(any(),
+                telephonyCallbackCaptor.capture());
+
+        assertNotNull(telephonyCallbackCaptor.getValue());
+    }
+
+    @Test
+    public void testEmergencyCallbackModeChanged() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback registered
+        verify(mTelephonyManager).registerTelephonyCallback(any(),
+                telephonyCallbackCaptor.capture());
+
+        TelephonyCallback telephonyCallback = telephonyCallbackCaptor.getValue();
+
+        assertNotNull(telephonyCallback);
+
+        // Carrier config changes, ECBM not supported
+        b = getPersistableBundle(false);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        // TelephonyCallback unregistered
+        verify(mTelephonyManager).unregisterTelephonyCallback(eq(telephonyCallback));
+    }
+
+    @Test
+    public void testEmergencyCallbackModeEnter() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+        callback.onCarrierConfigChanged(SLOT_1, SUB_2, 0, 0);
+
+        // Enter ECBM on slot 1
+        mContext.sendStickyBroadcast(getIntent(true, SLOT_1));
+
+        assertFalse(mEcbmHelper.isInEmergencyCallbackMode(SLOT_0));
+        assertTrue(mEcbmHelper.isInEmergencyCallbackMode(SLOT_1));
+    }
+
+    @Test
+    public void testEmergencyCallbackModeExit() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        // Exit ECBM
+        mContext.sendStickyBroadcast(getIntent(false, SLOT_0));
+
+        assertFalse(mEcbmHelper.isInEmergencyCallbackMode(SLOT_0));
+    }
+
+    private static Intent getIntent(boolean inEcm, int slotIndex) {
+        Intent intent = new Intent(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+        intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, inEcm);
+        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
+        return intent;
+    }
+
+    private static PersistableBundle getPersistableBundle(boolean supported) {
+        PersistableBundle bundle  = new PersistableBundle();
+        bundle.putBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, supported);
+        return bundle;
+    }
+
+    private static void logd(String str) {
+        Log.d(TAG, str);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
index ed064cb..a900fda 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencySmsDomainSelectorTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -39,16 +40,17 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.DomainSelectionService.SelectionAttributes;
+import android.telephony.EmergencyRegistrationResult;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.TransportSelectorCallback;
 import android.telephony.VopsSupportInfo;
 import android.telephony.WwanSelectorCallback;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 import android.util.SparseArray;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TestContext;
@@ -92,6 +94,7 @@
     private ImsStateTracker.BarringInfoListener mBarringInfoListener;
     private ImsStateTracker.ServiceStateListener mServiceStateListener;
     private EmergencySmsDomainSelector mDomainSelector;
+    private EmergencyRegistrationResult mEmergencyRegistrationResult;
 
     @Before
     public void setUp() throws Exception {
@@ -151,6 +154,7 @@
             mLooper = null;
         }
 
+        mEmergencyRegistrationResult = null;
         mDomainSelector = null;
         mNetworkRegistrationInfo = null;
         mVopsSupportInfo = null;
@@ -246,6 +250,26 @@
 
     @Test
     @SmallTest
+    public void testIsSmsOverImsAvailableWhenImsRegisteredAndConfigEnabledAndNrAvailable() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, true, false);
+
+        assertTrue(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
+    public void testIsSmsOverImsAvailableWhenImsRegisteredAndConfigEnabledAndNrNotAvailable() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, false);
+
+        assertFalse(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
     public void testIsSmsOverImsAvailableWhenCarrierConfigManagerIsNull() {
         setUpImsStateTracker(AccessNetworkType.UNKNOWN);
         mCarrierConfigManagerNullTest = true;
@@ -291,7 +315,7 @@
 
     @Test
     @SmallTest
-    public void testIsSmsOverImsAvailableWhenNoLte() {
+    public void testIsSmsOverImsAvailableWhenNoLteOrNr() {
         setUpImsStateTracker(AccessNetworkType.UNKNOWN);
         setUpCarrierConfig(true);
         mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
@@ -395,6 +419,56 @@
 
     @Test
     @SmallTest
+    public void testIsSmsOverImsAvailableWhenNrNotRegisteredOrEmergencyNotEnabled() {
+        setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, false);
+
+        assertFalse(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
+    public void testIsSmsOverImsAvailableWhenNrInServiceAndNoDataSpecificRegistrationInfo() {
+        setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+        setUpCarrierConfig(true);
+        setUpNrInService(true, true, false, false);
+
+        assertFalse(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
+    public void testIsSmsOverImsAvailableWhenNrInServiceAndNoVopsSupportInfo() {
+        setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+        setUpCarrierConfig(true);
+        setUpNrInService(false, true, false, false);
+
+        assertFalse(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
+    public void testIsSmsOverImsAvailableWhenNrInServiceAndEmergencyServiceSupported() {
+        setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, true, false);
+
+        assertTrue(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
+    public void testIsSmsOverImsAvailableWhenNrInServiceAndEmergencyServiceFallbackSupported() {
+        setUpImsStateTracker(AccessNetworkType.UNKNOWN);
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, true);
+
+        assertTrue(mDomainSelector.isSmsOverImsAvailable());
+    }
+
+    @Test
+    @SmallTest
     public void testSelectDomainWhilePreviousRequestInProgress() {
         setUpImsStateTracker(AccessNetworkType.EUTRAN);
         setUpWwanSelectorCallback();
@@ -675,6 +749,116 @@
                 eq(true));
     }
 
+    @Test
+    @SmallTest
+    public void testSelectDomainWhileEmergencyNetworkScanInProgress() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpEmergencyRegResult(AccessNetworkType.NGRAN, NetworkRegistrationInfo.DOMAIN_PS, 1, 0);
+        setUpWwanSelectorCallback();
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, true);
+        setUpImsStateListener(true, true, true);
+
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        // Call the domain selection before completing the emergency network scan.
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        processAllMessages();
+
+        // onRequestEmergencyNetworkScan is invoked only once.
+        verify(mWwanSelectorCallback).onRequestEmergencyNetworkScan(any(), anyInt(),
+                anyBoolean(), any(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testSelectDomainWhenNrEmergencyServiceSupported() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpEmergencyRegResult(AccessNetworkType.NGRAN, NetworkRegistrationInfo.DOMAIN_PS, 1, 0);
+        setUpWwanSelectorCallback();
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, true, false);
+        setUpImsStateListener(true, true, true);
+
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        processAllMessages();
+
+        // Expected: PS network
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(true));
+    }
+
+    @Test
+    @SmallTest
+    public void testSelectDomainWhenEmergencyRegistrationResultNgranAndPsDomain() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpEmergencyRegResult(AccessNetworkType.NGRAN, NetworkRegistrationInfo.DOMAIN_PS, 1, 0);
+        setUpWwanSelectorCallback();
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, true);
+        setUpImsStateListener(true, true, true);
+
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        processAllMessages();
+
+        // Expected: PS network
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(true));
+    }
+
+    @Test
+    @SmallTest
+    public void testSelectDomainWhenEmergencyRegistrationResultEutranAndPsDomain() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpEmergencyRegResult(AccessNetworkType.EUTRAN, NetworkRegistrationInfo.DOMAIN_PS, 0, 0);
+        setUpWwanSelectorCallback();
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, true);
+        setUpImsStateListener(true, true, true);
+
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        processAllMessages();
+
+        // Expected: PS network
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_PS),
+                eq(true));
+    }
+
+    @Test
+    @SmallTest
+    public void testSelectDomainWhenEmergencyRegistrationResultEutranAndCsDomain() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpEmergencyRegResult(AccessNetworkType.EUTRAN, NetworkRegistrationInfo.DOMAIN_CS, 0, 0);
+        setUpWwanSelectorCallback();
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, true);
+        setUpImsStateListener(true, true, true);
+
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        processAllMessages();
+
+        // Expected: CS network
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
+    }
+
+    @Test
+    @SmallTest
+    public void testSelectDomainWhenEmergencyRegistrationResultUtranAndCsDomain() {
+        setUpImsStateTracker(AccessNetworkType.NGRAN);
+        setUpEmergencyRegResult(AccessNetworkType.UTRAN, NetworkRegistrationInfo.DOMAIN_CS, 0, 0);
+        setUpWwanSelectorCallback();
+        setUpCarrierConfig(true);
+        setUpNrInService(false, false, false, true);
+        setUpImsStateListener(true, true, true);
+
+        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
+        processAllMessages();
+
+        // Expected: CS network
+        verify(mWwanSelectorCallback).onDomainSelected(eq(NetworkRegistrationInfo.DOMAIN_CS),
+                eq(false));
+    }
+
     private void setUpCarrierConfig(boolean supported) {
         PersistableBundle b = new PersistableBundle();
         b.putBoolean(CarrierConfigManager.KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, supported);
@@ -760,6 +944,29 @@
         mBarringInfoListener.onBarringInfoUpdated(barringInfo);
     }
 
+    private void setUpNrInService(boolean noDataSpecificRegistrationInfo,
+            boolean noVopsSupportInfo, boolean emergencyServiceSupported,
+            boolean emergencyServiceFallbackSupported) {
+        DataSpecificRegistrationInfo dsri = noDataSpecificRegistrationInfo
+                ? null : new DataSpecificRegistrationInfo(
+                        8, false, false, false, noVopsSupportInfo ? null : mVopsSupportInfo);
+
+        mNetworkRegistrationInfo = new NetworkRegistrationInfo.Builder()
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
+                .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+                .setDataSpecificInfo(dsri)
+                .build();
+        when(mServiceState.getNetworkRegistrationInfo(
+                anyInt(), eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
+                .thenReturn(mNetworkRegistrationInfo);
+        when(mVopsSupportInfo.isEmergencyServiceSupported()).thenReturn(emergencyServiceSupported);
+        when(mVopsSupportInfo.isEmergencyServiceFallbackSupported())
+                .thenReturn(emergencyServiceFallbackSupported);
+
+        mServiceStateListener.onServiceStateUpdated(mServiceState);
+        mBarringInfoListener.onBarringInfoUpdated(null);
+    }
+
     private void setUpImsStateTracker(@RadioAccessNetworkType int accessNetworkType) {
         setUpImsStateTracker(accessNetworkType, true, true);
     }
@@ -783,6 +990,23 @@
             callback.accept(mWwanSelectorCallback);
             return null;
         }).when(mTransportSelectorCallback).onWwanSelected(any(Consumer.class));
+
+        doAnswer((invocation) -> {
+            Object[] args = invocation.getArguments();
+            final Consumer<EmergencyRegistrationResult> result =
+                    (Consumer<EmergencyRegistrationResult>) args[4];
+            result.accept(mEmergencyRegistrationResult);
+            return null;
+        }).when(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+    }
+
+    private void setUpEmergencyRegResult(
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
+            @NetworkRegistrationInfo.Domain int domain, int nrEs, int nrEsfb) {
+        mEmergencyRegistrationResult = new EmergencyRegistrationResult(accessNetwork,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+                domain, true, true, nrEs, nrEsfb, "001", "01", "");
     }
 
     private void setUpImsStateListener(boolean notifyMmTelFeatureAvailable,
diff --git a/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java b/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
index 6519835..f9a56a8 100644
--- a/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/ImsStateTrackerTest.java
@@ -47,8 +47,8 @@
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.test.suitebuilder.annotation.SmallTest;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TestContext;
diff --git a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
index 002c7d5..6e438bf 100644
--- a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
@@ -17,7 +17,6 @@
 package com.android.services.telephony.domainselection;
 
 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
-import static android.telephony.DomainSelectionService.SELECTOR_TYPE_UT;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -28,17 +27,19 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.Uri;
 import android.os.CancellationSignal;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
+import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelector;
-import android.telephony.EmergencyRegResult;
+import android.telephony.EmergencyRegistrationResult;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -69,10 +70,12 @@
 public class NormalCallDomainSelectorTest {
     private static final String TAG = "NormalCallDomainSelectorTest";
 
+    private static final int SELECTOR_TYPE_UT = 3;
     private static final int SLOT_ID = 0;
     private static final int SUB_ID_1 = 1;
     private static final int SUB_ID_2 = 2;
     private static final String TEST_CALLID = "01234";
+    private static final Uri TEST_URI = Uri.fromParts(PhoneAccount.SCHEME_TEL, "123456789", null);
 
     private HandlerThread mHandlerThread;
     private NormalCallDomainSelector mNormalCallDomainSelector;
@@ -150,13 +153,54 @@
     }
 
     @Test
-    public void testSelectDomainInputParams() {
+    public void testInitialState() {
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+    }
+
+    @Test
+    public void testDestroyedState() {
+        mNormalCallDomainSelector.destroy();
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
+    }
+
+    @Test
+    public void testDestroyedDuringActiveState() {
         MockTransportSelectorCallback transportSelectorCallback =
-                new MockTransportSelectorCallback();
+                new MockTransportSelectorCallback(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(true)
+                        .setExitedFromAirplaneMode(false)
+                        .build();
+
+        mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.ACTIVE);
+
+        mNormalCallDomainSelector.destroy();
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
+    }
+
+    @Test
+    public void testSelectDomainInputParams() {
+        MockTransportSelectorCallback transportSelectorCallback =
+                new MockTransportSelectorCallback(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(true)
@@ -164,6 +208,8 @@
                         .build();
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.ACTIVE);
 
         // Case 1: null inputs
         try {
@@ -172,6 +218,9 @@
             fail("Invalid input params not handled." + e.getMessage());
         }
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         // Case 2: null TransportSelectorCallback
         try {
             mNormalCallDomainSelector.selectDomain(attributes, null);
@@ -179,6 +228,9 @@
             fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         // Case 3: null SelectionAttributes
         transportSelectorCallback.mSelectionTerminated = false;
         try {
@@ -190,9 +242,13 @@
         assertTrue(transportSelectorCallback
                 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
+
         // Case 4: Invalid Subscription-id
         attributes = new DomainSelectionService.SelectionAttributes.Builder(
                 SLOT_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
                 .setCallId(TEST_CALLID)
                 .setEmergency(false)
                 .setVideoCall(true)
@@ -207,10 +263,14 @@
         assertTrue(transportSelectorCallback
                 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
+
         // Case 5: Invalid SELECTOR_TYPE
         attributes =
                 new DomainSelectionService.SelectionAttributes.Builder(
                         SLOT_ID, SUB_ID_1, SELECTOR_TYPE_UT)
+                        .setAddress(TEST_URI)
                         .setCallId(TEST_CALLID)
                         .setEmergency(false)
                         .setVideoCall(true)
@@ -225,9 +285,13 @@
         assertTrue(transportSelectorCallback
                 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
+
         // Case 6: Emergency Call
         attributes = new DomainSelectionService.SelectionAttributes.Builder(
                 SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
                 .setCallId(TEST_CALLID)
                 .setEmergency(true)
                 .setVideoCall(true)
@@ -239,6 +303,9 @@
             fail("Invalid params (SelectionAttributes) not handled." + e.getMessage());
         }
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
+
         assertTrue(transportSelectorCallback
                 .verifyOnSelectionTerminated(DisconnectCause.OUTGOING_FAILURE));
     }
@@ -246,30 +313,38 @@
     @Test
     public void testOutOfService() {
         MockTransportSelectorCallback transportSelectorCallback =
-                new MockTransportSelectorCallback();
+                new MockTransportSelectorCallback(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(true)
                         .setExitedFromAirplaneMode(false)
                         .build();
+
         ServiceState serviceState = new ServiceState();
         serviceState.setStateOutOfService();
         initialize(serviceState, false, false, false, false);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback
                 .verifyOnSelectionTerminated(DisconnectCause.OUT_OF_SERVICE));
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.DESTROYED);
     }
 
     @Test
     public void testDomainSelection() {
         MockTransportSelectorCallback transportSelectorCallback =
-                new MockTransportSelectorCallback();
+                new MockTransportSelectorCallback(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)
@@ -280,32 +355,49 @@
         ServiceState serviceState = new ServiceState();
         serviceState.setState(ServiceState.STATE_IN_SERVICE);
         initialize(serviceState, true, true, true, true);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWlanSelected());
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         // Case 2: 5G
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         initialize(serviceState, true, false, true, true);
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         // Case 3: PS -> CS redial
         ImsReasonInfo imsReasonInfo = new ImsReasonInfo();
         imsReasonInfo.mCode = ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED;
         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)
                 .setPsDisconnectCause(imsReasonInfo)
                 .build();
+
         mNormalCallDomainSelector.reselectDomain(attributes);
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         // Case 4: CS call
         NetworkRegistrationInfo nwRegistrationInfo = new NetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_CS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
@@ -313,28 +405,40 @@
                 AccessNetworkConstants.AccessNetworkType.UTRAN, 0, false,
                 null, null, null, false, 0, 0, 0);
         serviceState.addNetworkRegistrationInfo(nwRegistrationInfo);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         initialize(serviceState, false, false, false, false);
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         //Case 5: Backup calling
         serviceState.setStateOutOfService();
         initialize(serviceState, true, true, true, true);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWlanSelected());
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.ACTIVE);
     }
 
     @Test
     public void testWPSCallDomainSelection() {
         MockTransportSelectorCallback transportSelectorCallback =
-                new MockTransportSelectorCallback();
+                new MockTransportSelectorCallback(mNormalCallDomainSelector);
         DomainSelectionService.SelectionAttributes attributes =
                 new DomainSelectionService.SelectionAttributes.Builder(
                         SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
-                        .setNumber("*272121")
+                        .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, "*272121", null))
                         .setCallId(TEST_CALLID)
                         .setEmergency(false)
                         .setVideoCall(false)
@@ -346,38 +450,57 @@
         config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, false);
         doReturn(config).when(mMockCarrierConfigMgr).getConfigForSubId(SUB_ID_1,
                 new String[]{CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL});
+
         ServiceState serviceState = new ServiceState();
         serviceState.setState(ServiceState.STATE_IN_SERVICE);
         initialize(serviceState, true, true, true, true);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         //Case 2: WPS supported by IMS and WLAN registered
         config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         serviceState.setState(ServiceState.STATE_IN_SERVICE);
         initialize(serviceState, true, true, true, true);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWlanSelected());
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         //Case 2: WPS supported by IMS and LTE registered
         config.putBoolean(CarrierConfigManager.KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         serviceState.setState(ServiceState.STATE_IN_SERVICE);
         initialize(serviceState, true, false, true, true);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
     }
 
     @Test
     public void testTtyCallDomainSelection() {
         MockTransportSelectorCallback transportSelectorCallback =
-                new MockTransportSelectorCallback();
+                new MockTransportSelectorCallback(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)
@@ -390,29 +513,48 @@
         config.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
         doReturn(config).when(mMockCarrierConfigMgr).getConfigForSubId(SUB_ID_1,
                 new String[]{CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL});
+
         ServiceState serviceState = new ServiceState();
         serviceState.setState(ServiceState.STATE_IN_SERVICE);
         initialize(serviceState, true, false, true, true);
+
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_CS));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         //Case 2: TTY supported by IMS and TTY enabled
         config.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
 
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
+
         //Case 3: TTY supported by IMS and TTY disabled
         doReturn(TelecomManager.TTY_MODE_OFF).when(mMockTelecomManager).getCurrentTtyMode();
         mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+
         assertTrue(transportSelectorCallback.verifyOnWwanSelected());
+
         assertTrue(transportSelectorCallback
                 .verifyOnDomainSelected(NetworkRegistrationInfo.DOMAIN_PS));
+
+        assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                NormalCallDomainSelector.SelectorState.INACTIVE);
     }
 
+
+
     static class MockTransportSelectorCallback implements TransportSelectorCallback,
             WwanSelectorCallback {
         public boolean mCreated;
@@ -422,11 +564,20 @@
         public boolean mDomainSelected;
         int mCauseCode;
         int mSelectedDomain;
+        NormalCallDomainSelector mNormalCallDomainSelector;
+
+        MockTransportSelectorCallback(NormalCallDomainSelector normalCallDomainSelector) {
+            mNormalCallDomainSelector = normalCallDomainSelector;
+        }
 
         @Override
         public synchronized void onCreated(DomainSelector selector) {
             Log.d(TAG, "onCreated");
             mCreated = true;
+
+            assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                    NormalCallDomainSelector.SelectorState.INACTIVE);
+
             notifyAll();
         }
 
@@ -441,6 +592,10 @@
         public synchronized void onWlanSelected(boolean useEmergencyPdn) {
             Log.d(TAG, "onWlanSelected");
             mWlanSelected = true;
+
+            assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                    NormalCallDomainSelector.SelectorState.INACTIVE);
+
             notifyAll();
         }
 
@@ -451,18 +606,14 @@
         }
 
         @Override
-        public synchronized WwanSelectorCallback onWwanSelected() {
-            mWwanSelected = true;
-            notifyAll();
-            return (WwanSelectorCallback) this;
-        }
-
-        @Override
         public void onWwanSelected(final Consumer<WwanSelectorCallback> consumer) {
             mWwanSelected = true;
             Executors.newSingleThreadExecutor().execute(() -> {
                 consumer.accept(this);
             });
+
+            assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                    NormalCallDomainSelector.SelectorState.INACTIVE);
         }
 
         public boolean verifyOnWwanSelected() {
@@ -475,6 +626,10 @@
             Log.i(TAG, "onSelectionTerminated - called");
             mCauseCode = cause;
             mSelectionTerminated = true;
+
+            assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                    NormalCallDomainSelector.SelectorState.INACTIVE);
+
             notifyAll();
         }
 
@@ -499,9 +654,10 @@
 
         @Override
         public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks,
-                                                  int scanType,
-                                                  @NonNull CancellationSignal signal,
-                                                  @NonNull Consumer<EmergencyRegResult> consumer) {
+                int scanType,
+                boolean resetScan,
+                @NonNull CancellationSignal signal,
+                @NonNull Consumer<EmergencyRegistrationResult> consumer) {
             Log.i(TAG, "onRequestEmergencyNetworkScan - called");
 
         }
@@ -511,6 +667,10 @@
             Log.i(TAG, "onDomainSelected - called");
             mSelectedDomain = domain;
             mDomainSelected = true;
+
+            assertEquals(mNormalCallDomainSelector.getSelectorState(),
+                    NormalCallDomainSelector.SelectorState.INACTIVE);
+
             notifyAll();
         }
 
diff --git a/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
index 8f78a58..fc577c4 100644
--- a/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/SmsDomainSelectorTest.java
@@ -37,9 +37,9 @@
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.TransportSelectorCallback;
 import android.telephony.WwanSelectorCallback;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TestContext;
@@ -190,21 +190,6 @@
 
     @Test
     @SmallTest
-    public void testCancelSelection() {
-        setUpImsStateTracker(AccessNetworkType.EUTRAN);
-
-        mDomainSelector.selectDomain(mSelectionAttributes, mTransportSelectorCallback);
-
-        assertTrue(mDomainSelector.isDomainSelectionRequested());
-
-        mDomainSelector.cancelSelection();
-
-        assertFalse(mDomainSelector.isDomainSelectionRequested());
-        verify(mDomainSelectorDestroyListener).onDomainSelectorDestroyed(eq(mDomainSelector));
-    }
-
-    @Test
-    @SmallTest
     public void testFinishSelection() {
         setUpImsStateTracker(AccessNetworkType.EUTRAN);
 
diff --git a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
index f4d2732..e0f7ffb 100644
--- a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -39,9 +40,9 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TransportSelectorCallback;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TestContext;
@@ -81,11 +82,11 @@
                         @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
                         @NonNull DomainSelectorBase.DestroyListener listener,
                         @NonNull CrossSimRedialingController crossSimRedialingController,
-                        @NonNull CarrierConfigHelper carrierConfigHelper) {
+                        @NonNull CarrierConfigHelper carrierConfigHelper,
+                        @NonNull EmergencyCallbackModeHelper ecbmHelper) {
                     switch (selectorType) {
                         case DomainSelectionService.SELECTOR_TYPE_CALLING: // fallthrough
-                        case DomainSelectionService.SELECTOR_TYPE_SMS: // fallthrough
-                        case DomainSelectionService.SELECTOR_TYPE_UT:
+                        case DomainSelectionService.SELECTOR_TYPE_SMS:
                             mDomainSelectorDestroyListener = listener;
                             if (subId == SUB_1) {
                                 return mDomainSelectorBase1;
@@ -97,6 +98,26 @@
                     }
                 }
             };
+    private static class TestTelephonyDomainSelectionService
+            extends TelephonyDomainSelectionService {
+        private final Context mContext;
+
+        TestTelephonyDomainSelectionService(Context context,
+                @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
+                @NonNull DomainSelectorFactory domainSelectorFactory,
+                @Nullable CarrierConfigHelper carrierConfigHelper,
+                @Nullable EmergencyCallbackModeHelper ecbmHelper) {
+            super(imsStateTrackerFactory, domainSelectorFactory, carrierConfigHelper, ecbmHelper);
+            mContext = context;
+        }
+
+        @Override
+        public void onCreate() {
+            // attach test context.
+            attachBaseContext(mContext);
+            super.onCreate();
+        }
+    }
     private static final int SLOT_0 = 0;
     private static final int SUB_1 = 1;
     private static final int SUB_2 = 2;
@@ -111,6 +132,7 @@
     @Mock private TransportSelectorCallback mSelectorCallback2;
     @Mock private ImsStateTracker mImsStateTracker;
     @Mock private CarrierConfigHelper mCarrierConfigHelper;
+    @Mock private EmergencyCallbackModeHelper mEcbmHelper;
 
     private final ServiceState mServiceState = new ServiceState();
     private final BarringInfo mBarringInfo = new BarringInfo();
@@ -131,8 +153,9 @@
         }
 
         mContext = new TestContext();
-        mDomainSelectionService = new TelephonyDomainSelectionService(mContext,
-                mImsStateTrackerFactory, mDomainSelectorFactory, mCarrierConfigHelper);
+        mDomainSelectionService = new TestTelephonyDomainSelectionService(mContext,
+                mImsStateTrackerFactory, mDomainSelectorFactory, mCarrierConfigHelper, mEcbmHelper);
+        mDomainSelectionService.onCreate();
         mServiceHandler = new Handler(mDomainSelectionService.getLooper());
         mTestableLooper = new TestableLooper(mDomainSelectionService.getLooper());
 
@@ -184,9 +207,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_1));
@@ -203,9 +224,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker, never()).start(anyInt());
@@ -222,9 +241,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_1));
@@ -240,9 +257,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
-        });
+        mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_2));
@@ -259,9 +274,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_1));
@@ -274,9 +287,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
-        });
+        mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_2));
@@ -309,9 +320,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         mDomainSelectionService.onDestroy();
diff --git a/utils/satellite/README.md b/utils/satellite/README.md
index e219823..77ee0fb 100644
--- a/utils/satellite/README.md
+++ b/utils/satellite/README.md
@@ -8,7 +8,7 @@
 - `src/write` S2 write code used by tools to write the s2 cells into a
   binary file. This code is also used by `TeleServiceTests`.
 - `src/readonly` S2 read-only code used by the above read-write code and the class
- `S2RangeFileBasedSatelliteLocationLookup`.
+ `S2RangeSatelliteOnDeviceAccessController`.
 
 `tools`
 - `src/main` Contains the tools for generating binary satellite s2 file, and tools
@@ -29,7 +29,7 @@
   list of S2 cells ID.
 - Command: `$satellite_createsats2file --input-file <s2cells.txt> --s2-level <12>
   --is-allowed-list <true> --output-file <sats2.dat>`
-  - `--input-file` Each line in the file contains a `signed-64bit` number which represents
+  - `--input-file` Each line in the file contains a `unsigned-64bit` number which represents
     the ID of a S2 cell.
   - `--s2-level` The S2 level of all the cells in the input file.
   - `--is-allowed-list` Should be either `trrue` or `false`
@@ -51,7 +51,7 @@
   - [(prefix=0b100_11111111, suffix=1000), (prefix=0b100_11111111, suffix=2000))
   - [(prefix=0b100_11111111, suffix=2000), (prefix=0b100_11111111, suffix=3000))
   - [(prefix=0b101_11111111, suffix=1000), (prefix=0b101_11111111, suffix=2000))
-- Run the test tool: `$satellite_createtestsats2file /tmp/foo.dat`
+- Run the test tool: `satellite_createsats2file_test /tmp/foo.dat`
   - This command will generate the binary satellite S2 cell file `/tmp/foo.dat` with
   the above S2 ranges.
 
@@ -59,4 +59,9 @@
 - Dump the input binary satellite S2 cell file into human-readable text format.
 - Run the tool: `$satellite_dumpsats2file /tmp/foo.dat /tmp/foo`
   - `/tmp/foo.dat` Input binary satellite S2 cell file.
-  - `/tmp/foo` Output directory which contains the output text files.
\ No newline at end of file
+  - `/tmp/foo` Output directory which contains the output text files.
+
+`satellite_location_lookup`
+- Check if a location is present in the input satellite S2 file.
+- Run the tool: `$satellite_location_lookup --input-file <...> --lat-degrees <...>
+  --lng-degrees <...>`
\ No newline at end of file
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 4b8a026..3cf2c78 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
@@ -107,19 +107,22 @@
         try (PrintStream printer = new PrintStream(outputFile)) {
             // Range 1
             for (int suffix = 1000; suffix < 2000; suffix++) {
-                printer.println(String.valueOf(fileFormat.createCellId(0b100_11111111, suffix)));
+                printer.println(
+                        Long.toUnsignedString(fileFormat.createCellId(0b100_11111111, suffix)));
             }
 
             // Range 2
             for (int suffix = 2001; suffix < 3000; suffix++) {
-                printer.println(String.valueOf(fileFormat.createCellId(0b100_11111111, suffix)));
+                printer.println(
+                        Long.toUnsignedString(fileFormat.createCellId(0b100_11111111, suffix)));
             }
 
             // Range 3
             for (int suffix = 1000; suffix < 2000; suffix++) {
-                printer.println(String.valueOf(fileFormat.createCellId(0b101_11111111, suffix)));
+                printer.println(
+                        Long.toUnsignedString(fileFormat.createCellId(0b101_11111111, suffix)));
             }
-            printer.print(String.valueOf(fileFormat.createCellId(0b101_11111111, 2000)));
+            printer.print(Long.toUnsignedString(fileFormat.createCellId(0b101_11111111, 2000)));
 
             printer.close();
         }
@@ -130,13 +133,13 @@
             File outputFile, SatS2RangeFileFormat fileFormat) throws Exception {
         try (PrintStream printer = new PrintStream(outputFile)) {
             // Valid line
-            printer.println(String.valueOf(fileFormat.createCellId(0b100_11111111, 100)));
+            printer.println(Long.toUnsignedString(fileFormat.createCellId(0b100_11111111, 100)));
 
             // Invalid line
             printer.print("Invalid line");
 
             // Another valid line
-            printer.println(String.valueOf(fileFormat.createCellId(0b100_11111111, 200)));
+            printer.println(Long.toUnsignedString(fileFormat.createCellId(0b100_11111111, 200)));
 
             printer.close();
         }
diff --git a/utils/satellite/tools/Android.bp b/utils/satellite/tools/Android.bp
index 9aacdd9..d48b911 100644
--- a/utils/satellite/tools/Android.bp
+++ b/utils/satellite/tools/Android.bp
@@ -39,6 +39,15 @@
     ],
 }
 
+// A tool to look up a location in the input binary satellite S2 file.
+java_binary_host {
+    name: "satellite_location_lookup",
+    main_class: "com.android.telephony.tools.sats2.SatS2LocationLookup",
+    static_libs: [
+        "satellite-s2storage-tools",
+    ],
+}
+
 // A tool to create a test satellite S2 file.
 java_binary_host {
     name: "satellite_createsats2file_test",
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 b701a7b..bc25d6b 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
@@ -30,18 +30,19 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Scanner;
+import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
 
 /** A util class for creating a satellite S2 file from the list of S2 cells. */
 public final class SatS2FileCreator {
     /**
      * @param inputFile The input text file containing the list of S2 Cell IDs. Each line in the
-     *                  file contains a number in the range of a signed-64bit number which
+     *                  file contains a number in the range of an unsigned-64bit number which
      *                  represents the ID of a S2 cell.
      * @param s2Level The S2 level of all S2 cells in the input file.
      * @param isAllowedList {@code true} means the input file contains an allowed list of S2 cells.
@@ -57,12 +58,12 @@
         System.out.println("Number of S2 cells read from file:" + s2Cells.size());
 
         // Convert the input list of S2 Cells into the list of sorted S2CellId
-        List<S2CellId> sortedS2CellIds = s2Cells.stream()
-                .map(x -> new S2CellId(x))
-                .collect(Collectors.toList());
+        System.out.println("Denormalizing S2 Cell IDs to the expected s2 level=" + s2Level);
+        List<S2CellId> sortedS2CellIds = denormalize(s2Cells, s2Level);
         // IDs of S2CellId are converted to unsigned long numbers, which will be then used to
         // compare S2CellId.
         Collections.sort(sortedS2CellIds);
+        System.out.println("Number of S2 cell IDs:" + sortedS2CellIds.size());
 
         // Compress the list of S2CellId into S2 ranges
         List<SatS2Range> satS2Ranges = createSatS2Ranges(sortedS2CellIds, s2Level);
@@ -132,25 +133,56 @@
      * Read a list of S2 cells from the inputFile.
      *
      * @param inputFile A file containing the list of S2 cells. Each line in the inputFile contains
-     *                  a long number - the ID of a S2 cell.
+     *                  an unsigned long number - the ID of a S2 cell.
      * @return A list of S2 cells.
      */
     private static List<Long> readS2CellsFromFile(String inputFile) throws Exception {
         List<Long> s2Cells = new ArrayList();
         InputStream inputStream = new FileInputStream(inputFile);
         try (Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name())) {
-            while (scanner.hasNextLong()) {
-                s2Cells.add(scanner.nextLong());
-            }
-            if (scanner.hasNextLine()) {
-                throw new IllegalStateException("Input s2 cell file has invalid format, "
-                        + "current line=" + scanner.nextLine());
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine();
+                try {
+                    s2Cells.add(Long.parseUnsignedLong(line));
+                } catch (Exception ex) {
+                    throw new IllegalStateException("Input s2 cell file has invalid format, "
+                            + "current line=" + line);
+                }
             }
         }
         return s2Cells;
     }
 
     /**
+     * Convert the list of S2 Cell numbers into the list of S2 Cell IDs at the expected level.
+     */
+    private static List<S2CellId> denormalize(List<Long> s2CellNumbers, int s2Level) {
+        Set<S2CellId> result = new HashSet<>();
+        for (long s2CellNumber : s2CellNumbers) {
+            S2CellId s2CellId = new S2CellId(s2CellNumber);
+            if (s2CellId.level() == s2Level) {
+                if (!result.contains(s2CellId)) {
+                    result.add(s2CellId);
+                }
+            } else if (s2CellId.level() < s2Level) {
+                S2CellId childEnd = s2CellId.childEnd(s2Level);
+                for (s2CellId = s2CellId.childBegin(s2Level); !s2CellId.equals(childEnd);
+                        s2CellId = s2CellId.next()) {
+                    if (!result.contains(s2CellId)) {
+                        result.add(s2CellId);
+                    }
+                }
+            } else {
+                S2CellId parent = s2CellId.parent(s2Level);
+                if (!result.contains(parent)) {
+                    result.add(parent);
+                }
+            }
+        }
+        return new ArrayList(result);
+    }
+
+    /**
      * Compress the list of sorted S2CellId into S2 ranges.
      *
      * @param sortedS2CellIds List of S2CellId sorted in ascending order.
diff --git a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java
new file mode 100644
index 0000000..444ff8d
--- /dev/null
+++ b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telephony.tools.sats2;
+
+import com.android.telephony.sats2range.read.SatS2RangeFileReader;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.Parameter;
+import com.google.common.geometry.S2CellId;
+import com.google.common.geometry.S2LatLng;
+
+import java.io.File;
+
+/** A util class for checking if a location is in the input satellite S2 file. */
+public final class SatS2LocationLookup {
+    /**
+     *  A util method for checking if a location is in the input satellite S2 file.
+     */
+    public static void main(String[] args) throws Exception {
+        Arguments arguments = new Arguments();
+        JCommander.newBuilder()
+                .addObject(arguments)
+                .build()
+                .parse(args);
+
+        try (SatS2RangeFileReader satS2RangeFileReader =
+                     SatS2RangeFileReader.open(new File(arguments.inputFile))) {
+            S2CellId s2CellId = getS2CellId(arguments.latDegrees, arguments.lngDegrees,
+                    satS2RangeFileReader.getS2Level());
+            System.out.println("s2CellId=" + Long.toUnsignedString(s2CellId.id()));
+            if (satS2RangeFileReader.findEntryByCellId(s2CellId.id()) == null) {
+                System.out.println("The input file does not contain the input location");
+            } else {
+                System.out.println("The input file contains the input location");
+            }
+        }
+    }
+
+    private static S2CellId getS2CellId(double latDegrees, double lngDegrees, int s2Level) {
+        // Create the leaf S2 cell containing the given S2LatLng
+        S2CellId cellId = S2CellId.fromLatLng(S2LatLng.fromDegrees(latDegrees, lngDegrees));
+
+        // Return the S2 cell at the expected S2 level
+        return cellId.parent(s2Level);
+    }
+
+    private static class Arguments {
+        @Parameter(names = "--input-file",
+                description = "sat s2 file",
+                required = true)
+        public String inputFile;
+
+        @Parameter(names = "--lat-degrees",
+                description = "lat degress of the location",
+                required = true)
+        public double latDegrees;
+
+        @Parameter(names = "--lng-degrees",
+                description = "lng degress of the location",
+                required = true)
+        public double lngDegrees;
+    }
+}