Fixed selfManaged call issue

The self-managed call will set active directly. If there is an active
call, we should hold or disconnect it based on the holdability.

Test: runtest -x CallsManagerTest.java
Bug: 76016794
Merged-In: I053a50997facd108053380eac115464c7fa7e011
Change-Id: I053a50997facd108053380eac115464c7fa7e011
(cherry picked from commit 8229bc968c48ad87cb251f9c198495fd79a47807)
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 334667c..90ee652 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1469,34 +1469,7 @@
             // Hold or disconnect the active call and request call focus for the incoming call.
             Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
             Log.d(this, "Incoming call = %s Ongoing call %s", call, activeCall);
-            if (activeCall != null && activeCall != call) {
-                // We purposely don't check if the active call CAN current hold, but rather we check
-                // whether it CAN support hold.  Consider this scenario:
-                // Call A - Active (CAPABILITY_SUPPORT_HOLD, but not CAPABILITY_HOLD)
-                // Call B - Held (CAPABILITY_SUPPORT_HOLD, but not CAPABILITY_HOLD)
-                // Call C - Incoming call
-                // In this scenario we are going to first disconnect the held call (Call B), which
-                // will mean that the active call (Call A) will now support hold.
-                if (supportsHold(activeCall)) {
-                    Call heldCall = getHeldCall();
-                    if (heldCall != null) {
-                        Log.i(this, "Disconnecting held call %s before holding active call.",
-                                heldCall);
-                        heldCall.disconnect();
-                    }
-
-                    Log.d(this, "Answer %s, hold %s", call, activeCall);
-                    activeCall.hold();
-                } else {
-                    // This call does not support hold. If it is from a different connection
-                    // service, then disconnect it, otherwise allow the connection service to
-                    // figure out the right states.
-                    if (activeCall.getConnectionService() != call.getConnectionService()) {
-                        activeCall.disconnect("Can't hold when answering " + call.getId());
-                    }
-                }
-            }
-
+            holdActiveCallForNewCall(call);
             mConnectionSvrFocusMgr.requestFocus(
                     call,
                     new RequestCallback(new ActionAnswerCall(call, videoState)));
@@ -1932,11 +1905,43 @@
         maybeMoveToSpeakerPhone(call);
     }
 
-    void markCallAsActive(Call call) {
+    /**
+     * Returns true if the active call is held.
+     */
+    boolean holdActiveCallForNewCall(Call call) {
+        Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
+        if (activeCall != null && activeCall != call) {
+            if (canHold(activeCall)) {
+                activeCall.hold();
+                return true;
+            } else if (supportsHold(call)) {
+                Call heldCall = getHeldCallByConnectionService(call.getConnectionService());
+                if (heldCall != null) {
+                    heldCall.disconnect();
+                    Log.i(this, "Disconnecting held call %s before holding active call.", heldCall);
+                }
+                activeCall.hold();
+                return true;
+            } else {
+                // This call does not support hold. If it is from a different connection
+                // service, then disconnect it, otherwise allow the connection service to
+                // figure out the right states.
+                if (activeCall.getConnectionService() != call.getConnectionService()) {
+                    activeCall.disconnect();
+                }
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    public void markCallAsActive(Call call) {
         if (call.isSelfManaged()) {
             // backward compatibility, the self-managed connection service will set the call state
-            // to active directly. We should request the call focus for self-managed call before
-            // the state change
+            // to active directly. We should hold or disconnect the current active call based on the
+            // holdability, and request the call focus for the self-managed call before the state
+            // change.
+            holdActiveCallForNewCall(call);
             mConnectionSvrFocusMgr.requestFocus(
                     call,
                     new RequestCallback(new ActionSetCallState(
@@ -2168,6 +2173,14 @@
         return getFirstCallWithState(CallState.ON_HOLD);
     }
 
+    public Call getHeldCallByConnectionService(ConnectionServiceWrapper connSvr) {
+        Optional<Call> heldCall = mCalls.stream()
+                .filter(call -> call.getConnectionService() == connSvr
+                        && call.getState() == CallState.ON_HOLD)
+                .findFirst();
+        return heldCall.isPresent() ? heldCall.get() : null;
+    }
+
     @VisibleForTesting
     public int getNumHeldCalls() {
         int count = 0;
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 6bba7af..ed2f6b1 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -23,8 +23,6 @@
 import static org.mockito.ArgumentMatchers.anyChar;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
@@ -421,6 +419,7 @@
         // GIVEN a CallsManager with ongoing call, and this call can be held
         Call ongoingCall = addSpyCall();
         doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
         when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
 
         // and a held call
@@ -430,11 +429,11 @@
         mCallsManager.unholdCall(heldCall);
 
         // THEN the ongoing call is held, and the focus request for incoming call is sent
-        verify(ongoingCall).hold();
+        verify(ongoingCall).hold(any());
         verifyFocusRequestAndExecuteCallback(heldCall);
 
         // and held call is unhold now
-        verify(heldCall).unhold();
+        verify(heldCall).unhold(any());
     }
 
     @SmallTest
@@ -446,6 +445,7 @@
         // GIVEN a CallsManager with ongoing call, and this call can not be held
         Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
         doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
         when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
 
         // and a held call which has different ConnectionService
@@ -455,13 +455,14 @@
         mCallsManager.unholdCall(heldCall);
 
         // THEN the ongoing call is disconnected, and the focus request for incoming call is sent
-        verify(ongoingCall).disconnect();
+        verify(ongoingCall).disconnect(any());
         verifyFocusRequestAndExecuteCallback(heldCall);
 
         // and held call is unhold now
-        verify(heldCall).unhold();
+        verify(heldCall).unhold(any());
     }
 
+    @SmallTest
     @Test
     public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
         ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class);
@@ -469,6 +470,7 @@
         // GIVEN a CallsManager with ongoing call, and this call can not be held
         Call ongoingCall = addSpyCallWithConnectionService(connSvr);
         doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
         when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
 
         // and a held call which has different ConnectionService
@@ -478,11 +480,11 @@
         mCallsManager.unholdCall(heldCall);
 
         // THEN the ongoing call is held
-        verify(ongoingCall).hold();
+        verify(ongoingCall).hold(any());
         verifyFocusRequestAndExecuteCallback(heldCall);
 
         // and held call is unhold now
-        verify(heldCall).unhold();
+        verify(heldCall).unhold(any());
     }
 
     @SmallTest
@@ -491,6 +493,7 @@
         // GIVEN a CallsManager with ongoing call, and this call can be held
         Call ongoingCall = addSpyCall();
         doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
         when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
 
         // WHEN answer an incoming call
@@ -528,13 +531,14 @@
 
     @SmallTest
     @Test
-    public void testANswerCallWhenOngoingHasDifferentConnectionService() {
+    public void testAnswerCallWhenOngoingHasDifferentConnectionService() {
         ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
         ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
 
         // GIVEN a CallsManager with ongoing call, and this call can not be held
         Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
         doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
         when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
 
         // WHEN answer an incoming call
@@ -551,6 +555,44 @@
 
     @SmallTest
     @Test
+    public void testAnswerCallWhenMultipleHeldCallsExisted() {
+        ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
+        ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // Given an ongoing call and held call with the ConnectionService connSvr1. The
+        // ConnectionService connSvr1 can handle one held call
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        Call heldCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(CallState.ON_HOLD).when(heldCall).getState();
+
+        // and other held call has difference ConnectionService
+        Call heldCall2 = addSpyCallWithConnectionService(connSvr2);
+        doReturn(CallState.ON_HOLD).when(heldCall2).getState();
+
+        // WHEN answer an incoming call which ConnectionService is connSvr1
+        Call incomingCall = addSpyCallWithConnectionService(connSvr1);
+        mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY);
+
+        // THEN the previous held call is disconnected
+        verify(heldCall).disconnect();
+
+        // and the ongoing call is held
+        verify(ongoingCall).hold();
+
+        // and the heldCall2 is not disconnected
+        verify(heldCall2, never()).disconnect();
+
+        // and the focus request is sent
+        verifyFocusRequestAndExecuteCallback(incomingCall);
+
+        // and the incoming call is answered
+        verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
+    }
+
+    @SmallTest
+    @Test
     public void testAnswerCallWhenNoOngoingCallExisted() {
         // GIVEN a CallsManager with no ongoing call.
 
@@ -565,6 +607,83 @@
         verify(incomingCall).answer(VideoProfile.STATE_AUDIO_ONLY);
     }
 
+    @SmallTest
+    @Test
+    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() {
+        ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class);
+        ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr1);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        doReturn(ongoingCall).when(mConnectionSvrFocusMgr).getCurrentFocusCall();
+
+        // and a new self-managed call which has different ConnectionService
+        Call newCall = addSpyCallWithConnectionService(connSvr2);
+        doReturn(true).when(newCall).isSelfManaged();
+
+        // WHEN active the new call
+        mCallsManager.markCallAsActive(newCall);
+
+        // THEN the ongoing call is disconnected, and the focus request for the new call is sent
+        verify(ongoingCall).disconnect();
+        verifyFocusRequestAndExecuteCallback(newCall);
+
+        // and the new call is active
+        assertEquals(CallState.ACTIVE, newCall.getState());
+    }
+
+    @SmallTest
+    @Test
+    public void testSetActiveCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() {
+        ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class);
+
+        // GIVEN a CallsManager with ongoing call, and this call can not be held
+        Call ongoingCall = addSpyCallWithConnectionService(connSvr);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall);
+
+        // and a new self-managed call which has the same ConnectionService
+        Call newCall = addSpyCallWithConnectionService(connSvr);
+        doReturn(true).when(newCall).isSelfManaged();
+
+        // WHEN active the new call
+        mCallsManager.markCallAsActive(newCall);
+
+        // THEN the ongoing call isn't disconnected
+        verify(ongoingCall, never()).disconnect();
+        verifyFocusRequestAndExecuteCallback(newCall);
+
+        // and the new call is active
+        assertEquals(CallState.ACTIVE, newCall.getState());
+    }
+
+    @SmallTest
+    @Test
+    public void testSetActiveCallWhenOngoingCallCanBeHeld() {
+        // GIVEN a CallsManager with ongoing call, and this call can be held
+        Call ongoingCall = addSpyCall();
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_HOLD);
+        doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD);
+        doReturn(ongoingCall).when(mConnectionSvrFocusMgr).getCurrentFocusCall();
+
+        // and a new self-managed call
+        Call newCall = addSpyCall();
+        doReturn(true).when(newCall).isSelfManaged();
+
+        // WHEN active the new call
+        mCallsManager.markCallAsActive(newCall);
+
+        // THEN the ongoing call is held
+        verify(ongoingCall).hold();
+        verifyFocusRequestAndExecuteCallback(newCall);
+
+        // and the new call is active
+        assertEquals(CallState.ACTIVE, newCall.getState());
+    }
+
     private Call addSpyCallWithConnectionService(ConnectionServiceWrapper connSvr) {
         Call call = addSpyCall();
         doReturn(connSvr).when(call).getConnectionService();