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();