Merge "[log] debug getDataNetworkType" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f1d877f..355f6ef 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -281,6 +281,16 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.phone.settings.SatelliteConfigViewer"
+            android:label="@string/satellite_config_viewer"
+            android:exported="true"
+            android:theme="@style/DialerSettingsLight">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="CdmaCallOptions"
                 android:label="@string/cdma_options"
                 android:exported="false"
diff --git a/OWNERS b/OWNERS
index 96033ab..dd12ee3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,4 +2,4 @@
 
 per-file *SimPhonebookProvider* = file:platform/packages/apps/Contacts:/OWNERS
 
-per-file config.xml=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com
+per-file config.xml=hwangoo@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com,jdyou@google.com
diff --git a/res/layout/radio_info.xml b/res/layout/radio_info.xml
index c4ab74f..9084891 100644
--- a/res/layout/radio_info.xml
+++ b/res/layout/radio_info.xml
@@ -333,6 +333,16 @@
                 android:text="@string/demo_esos_satellite_string"
         />
 
+        <!-- Satellite Config Viewer -->
+        <Button android:id="@+id/satellite_config_viewer"
+            android:textSize="14sp"
+            android:layout_marginTop="8dip"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAllCaps="false"
+            android:text="@string/satellite_config_viewer"
+            />
+
         <!-- VoLTE provisioned -->
         <Switch android:id="@+id/volte_provisioned_switch"
                 android:textSize="14sp"
diff --git a/res/layout/satellite_config_viewer.xml b/res/layout/satellite_config_viewer.xml
new file mode 100644
index 0000000..240e697
--- /dev/null
+++ b/res/layout/satellite_config_viewer.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2025 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginTop="120dp"
+    android:layoutDirection="locale"
+    android:textDirection="locale">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="16dp">
+
+        <!-- VERSION -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/satellite_config_version_label" style="@style/info_label" />
+            <TextView android:id="@+id/version" style="@style/info_value" />
+            <View
+                android:layout_width="fill_parent"
+                android:layout_height="1dip"
+                android:background="?android:attr/listDivider"/>
+        </LinearLayout>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="2dip"
+            android:background="?android:attr/listDivider" />
+
+        <!-- cids, plmns, svcTypes -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/satellite_config_service_type_label" style="@style/info_label" />
+            <TextView android:id="@+id/svc_type" style="@style/info_value" />
+        </LinearLayout>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="3dip"
+            android:background="?android:attr/listDivider"/>
+
+        <!-- allow access -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/satellite_config_allow_access_label" style="@style/info_label" />
+            <TextView android:id="@+id/allow_access" style="@style/info_value" />
+        </LinearLayout>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="4dip"
+            android:background="?android:attr/listDivider"/>
+
+        <!-- country codes -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/satellite_config_country_code_label" style="@style/info_label" />
+            <TextView android:id="@+id/country_codes" style="@style/info_value" />
+        </LinearLayout>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="5dip"
+            android:background="?android:attr/listDivider"/>
+
+        <!-- size of sats2dat file -->
+        <LinearLayout style="@style/RadioInfo_entry_layout">
+            <TextView android:text="@string/satellite_config_size_of_sats2_dat_label" style="@style/info_label" />
+            <TextView android:id="@+id/size_of_sats2" style="@style/info_value" />
+        </LinearLayout>
+
+        <View
+            android:layout_width="fill_parent"
+            android:layout_height="1dip"
+            android:background="?android:attr/listDivider"/>
+
+        <!-- satellite access config json -->
+        <LinearLayout style="@style/entry_layout">
+            <TextView android:text="@string/satellite_config_json_label" style="@style/info_label" />
+            <TextView android:id="@+id/config_json" style="@style/info_value" />
+        </LinearLayout>
+
+    </LinearLayout>
+</ScrollView>
+
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 49485ae..7f11fd1 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -85,7 +85,7 @@
     <string name="smart_forwarding_settings_menu_summary" msgid="5096947726032885325">"Cuando no se puede contactar con un número, las llamadas se desvían siempre a otro número"</string>
     <string name="voicemail_notifications_preference_title" msgid="7829238858063382977">"Notificaciones"</string>
     <string name="cell_broadcast_settings" msgid="8135324242541809924">"Difusiones de emergencia"</string>
-    <string name="call_settings" msgid="3677282690157603818">"Ajustes de llamadas"</string>
+    <string name="call_settings" msgid="3677282690157603818">"Ajustes de llamada"</string>
     <string name="additional_gsm_call_settings" msgid="1561980168685658846">"Ajustes adicionales"</string>
     <string name="additional_gsm_call_settings_with_label" msgid="7973920539979524908">"Ajustes adicionales (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
     <string name="sum_gsm_call_settings" msgid="7964692601608878138">"Ajustes adicionales de llamadas solo GSM"</string>
@@ -131,7 +131,7 @@
     <string name="disable_cdma_cw" msgid="7119290446496301734">"Cancelar"</string>
     <string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"Llamada en espera de CDMA en IMS activada"</string>
     <string name="cdma_call_waiting_in_ims_off" msgid="1099246114368636334">"Llamada en espera de CDMA en IMS desactivada"</string>
-    <string name="updating_title" msgid="6130548922615719689">"Ajustes de llamadas"</string>
+    <string name="updating_title" msgid="6130548922615719689">"Ajustes de llamada"</string>
     <string name="call_settings_admin_user_only" msgid="7238947387649986286">"El administrador es el único usuario que puede cambiar los ajustes de llamada."</string>
     <string name="phone_account_settings_user_restriction" msgid="9142685151087208396">"Solo el administrador o el usuario de trabajo pueden cambiar la configuración de la cuenta del teléfono."</string>
     <string name="phone_account_no_config_mobile_networks" msgid="7351062247756521227">"El propietario del dispositivo ha restringido la posibilidad de cambiar la configuración de la red móvil."</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 06e81b2..41c98e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2249,4 +2249,21 @@
     <string name="send_from_work_profile_action_str">Switch to work profile</string>
     <string name="install_messages_on_work_profile_action_str">Install a work messages app</string>
 
+    <!-- The title of option menu from phoneInfo test screen, to show satellite config -->
+    <string name="radio_info_data_view_satellite_config">Show Satellite Config</string>
+    <!-- Title of SatelliteConfigViewer screen -->
+    <string name="satellite_config_viewer">Satellite Config Viewer</string>
+    <!-- Satellite config viewer screen. Label for the config data version -->
+    <string name="satellite_config_version_label">version: </string>
+    <!-- Satellite config viewer screen. Label for the subId/plmn/servicetype -->
+    <string name="satellite_config_service_type_label">subId/plmn/servicetype: </string>
+    <!-- Satellite config viewer screen. Label for the allow access -->
+    <string name="satellite_config_allow_access_label">allow_access: </string>
+    <!-- Satellite config viewer screen. Label for the country codes -->
+    <string name="satellite_config_country_code_label">country codes: </string>
+    <!-- Satellite config viewer screen. Label for size of the s2sat.dat file -->
+    <string name="satellite_config_size_of_sats2_dat_label">size of sats2.dat: </string>
+    <!-- Satellite config viewer screen. satellite access config json file -->
+    <string name="satellite_config_json_label">satellite access config json: </string>
+
 </resources>
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 1242fec..26ff9c7 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -1928,8 +1928,7 @@
             return;
         }
 
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
                 Binder.getCallingUserHandle())
                 || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             // Skip to check associated telephony feature,
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index e2ae343..65ca6f5 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -1009,8 +1009,7 @@
             return;
         }
 
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
                 Binder.getCallingUserHandle())
                 || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             // Skip to check associated telephony feature,
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 68773f1..df6b766 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -501,11 +501,7 @@
     public PhoneGlobals(Context context) {
         super(context);
         sMe = this;
-        if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) {
-            if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-                mSettingsObserver = new SettingsObserver(context, mHandler);
-            }
-        } else {
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             mSettingsObserver = new SettingsObserver(context, mHandler);
         }
     }
@@ -515,9 +511,8 @@
 
         ContentResolver resolver = getContentResolver();
 
-        if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                && !getResources().getBoolean(
-                    com.android.internal.R.bool.config_force_phone_globals_creation)) {
+        if (!getResources().getBoolean(
+                com.android.internal.R.bool.config_force_phone_globals_creation)) {
             if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
                 Log.v(LOG_TAG, "onCreate()... but not defined FEATURE_TELEPHONY");
                 return;
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 5a080aa..8fef578 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -8448,10 +8448,8 @@
     public boolean isRttEnabled(int subscriptionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()) {
-                if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
-                    return false;
-                }
+            if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+                return false;
             }
 
             boolean isRttSupported = isRttSupported(subscriptionId);
@@ -11267,8 +11265,7 @@
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
 
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
                 Binder.getCallingUserHandle())) {
             if (!isImsAvailableOnDevice()) {
                 // ProvisioningManager can not handle ServiceSpecificException.
@@ -11872,8 +11869,7 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
                 Binder.getCallingUserHandle())) {
             if (!isImsAvailableOnDevice()) {
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -11911,8 +11907,7 @@
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
 
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
                 Binder.getCallingUserHandle())) {
             if (!isImsAvailableOnDevice()) {
                 // operation failed silently
@@ -11945,8 +11940,7 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
                 Binder.getCallingUserHandle())) {
             if (!isImsAvailableOnDevice()) {
                 // ProvisioningManager can not handle ServiceSpecificException.
@@ -11977,8 +11971,7 @@
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, getCurrentPackageName(),
                 Binder.getCallingUserHandle())) {
             if (!isImsAvailableOnDevice()) {
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -13292,6 +13285,7 @@
                                 (r == SATELLITE_DISALLOWED_REASON_UNSUPPORTED_DEFAULT_MSG_APP
                                         || r == SATELLITE_DISALLOWED_REASON_NOT_PROVISIONED
                                         || r == SATELLITE_DISALLOWED_REASON_NOT_SUPPORTED))) {
+                            Log.d(LOG_TAG, "Satellite access is disallowed for current location.");
                             result.accept(SATELLITE_RESULT_ACCESS_BARRED);
                             return;
                         }
@@ -14190,6 +14184,30 @@
     }
 
     /**
+     * This API can be used by only CTS to override the satellite access allowed state for
+     * a list of subscription IDs.
+     *
+     * @param subIdListStr The string representation of the list of subscription IDs,
+     *                     which are numbers separated by comma.
+     * @return {@code true} if the satellite access allowed state is set successfully,
+     * {@code false} otherwise.
+     */
+    public boolean setSatelliteAccessAllowedForSubscriptions(@Nullable String subIdListStr) {
+        Log.d(LOG_TAG, "setSatelliteAccessAllowedForSubscriptions - " + subIdListStr);
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setSatelliteAccessAllowedForSubscriptions");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setSatelliteAccessAllowedForSubscriptions");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setSatelliteAccessAllowedForSubscriptions(subIdListStr);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * This API can be used by only CTS to update satellite gateway service package name.
      *
      * @param servicePackageName The package name of the satellite gateway service.
@@ -14260,6 +14278,37 @@
     }
 
     /**
+     * This API can be used by only CTS to override TN scanning support.
+     *
+     * @param reset {@code true} mean the overridden configs should not be used, {@code false}
+     *              otherwise.
+     * @param concurrentTnScanningSupported Whether concurrent TN scanning is supported.
+     * @param tnScanningDuringSatelliteSessionAllowed Whether TN scanning is allowed during
+     * a satellite session.
+     * @return {@code true} if the TN scanning support is set successfully,
+     * {@code false} otherwise.
+     */
+    public boolean setTnScanningSupport(boolean reset, boolean concurrentTnScanningSupported,
+        boolean tnScanningDuringSatelliteSessionAllowed) {
+        Log.d(LOG_TAG, "setTnScanningSupport: reset= " + reset
+            + ", concurrentTnScanningSupported=" + concurrentTnScanningSupported
+            + ", tnScanningDuringSatelliteSessionAllowed="
+            + tnScanningDuringSatelliteSessionAllowed);
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setTnScanningSupport");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setTnScanningSupport");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mSatelliteController.setTnScanningSupport(reset,
+                concurrentTnScanningSupported, tnScanningDuringSatelliteSessionAllowed);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * This API can be used by only CTS to control ingoring cellular service state event.
      *
      * @param enabled Whether to enable boolean config.
@@ -14752,8 +14801,7 @@
             return;
         }
 
-        if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis()
-                || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
+        if (!CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage,
                 Binder.getCallingUserHandle())
                 || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             // Skip to check associated telephony feature,
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index cd6a369..c1692f8 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -189,6 +189,8 @@
             "set-satellite-listening-timeout-duration";
     private static final String SET_SATELLITE_IGNORE_CELLULAR_SERVICE_STATE =
             "set-satellite-ignore-cellular-service-state";
+    private static final String SET_SATELLITE_TN_SCANNING_SUPPORT =
+            "set-satellite-tn-scanning-support";
     private static final String SET_SATELLITE_POINTING_UI_CLASS_NAME =
             "set-satellite-pointing-ui-class-name";
     private static final String SET_DATAGRAM_CONTROLLER_TIMEOUT_DURATION =
@@ -214,6 +216,8 @@
 
     private static final String SET_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT =
             "set-satellite-access-restriction-checking-result";
+    private static final String SET_SATELLITE_ACCESS_ALLOWED_FOR_SUBSCRIPTIONS =
+            "set-satellite-access-allowed-for-subscriptions";
 
     private static final String DOMAIN_SELECTION_SUBCOMMAND = "domainselection";
     private static final String DOMAIN_SELECTION_SET_SERVICE_OVERRIDE = "set-dss-override";
@@ -432,6 +436,10 @@
                 return handleSetSatelliteSubscriberIdListChangedIntentComponent();
             case SET_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT:
                 return handleOverrideCarrierRoamingNtnEligibilityChanged();
+            case SET_SATELLITE_ACCESS_ALLOWED_FOR_SUBSCRIPTIONS:
+                return handleSetSatelliteAccessAllowedForSubscriptions();
+            case SET_SATELLITE_TN_SCANNING_SUPPORT:
+                return handleSetSatelliteTnScanningSupport();
             case COMMAND_DELETE_IMSI_KEY:
                 return handleDeleteTestImsiKey();
             default: {
@@ -3231,6 +3239,38 @@
         return 0;
     }
 
+    private int handleSetSatelliteAccessAllowedForSubscriptions() {
+        PrintWriter errPw = getErrPrintWriter();
+        String subIdListStr = null;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s": {
+                    subIdListStr = getNextArgRequired();
+                    break;
+                }
+            }
+        }
+        Log.d(LOG_TAG, "handleSetSatelliteAccessAllowedForSubscriptions: subIdListStr="
+            + subIdListStr);
+
+        try {
+            boolean result = mInterface.setSatelliteAccessAllowedForSubscriptions(subIdListStr);
+            if (VDBG) {
+                Log.v(LOG_TAG, "SetSatelliteAccessAllowedForSubscriptions " + subIdListStr
+                    + ", result = " + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "SetSatelliteAccessAllowedForSubscriptions: error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+
+        return 0;
+    }
+
     private int handleSetSatelliteGatewayServicePackageNameCommand() {
         PrintWriter errPw = getErrPrintWriter();
         String serviceName = null;
@@ -3411,6 +3451,50 @@
         return 0;
     }
 
+    private int handleSetSatelliteTnScanningSupport() {
+        PrintWriter errPw = getErrPrintWriter();
+        boolean reset = false;
+        boolean concurrentTnScanningSupported = false;
+        boolean tnScanningDuringSatelliteSessionAllowed = false;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-r": {
+                    reset = true;
+                    break;
+                }
+                case "-s": {
+                    concurrentTnScanningSupported = Boolean.parseBoolean(getNextArgRequired());
+                    break;
+                }
+                case "-a": {
+                    tnScanningDuringSatelliteSessionAllowed =
+                            Boolean.parseBoolean(getNextArgRequired());
+                    break;
+                }
+            }
+        }
+        Log.d(LOG_TAG, "handleSetSatelliteTnScanningSupport: reset=" + reset
+            + ", concurrentTnScanningSupported =" + concurrentTnScanningSupported
+            + ", tnScanningDuringSatelliteSessionAllowed="
+            + tnScanningDuringSatelliteSessionAllowed);
+
+        try {
+            boolean result = mInterface.setTnScanningSupport(reset,
+                concurrentTnScanningSupported, tnScanningDuringSatelliteSessionAllowed);
+            if (VDBG) {
+                Log.v(LOG_TAG, "handleSetSatelliteTnScanningSupport: result = " + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "handleSetSatelliteTnScanningSupport: error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     private int handleSetDatagramControllerTimeoutDuration() {
         PrintWriter errPw = getErrPrintWriter();
         boolean reset = false;
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessConfigurationParser.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessConfigurationParser.java
index ad0926b..b22fb64 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessConfigurationParser.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessConfigurationParser.java
@@ -26,8 +26,6 @@
 import android.telephony.satellite.SatellitePosition;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -302,7 +300,6 @@
      * @return json string type json contents
      */
     @Nullable
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public static String readJsonStringFromFile(@NonNull String jsonFilePath) {
         logd("jsonFilePath is " + jsonFilePath);
         String json = null;
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
index cfe65c8..aaba0f7 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
@@ -60,6 +60,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
@@ -620,7 +621,6 @@
                         + satelliteSubscriberProvisionStatus);
             }
         };
-        initializeSatelliteSystemNotification(context);
         result = mSatelliteController.registerForSatelliteProvisionStateChanged(
                 mInternalSatelliteProvisionStateCallback);
         plogd("registerForSatelliteProvisionStateChanged result: " + result);
@@ -705,7 +705,9 @@
                 plogd("EVENT_LOCATION_SETTINGS_ENABLED");
             case EVENT_LOCATION_SETTINGS_DISABLED:
                 // Fall through
+                plogd("EVENT_LOCATION_SETTINGS_DISABLED");
             case EVENT_COUNTRY_CODE_CHANGED:
+                plogd("EVENT_COUNTRY_CODE_CHANGED");
                 handleSatelliteAllowedRegionPossiblyChanged(msg.what);
                 break;
             case CMD_UPDATE_SYSTEM_SELECTION_CHANNELS:
@@ -713,6 +715,7 @@
                 break;
             case EVENT_SATELLITE_SUBSCRIPTION_CHANGED:
                 plogd("Event: EVENT_SATELLITE_SUBSCRIPTION_CHANGED");
+                initializeSatelliteSystemNotification(mContext);
                 handleEventDisallowedReasonsChanged();
                 break;
             default:
@@ -825,6 +828,9 @@
             if (reset) {
                 mIsOverlayConfigOverridden = false;
                 cleanUpCtsResources();
+                cleanUpTelephonyConfigs();
+                cleanUpSatelliteAccessConfigOtaResources();
+                cleanupSatelliteConfigOtaResources();
             } else {
                 mIsOverlayConfigOverridden = true;
                 mOverriddenIsSatelliteAllowAccessControl = isAllowed;
@@ -836,7 +842,6 @@
                                 + " does not exist");
                         mOverriddenSatelliteS2CellFile = null;
                     }
-                    ///TODO :: need to check when thi will be reloaded this map
                     mCachedAccessRestrictionMap.clear();
                 } else {
                     mOverriddenSatelliteS2CellFile = null;
@@ -1046,6 +1051,39 @@
         }
     }
 
+    private void cleanUpTelephonyConfigs() {
+        mSatelliteController.cleanUpTelephonyConfigs();
+    }
+
+    private void cleanUpSatelliteAccessConfigOtaResources() {
+        PhoneGlobals phoneGlobals = PhoneGlobals.getInstance();
+        File satelliteAccessControlDir =
+                phoneGlobals.getDir(SATELLITE_ACCESS_CONTROL_DATA_DIR, Context.MODE_PRIVATE);
+        if (satelliteAccessControlDir == null || !satelliteAccessControlDir.exists()) {
+            plogd(
+                    "cleanUpSatelliteAccessConfigOtaResources: "
+                            + SATELLITE_ACCESS_CONTROL_DATA_DIR
+                            + " does not exist");
+            return;
+        }
+        plogd(
+                "cleanUpSatelliteAccessConfigOtaResources: Deleting contents under "
+                        + SATELLITE_ACCESS_CONTROL_DATA_DIR);
+        FileUtils.deleteContents(satelliteAccessControlDir);
+    }
+
+    private void cleanupSatelliteConfigOtaResources() {
+        SatelliteConfig satelliteConfig = mSatelliteController.getSatelliteConfig();
+        if (satelliteConfig == null) {
+            plogd(
+                    "cleanupSatelliteConfigOtaResources: satelliteConfig is null. Cannot or Not"
+                        + " needed to delete satellite config OTA files");
+            return;
+        }
+        plogd("cleanupSatelliteConfigOtaResources: Deleting satellite config OTA files");
+        satelliteConfig.cleanOtaResources(mContext);
+    }
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected long getElapsedRealtimeNanos() {
         return SystemClock.elapsedRealtimeNanos();
@@ -1494,8 +1532,13 @@
         }
     }
 
+    /**
+     * Returns a list of satellite country codes.
+     *
+     * @return The list of satellite country codes.
+     */
     @NonNull
-    private List<String> getSatelliteCountryCodes() {
+    public List<String> getSatelliteCountryCodes() {
         synchronized (mLock) {
             if (mIsOverlayConfigOverridden) {
                 return mOverriddenSatelliteCountryCodes;
@@ -1504,8 +1547,13 @@
         }
     }
 
+    /**
+     * Returns a satellite s2 cell file
+     *
+     * @return The file of satellite s2 cell
+     */
     @Nullable
-    protected File getSatelliteS2CellFile() {
+    public File getSatelliteS2CellFile() {
         synchronized (mLock) {
             if (mIsOverlayConfigOverridden) {
                 return mOverriddenSatelliteS2CellFile;
@@ -1514,8 +1562,13 @@
         }
     }
 
+    /**
+     * Returns a satellite access config file
+     *
+     * @return The file of satellite access config
+     */
     @Nullable
-    protected File getSatelliteAccessConfigFile() {
+    public File getSatelliteAccessConfigFile() {
         synchronized (mLock) {
             if (mIsOverlayConfigOverridden) {
                 logd("mIsOverlayConfigOverridden: " + mIsOverlayConfigOverridden);
@@ -1529,8 +1582,12 @@
         }
     }
 
-
-    private boolean isSatelliteAllowAccessControl() {
+    /**
+     * Checks if satellite access control is allowed.
+     *
+     * @return {@code true} if satellite access control is allowed, {@code false} otherwise.
+     */
+    public boolean isSatelliteAllowAccessControl() {
         synchronized (mLock) {
             if (mIsOverlayConfigOverridden) {
                 return mOverriddenIsSatelliteAllowAccessControl;
@@ -1942,7 +1999,8 @@
                 .setAutoCancel(true)
                 .setColor(context.getColor(
                         com.android.internal.R.color.system_notification_accent_color))
-                .setVisibility(Notification.VISIBILITY_PUBLIC);
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                .setLocalOnly(true);
 
         return notificationBuilder.build();
     }
@@ -2537,6 +2595,8 @@
      */
     private boolean initSatelliteOnDeviceAccessController()
             throws IllegalStateException {
+        plogd("initSatelliteOnDeviceAccessController");
+
         synchronized (mLock) {
             if (getSatelliteS2CellFile() == null) return false;
 
@@ -2550,6 +2610,10 @@
                 mSatelliteOnDeviceAccessController =
                         SatelliteOnDeviceAccessController.create(
                                 getSatelliteS2CellFile(), mFeatureFlags);
+
+                plogd(
+                        "initSatelliteOnDeviceAccessController: initialized"
+                            + " SatelliteOnDeviceAccessController");
                 restartKeepOnDeviceAccessControllerResourcesTimer();
                 mS2Level = mSatelliteOnDeviceAccessController.getS2Level();
                 plogd("mS2Level=" + mS2Level);
@@ -2705,7 +2769,6 @@
         return accessAllowed;
     }
 
-
     @Nullable
     protected String getSatelliteConfigurationFileNameFromOverlayConfig(
             @NonNull Context context) {
@@ -3338,6 +3401,17 @@
         return satelliteDisallowedReasons;
     }
 
+    /**
+     * Returns the satellite access configuration version.
+     *
+     * If the satellite config data hasn't been updated by configUpdater,
+     * it returns 0. If it has been updated, it returns the updated version information.
+     */
+    @NonNull
+    public int getSatelliteAccessConfigVersion() {
+        return mSatelliteAccessConfigVersion;
+    }
+
     private void plogv(@NonNull String log) {
         Rlog.v(TAG, log);
         if (mPersistentLogger != null) {
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
index b3c0fdd..60b57c3 100644
--- a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
@@ -37,7 +37,6 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ExponentialBackoff;
@@ -249,6 +248,51 @@
         sendEmptyMessage(CMD_START_QUERY_ENTITLEMENT);
     }
 
+    private int[] getServiceTypeForEntitlementMetrics(Map<String, List<Integer>> map) {
+        if (map == null || map.isEmpty()) {
+            return new int[]{};
+        }
+
+        return map.entrySet().stream()
+                .findFirst()
+                .map(entry -> {
+                    List<Integer> list = entry.getValue();
+                    if (list == null) {
+                        return new int[]{}; // Return empty array if the list is null
+                    }
+                    return list.stream().mapToInt(Integer::intValue).toArray();
+                })
+                .orElse(new int[]{}); // Return empty array if no entry is found
+    }
+
+    private int getDataPolicyForEntitlementMetrics(Map<String, Integer> dataPolicyMap) {
+        if (dataPolicyMap != null && !dataPolicyMap.isEmpty()) {
+            return dataPolicyMap.values().stream().findFirst()
+                    .orElse(-1);
+        }
+        return -1;
+    }
+
+    private void reportSuccessForEntitlement(int subId, SatelliteEntitlementResult
+            entitlementResult) {
+        // allowed service info entitlement status
+        boolean isAllowedServiceInfo = !entitlementResult
+                .getAvailableServiceTypeInfoForPlmnList().isEmpty();
+
+        int[] serviceType = new int[0];
+        int dataPolicy = 0;
+        if (isAllowedServiceInfo) {
+            serviceType = getServiceTypeForEntitlementMetrics(
+                    entitlementResult.getAvailableServiceTypeInfoForPlmnList());
+            dataPolicy = SatelliteController.getInstance().mapDataPolicyForMetrics(
+                    getDataPolicyForEntitlementMetrics(
+                    entitlementResult.getDataServicePolicyInfoForPlmnList()));
+        }
+        mEntitlementMetricsStats.reportSuccess(subId,
+                getEntitlementStatus(entitlementResult), true, isAllowedServiceInfo,
+                serviceType, dataPolicy);
+    }
+
     /**
      * 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
@@ -269,8 +313,7 @@
                     SatelliteEntitlementResult entitlementResult =  getSatelliteEntitlementApi(
                             subId).checkEntitlementStatus();
                     mSatelliteEntitlementResultPerSub.put(subId, entitlementResult);
-                    mEntitlementMetricsStats.reportSuccess(subId,
-                            getEntitlementStatus(entitlementResult), false);
+                    reportSuccessForEntitlement(subId, entitlementResult);
                 }
             } catch (ServiceEntitlementException e) {
                 loge(e.toString());
@@ -337,8 +380,8 @@
                 SatelliteEntitlementResult entitlementResult =  getSatelliteEntitlementApi(
                         subId).checkEntitlementStatus();
                 mSatelliteEntitlementResultPerSub.put(subId, entitlementResult);
-                mEntitlementMetricsStats.reportSuccess(subId,
-                        getEntitlementStatus(entitlementResult), true);
+                reportSuccessForEntitlement(subId, entitlementResult);
+
             }
         } catch (ServiceEntitlementException e) {
             loge(e.toString());
diff --git a/src/com/android/phone/security/SafetySourceReceiver.java b/src/com/android/phone/security/SafetySourceReceiver.java
index 99394c2..835c79b 100644
--- a/src/com/android/phone/security/SafetySourceReceiver.java
+++ b/src/com/android/phone/security/SafetySourceReceiver.java
@@ -26,7 +26,6 @@
 
 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;
 
@@ -45,11 +44,7 @@
             return;
         }
 
-        if (Flags.enforceTelephonyFeatureMappingForPublicApis()) {
-            if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-                refreshSafetySources(refreshBroadcastId);
-            }
-        } else {
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             refreshSafetySources(refreshBroadcastId);
         }
     }
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 5f0b2c1..c0647d9 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -123,7 +123,6 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.euicc.EuiccConnector;
-import com.android.internal.telephony.satellite.SatelliteServiceUtils;
 import com.android.phone.R;
 
 import java.io.IOException;
@@ -134,8 +133,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
@@ -335,6 +332,7 @@
     private Button mEsosButton;
     private Button mSatelliteEnableNonEmergencyModeButton;
     private Button mEsosDemoButton;
+    private Button mSatelliteConfigViewerButton;
     private Switch mImsVolteProvisionedSwitch;
     private Switch mImsVtProvisionedSwitch;
     private Switch mImsWfcProvisionedSwitch;
@@ -576,6 +574,7 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        SettingsConstants.setupEdgeToEdge(this);
         mSystemUser = android.os.Process.myUserHandle().isSystem();
         log("onCreate: mSystemUser=" + mSystemUser);
         UserManager userManager = getSystemService(UserManager.class);
@@ -808,6 +807,7 @@
         mEsosDemoButton  = (Button) findViewById(R.id.demo_esos_questionnaire);
         mSatelliteEnableNonEmergencyModeButton = (Button) findViewById(
                 R.id.satellite_enable_non_emergency_mode);
+        mSatelliteConfigViewerButton = (Button) findViewById(R.id.satellite_config_viewer);
 
         if (shouldHideButton(mActionEsos)) {
             mEsosButton.setVisibility(View.GONE);
@@ -837,6 +837,14 @@
             });
         }
 
+        mSatelliteConfigViewerButton.setOnClickListener(v -> {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.putExtra("mSubId", mSubId);
+            intent.setClassName("com.android.phone",
+                    "com.android.phone.settings.SatelliteConfigViewer");
+            startActivityAsUser(intent, UserHandle.CURRENT);
+        });
+
         mOemInfoButton = (Button) findViewById(R.id.oem_info);
         mOemInfoButton.setOnClickListener(mOemInfoButtonHandler);
         PackageManager pm = getPackageManager();
@@ -2121,31 +2129,38 @@
     }
 
     /**
+     * Method will create the PersistableBundle and pack the satellite services like
+     * SMS, MMS, EMERGENCY CALL, DATA in it.
+     *
+     * @return PersistableBundle
+     */
+    public PersistableBundle getSatelliteServicesBundleForOperatorPlmn() {
+        PersistableBundle satServiceBundle = new PersistableBundle();
+        String plmn = mTelephonyManager.getNetworkOperatorForPhone(mPhoneId);
+        if (TextUtils.isEmpty(plmn)) {
+            loge("satData: NetworkOperator PLMN is empty");
+            plmn = mTelephonyManager.getSimOperatorNumeric(mSubId);
+            loge("satData: SimOperator PLMN = " + plmn);
+        }
+        int[] supportedServicesArray = {NetworkRegistrationInfo.SERVICE_TYPE_DATA,
+                NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY,
+                NetworkRegistrationInfo.SERVICE_TYPE_MMS};
+        satServiceBundle.putIntArray(plmn, supportedServicesArray);
+        log("satData: New SatelliteServicesBundle = " + satServiceBundle);
+        return satServiceBundle;
+    }
+
+    /**
      * This method will check the required carrier config keys which plays role in enabling /
      * supporting satellite data and update the keys accordingly.
      * @param bundleToModify : PersistableBundle
      */
     private void updateCarrierConfigToSupportData(PersistableBundle bundleToModify) {
         // KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE key info update
-        PersistableBundle providerBundle = bundleToModify.getPersistableBundle(
-                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE);
-        Map<String, Set<Integer>> satServicesBundle =
-                SatelliteServiceUtils.parseSupportedSatelliteServices(providerBundle);
-        String plmn = mTelephonyManager.getSimOperatorNumeric(mSubId);
-        log("satData: currentplmn = " + plmn);
-        if (!satServicesBundle.isEmpty() && satServicesBundle.containsKey(plmn)) {
-            Set<Integer> supportedServices = satServicesBundle.get(plmn);
-            log("satData: BEFORE supportedServices = " + supportedServices);
-            if (!supportedServices.contains(NetworkRegistrationInfo.SERVICE_TYPE_DATA)) {
-                supportedServices.add(NetworkRegistrationInfo.SERVICE_TYPE_DATA);
-                providerBundle.putIntArray(plmn, supportedServices.stream()
-                        .mapToInt(Integer::intValue)
-                        .toArray());
-                log("satData: AFTER supportedServices = " + supportedServices);
-            }
-            bundleToModify.putPersistableBundle(
-                    KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE, providerBundle);
-        }
+        bundleToModify.putPersistableBundle(
+                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                getSatelliteServicesBundleForOperatorPlmn());
 
         // KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY key info update
         int[] availableServices = bundleToModify.getIntArray(
@@ -2165,13 +2180,11 @@
             newServices = new int[1];
             newServices[0] = NetworkRegistrationInfo.SERVICE_TYPE_DATA;
         }
-        bundleToModify.putIntArray(
-                KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
+        bundleToModify.putIntArray(KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
                 newServices);
 
         // KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL setting to false.
-        bundleToModify.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
-                false);
+        bundleToModify.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
         bundleToModify.remove(KEY_SATELLITE_DATA_SUPPORT_MODE_INT);
         log("satData: changing carrierConfig to : " + bundleToModify);
         getCarrierConfig().overrideConfig(mSubId, bundleToModify, false);
@@ -2312,8 +2325,7 @@
             (buttonView, isChecked) -> {
                 int subId = mSubId;
                 int phoneId = mPhoneId;
-                if (SubscriptionManager.isValidPhoneId(phoneId)
-                        && isValidSubscription(subId)) {
+                if (SubscriptionManager.isValidPhoneId(phoneId) && isValidSubscription(subId)) {
                     if (getCarrierConfig() == null) return;
                     if (isChecked) {
                         if (!isValidOperator(subId)) {
@@ -2323,26 +2335,15 @@
                             return;
                         }
                         PersistableBundle originalBundle = getCarrierConfig().getConfigForSubId(
-                                subId,
-                                KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                                subId, KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
                                 KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
-                                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE
-                        );
+                                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE);
                         PersistableBundle overrideBundle = new PersistableBundle();
-                        overrideBundle.putBoolean(
-                                KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
+                        overrideBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
                         overrideBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
-                        PersistableBundle capableProviderBundle = new PersistableBundle();
-                        capableProviderBundle.putIntArray(mTelephonyManager
-                                        .getNetworkOperatorForPhone(phoneId),
-                                new int[]{
-                                        // Currently satellite only supports below
-                                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
-                                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY
-                        });
                         overrideBundle.putPersistableBundle(
                                 KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
-                                capableProviderBundle);
+                                getSatelliteServicesBundleForOperatorPlmn());
                         log("mMockSatelliteListener: new " + overrideBundle);
                         log("mMockSatelliteListener: old " + originalBundle);
                         getCarrierConfig().overrideConfig(subId, overrideBundle, false);
diff --git a/src/com/android/phone/settings/SatelliteConfigViewer.java b/src/com/android/phone/settings/SatelliteConfigViewer.java
new file mode 100644
index 0000000..4d7309f
--- /dev/null
+++ b/src/com/android/phone/settings/SatelliteConfigViewer.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2025 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.settings;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static android.telephony.SubscriptionManager.getDefaultSubscriptionId;
+
+import android.annotation.ArrayRes;
+import android.annotation.NonNull;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.TextView;
+
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
+import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.phone.R;
+import com.android.phone.satellite.accesscontrol.SatelliteAccessConfigurationParser;
+import com.android.phone.satellite.accesscontrol.SatelliteAccessController;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+
+public class SatelliteConfigViewer extends Activity {
+    private static final String TAG = SatelliteConfigViewer.class.getSimpleName();
+
+    private TextView mVersion;
+    private TextView mServiceType;
+    private TextView mAllowAccess;
+    private TextView mCountryCodes;
+    private TextView mSizeOfSats2;
+    private TextView mConfigAccessJson;
+
+    private SatelliteController mSatelliteController;
+    private SatelliteAccessController mSatelliteAccessController;
+
+    private int mSubId = INVALID_SUBSCRIPTION_ID;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.satellite_config_viewer);
+        Log.d(TAG, "SatelliteConfigViewer: onCreate");
+
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
+        Intent intentRadioInfo = getIntent();
+        mSubId = intentRadioInfo.getIntExtra("mSubId", getDefaultSubscriptionId());
+        Log.d(TAG, "SatelliteConfigViewer: mSubId: " + mSubId);
+
+        mVersion = (TextView) findViewById(R.id.version);
+        mServiceType = (TextView) findViewById(R.id.svc_type);
+        mAllowAccess = (TextView) findViewById(R.id.allow_access);
+        mCountryCodes = (TextView) findViewById(R.id.country_codes);
+        mSizeOfSats2 = (TextView) findViewById(R.id.size_of_sats2);
+        mConfigAccessJson = (TextView) findViewById(R.id.config_json);
+
+        mSatelliteController = SatelliteController.getInstance();
+        mSatelliteAccessController = SatelliteAccessController.getOrCreateInstance(
+                getApplicationContext(), new FeatureFlagsImpl());
+
+        mVersion.setText(getSatelliteConfigVersion());
+        mServiceType.setText(getSatelliteCarrierConfigUpdateData());
+        mAllowAccess.setText(getSatelliteAllowAccess());
+        mCountryCodes.setText(getSatelliteConfigCountryCodes());
+        mSizeOfSats2.setText(getSatelliteS2SatFileSize(getApplicationContext()));
+        mConfigAccessJson.setText(getSatelliteConfigJsonFile(getApplicationContext()));
+    }
+
+    private String getSatelliteConfigVersion() {
+        logd("getSatelliteConfigVersion");
+        return Integer.toString(mSatelliteAccessController.getSatelliteAccessConfigVersion());
+    }
+
+    private String getSatelliteCarrierConfigUpdateData() {
+        logd("getSatelliteCarrierConfigUpdateData");
+        HashMap<String, List<Integer>> mapPlmnServiceType = new HashMap<>();
+        List<String> plmnList = mSatelliteController.getSatellitePlmnsForCarrier(mSubId);
+        for (String plmn : plmnList) {
+            List<Integer> listServiceType =
+                    mSatelliteController.getSupportedSatelliteServicesForPlmn(mSubId, plmn);
+            mapPlmnServiceType.put(plmn, listServiceType);
+        }
+        logd("getSatelliteCarrierConfigUpdateData: " + "subId: " + mSubId + ": "
+                + mapPlmnServiceType);
+        return "subId: " + mSubId + ": " + mapPlmnServiceType;
+    }
+
+    private String getSatelliteAllowAccess() {
+        logd("getSatelliteAllowAccess");
+        return Boolean.toString(mSatelliteAccessController.isSatelliteAllowAccessControl());
+    }
+
+    private String getSatelliteConfigCountryCodes() {
+        logd("getSatelliteConfigCountryCodes");
+        return String.join(",", mSatelliteAccessController.getSatelliteCountryCodes());
+    }
+
+    private String getSatelliteConfigJsonFile(Context context) {
+        logd("getSatelliteConfigJsonFile");
+
+        File jsonFile = mSatelliteAccessController.getSatelliteAccessConfigFile();
+        if (jsonFile == null) {
+            loge("getSatelliteConfigJsonFile: satellite access config json file is null");
+            return "satellite access config json file is not ready";
+        }
+        return SatelliteAccessConfigurationParser
+                .readJsonStringFromFile(jsonFile.getAbsolutePath());
+    }
+
+    private String getSatelliteS2SatFileSize(Context context) {
+        logd("getSatelliteS2SatFileSize");
+        File s2CellFile = mSatelliteAccessController.getSatelliteS2CellFile();
+        if (s2CellFile == null) {
+            loge("getSatelliteS2SatFileSize: s2satFile is null");
+            return "s2satFile is null";
+        }
+        return Long.toString(s2CellFile.length());
+    }
+
+    @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;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(@androidx.annotation.NonNull MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    private static void logd(@NonNull String log) {
+        Log.d(TAG, log);
+    }
+
+    private static void loge(@NonNull String log) {
+        Log.e(TAG, log);
+    }
+}
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index 909a3ad..baae26b 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -264,6 +264,8 @@
                 NotificationChannelController.CHANNEL_ID_VOICE_MAIL);
         intent.putExtra(Settings.EXTRA_APP_PACKAGE, mPhone.getContext().getPackageName());
         mVoicemailNotificationPreference.setIntent(intent);
+
+        SettingsConstants.setupEdgeToEdge(this);
     }
 
     @Override
@@ -289,6 +291,10 @@
         mPreviousVMProviderKey = mVoicemailProviders.getValue();
 
         mVoicemailSettings = (PreferenceScreen) findPreference(BUTTON_VOICEMAIL_SETTING_KEY);
+        // 😮‍💨 the legacy PreferenceScreen displays a dialog in its onClick.  Set a property on the
+        // PreferenceScreen to ensure that it will fit system windows to accommodate for edge to
+        // edge.
+        mVoicemailSettings.setDialogFitsSystemWindows(true);
 
         maybeHidePublicSettings();
 
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index de4076d..2222fb8 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -1398,8 +1398,7 @@
                     Build.VERSION.DEVICE_INITIAL_SDK_INT);
             PackageManager pm = context.getPackageManager();
 
-            if (Flags.enforceTelephonyFeatureMappingForPublicApis()
-                    && vendorApiLevel >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            if (vendorApiLevel >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
                 if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
                         && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
                     sInstance = new TelecomAccountRegistry(context);
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 6860b25..4cb0575 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -126,6 +126,7 @@
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.regex.Pattern;
 import java.util.stream.Stream;
@@ -147,11 +148,15 @@
 
     // 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 int DEFAULT_DSDA_CALL_STATE_CHANGE_TIMEOUT_MS = 5000;
 
     // 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.
 
+    // Timeout to wait for ending active call on other domain before continuing with
+    // the emergency call.
+    private static final int DEFAULT_DISCONNECT_CALL_ON_OTHER_DOMAIN_TIMEOUT_MS = 2 * 1000;
+
     // If configured, reject attempts to dial numbers matching this pattern.
     private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
             Pattern.compile("\\*228[0-9]{0,2}");
@@ -220,6 +225,8 @@
     private final CdmaConferenceController mCdmaConferenceController =
             new CdmaConferenceController(this);
 
+    private com.android.server.telecom.flags.FeatureFlags mTelecomFlags =
+            new com.android.server.telecom.flags.FeatureFlagsImpl();
     private FeatureFlags mFeatureFlags = new FeatureFlagsImpl();
 
     private ImsConferenceController mImsConferenceController;
@@ -743,6 +750,32 @@
         }
     }
 
+    private static class StateDisconnectListener extends
+            TelephonyConnection.TelephonyConnectionListener {
+        private final CompletableFuture<Boolean> mDisconnectFuture;
+
+        StateDisconnectListener(CompletableFuture<Boolean> future) {
+            mDisconnectFuture = future;
+        }
+
+        @Override
+        public void onStateChanged(
+                Connection connection, @Connection.ConnectionState int state) {
+            TelephonyConnection c = (TelephonyConnection) connection;
+            if (c != null) {
+                switch (c.getState()) {
+                    case Connection.STATE_DISCONNECTED: {
+                        Log.d(LOG_TAG, "Connection " + connection.getTelecomCallId()
+                                + " changed to STATE_DISCONNECTED!");
+                        mDisconnectFuture.complete(true);
+                        c.removeTelephonyConnectionListener(this);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
     private static class OnDisconnectListener extends
             com.android.internal.telephony.Connection.ListenerBase {
         private final CompletableFuture<Boolean> mFuture;
@@ -1337,7 +1370,11 @@
                     }
                     return resultConnection;
                 } else {
-                    if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
+                    // If call sequencing is enabled, Telecom will take care of holding calls across
+                    // subscriptions if needed before delegating the connection creation over to
+                    // Telephony.
+                    if (mTelephonyManagerProxy.isConcurrentCallsPossible()
+                            && !mTelecomFlags.enableCallSequencing()) {
                         Conferenceable c = maybeHoldCallsOnOtherSubs(request.getAccountHandle());
                         if (c != null) {
                             delayDialForOtherSubHold(phone, c, (success) -> {
@@ -1367,44 +1404,61 @@
                     }
                 }
 
-                CompletableFuture<Void> maybeHoldFuture =
-                        checkAndHoldCallsOnOtherSubsForEmergencyCall(request,
+                CompletableFuture<Void> maybeHoldOrDisconnectOnOtherSubsFuture =
+                        checkAndHoldOrDisconnectCallsOnOtherSubsForEmergencyCall(request,
                                 resultConnection, phone);
                 Consumer<Boolean> ddsSwitchConsumer = (result) -> {
                     Log.i(this, "onCreateOutgoingConn emergency-"
                             + " delayDialForDdsSwitch result = " + result);
                     placeOutgoingConnection(request, resultConnection, phone);
                 };
-                maybeHoldFuture.thenRun(() -> delayDialForDdsSwitch(phone, ddsSwitchConsumer));
+                maybeHoldOrDisconnectOnOtherSubsFuture.thenRun(() -> delayDialForDdsSwitch(phone,
+                        ddsSwitchConsumer));
                 return resultConnection;
             }
         }
     }
 
-    private CompletableFuture<Void> checkAndHoldCallsOnOtherSubsForEmergencyCall(
+    private CompletableFuture<Void> checkAndHoldOrDisconnectCallsOnOtherSubsForEmergencyCall(
             ConnectionRequest request, Connection resultConnection, Phone phone) {
-        CompletableFuture<Void> maybeHoldFuture = CompletableFuture.completedFuture(null);
-        if (mTelephonyManagerProxy.isConcurrentCallsPossible()
-                && shouldHoldForEmergencyCall(phone)) {
+        CompletableFuture<Void> future = CompletableFuture.completedFuture(null);
+        if (mTelephonyManagerProxy.isConcurrentCallsPossible()) {
             // 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);
-                    }
-                });
+                            ? request.getAccountHandle()
+                            : resultConnection.getPhoneAccountHandle();
+            if (shouldHoldForEmergencyCall(phone) && !mTelecomFlags.enableCallSequencing()) {
+                Conferenceable c = maybeHoldCallsOnOtherSubs(phoneAccountHandle);
+                if (c != null) {
+                    future = delayDialForOtherSubHold(phone, c, (success) -> {
+                        Log.i(this, "checkAndHoldOrDisconnectCallsOnOtherSubsForEmergencyCall"
+                                + " 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);
+                        }
+                    });
+                }
+            } else {
+                Log.i(this, "checkAndHoldOrDisconnectCallsOnOtherSubsForEmergencyCall"
+                        + " disconnectAllCallsOnOtherSubs, phoneAccountExcluded: "
+                        + phoneAccountHandle);
+                // Disconnect any calls on other subscription as part of call sequencing. This will
+                // cover the shared data call case too when we have a call on the shared data sim
+                // as the call will always try to be placed on the sim in service. Refer to
+                // #isAvailableForEmergencyCalls.
+                List<Conferenceable> disconnectedConferenceables =
+                        disconnectAllConferenceablesOnOtherSubs(phoneAccountHandle);
+                future = delayDialForOtherSubDisconnects(phone, disconnectedConferenceables,
+                        (success) -> Log.i(this,
+                                "checkAndHoldOrDisconnectCallsOnOtherSubsForEmergencyCall"
+                                        + " delayDialForOtherSubDisconnects success = " + success));
             }
         }
-        return maybeHoldFuture;
+        return future;
     }
 
     private Connection placeOutgoingConnection(ConnectionRequest request,
@@ -2677,10 +2731,12 @@
                     phone);
         }
 
-        CompletableFuture<Void> maybeHoldFuture =
-                checkAndHoldCallsOnOtherSubsForEmergencyCall(request, resultConnection, phone);
-        maybeHoldFuture.thenRun(() -> placeEmergencyConnectionInternal(resultConnection,
-                phone, request, numberToDial, isTestEmergencyNumber, needToTurnOnRadio));
+        CompletableFuture<Void> maybeHoldOrDisconnectOnOtherSubFuture =
+                checkAndHoldOrDisconnectCallsOnOtherSubsForEmergencyCall(request,
+                        resultConnection, phone);
+        maybeHoldOrDisconnectOnOtherSubFuture.thenRun(() -> placeEmergencyConnectionInternal(
+                resultConnection, phone, request, numberToDial, isTestEmergencyNumber,
+                needToTurnOnRadio));
 
         // Non TelephonyConnection type instance means dialing failure.
         return resultConnection;
@@ -2803,11 +2859,142 @@
                             + "reject incoming, dialing canceled");
                     return;
                 }
-                placeEmergencyConnectionOnSelectedDomain(request, resultConnection, phone);
+                // Hang up the active calls if the domain of currently active call is different
+                // from the domain selected by domain selector.
+                if (Flags.hangupActiveCallBasedOnEmergencyCallDomain()) {
+                    CompletableFuture<Void> disconnectCall = maybeDisconnectCallsOnOtherDomain(
+                            phone, resultConnection, result,
+                            getAllConnections(), getAllConferences(), (ret) -> {
+                                if (!ret) {
+                                    Log.i(this, "createEmergencyConnection: "
+                                            + "disconnecting call on other domain failed");
+                                }
+                            });
+
+                    CompletableFuture<Void> unused = disconnectCall.thenRun(() -> {
+                        if (resultConnection.getState() == Connection.STATE_DISCONNECTED) {
+                            Log.i(this, "createEmergencyConnection: "
+                                    + "disconnect call on other domain, dialing canceled");
+                            return;
+                        }
+                        placeEmergencyConnectionOnSelectedDomain(request, resultConnection, phone);
+                    });
+                } else {
+                    placeEmergencyConnectionOnSelectedDomain(request, resultConnection, phone);
+                }
             });
         }, mDomainSelectionMainExecutor);
     }
 
+    /**
+     * Disconnect the active calls on the other domain for an emergency call.
+     * For example,
+     *  - Active IMS normal call and CS emergency call
+     *  - Active CS normal call and IMS emergency call
+     *
+     * @param phone The Phone to be used for an emergency call.
+     * @param emergencyConnection The connection created for an emergency call.
+     * @param emergencyDomain The selected domain for an emergency call.
+     * @param connections All individual connections, including conference participants.
+     * @param conferences All conferences.
+     * @param completeConsumer The consumer to call once the call hangup has been completed.
+     *        {@code true} if the operation commpletes successfully, or
+     *        {@code false} if the operation timed out/failed.
+     */
+    @VisibleForTesting
+    public static CompletableFuture<Void> maybeDisconnectCallsOnOtherDomain(Phone phone,
+            Connection emergencyConnection,
+            @NetworkRegistrationInfo.Domain int emergencyDomain,
+            @NonNull Collection<Connection> connections,
+            @NonNull Collection<Conference> conferences,
+            Consumer<Boolean> completeConsumer) {
+        List<Connection> activeConnections = connections.stream()
+                .filter(c -> {
+                    return !c.equals(emergencyConnection)
+                            && isConnectionOnOtherDomain(c, phone, emergencyDomain);
+                }).toList();
+        List<Conference> activeConferences = conferences.stream()
+                .filter(c -> {
+                    Connection pc = c.getPrimaryConnection();
+                    return isConnectionOnOtherDomain(pc, phone, emergencyDomain);
+                }).toList();
+
+        if (activeConnections.isEmpty() && activeConferences.isEmpty()) {
+            // There are no active calls.
+            completeConsumer.accept(true);
+            return CompletableFuture.completedFuture(null);
+        }
+
+        Log.i(LOG_TAG, "maybeDisconnectCallsOnOtherDomain: "
+                + "connections=" + activeConnections.size()
+                + ", conferences=" + activeConferences.size());
+
+        try {
+            CompletableFuture<Boolean> future = null;
+
+            for (Connection c : activeConnections) {
+                TelephonyConnection tc = (TelephonyConnection) c;
+                if (tc.getState() != Connection.STATE_DISCONNECTED) {
+                    if (future == null) {
+                        future = new CompletableFuture<>();
+                        tc.getOriginalConnection().addListener(new OnDisconnectListener(future));
+                    }
+                    tc.hangup(android.telephony.DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED);
+                }
+            }
+
+            for (Conference c : activeConferences) {
+                if (c.getState() != Connection.STATE_DISCONNECTED) {
+                    c.onDisconnect();
+                }
+            }
+
+            if (future != null) {
+                // 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_DISCONNECT_CALL_ON_OTHER_DOMAIN_TIMEOUT_MS);
+                // Ensure that the Consumer is completed on the main thread.
+                return future.acceptEitherAsync(timeout, completeConsumer,
+                        phone.getContext().getMainExecutor()).exceptionally((ex) -> {
+                            Log.w(LOG_TAG, "maybeDisconnectCallsOnOtherDomain: exceptionally="
+                                    + ex);
+                            return null;
+                        });
+            } else {
+                completeConsumer.accept(true);
+                return CompletableFuture.completedFuture(null);
+            }
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "maybeDisconnectCallsOnOtherDomain: exception=" + e.getMessage());
+            completeConsumer.accept(false);
+            return CompletableFuture.completedFuture(null);
+        }
+    }
+
+    private static boolean isConnectionOnOtherDomain(Connection c, Phone phone,
+            @NetworkRegistrationInfo.Domain int domain) {
+        if (c instanceof TelephonyConnection) {
+            TelephonyConnection tc = (TelephonyConnection) c;
+            Phone callPhone = tc.getPhone();
+            int callDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
+
+            if (callPhone != null && callPhone.getSubId() == phone.getSubId()) {
+                if (tc.isGsmCdmaConnection()) {
+                    callDomain = NetworkRegistrationInfo.DOMAIN_CS;
+                } else if (tc.isImsConnection()) {
+                    callDomain = NetworkRegistrationInfo.DOMAIN_PS;
+                }
+            }
+
+            return callDomain != NetworkRegistrationInfo.DOMAIN_UNKNOWN
+                    && callDomain != domain;
+        }
+        return false;
+    }
+
     private void dialCsEmergencyCall(final Phone phone,
             final TelephonyConnection resultConnection, final ConnectionRequest request) {
         Log.d(this, "dialCsEmergencyCall");
@@ -3831,7 +4018,7 @@
             // 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_DSDA_OUTGOING_CALL_HOLD_TIMEOUT_MS);
+                    () -> timeout.complete(false), DEFAULT_DSDA_CALL_STATE_CHANGE_TIMEOUT_MS);
             // Ensure that the Consumer is completed on the main thread.
             return stateHoldingFuture.acceptEitherAsync(timeout, completeConsumer,
                     phone.getContext().getMainExecutor());
@@ -3844,6 +4031,63 @@
     }
 
     /**
+     * For DSDA devices, block until the connections passed in are disconnected (STATE_DISCONNECTED)
+     * or time out.
+     * @return {@link CompletableFuture} indicating the completion result after performing
+     * the bulk disconnect
+     */
+    private CompletableFuture<Void> delayDialForOtherSubDisconnects(Phone phone,
+            List<Conferenceable> conferenceables, Consumer<Boolean> completeConsumer) {
+        if (conferenceables.isEmpty()) {
+            completeConsumer.accept(true);
+            return CompletableFuture.completedFuture(null);
+        }
+        if (phone == null) {
+            // Unexpected inputs
+            completeConsumer.accept(false);
+            return CompletableFuture.completedFuture(null);
+        }
+        List<CompletableFuture<Void>> disconnectFutures = new ArrayList<>();
+        for (Conferenceable conferenceable : conferenceables) {
+            CompletableFuture<Void> disconnectFuture = CompletableFuture.completedFuture(null);
+            try {
+                if (conferenceable == null) {
+                    disconnectFuture = CompletableFuture.completedFuture(null);
+                } else {
+                    // Listen for each disconnect as part of an individual future.
+                    disconnectFuture = CompletableFuture.runAsync(() ->
+                            listenForDisconnectStateChanged(conferenceable)
+                                    .completeOnTimeout(false,
+                                            DEFAULT_DSDA_CALL_STATE_CHANGE_TIMEOUT_MS,
+                                            TimeUnit.MILLISECONDS),
+                            phone.getContext().getMainExecutor());
+                }
+            } catch (Exception e) {
+                Log.w(this, "delayDialForOtherSubDisconnects - exception= " + e.getMessage());
+                disconnectFuture = CompletableFuture.completedFuture(null);
+            } finally {
+                disconnectFutures.add(disconnectFuture);
+            }
+        }
+        // Return a future that waits for all the disconnect futures to complete.
+        return CompletableFuture.allOf(disconnectFutures.toArray(CompletableFuture[]::new));
+    }
+
+    /**
+     * Listen for the disconnect state change from the passed in {@link Conferenceable}.
+     * @param conferenceable
+     * @return {@link CompletableFuture} that provides the result of waiting on the
+     * disconnect state change.
+     */
+    private CompletableFuture<Boolean> listenForDisconnectStateChanged(
+            @NonNull Conferenceable conferenceable) {
+        CompletableFuture<Boolean> future = new CompletableFuture<>();
+        final StateDisconnectListener disconnectListener = new StateDisconnectListener(future);
+        addTelephonyConnectionListener(conferenceable, disconnectListener);
+        return future;
+    }
+
+    /**
      * 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
@@ -4512,6 +4756,8 @@
      */
     public void maybeIndicateAnsweringWillDisconnect(@NonNull TelephonyConnection connection,
             @NonNull PhoneAccountHandle phoneAccountHandle) {
+        // With sequencing, Telecom handles setting the extra.
+        if (mTelecomFlags.enableCallSequencing()) return;
         if (isCallPresentOnOtherSub(phoneAccountHandle)) {
             if (mTelephonyManagerProxy.isConcurrentCallsPossible()
                     && allCallsSupportHold(connection)) {
@@ -4775,8 +5021,47 @@
         return null;
     }
 
-    private void disconnectAllCallsOnOtherSubs (@NonNull PhoneAccountHandle handle) {
-        Collection<Connection>connections = getAllConnections();
+    /**
+     * For DSDA devices, disconnects all calls (and conferences) on other subs when placing an
+     * emergency call.
+     * @param handle The {@link PhoneAccountHandle} to exclude when disconnecting calls
+     * @return {@link List} compromised of the conferenceables that have been disconnected.
+     */
+    @VisibleForTesting
+    protected List<Conferenceable> disconnectAllConferenceablesOnOtherSubs(
+            @NonNull PhoneAccountHandle handle) {
+        List<Conferenceable> conferenceables = new ArrayList<>();
+        Collection<Conference> conferences = getAllConferences();
+        // Add the conferences
+        conferences.stream()
+                .filter(c ->
+                        (c.getState() == Connection.STATE_ACTIVE
+                                || c.getState() == Connection.STATE_HOLDING)
+                                // Include any calls not on same sub as current connection.
+                                && !Objects.equals(c.getPhoneAccountHandle(), handle))
+                .forEach(c -> {
+                    if (c instanceof TelephonyConference) {
+                        TelephonyConference tc = (TelephonyConference) c;
+                        Log.i(LOG_TAG, "disconnectAllConferenceablesOnOtherSubs: disconnect"
+                                        + " %s due to redial happened on other sub.",
+                                tc.getTelecomCallId());
+                        tc.onDisconnect();
+                        conferenceables.add(c);
+                    }
+                });
+        // Add the connections.
+        conferenceables.addAll(disconnectAllCallsOnOtherSubs(handle));
+        return conferenceables;
+    }
+
+    /**
+     * For DSDA devices, disconnects all calls on other subs when placing an emergency call.
+     * @param handle The {@link PhoneAccountHandle} to exclude when disconnecting calls
+     * @return {@link List} including compromised of the connections that have been disconnected.
+     */
+    private List<Connection> disconnectAllCallsOnOtherSubs(@NonNull PhoneAccountHandle handle) {
+        Collection<Connection> connections = getAllConnections();
+        List<Connection> disconnectedConnections = new ArrayList<>();
         connections.stream()
                 .filter(c ->
                         (c.getState() == Connection.STATE_ACTIVE
@@ -4790,8 +5075,10 @@
                                 " %s due to redial happened on other sub.",
                                 tc.getTelecomCallId());
                         tc.hangup(android.telephony.DisconnectCause.LOCAL);
+                        disconnectedConnections.add(c);
                     }
                 });
+        return disconnectedConnections;
     }
 
     private @NetworkRegistrationInfo.Domain int getActiveCallDomain(int subId) {
@@ -4922,8 +5209,10 @@
 
     /* Only for testing */
     @VisibleForTesting
-    public void setFeatureFlags(FeatureFlags featureFlags) {
+    public void setFeatureFlags(FeatureFlags featureFlags,
+            com.android.server.telecom.flags.FeatureFlags telecomFlags) {
         mFeatureFlags = featureFlags;
+        mTelecomFlags = telecomFlags;
     }
 
     private void loge(String s) {
diff --git a/testapps/TestSatelliteApp/AndroidManifest.xml b/testapps/TestSatelliteApp/AndroidManifest.xml
index 1825df2..de455f2 100644
--- a/testapps/TestSatelliteApp/AndroidManifest.xml
+++ b/testapps/TestSatelliteApp/AndroidManifest.xml
@@ -17,7 +17,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.phone.testapps.satellitetestapp">
 
-    <application android:label="SatelliteTestApp">
+    <application
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:label="SatelliteTestApp">
         <activity
             android:name=".SatelliteTestApp"
             android:exported="true"
@@ -55,10 +57,17 @@
                 <action android:name="android.telephony.satellite.SatelliteService" />
             </intent-filter>
         </service>
+
+        <meta-data
+            android:name="android.telephony.PROPERTY_SATELLITE_DATA_OPTIMIZED"
+            android:value="true"/>
     </application>
 
     <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.INTERNET" />
 </manifest>
diff --git a/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
index 26b45e3..43cce9b 100644
--- a/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
+++ b/testapps/TestSatelliteApp/res/layout/activity_SatelliteTestApp.xml
@@ -81,5 +81,12 @@
             android:paddingStart="4dp"
             android:paddingEnd="4dp"
             android:text="@string/TestSatelliteWrapper"/>
+        <Button
+            android:id="@+id/TestSatelliteConstrainConnection"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingStart="4dp"
+            android:paddingEnd="4dp"
+            android:text="@string/TestSatelliteConstrainConnection"/>
     </LinearLayout>
 </ScrollView>
diff --git a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
index 5c3a72d..f48c022 100644
--- a/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestSatelliteApp/res/values/donottranslate_strings.xml
@@ -62,6 +62,8 @@
     <string name="sendMessage">sendMessage</string>
     <string name="receiveMessage">receiveMessage</string>
 
+    <string name="TestSatelliteConstrainConnection">Test Satellite Constrain Connection</string>
+
     <string name="TestSatelliteWrapper">Test Satellite Wrapper</string>
     <string name="requestNtnSignalStrength">requestNtnSignalStrength</string>
     <string name="registerForNtnSignalStrengthChanged">registerForNtnSignalStrengthChanged</string>
diff --git a/testapps/TestSatelliteApp/res/xml/network_security_config.xml b/testapps/TestSatelliteApp/res/xml/network_security_config.xml
new file mode 100644
index 0000000..463e65a
--- /dev/null
+++ b/testapps/TestSatelliteApp/res/xml/network_security_config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config cleartextTrafficPermitted="true">
+    <domain includeSubdomains="true">www.google.com</domain>
+  </domain-config>
+</network-security-config>
\ No newline at end of file
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/PingTask.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/PingTask.java
new file mode 100644
index 0000000..fe86c21
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/PingTask.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 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.testapps.satellitetestapp;
+
+import android.net.Network;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Scanner;
+
+class PingTask extends AsyncTask<Network, Integer, Integer> {
+  protected Integer doInBackground(Network... network) {
+    ping(network[0]);
+    return 0;
+  }
+  String ping(Network network) {
+    URL url = null;
+    try {
+      url = new URL("http://www.google.com");
+    } catch (Exception e) {
+      Log.d("SatelliteDataConstrained", "exception: " + e);
+    }
+    if (url != null) {
+      try {
+        Log.d("SatelliteDataConstrained", "ping " + url);
+        String result = httpGet(network, url);
+        Log.d("SatelliteDataConstrained", "Ping Success");
+        return result;
+      } catch (Exception e) {
+        Log.d("SatelliteDataConstrained", "exception: " + e);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Performs a HTTP GET to the specified URL on the specified Network, and returns
+   * the response body decoded as UTF-8.
+   */
+  private static String httpGet(Network network, URL httpUrl) throws IOException {
+    HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
+    try {
+      InputStream inputStream = connection.getInputStream();
+      Log.d("httpGet", "httpUrl + " + httpUrl);
+      Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
+      return scanner.hasNext() ? scanner.next() : "";
+    } finally {
+      connection.disconnect();
+    }
+  }
+}
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 cb56e87..911e179 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestApp.java
@@ -23,15 +23,24 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.Bundle;
+import android.os.Looper;
 import android.os.IBinder;
 import android.telephony.satellite.stub.SatelliteDatagram;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 /**
  * SatelliteTestApp main activity to navigate to other APIs related to satellite.
@@ -41,14 +50,23 @@
     private static final String TAG = "SatelliteTestApp";
     public static TestSatelliteService sSatelliteService;
     private final Object mSendDatagramLock = new Object();
-
+    Network mNetwork = null;
+    Context mContext;
+    ConnectivityManager mConnectivityManager;
+    NetworkCallback mSatelliteConstrainNetworkCallback;
+    private final ExecutorService executor = Executors.newSingleThreadExecutor();
     private TestSatelliteServiceConnection mSatelliteServiceConn;
     private List<SatelliteDatagram> mSentSatelliteDatagrams = new ArrayList<>();
     private static final int REQUEST_CODE_SEND_SMS = 1;
+    private final int NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED = 37;
+    private boolean isNetworkRequested = false;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        mContext = getApplicationContext();
+
+        mConnectivityManager = getSystemService(ConnectivityManager.class);
 
         if (mSatelliteServiceConn == null) {
             mSatelliteServiceConn = new TestSatelliteServiceConnection();
@@ -106,6 +124,21 @@
                 startActivity(intent);
             }
         });
+
+      findViewById(R.id.TestSatelliteConstrainConnection).setOnClickListener(view -> {
+        executor.execute(() -> {
+          Log.e(TAG, "onClick");
+          mSatelliteConstrainNetworkCallback = new NetworkCallback() {
+            @Override
+            public void onAvailable(final Network network) {
+              makeSatelliteDataConstrainedPing(network);
+            }
+          };
+          if(isNetworkRequested == false) {
+            requestingNetwork();
+          }
+        });
+      });
     }
 
     @Override
@@ -117,6 +150,61 @@
         }
     }
 
+    @Override
+    protected void onDestroy() {
+      super.onDestroy();
+      if(isNetworkRequested == true) {
+        releasingNetwork();
+      }
+    }
+
+    private void requestingNetwork() {
+      Log.e(TAG, "Requesting Network");
+      isNetworkRequested = true;
+      NetworkRequest request = new NetworkRequest.Builder()
+          .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+          .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+          .removeCapability(NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED)
+          .addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE)
+          .build();
+
+      // Requesting for Network
+      mConnectivityManager.requestNetwork(request, mSatelliteConstrainNetworkCallback);
+      Log.e(TAG, "onClick + " + request);
+    }
+
+
+    private void makeSatelliteDataConstrainedPing(final Network network) {
+      Log.e(TAG, "onAvailable + " + network);
+      mNetwork = network;
+
+      try {
+        PingTask pingTask = new PingTask();
+        Log.d(TAG, "Connecting Satellite for ping");
+        String pingResult = pingTask.ping(mNetwork);
+        if(pingResult != null) {
+          Toast.makeText(mContext, "Ping Passed!", Toast.LENGTH_SHORT).show();
+        } else {
+          Toast.makeText(mContext, "Ping Failed!", Toast.LENGTH_SHORT).show();
+        }
+      } catch (Exception e) {
+        Log.d(TAG, "Exception at ping: " + e);
+      } finally {
+        // Releasing the callback in the background thread
+        releasingNetwork();
+      }
+    }
+
+    private void releasingNetwork() {
+      Log.e(TAG, "Realsing Network");
+      try {
+        mConnectivityManager
+            .unregisterNetworkCallback(mSatelliteConstrainNetworkCallback);
+      } catch (Exception e) {
+        Log.d("SatelliteDataConstrined", "Exception: " + e);
+      }
+      isNetworkRequested = false;
+    }
 
     private final ILocalSatelliteListener mSatelliteListener =
             new ILocalSatelliteListener.Stub() {
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index 94e91d3..d8c3727 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -24,7 +24,9 @@
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
+import android.os.TestLooperManager;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -37,6 +39,7 @@
 import com.android.internal.telephony.data.DataNetworkController;
 import com.android.internal.telephony.metrics.MetricsCollector;
 import com.android.internal.telephony.metrics.PersistAtomsStorage;
+import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneInterfaceManager;
 
@@ -69,6 +72,10 @@
     @Mock protected DataNetworkController mDataNetworkController;
     @Mock private MetricsCollector mMetricsCollector;
 
+    private HandlerThread mTestHandlerThread;
+    protected Looper mTestLooper;
+    protected TestLooperManager mLooperManager;
+
     private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
     private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
 
@@ -80,6 +87,9 @@
 
         doCallRealMethod().when(mPhoneGlobals).getBaseContext();
         doCallRealMethod().when(mPhoneGlobals).getResources();
+        doCallRealMethod().when(mPhoneGlobals).getSystemService(Mockito.anyString());
+        doCallRealMethod().when(mPhoneGlobals).getSystemService(Mockito.any(Class.class));
+        doCallRealMethod().when(mPhoneGlobals).getSystemServiceName(Mockito.any(Class.class));
         doCallRealMethod().when(mPhone).getServiceState();
 
         mContext = spy(new TestContext());
@@ -96,6 +106,8 @@
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone});
         replaceInstance(PhoneGlobals.class, "sMe", null, mPhoneGlobals);
         replaceInstance(PhoneFactory.class, "sMetricsCollector", null, mMetricsCollector);
+        replaceInstance(SatelliteController.class, "sInstance", null,
+                Mockito.mock(SatelliteController.class));
 
         doReturn(Mockito.mock(PersistAtomsStorage.class)).when(mMetricsCollector).getAtomsStorage();
 
@@ -112,9 +124,47 @@
     public void tearDown() throws Exception {
         // Ensure there are no static references to handlers after test completes.
         PhoneConfigurationManager.unregisterAllMultiSimConfigChangeRegistrants();
+        cleanupTestLooper();
         restoreInstances();
     }
 
+    protected void setupTestLooper() {
+        mTestHandlerThread = new HandlerThread("TestHandlerThread");
+        mTestHandlerThread.start();
+        mTestLooper = mTestHandlerThread.getLooper();
+        mLooperManager = new TestLooperManager(mTestLooper);
+    }
+
+    private void cleanupTestLooper() {
+        mTestLooper = null;
+        if (mLooperManager != null) {
+            mLooperManager.release();
+            mLooperManager = null;
+        }
+        if (mTestHandlerThread != null) {
+            mTestHandlerThread.quit();
+            try {
+                mTestHandlerThread.join();
+            } catch (InterruptedException ex) {
+                Log.w("TelephonyTestBase", "HandlerThread join interrupted", ex);
+            }
+            mTestHandlerThread = null;
+        }
+    }
+
+    protected void processOneMessage() {
+        var msg = mLooperManager.next();
+        mLooperManager.execute(msg);
+        mLooperManager.recycle(msg);
+    }
+
+    protected void processAllMessages() {
+        for (var msg = mLooperManager.poll(); msg != null && msg.getTarget() != null;) {
+            mLooperManager.execute(msg);
+            mLooperManager.recycle(msg);
+        }
+    }
+
     protected final boolean waitForExecutorAction(Executor executor, long timeoutMillis) {
         final CountDownLatch lock = new CountDownLatch(1);
         executor.execute(() -> {
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index bf7832a..54ee6e0 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -33,6 +33,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
+import android.net.ConnectivityManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
@@ -72,6 +73,7 @@
     @Mock ImsManager mMockImsManager;
     @Mock UserManager mMockUserManager;
     @Mock PackageManager mPackageManager;
+    @Mock ConnectivityManager mMockConnectivityManager;
 
     private final SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
 
@@ -192,6 +194,9 @@
             case Context.CARRIER_CONFIG_SERVICE: {
                 return mMockCarrierConfigManager;
             }
+            case Context.CONNECTIVITY_SERVICE: {
+                return mMockConnectivityManager;
+            }
             case Context.TELECOM_SERVICE: {
                 return mMockTelecomManager;
             }
@@ -216,6 +221,9 @@
         if (serviceClass == CarrierConfigManager.class) {
             return Context.CARRIER_CONFIG_SERVICE;
         }
+        if (serviceClass == ConnectivityManager.class) {
+            return Context.CONNECTIVITY_SERVICE;
+        }
         if (serviceClass == TelecomManager.class) {
             return Context.TELECOM_SERVICE;
         }
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
index 00726c1..5b306e6 100644
--- a/tests/src/com/android/phone/CarrierConfigLoaderTest.java
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -52,7 +52,6 @@
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyRegistryManager;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -82,7 +81,6 @@
  * Unit Test for CarrierConfigLoader.
  */
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class CarrierConfigLoaderTest extends TelephonyTestBase {
     @Rule
     public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -109,7 +107,6 @@
     private TelephonyManager mTelephonyManager;
     private CarrierConfigLoader mCarrierConfigLoader;
     private Handler mHandler;
-    private TestableLooper mTestableLooper;
 
     // The AIDL stub will use PermissionEnforcer to check permission from the caller.
     private FakePermissionEnforcer mFakePermissionEnforcer = new FakePermissionEnforcer();
@@ -117,6 +114,9 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        setupTestLooper();
+        doReturn(true).when(mPackageManager).hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
         doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName(
                 eq(PermissionEnforcer.class));
         doReturn(mFakePermissionEnforcer).when(mContext).getSystemService(
@@ -150,8 +150,7 @@
         when(mContext.getSystemService(TelephonyRegistryManager.class)).thenReturn(
                 mTelephonyRegistryManager);
 
-        mTestableLooper = TestableLooper.get(this);
-        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestableLooper.getLooper(),
+        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mTestLooper,
                 mFeatureFlags);
         mHandler = mCarrierConfigLoader.getHandler();
 
@@ -211,7 +210,10 @@
         mCarrierConfigLoader.saveNoSimConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, config);
         mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
                 IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-        mTestableLooper.processAllMessages();
+        processOneMessage();
+        processOneMessage();
+        processOneMessage();
+        processOneMessage();
 
         assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID)).isNull();
         assertThat(mCarrierConfigLoader.getConfigFromCarrierApp(DEFAULT_PHONE_ID)).isNull();
@@ -250,7 +252,7 @@
                 DEFAULT_PHONE_ID, carrierId, config);
         mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
                 IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        mTestableLooper.processAllMessages();
+        processAllMessages();
 
         assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID).getInt(
                 CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
@@ -292,7 +294,8 @@
 
         mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, null /*overrides*/,
                 false/*persistent*/);
-        mTestableLooper.processAllMessages();
+        processOneMessage();
+        processOneMessage();
 
         assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).isEmpty()).isTrue();
         verify(mSubscriptionManagerService).updateSubscriptionByCarrierConfig(
@@ -314,7 +317,8 @@
         PersistableBundle config = getTestConfig();
         mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, config /*overrides*/,
                 false/*persistent*/);
-        mTestableLooper.processAllMessages();
+        processOneMessage();
+        processOneMessage();
 
         assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).getInt(
                 CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
@@ -433,7 +437,6 @@
         replaceInstance(CarrierConfigLoader.class, "mVendorApiLevel", mCarrierConfigLoader,
                 vendorApiLevel);
 
-        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         doReturn(false).when(mPackageManager).hasSystemFeature(
                 eq(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION));
 
@@ -479,10 +482,10 @@
                 any(Intent.class), any(ServiceConnection.class), anyInt());
         doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
         mHandler.sendMessage(mHandler.obtainMessage(17 /* EVENT_MULTI_SIM_CONFIG_CHANGED */));
-        mTestableLooper.processAllMessages();
+        processAllMessages();
 
         mCarrierConfigLoader.updateConfigForPhoneId(1, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-        mTestableLooper.processAllMessages();
+        processAllMessages();
     }
 
     @Test
@@ -502,18 +505,20 @@
         Mockito.clearInvocations(mTelephonyRegistryManager);
         Mockito.clearInvocations(mContext);
         mHandler.sendMessage(mHandler.obtainMessage(13 /* EVENT_SYSTEM_UNLOCKED */));
-        mTestableLooper.processAllMessages();
+        processOneMessage();
         mHandler.sendMessage(mHandler.obtainMessage(5 /* EVENT_FETCH_DEFAULT_DONE */));
-        mTestableLooper.processAllMessages();
+        processOneMessage();
+        processOneMessage();
         mHandler.sendMessage(mHandler.obtainMessage(6 /* EVENT_FETCH_CARRIER_DONE */));
-        mTestableLooper.processAllMessages();
+        processOneMessage();
+        processOneMessage();
 
         ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
         verify(mSubscriptionManagerService).updateSubscriptionByCarrierConfig(eq(0), anyString(),
                 any(PersistableBundle.class), runnableCaptor.capture());
 
         runnableCaptor.getValue().run();
-        mTestableLooper.processAllMessages();
+        processAllMessages();
 
         // Broadcast should be sent for backwards compatibility.
         verify(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
diff --git a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
index 5521ac0..0c91af7 100644
--- a/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
+++ b/tests/src/com/android/phone/ImsStateCallbackControllerTest.java
@@ -985,7 +985,9 @@
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(subIds);
     }
 
-    private void processAllMessages() {
+    // Override - not using mTestLooper from the base class
+    @Override
+    protected void processAllMessages() {
         while (!mLooper.getLooper().getQueue().isIdle()) {
             mLooper.processAllMessages();
         }
diff --git a/tests/src/com/android/phone/LocationAccessPolicyTest.java b/tests/src/com/android/phone/LocationAccessPolicyTest.java
index 551c2cb..7acdb77 100644
--- a/tests/src/com/android/phone/LocationAccessPolicyTest.java
+++ b/tests/src/com/android/phone/LocationAccessPolicyTest.java
@@ -148,7 +148,7 @@
         }
     }
 
-    private static final int TESTING_UID = 10001;
+    private static final int TESTING_UID = UserHandle.getUid(UserHandle.myUserId(), 10001);
     private static final int TESTING_PID = 8009;
     private static final String TESTING_CALLING_PACKAGE = "com.android.phone";
 
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index bbcb52b..266481e 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -59,6 +59,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.phone.satellite.accesscontrol.SatelliteAccessController;
 
@@ -118,6 +119,9 @@
         replaceInstance(SatelliteAccessController.class, "sInstance", null,
                 Mockito.mock(SatelliteAccessController.class));
 
+        replaceInstance(SatelliteController.class, "sInstance", null,
+                Mockito.mock(SatelliteController.class));
+
         mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(
                 InstrumentationRegistry.getInstrumentation().getTargetContext());
         doReturn(mSharedPreferences).when(mPhoneGlobals)
@@ -142,7 +146,6 @@
         // 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();
         doReturn(true).when(mFeatureFlags).hsumPackageManager();
         mPhoneInterfaceManager.setPackageManager(mPackageManager);
         doReturn(mPackageManager).when(mPhoneGlobals).getPackageManager();
@@ -508,7 +511,6 @@
     @Test
     @EnableCompatChanges({TelephonyManager.ENABLE_FEATURE_MAPPING})
     public void testWithTelephonyFeatureAndCompatChanges() throws Exception {
-        doReturn(true).when(mFeatureFlags).enforceTelephonyFeatureMappingForPublicApis();
         mPhoneInterfaceManager.setFeatureFlags(mFeatureFlags);
         doNothing().when(mPhoneInterfaceManager).enforceModifyPermission();
 
@@ -533,7 +535,6 @@
         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();
 
diff --git a/tests/src/com/android/phone/SimPhonebookProviderTest.java b/tests/src/com/android/phone/SimPhonebookProviderTest.java
index d8518f8..817c53e 100644
--- a/tests/src/com/android/phone/SimPhonebookProviderTest.java
+++ b/tests/src/com/android/phone/SimPhonebookProviderTest.java
@@ -29,11 +29,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -169,8 +169,7 @@
 
     @Test
     public void query_entityFiles_noSim_returnsEmptyCursor() {
-        when(mMockSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(
-                ImmutableList.of());
+        doReturn(ImmutableList.of()).when(mMockSubscriptionManager).getActiveSubscriptionInfoList();
 
         try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) {
             assertThat(cursor).hasCount(0);
@@ -363,7 +362,7 @@
         // Use a mock so that a null list can be returned
         IIccPhoneBook mockIccPhoneBook = mock(
                 IIccPhoneBook.class, AdditionalAnswers.delegatesTo(mIccPhoneBook));
-        when(mockIccPhoneBook.getAdnRecordsInEfForSubscriber(anyInt(), anyInt())).thenReturn(null);
+        doReturn(null).when(mockIccPhoneBook).getAdnRecordsInEfForSubscriber(anyInt(), anyInt());
         TestableSimPhonebookProvider.setup(mResolver, mMockSubscriptionManager, mockIccPhoneBook);
 
         try (Cursor adnCursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null,
@@ -1334,14 +1333,14 @@
     }
 
     private void setupSimsWithSubscriptionIds(int... subscriptionIds) {
-        when(mMockSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(subscriptionIds);
-        when(mMockSubscriptionManager.getActiveSubscriptionInfoCount())
-                .thenReturn(subscriptionIds.length);
+        doReturn(subscriptionIds).when(mMockSubscriptionManager).getActiveSubscriptionIdList();
+        doReturn(subscriptionIds.length).when(mMockSubscriptionManager)
+                .getActiveSubscriptionInfoCount();
         List<SubscriptionInfo> subscriptions = createSubscriptionsWithIds(subscriptionIds);
-        when(mMockSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(subscriptions);
+        doReturn(subscriptions).when(mMockSubscriptionManager).getActiveSubscriptionInfoList();
         for (SubscriptionInfo info : subscriptions) {
-            when(mMockSubscriptionManager.getActiveSubscriptionInfo(info.getSubscriptionId()))
-                    .thenReturn(info);
+            doReturn(info).when(mMockSubscriptionManager)
+                    .getActiveSubscriptionInfo(info.getSubscriptionId());
         }
     }
 
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
index 8c97ab7..8df197c 100644
--- a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
+++ b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
@@ -126,6 +126,7 @@
 import com.android.internal.telephony.satellite.SatelliteConfigParser;
 import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.satellite.SatelliteModemInterface;
+import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteControllerStats;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
@@ -243,6 +244,8 @@
     @Mock
     private ConcurrentHashMap<IBinder, ISatelliteCommunicationAccessStateCallback>
             mMockSatelliteCommunicationAccessStateChangedListeners;
+    @Mock
+    private CarrierRoamingSatelliteControllerStats mCarrierRoamingSatelliteControllerStats;
 
     private SatelliteInfo mSatelliteInfo;
 
@@ -366,6 +369,8 @@
                 mMockCountryDetector);
         replaceInstance(ControllerMetricsStats.class, "sInstance", null,
                 mock(ControllerMetricsStats.class));
+        replaceInstance(CarrierRoamingSatelliteControllerStats.class, "sInstance", null,
+                mCarrierRoamingSatelliteControllerStats);
         when(mMockSatelliteController.getSatellitePhone()).thenReturn(mMockPhone);
         when(mMockPhone.getSubId()).thenReturn(SubscriptionManager.getDefaultSubscriptionId());
 
@@ -444,6 +449,7 @@
         mMockApplicationInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
         when(mMockPackageManager.getApplicationInfo(anyString(), anyInt()))
                 .thenReturn(mMockApplicationInfo);
+        when(mCarrierRoamingSatelliteControllerStats.isMultiSim()).thenReturn(false);
 
         mSatelliteInfo = new SatelliteInfo(
                 UUID.randomUUID(),
@@ -1761,7 +1767,6 @@
                 mSatelliteAccessControllerUT,
                 DEFAULT_S2_LEVEL);
         when(mMockFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources.getBoolean(
                         com.android.internal.R.bool.config_oem_enabled_satellite_access_allow))
diff --git a/tests/src/com/android/phone/security/SafetySourceReceiverTest.java b/tests/src/com/android/phone/security/SafetySourceReceiverTest.java
index 584fb25..7268771 100644
--- a/tests/src/com/android/phone/security/SafetySourceReceiverTest.java
+++ b/tests/src/com/android/phone/security/SafetySourceReceiverTest.java
@@ -35,7 +35,6 @@
 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;
@@ -91,9 +90,6 @@
 
     @Test
     public void testOnReceive_noTelephonyFeature() {
-        mSetFlagsRule.enableFlags(
-                Flags.FLAG_ENFORCE_TELEPHONY_FEATURE_MAPPING_FOR_PUBLIC_APIS);
-
         when(mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
 
diff --git a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
index 5637c3a..a792780 100644
--- a/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
+++ b/tests/src/com/android/phone/slice/SlicePurchaseControllerTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -40,6 +41,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -56,6 +58,7 @@
 import android.telephony.data.UrspRule;
 import android.testing.TestableLooper;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
@@ -335,6 +338,12 @@
         mSlicePurchaseController.purchasePremiumCapability(
                 TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mHandler.obtainMessage());
         mTestableLooper.processAllMessages();
+        if (isAutomotive()) {
+            // TODO(b/401032628): this test is flaky here
+            assumeTrue(
+                    TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE
+                    != mResult);
+        }
         assertEquals(
                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
                 mResult);
@@ -350,6 +359,11 @@
                 mResult);
     }
 
+    private boolean isAutomotive() {
+        return InstrumentationRegistry.getTargetContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
     @Test
     public void testPurchasePremiumCapabilityResultNetworkNotAvailable() {
         doReturn((int) TelephonyManager.NETWORK_TYPE_BITMASK_NR).when(mPhone)
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index c0bd2dd..2605114 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -109,6 +109,8 @@
 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;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.satellite.SatelliteSOSMessageRecommender;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
@@ -197,9 +199,15 @@
         public void onHold() {
             wasHeld = true;
         }
+
+        @Override
+        void setOriginalConnection(com.android.internal.telephony.Connection connection) {
+            mOriginalConnection = connection;
+        }
     }
 
     public static class SimpleConference extends Conference {
+        public boolean wasDisconnected = false;
         public boolean wasUnheld = false;
 
         public SimpleConference(PhoneAccountHandle phoneAccountHandle) {
@@ -207,6 +215,11 @@
         }
 
         @Override
+        public void onDisconnect() {
+            wasDisconnected = true;
+        }
+
+        @Override
         public void onUnhold() {
             wasUnheld = true;
         }
@@ -258,6 +271,7 @@
     @Mock private EmergencyStateTracker mEmergencyStateTracker;
     @Mock private Resources mMockResources;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private com.android.server.telecom.flags.FeatureFlags mTelecomFlags;
     private Phone mPhone0;
     private Phone mPhone1;
 
@@ -285,7 +299,7 @@
         super.setUp();
 
         mTestConnectionService = new TestTelephonyConnectionService(mContext);
-        mTestConnectionService.setFeatureFlags(mFeatureFlags);
+        mTestConnectionService.setFeatureFlags(mFeatureFlags, mTelecomFlags);
         mTestConnectionService.setPhoneFactoryProxy(mPhoneFactoryProxy);
         mTestConnectionService.setSubscriptionManagerProxy(mSubscriptionManagerProxy);
         // Set configurations statically
@@ -339,6 +353,7 @@
         mBinderStub = (IConnectionService.Stub) mTestConnectionService.onBind(null);
         mSetFlagsRule.enableFlags(Flags.FLAG_DO_NOT_OVERRIDE_PRECISE_LABEL);
         mSetFlagsRule.enableFlags(Flags.FLAG_CALL_EXTRA_FOR_NON_HOLD_SUPPORTED_CARRIERS);
+        mSetFlagsRule.disableFlags(Flags.FLAG_HANGUP_ACTIVE_CALL_BASED_ON_EMERGENCY_CALL_DOMAIN);
     }
 
     @After
@@ -1861,6 +1876,7 @@
     @Test
     @SmallTest
     public void testSecondCallSameSubWontDisconnect() throws Exception {
+        doReturn(false).when(mTelecomFlags).enableCallSequencing();
         // Previous test gets us into a good enough state
         testIncomingDoesntRequestDisconnect();
 
@@ -2277,6 +2293,35 @@
     }
 
     /**
+     * For DSDA devices, verifies that calls on other subs are disconnected based on the passed in
+     * phone account
+     */
+    @Test
+    @SmallTest
+    public void testDisconnectCallsOnOtherSubs() throws Exception {
+        setupForCallTest();
+        when(mTelephonyManagerProxy.isConcurrentCallsPossible()).thenReturn(true);
+        doNothing().when(mContext).startActivityAsUser(any(), any());
+
+        mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@1",
+                new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551212"),
+                        new Bundle()),
+                true, false, null);
+        waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+        assertEquals(1, mTestConnectionService.getAllConnections().size());
+
+        TelephonyConnection cn = (TelephonyConnection)
+                mTestConnectionService.getAllConnections().toArray()[0];
+        cn.setActive();
+
+        List<Conferenceable> conferenceables = mTestConnectionService
+                .disconnectAllConferenceablesOnOtherSubs(PHONE_ACCOUNT_HANDLE_2);
+        assertFalse(conferenceables.isEmpty());
+        assertEquals(conferenceables.getFirst(), cn);
+        assertEquals(cn.getState(), android.telecom.Connection.STATE_DISCONNECTED);
+    }
+
+    /**
      * Verifies that TelephonyManager is used to determine whether a connection is Emergency when
      * creating an outgoing connection.
      */
@@ -3708,6 +3753,234 @@
     }
 
     @Test
+    public void testDomainSelectionAddCsEmergencyCallWhenImsCallActive() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_HANGUP_ACTIVE_CALL_BASED_ON_EMERGENCY_CALL_DOMAIN);
+
+        setupForCallTest();
+        doReturn(1).when(mPhone0).getSubId();
+        doReturn(1).when(mImsPhone).getSubId();
+        ImsPhoneCall imsPhoneCall = Mockito.mock(ImsPhoneCall.class);
+        ImsPhoneConnection imsPhoneConnection = Mockito.mock(ImsPhoneConnection.class);
+        when(imsPhoneCall.getPhone()).thenReturn(mImsPhone);
+        when(imsPhoneConnection.getCall()).thenReturn(imsPhoneCall);
+        when(imsPhoneConnection.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
+
+        // PROPERTY_IS_EXTERNAL_CALL: to avoid extra processing that is not related to this test.
+        SimpleTelephonyConnection tc1 = createTestConnection(PHONE_ACCOUNT_HANDLE_1,
+                android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
+        // IMS connection is set.
+        tc1.setOriginalConnection(imsPhoneConnection);
+        mTestConnectionService.addExistingConnection(PHONE_ACCOUNT_HANDLE_1, tc1);
+
+        assertEquals(1, mTestConnectionService.getAllConnections().size());
+        TelephonyConnection connection1 = (TelephonyConnection)
+                mTestConnectionService.getAllConnections().toArray()[0];
+        assertEquals(tc1, connection1);
+
+        // Add a CS emergency call.
+        String telecomCallId2 = "TC2";
+        int selectedDomain = DOMAIN_CS;
+        setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+        getTestContext().getCarrierConfig(0 /*subId*/).putBoolean(
+                CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, telecomCallId2));
+
+        // Hang up the active IMS call due to CS emergency call.
+        ArgumentCaptor<Connection.Listener> listenerCaptor =
+                ArgumentCaptor.forClass(Connection.Listener.class);
+        verify(imsPhoneConnection).addListener(listenerCaptor.capture());
+        assertTrue(tc1.wasDisconnected);
+
+        // Call disconnection completed.
+        Connection.Listener listener = listenerCaptor.getValue();
+        assertNotNull(listener);
+        listener.onDisconnect(0);
+
+        // Continue to proceed the outgoing emergency call after active call is disconnected.
+        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(mSatelliteSOSMessageRecommender, times(2))
+                .onEmergencyCallStarted(any(), anyBoolean());
+        verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+        verify(mPhone0).dial(anyString(), any(), any());
+
+        android.telecom.Connection tc = connectionCaptor.getValue();
+        assertNotNull(tc);
+        assertEquals(telecomCallId2, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+    }
+
+    @Test
+    public void testDomainSelectionAddImsEmergencyCallWhenCsCallActive() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_HANGUP_ACTIVE_CALL_BASED_ON_EMERGENCY_CALL_DOMAIN);
+
+        setupForCallTest();
+
+        // PROPERTY_IS_EXTERNAL_CALL: to avoid extra processing that is not related to this test.
+        SimpleTelephonyConnection tc1 = createTestConnection(PHONE_ACCOUNT_HANDLE_1,
+                android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
+        // CS connection is set.
+        tc1.setOriginalConnection(mInternalConnection);
+        mTestConnectionService.addExistingConnection(PHONE_ACCOUNT_HANDLE_1, tc1);
+
+        assertEquals(1, mTestConnectionService.getAllConnections().size());
+        TelephonyConnection connection1 = (TelephonyConnection)
+                mTestConnectionService.getAllConnections().toArray()[0];
+        assertEquals(tc1, connection1);
+
+        // Add an IMS emergency call.
+        String telecomCallId2 = "TC2";
+        int selectedDomain = DOMAIN_PS;
+        setupForDialForDomainSelection(mPhone0, selectedDomain, true);
+        getTestContext().getCarrierConfig(0 /*subId*/).putBoolean(
+                CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, telecomCallId2));
+
+        // Hang up the active CS call due to IMS emergency call.
+        ArgumentCaptor<Connection.Listener> listenerCaptor =
+                ArgumentCaptor.forClass(Connection.Listener.class);
+        verify(mInternalConnection).addListener(listenerCaptor.capture());
+        assertTrue(tc1.wasDisconnected);
+
+        // Call disconnection completed.
+        Connection.Listener listener = listenerCaptor.getValue();
+        assertNotNull(listener);
+        listener.onDisconnect(0);
+
+        // Continue to proceed the outgoing emergency call after active call is disconnected.
+        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(mSatelliteSOSMessageRecommender, times(2))
+                .onEmergencyCallStarted(any(), anyBoolean());
+        verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(any(), any());
+        verify(mPhone0).dial(anyString(), any(), any());
+
+        android.telecom.Connection tc = connectionCaptor.getValue();
+        assertNotNull(tc);
+        assertEquals(telecomCallId2, tc.getTelecomCallId());
+        assertEquals(mTestConnectionService.getEmergencyConnection(), tc);
+    }
+
+    @Test
+    @SmallTest
+    public void testDomainSelectionMaybeDisconnectCallsOnOtherDomainWhenNoActiveCalls() {
+        SimpleTelephonyConnection ec = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, true);
+        Consumer<Boolean> consumer = (result) -> {
+            if (!result) {
+                fail("Unexpected result=" + result);
+            }
+        };
+        CompletableFuture<Void> unused =
+                TelephonyConnectionService.maybeDisconnectCallsOnOtherDomain(mPhone0,
+                        ec, DOMAIN_PS, Collections.emptyList(), Collections.emptyList(), consumer);
+
+        assertTrue(unused.isDone());
+    }
+
+    @Test
+    @SmallTest
+    public void testDomainSelectionMaybeDisconnectCallsOnOtherDomainWhenConferenceOnly() {
+        setupForCallTest();
+        ArrayList<android.telecom.Conference> conferences = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, false);
+        SimpleConference conference = createTestConference(PHONE_ACCOUNT_HANDLE_1, 0);
+        tc1.setOriginalConnection(mInternalConnection);
+        conference.addConnection(tc1);
+        conferences.add(conference);
+
+        SimpleTelephonyConnection ec = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, true);
+        Consumer<Boolean> consumer = (result) -> {
+            if (!result) {
+                fail("Unexpected result=" + result);
+            }
+        };
+        CompletableFuture<Void> unused =
+                TelephonyConnectionService.maybeDisconnectCallsOnOtherDomain(
+                        mPhone0, ec, DOMAIN_PS, Collections.emptyList(), conferences, consumer);
+
+        assertTrue(unused.isDone());
+        assertTrue(conference.wasDisconnected);
+    }
+
+    @Test
+    @SmallTest
+    public void testDomainSelectionMaybeDisconnectCallsOnOtherDomainWhenActiveCall() {
+        setupForCallTest();
+        ArrayList<android.telecom.Connection> connections = new ArrayList<>();
+        ArrayList<android.telecom.Conference> conferences = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, false);
+        SimpleConference conference = createTestConference(PHONE_ACCOUNT_HANDLE_1, 0);
+        tc1.setOriginalConnection(mInternalConnection);
+        connections.add(tc1);
+        conference.addConnection(tc1);
+        conferences.add(conference);
+
+        SimpleTelephonyConnection ec = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, true);
+        Consumer<Boolean> consumer = (result) -> {
+            if (!result) {
+                fail("Unexpected result=" + result);
+            }
+        };
+        CompletableFuture<Void> unused =
+                TelephonyConnectionService.maybeDisconnectCallsOnOtherDomain(
+                        mPhone0, ec, DOMAIN_PS, connections, conferences, consumer);
+
+        assertFalse(unused.isDone());
+        assertTrue(tc1.wasDisconnected);
+        assertTrue(conference.wasDisconnected);
+
+        ArgumentCaptor<Connection.Listener> listenerCaptor =
+                ArgumentCaptor.forClass(Connection.Listener.class);
+        verify(mInternalConnection).addListener(listenerCaptor.capture());
+
+        // Call disconnection completed.
+        Connection.Listener listener = listenerCaptor.getValue();
+        assertNotNull(listener);
+        listener.onDisconnect(0);
+
+        assertTrue(unused.isDone());
+    }
+
+    @Test
+    @SmallTest
+    public void testDomainSelectionMaybeDisconnectCallsOnOtherDomainWhenExceptionOccurs() {
+        setupForCallTest();
+        ArrayList<android.telecom.Connection> connections = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, false);
+        tc1.setOriginalConnection(mInternalConnection);
+        connections.add(tc1);
+        doThrow(new NullPointerException("Intended: Connection is null"))
+                .when(mInternalConnection).addListener(any());
+
+        SimpleTelephonyConnection ec = createTestConnection(PHONE_ACCOUNT_HANDLE_1, 0, true);
+        Consumer<Boolean> consumer = (result) -> {
+            if (result) {
+                fail("Unexpected result=" + result);
+            }
+        };
+        CompletableFuture<Void> unused =
+                TelephonyConnectionService.maybeDisconnectCallsOnOtherDomain(
+                        mPhone0, ec, DOMAIN_PS, connections, Collections.emptyList(), consumer);
+
+        assertTrue(unused.isDone());
+        assertFalse(tc1.wasDisconnected);
+    }
+
+    @Test
     public void testDomainSelectionWithMmiCode() {
         //UT domain selection should not be handled by new domain selector.
         doNothing().when(mContext).startActivityAsUser(any(), any());