Merge "[Satellite] Mocking carrierconfig properly to include right plmn and satellite data." into main
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 457e8f8..4cb0575 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -153,6 +153,10 @@
     // 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}");
@@ -1366,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) -> {
@@ -2851,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");
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/satellite/accesscontrol/SatelliteAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
index b4ae4b9..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(),
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 e27bea5..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;
         }
@@ -340,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
@@ -3739,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());