Loading src/com/android/server/telecom/Call.java +10 −9 Original line number Diff line number Diff line Loading @@ -802,21 +802,18 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, /** {@inheritDoc} */ @Override public String toString() { String component = null; if (mConnectionService != null && mConnectionService.getComponentName() != null) { component = mConnectionService.getComponentName().flattenToShortString(); } return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), %s, %s]", return String.format(Locale.US, "[Call id=%s, state=%s, tpac=%s, cmgr=%s, handle=%s, " + "vidst=%s, childs(%d), has_parent(%b), cap=%s, prop=%s]", mId, CallState.toString(mState), component, getTargetPhoneAccount(), getConnectionManagerPhoneAccount(), Log.piiHandle(mHandle), getVideoStateDescription(getVideoState()), getChildCalls().size(), getParentCall() != null, Connection.capabilitiesToString(getConnectionCapabilities()), Connection.propertiesToString(getConnectionProperties())); Connection.capabilitiesToStringShort(getConnectionCapabilities()), Connection.propertiesToStringShort(getConnectionProperties())); } @Override Loading Loading @@ -844,6 +841,10 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, } else { s.append("not set"); } if (getConnectionManagerPhoneAccount() != null) { s.append("\n\tConn mgr: "); s.append(getConnectionManagerPhoneAccount()); } s.append("\n\tTo address: "); s.append(Log.piiHandle(getHandle())); Loading src/com/android/server/telecom/CallsManager.java +46 −14 Original line number Diff line number Diff line Loading @@ -2607,10 +2607,10 @@ public class CallsManager extends Call.ListenerBase Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId()); } else { // This call does not support hold. If it is from a different connection // service, then disconnect it, otherwise invoke call.hold() and allow the // connection service to handle the situation. if (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { // service or connection manager, then disconnect it, otherwise invoke // call.hold() and allow the connection service or connection manager to handle // the situation. if (!areFromSameSource(activeCall, call)) { if (!activeCall.isEmergencyCall()) { activeCall.disconnect("Swap to " + call.getId()); } else { Loading Loading @@ -2856,11 +2856,11 @@ public class CallsManager extends Call.ListenerBase activeCall.hold(); return true; } else if (supportsHold(activeCall) && PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { && areFromSameSource(activeCall, call)) { // Handle the case where the active call and the new call are from the same CS, and // the currently active call supports hold but cannot currently be held. // Handle the case where the active call and the new call are from the same CS or // connection manager, and the currently active call supports hold but cannot // currently be held. // In this case we'll look for the other held call for this connectionService and // disconnect it prior to holding the active call. // E.g. Loading @@ -2882,10 +2882,9 @@ public class CallsManager extends Call.ListenerBase 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 (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { // service or connection manager, then disconnect it, otherwise allow the connection // service or connection manager to figure out the right states. if (!areFromSameSource(activeCall, call)) { Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be " + "made active.", activeCall.getId(), call.getId()); if (!activeCall.isEmergencyCall()) { Loading Loading @@ -3297,6 +3296,9 @@ public class CallsManager extends Call.ListenerBase Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : parcelableConference.getConnectElapsedTimeMillis(); PhoneAccountHandle connectionMgr = mPhoneAccountRegistrar.getSimCallManagerFromHandle(phoneAccount, mCurrentUserHandle); Call call = new Call( callId, mContext, Loading @@ -3306,7 +3308,7 @@ public class CallsManager extends Call.ListenerBase mPhoneNumberUtilsAdapter, null /* handle */, null /* gatewayInfo */, null /* connectionManagerPhoneAccount */, connectionMgr, phoneAccount, Call.CALL_DIRECTION_UNDEFINED /* callDirection */, false /* forceAttachToExistingConnection */, Loading Loading @@ -4085,6 +4087,10 @@ public class CallsManager extends Call.ListenerBase Call createCallForExistingConnection(String callId, ParcelableConnection connection) { boolean isDowngradedConference = (connection.getConnectionProperties() & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; PhoneAccountHandle connectionMgr = mPhoneAccountRegistrar.getSimCallManagerFromHandle(connection.getPhoneAccount(), mCurrentUserHandle); Call call = new Call( callId, mContext, Loading @@ -4094,7 +4100,7 @@ public class CallsManager extends Call.ListenerBase mPhoneNumberUtilsAdapter, connection.getHandle() /* handle */, null /* gatewayInfo */, null /* connectionManagerPhoneAccount */, connectionMgr, connection.getPhoneAccount(), /* targetPhoneAccountHandle */ Call.getRemappedCallDirection(connection.getCallDirection()) /* callDirection */, false /* forceAttachToExistingConnection */, Loading Loading @@ -5110,6 +5116,32 @@ public class CallsManager extends Call.ListenerBase .forEach(c -> c.setVideoCallingSupportedByPhoneAccount(isVideoNowSupported)); } /** * Determines if two {@link Call} instances originated from either the same target * {@link PhoneAccountHandle} or connection manager {@link PhoneAccountHandle}. * @param call1 The first call * @param call2 The second call * @return {@code true} if both calls are from the same target or connection manager * {@link PhoneAccountHandle}. */ public static boolean areFromSameSource(@NonNull Call call1, @NonNull Call call2) { PhoneAccountHandle call1ConnectionMgr = call1.getConnectionManagerPhoneAccount(); PhoneAccountHandle call2ConnectionMgr = call2.getConnectionManagerPhoneAccount(); if (call1ConnectionMgr != null && call2ConnectionMgr != null && PhoneAccountHandle.areFromSamePackage(call1ConnectionMgr, call2ConnectionMgr)) { // Both calls share the same connection manager package, so they are from the same // source. return true; } PhoneAccountHandle call1TargetAcct = call1.getTargetPhoneAccount(); PhoneAccountHandle call2TargetAcct = call2.getTargetPhoneAccount(); // Otherwise if the target phone account for both is the same package, they're the same // source. return PhoneAccountHandle.areFromSamePackage(call1TargetAcct, call2TargetAcct); } public LinkedList<HandlerThread> getGraphHandlerThreads() { return mGraphHandlerThreads; } Loading src/com/android/server/telecom/PhoneAccountRegistrar.java +8 −0 Original line number Diff line number Diff line Loading @@ -508,6 +508,14 @@ public class PhoneAccountRegistrar { */ public PhoneAccountHandle getSimCallManagerFromHandle(PhoneAccountHandle targetPhoneAccount, UserHandle userHandle) { // First, check if the specified target phone account handle is a connection manager; if // it is, then just return it. PhoneAccount phoneAccount = getPhoneAccountUnchecked(targetPhoneAccount); if (phoneAccount != null && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) { return targetPhoneAccount; } int subId = getSubscriptionIdForPhoneAccount(targetPhoneAccount); if (SubscriptionManager.isValidSubscriptionId(subId) && subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { Loading tests/src/com/android/server/telecom/tests/CallsManagerTest.java +78 −2 Original line number Diff line number Diff line Loading @@ -120,6 +120,10 @@ public class CallsManagerTest extends TelecomTestCase { ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1"); private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2"); private static final PhoneAccountHandle CONNECTION_MGR_1_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.bar/.Conn"), "Cm1"); private static final PhoneAccountHandle CONNECTION_MGR_2_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.spa/.Conn"), "Cm2"); private static final PhoneAccountHandle VOIP_1_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.voip/.Stuff"), "Voip1"); private static final PhoneAccountHandle SELF_MANAGED_HANDLE = new PhoneAccountHandle( Loading Loading @@ -1239,6 +1243,68 @@ public class CallsManagerTest extends TelecomTestCase { verify(screenedCall).setAudioProcessingRequestingApp(appName); } /** * Verify the behavior of the {@link CallsManager#areFromSameSource(Call, Call)} method. * @throws Exception */ @SmallTest @Test public void testAreFromSameSource() throws Exception { Call callSim1 = createCall(SIM_1_HANDLE, null, CallState.ACTIVE); Call callSim2 = createCall(SIM_2_HANDLE, null, CallState.ACTIVE); Call callVoip1 = createCall(VOIP_1_HANDLE, null, CallState.ACTIVE); assertTrue(CallsManager.areFromSameSource(callSim1, callSim1)); assertTrue(CallsManager.areFromSameSource(callSim1, callSim2)); assertFalse(CallsManager.areFromSameSource(callSim1, callVoip1)); assertFalse(CallsManager.areFromSameSource(callSim2, callVoip1)); Call callSim1ConnectionMgr1 = createCall(SIM_1_HANDLE, CONNECTION_MGR_1_HANDLE, CallState.ACTIVE); Call callSim2ConnectionMgr2 = createCall(SIM_2_HANDLE, CONNECTION_MGR_2_HANDLE, CallState.ACTIVE); assertFalse(CallsManager.areFromSameSource(callSim1ConnectionMgr1, callVoip1)); assertFalse(CallsManager.areFromSameSource(callSim2ConnectionMgr2, callVoip1)); // Even though the connection manager differs, the underlying telephony CS is the same // so hold/swap will still work as expected. assertTrue(CallsManager.areFromSameSource(callSim1ConnectionMgr1, callSim2ConnectionMgr2)); // Sometimes connection managers have been known to also have calls Call callConnectionMgr = createCall(CONNECTION_MGR_2_HANDLE, CONNECTION_MGR_2_HANDLE, CallState.ACTIVE); assertTrue(CallsManager.areFromSameSource(callSim2ConnectionMgr2, callConnectionMgr)); } /** * Ensures that if we have two calls hosted by the same connection manager, but with * different target phone accounts, we can swap between them. * @throws Exception */ @SmallTest @Test public void testSwapCallsWithSameConnectionMgr() throws Exception { // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCall(SIM_1_HANDLE, CONNECTION_MGR_1_HANDLE, CallState.ACTIVE); 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 the same connection manager, but a different target phone // account. We have seen cases where a connection mgr adds its own calls and these can // be problematic for swapping. Call heldCall = addSpyCall(CONNECTION_MGR_1_HANDLE, CONNECTION_MGR_1_HANDLE, CallState.ON_HOLD); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); // THEN the ongoing call is held verify(ongoingCall).hold(any()); verifyFocusRequestAndExecuteCallback(heldCall); // and held call is unhold now verify(heldCall).unhold(any()); } private Call addSpyCall() { return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE); } Loading @@ -1248,7 +1314,12 @@ public class CallsManagerTest extends TelecomTestCase { } private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, int initialState) { Call ongoingCall = createCall(targetPhoneAccount, initialState); return addSpyCall(targetPhoneAccount, null, initialState); } private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, PhoneAccountHandle connectionMgrAcct, int initialState) { Call ongoingCall = createCall(targetPhoneAccount, connectionMgrAcct, initialState); Call callSpy = Mockito.spy(ongoingCall); // Mocks some methods to not call the real method. Loading @@ -1263,6 +1334,11 @@ public class CallsManagerTest extends TelecomTestCase { } private Call createCall(PhoneAccountHandle targetPhoneAccount, int initialState) { return createCall(targetPhoneAccount, null /* connectionManager */, initialState); } private Call createCall(PhoneAccountHandle targetPhoneAccount, PhoneAccountHandle connectionManagerAccount, int initialState) { Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */ mContext, mCallsManager, Loading @@ -1271,7 +1347,7 @@ public class CallsManagerTest extends TelecomTestCase { mPhoneNumberUtilsAdapter, TEST_ADDRESS, null /* GatewayInfo */, null /* connectionManagerPhoneAccountHandle */, connectionManagerAccount, targetPhoneAccount, Call.CALL_DIRECTION_INCOMING, false /* shouldAttachToExistingConnection*/, Loading Loading
src/com/android/server/telecom/Call.java +10 −9 Original line number Diff line number Diff line Loading @@ -802,21 +802,18 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, /** {@inheritDoc} */ @Override public String toString() { String component = null; if (mConnectionService != null && mConnectionService.getComponentName() != null) { component = mConnectionService.getComponentName().flattenToShortString(); } return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), %s, %s]", return String.format(Locale.US, "[Call id=%s, state=%s, tpac=%s, cmgr=%s, handle=%s, " + "vidst=%s, childs(%d), has_parent(%b), cap=%s, prop=%s]", mId, CallState.toString(mState), component, getTargetPhoneAccount(), getConnectionManagerPhoneAccount(), Log.piiHandle(mHandle), getVideoStateDescription(getVideoState()), getChildCalls().size(), getParentCall() != null, Connection.capabilitiesToString(getConnectionCapabilities()), Connection.propertiesToString(getConnectionProperties())); Connection.capabilitiesToStringShort(getConnectionCapabilities()), Connection.propertiesToStringShort(getConnectionProperties())); } @Override Loading Loading @@ -844,6 +841,10 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable, } else { s.append("not set"); } if (getConnectionManagerPhoneAccount() != null) { s.append("\n\tConn mgr: "); s.append(getConnectionManagerPhoneAccount()); } s.append("\n\tTo address: "); s.append(Log.piiHandle(getHandle())); Loading
src/com/android/server/telecom/CallsManager.java +46 −14 Original line number Diff line number Diff line Loading @@ -2607,10 +2607,10 @@ public class CallsManager extends Call.ListenerBase Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId()); } else { // This call does not support hold. If it is from a different connection // service, then disconnect it, otherwise invoke call.hold() and allow the // connection service to handle the situation. if (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { // service or connection manager, then disconnect it, otherwise invoke // call.hold() and allow the connection service or connection manager to handle // the situation. if (!areFromSameSource(activeCall, call)) { if (!activeCall.isEmergencyCall()) { activeCall.disconnect("Swap to " + call.getId()); } else { Loading Loading @@ -2856,11 +2856,11 @@ public class CallsManager extends Call.ListenerBase activeCall.hold(); return true; } else if (supportsHold(activeCall) && PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { && areFromSameSource(activeCall, call)) { // Handle the case where the active call and the new call are from the same CS, and // the currently active call supports hold but cannot currently be held. // Handle the case where the active call and the new call are from the same CS or // connection manager, and the currently active call supports hold but cannot // currently be held. // In this case we'll look for the other held call for this connectionService and // disconnect it prior to holding the active call. // E.g. Loading @@ -2882,10 +2882,9 @@ public class CallsManager extends Call.ListenerBase 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 (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { // service or connection manager, then disconnect it, otherwise allow the connection // service or connection manager to figure out the right states. if (!areFromSameSource(activeCall, call)) { Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be " + "made active.", activeCall.getId(), call.getId()); if (!activeCall.isEmergencyCall()) { Loading Loading @@ -3297,6 +3296,9 @@ public class CallsManager extends Call.ListenerBase Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : parcelableConference.getConnectElapsedTimeMillis(); PhoneAccountHandle connectionMgr = mPhoneAccountRegistrar.getSimCallManagerFromHandle(phoneAccount, mCurrentUserHandle); Call call = new Call( callId, mContext, Loading @@ -3306,7 +3308,7 @@ public class CallsManager extends Call.ListenerBase mPhoneNumberUtilsAdapter, null /* handle */, null /* gatewayInfo */, null /* connectionManagerPhoneAccount */, connectionMgr, phoneAccount, Call.CALL_DIRECTION_UNDEFINED /* callDirection */, false /* forceAttachToExistingConnection */, Loading Loading @@ -4085,6 +4087,10 @@ public class CallsManager extends Call.ListenerBase Call createCallForExistingConnection(String callId, ParcelableConnection connection) { boolean isDowngradedConference = (connection.getConnectionProperties() & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; PhoneAccountHandle connectionMgr = mPhoneAccountRegistrar.getSimCallManagerFromHandle(connection.getPhoneAccount(), mCurrentUserHandle); Call call = new Call( callId, mContext, Loading @@ -4094,7 +4100,7 @@ public class CallsManager extends Call.ListenerBase mPhoneNumberUtilsAdapter, connection.getHandle() /* handle */, null /* gatewayInfo */, null /* connectionManagerPhoneAccount */, connectionMgr, connection.getPhoneAccount(), /* targetPhoneAccountHandle */ Call.getRemappedCallDirection(connection.getCallDirection()) /* callDirection */, false /* forceAttachToExistingConnection */, Loading Loading @@ -5110,6 +5116,32 @@ public class CallsManager extends Call.ListenerBase .forEach(c -> c.setVideoCallingSupportedByPhoneAccount(isVideoNowSupported)); } /** * Determines if two {@link Call} instances originated from either the same target * {@link PhoneAccountHandle} or connection manager {@link PhoneAccountHandle}. * @param call1 The first call * @param call2 The second call * @return {@code true} if both calls are from the same target or connection manager * {@link PhoneAccountHandle}. */ public static boolean areFromSameSource(@NonNull Call call1, @NonNull Call call2) { PhoneAccountHandle call1ConnectionMgr = call1.getConnectionManagerPhoneAccount(); PhoneAccountHandle call2ConnectionMgr = call2.getConnectionManagerPhoneAccount(); if (call1ConnectionMgr != null && call2ConnectionMgr != null && PhoneAccountHandle.areFromSamePackage(call1ConnectionMgr, call2ConnectionMgr)) { // Both calls share the same connection manager package, so they are from the same // source. return true; } PhoneAccountHandle call1TargetAcct = call1.getTargetPhoneAccount(); PhoneAccountHandle call2TargetAcct = call2.getTargetPhoneAccount(); // Otherwise if the target phone account for both is the same package, they're the same // source. return PhoneAccountHandle.areFromSamePackage(call1TargetAcct, call2TargetAcct); } public LinkedList<HandlerThread> getGraphHandlerThreads() { return mGraphHandlerThreads; } Loading
src/com/android/server/telecom/PhoneAccountRegistrar.java +8 −0 Original line number Diff line number Diff line Loading @@ -508,6 +508,14 @@ public class PhoneAccountRegistrar { */ public PhoneAccountHandle getSimCallManagerFromHandle(PhoneAccountHandle targetPhoneAccount, UserHandle userHandle) { // First, check if the specified target phone account handle is a connection manager; if // it is, then just return it. PhoneAccount phoneAccount = getPhoneAccountUnchecked(targetPhoneAccount); if (phoneAccount != null && phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)) { return targetPhoneAccount; } int subId = getSubscriptionIdForPhoneAccount(targetPhoneAccount); if (SubscriptionManager.isValidSubscriptionId(subId) && subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) { Loading
tests/src/com/android/server/telecom/tests/CallsManagerTest.java +78 −2 Original line number Diff line number Diff line Loading @@ -120,6 +120,10 @@ public class CallsManagerTest extends TelecomTestCase { ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1"); private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2"); private static final PhoneAccountHandle CONNECTION_MGR_1_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.bar/.Conn"), "Cm1"); private static final PhoneAccountHandle CONNECTION_MGR_2_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.spa/.Conn"), "Cm2"); private static final PhoneAccountHandle VOIP_1_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.voip/.Stuff"), "Voip1"); private static final PhoneAccountHandle SELF_MANAGED_HANDLE = new PhoneAccountHandle( Loading Loading @@ -1239,6 +1243,68 @@ public class CallsManagerTest extends TelecomTestCase { verify(screenedCall).setAudioProcessingRequestingApp(appName); } /** * Verify the behavior of the {@link CallsManager#areFromSameSource(Call, Call)} method. * @throws Exception */ @SmallTest @Test public void testAreFromSameSource() throws Exception { Call callSim1 = createCall(SIM_1_HANDLE, null, CallState.ACTIVE); Call callSim2 = createCall(SIM_2_HANDLE, null, CallState.ACTIVE); Call callVoip1 = createCall(VOIP_1_HANDLE, null, CallState.ACTIVE); assertTrue(CallsManager.areFromSameSource(callSim1, callSim1)); assertTrue(CallsManager.areFromSameSource(callSim1, callSim2)); assertFalse(CallsManager.areFromSameSource(callSim1, callVoip1)); assertFalse(CallsManager.areFromSameSource(callSim2, callVoip1)); Call callSim1ConnectionMgr1 = createCall(SIM_1_HANDLE, CONNECTION_MGR_1_HANDLE, CallState.ACTIVE); Call callSim2ConnectionMgr2 = createCall(SIM_2_HANDLE, CONNECTION_MGR_2_HANDLE, CallState.ACTIVE); assertFalse(CallsManager.areFromSameSource(callSim1ConnectionMgr1, callVoip1)); assertFalse(CallsManager.areFromSameSource(callSim2ConnectionMgr2, callVoip1)); // Even though the connection manager differs, the underlying telephony CS is the same // so hold/swap will still work as expected. assertTrue(CallsManager.areFromSameSource(callSim1ConnectionMgr1, callSim2ConnectionMgr2)); // Sometimes connection managers have been known to also have calls Call callConnectionMgr = createCall(CONNECTION_MGR_2_HANDLE, CONNECTION_MGR_2_HANDLE, CallState.ACTIVE); assertTrue(CallsManager.areFromSameSource(callSim2ConnectionMgr2, callConnectionMgr)); } /** * Ensures that if we have two calls hosted by the same connection manager, but with * different target phone accounts, we can swap between them. * @throws Exception */ @SmallTest @Test public void testSwapCallsWithSameConnectionMgr() throws Exception { // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCall(SIM_1_HANDLE, CONNECTION_MGR_1_HANDLE, CallState.ACTIVE); 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 the same connection manager, but a different target phone // account. We have seen cases where a connection mgr adds its own calls and these can // be problematic for swapping. Call heldCall = addSpyCall(CONNECTION_MGR_1_HANDLE, CONNECTION_MGR_1_HANDLE, CallState.ON_HOLD); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); // THEN the ongoing call is held verify(ongoingCall).hold(any()); verifyFocusRequestAndExecuteCallback(heldCall); // and held call is unhold now verify(heldCall).unhold(any()); } private Call addSpyCall() { return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE); } Loading @@ -1248,7 +1314,12 @@ public class CallsManagerTest extends TelecomTestCase { } private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, int initialState) { Call ongoingCall = createCall(targetPhoneAccount, initialState); return addSpyCall(targetPhoneAccount, null, initialState); } private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, PhoneAccountHandle connectionMgrAcct, int initialState) { Call ongoingCall = createCall(targetPhoneAccount, connectionMgrAcct, initialState); Call callSpy = Mockito.spy(ongoingCall); // Mocks some methods to not call the real method. Loading @@ -1263,6 +1334,11 @@ public class CallsManagerTest extends TelecomTestCase { } private Call createCall(PhoneAccountHandle targetPhoneAccount, int initialState) { return createCall(targetPhoneAccount, null /* connectionManager */, initialState); } private Call createCall(PhoneAccountHandle targetPhoneAccount, PhoneAccountHandle connectionManagerAccount, int initialState) { Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */ mContext, mCallsManager, Loading @@ -1271,7 +1347,7 @@ public class CallsManagerTest extends TelecomTestCase { mPhoneNumberUtilsAdapter, TEST_ADDRESS, null /* GatewayInfo */, null /* connectionManagerPhoneAccountHandle */, connectionManagerAccount, targetPhoneAccount, Call.CALL_DIRECTION_INCOMING, false /* shouldAttachToExistingConnection*/, Loading