Merge "Hang up active call based on emergency call domain" into main
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index f4b2545..b51ffff 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -13283,6 +13283,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;
                         }
@@ -14181,6 +14182,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.
@@ -14251,6 +14276,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.
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/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/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/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index dac566e..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,7 +148,7 @@
 
     // 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.
@@ -749,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;
@@ -1343,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) -> {
@@ -1373,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,
@@ -2683,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;
@@ -3968,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());
@@ -3981,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
@@ -4914,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
@@ -4929,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) {
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 324bcbc..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,7 @@
     @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(
@@ -152,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();
 
@@ -213,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();
@@ -252,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);
@@ -294,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(
@@ -316,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);
@@ -480,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
@@ -503,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 88b84a7..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)
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/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 6c577a5..2605114 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -2293,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.
      */