Loading src/com/android/server/telecom/CallsManager.java +32 −9 Original line number Diff line number Diff line Loading @@ -2152,8 +2152,17 @@ public class CallsManager extends Call.ListenerBase // 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 (activeCall.getConnectionService() != call.getConnectionService()) { if (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { if (!activeCall.isEmergencyCall()) { activeCall.disconnect("Swap to " + call.getId()); } else { Log.w(this, "unholdCall: % is an emergency call, aborting swap to %s", activeCall.getId(), call.getId()); // Don't unhold the call as requested; we don't want to drop an // emergency call. return; } } else { activeCall.hold("Swap to " + call.getId()); } Loading Loading @@ -2371,7 +2380,8 @@ public class CallsManager extends Call.ListenerBase activeCall.hold(); return true; } else if (supportsHold(activeCall) && activeCall.getConnectionService() == call.getConnectionService()) { && PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { // 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. Loading @@ -2383,7 +2393,7 @@ public class CallsManager extends Call.ListenerBase // Call C - Incoming // Here we need to disconnect A prior to holding B so that C can be answered. // This case is driven by telephony requirements ultimately. Call heldCall = getHeldCallByConnectionService(call.getConnectionService()); Call heldCall = getHeldCallByConnectionService(call.getTargetPhoneAccount()); if (heldCall != null) { heldCall.disconnect(); Log.i(this, "holdActiveCallForNewCall: Disconnect held call %s before " Loading @@ -2398,10 +2408,21 @@ public class CallsManager extends Call.ListenerBase // 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()) { if (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be " + "made active.", activeCall.getId(), call.getId()); if (!activeCall.isEmergencyCall()) { activeCall.disconnect(); } else { // It's not possible to hold the active call, and its an emergency call so // we will silently reject the incoming call instead of answering it. Log.w(this, "holdActiveCallForNewCall: rejecting incoming call %s as " + "the active call is an emergency call and it cannot be held.", call.getId()); call.reject(false /* rejectWithMessage */, "" /* message */, "active emergency call can't be held"); } } } } Loading Loading @@ -2672,9 +2693,10 @@ public class CallsManager extends Call.ListenerBase return getFirstCallWithState(CallState.ON_HOLD); } public Call getHeldCallByConnectionService(ConnectionServiceWrapper connSvr) { public Call getHeldCallByConnectionService(PhoneAccountHandle targetPhoneAccount) { Optional<Call> heldCall = mCalls.stream() .filter(call -> call.getConnectionService() == connSvr .filter(call -> PhoneAccountHandle.areFromSamePackage(call.getTargetPhoneAccount(), targetPhoneAccount) && call.getParentCall() == null && call.getState() == CallState.ON_HOLD) .findFirst(); Loading Loading @@ -3392,7 +3414,8 @@ public class CallsManager extends Call.ListenerBase // First thing, if we are trying to make a call with the same phone account as the live // call, then allow it so that the connection service can make its own decision about // how to handle the new call relative to the current one. if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) { if (PhoneAccountHandle.areFromSamePackage(liveCallPhoneAccount, call.getTargetPhoneAccount())) { Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches."); call.getAnalytics().setCallIsAdditional(true); liveCall.getAnalytics().setCallIsInterrupted(true); Loading tests/src/com/android/server/telecom/tests/CallsManagerTest.java +74 −51 Original line number Diff line number Diff line Loading @@ -56,14 +56,12 @@ import com.android.server.telecom.CallAudioManager; import com.android.server.telecom.CallAudioModeStateMachine; import com.android.server.telecom.CallAudioRouteStateMachine; import com.android.server.telecom.CallState; import com.android.server.telecom.CallerInfoAsyncQueryFactory; import com.android.server.telecom.CallerInfoLookupHelper; import com.android.server.telecom.CallsManager; import com.android.server.telecom.ClockProxy; import com.android.server.telecom.ConnectionServiceFocusManager; import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory; import com.android.server.telecom.ConnectionServiceWrapper; import com.android.server.telecom.ContactsAsyncHelper; import com.android.server.telecom.DefaultDialerCache; import com.android.server.telecom.EmergencyCallHelper; import com.android.server.telecom.HeadsetMediaButton; Loading Loading @@ -114,6 +112,8 @@ 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 VOIP_1_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.voip/.Stuff"), "Voip1"); private static final PhoneAccountHandle SELF_MANAGED_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.foo/.Self"), "Self"); private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.Builder(SIM_1_HANDLE, "Sim1") Loading Loading @@ -141,6 +141,7 @@ public class CallsManagerTest extends TelecomTestCase { put(TEST_ADDRESS3, SIM_2_HANDLE); }}; private static int sCallId = 1; private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { }; @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper; @Mock private MissedCallNotifier mMissedCallNotifier; Loading Loading @@ -523,17 +524,14 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testUnholdCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() { 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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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 Call heldCall = addSpyCallWithConnectionService(connSvr2); Call heldCall = addSpyCall(VOIP_1_HANDLE); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); Loading @@ -548,17 +546,39 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() { ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class); public void testUnholdCallWhenOngoingEmergCallCanNotBeHeldAndFromDifferentConnectionService() { // GIVEN a CallsManager with ongoing call, and this call can not be held, but it also an // emergency call. Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(true).when(ongoingCall).isEmergencyCall(); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); // and a held call which has different ConnectionService Call heldCall = addSpyCall(VOIP_1_HANDLE); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); // THEN the ongoing call will not be disconnected (because its an emergency call) verify(ongoingCall, never()).disconnect(any()); // and held call is not un-held verify(heldCall, never()).unhold(any()); } @SmallTest @Test public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() { // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCallWithConnectionService(connSvr); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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 Call heldCall = addSpyCallWithConnectionService(connSvr); // and a held call which has the same ConnectionService Call heldCall = addSpyCall(SIM_2_HANDLE); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); Loading Loading @@ -595,15 +615,13 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testAnswerCallWhenOngoingHasSameConnectionService() { ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class); // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCallWithConnectionService(connSvr); Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); // WHEN answer an incoming call Call incomingCall = addSpyCallWithConnectionService(connSvr); Call incomingCall = addSpyCall(VOIP_1_HANDLE); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); // THEN nothing happened on the ongoing call and the focus request for incoming call is sent Loading @@ -616,17 +634,14 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test 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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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 Call incomingCall = addSpyCallWithConnectionService(connSvr2); Call incomingCall = addSpyCall(VOIP_1_HANDLE); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); // THEN the ongoing call is disconnected and the focus request for incoming call is sent Loading @@ -639,27 +654,46 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testAnswerCallWhenMultipleHeldCallsExisted() { ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class); ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class); public void testAnswerCallWhenOngoingHasDifferentConnectionServiceButIsEmerg() { // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(true).when(ongoingCall).isEmergencyCall(); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); // WHEN answer an incoming call Call incomingCall = addSpyCall(VOIP_1_HANDLE); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); // THEN the ongoing call is not disconnected verify(ongoingCall, never()).disconnect(); // and the incoming call is not answered, but is rejected instead. verify(incomingCall, never()).answer(VideoProfile.STATE_AUDIO_ONLY); verify(incomingCall).reject(eq(false), any(), any()); } @SmallTest @Test public void testAnswerCallWhenMultipleHeldCallsExisted() { // Given an ongoing call and held call with the ConnectionService connSvr1. The // ConnectionService connSvr1 can handle one held call Call ongoingCall = addSpyCallWithConnectionService(connSvr1); Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(CallState.ACTIVE).when(ongoingCall).getState(); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); Call heldCall = addSpyCallWithConnectionService(connSvr1); Call heldCall = addSpyCall(SIM_1_HANDLE); doReturn(CallState.ON_HOLD).when(heldCall).getState(); // and other held call has difference ConnectionService Call heldCall2 = addSpyCallWithConnectionService(connSvr2); Call heldCall2 = addSpyCall(VOIP_1_HANDLE); doReturn(CallState.ON_HOLD).when(heldCall2).getState(); // WHEN answer an incoming call which ConnectionService is connSvr1 Call incomingCall = addSpyCallWithConnectionService(connSvr1); Call incomingCall = addSpyCall(SIM_1_HANDLE); doReturn(true).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); Loading Loading @@ -717,17 +751,14 @@ public class CallsManagerTest extends TelecomTestCase { @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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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); Call newCall = addSpyCall(VOIP_1_HANDLE); doReturn(true).when(newCall).isSelfManaged(); // WHEN active the new call Loading @@ -744,16 +775,14 @@ public class CallsManagerTest extends TelecomTestCase { @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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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); Call newCall = addSpyCall(SIM_1_HANDLE); doReturn(true).when(newCall).isSelfManaged(); // WHEN active the new call Loading Loading @@ -794,10 +823,8 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testNoFilteringOfSelfManagedCalls() { ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class); // GIVEN an incoming call which is self managed. Call incomingCall = addSpyCallWithConnectionService(connSvr1); Call incomingCall = addSpyCall(SELF_MANAGED_HANDLE); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_HOLD); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(true).when(incomingCall).isSelfManaged(); Loading Loading @@ -913,10 +940,8 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testNoFilteringOfCallsWhenPhoneAccountRequestsSkipped() { ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class); // GIVEN an incoming call which is from a PhoneAccount that requested to skip filtering. Call incomingCall = addSpyCallWithConnectionService(connSvr1); Call incomingCall = addSpyCall(SIM_1_HANDLE); Bundle extras = new Bundle(); extras.putBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING, true); PhoneAccount skipRequestedAccount = new PhoneAccount.Builder(SIM_2_HANDLE, "Skipper") Loading @@ -925,7 +950,7 @@ public class CallsManagerTest extends TelecomTestCase { .setExtras(extras) .setIsEnabled(true) .build(); when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(SIM_2_HANDLE)) when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(SIM_1_HANDLE)) .thenReturn(skipRequestedAccount); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_HOLD); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); Loading Loading @@ -1026,14 +1051,12 @@ public class CallsManagerTest extends TelecomTestCase { Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)); } private Call addSpyCallWithConnectionService(ConnectionServiceWrapper connSvr) { Call call = addSpyCall(); doReturn(connSvr).when(call).getConnectionService(); return call; private Call addSpyCall() { return addSpyCall(SIM_2_HANDLE); } private Call addSpyCall() { Call ongoingCall = new Call("1", /* callId */ private Call addSpyCall(PhoneAccountHandle targetPhoneAccount) { Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */ mComponentContextFixture.getTestDouble(), mCallsManager, mLock, /* ConnectionServiceRepository */ Loading @@ -1042,7 +1065,7 @@ public class CallsManagerTest extends TelecomTestCase { TEST_ADDRESS, null /* GatewayInfo */, null /* connectionManagerPhoneAccountHandle */, SIM_2_HANDLE, targetPhoneAccount, Call.CALL_DIRECTION_INCOMING, false /* shouldAttachToExistingConnection*/, false /* isConference */, Loading tests/src/com/android/server/telecom/tests/ComponentContextFixture.java +1 −1 Original line number Diff line number Diff line Loading @@ -440,7 +440,7 @@ public class ComponentContextFixture implements TestFixture<Context> { private final ApplicationInfo mTestApplicationInfo = new ApplicationInfo(); // private final RoleManager mRoleManager = mock(RoleManager.class); private TelecomManager mTelecomManager = null; private TelecomManager mTelecomManager = mock(TelecomManager.class); public ComponentContextFixture() { MockitoAnnotations.initMocks(this); Loading tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java +32 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.telecom.Log; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.MediumTest; import android.util.Xml; Loading Loading @@ -910,6 +911,37 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { assertEquals(account1, account2); } /** * Tests {@link PhoneAccountHandle#areFromSamePackage(PhoneAccountHandle, * PhoneAccountHandle)} comparison. */ @SmallTest @Test public void testSamePhoneAccountHandlePackage() { PhoneAccountHandle a = new PhoneAccountHandle(new ComponentName("packageA", "class1"), "id1"); PhoneAccountHandle b = new PhoneAccountHandle(new ComponentName("packageA", "class2"), "id2"); PhoneAccountHandle c = new PhoneAccountHandle(new ComponentName("packageA", "class1"), "id3"); PhoneAccountHandle d = new PhoneAccountHandle(new ComponentName("packageB", "class1"), "id1"); assertTrue(PhoneAccountHandle.areFromSamePackage(null, null)); assertTrue(PhoneAccountHandle.areFromSamePackage(a, b)); assertTrue(PhoneAccountHandle.areFromSamePackage(a, c)); assertTrue(PhoneAccountHandle.areFromSamePackage(b, c)); assertFalse(PhoneAccountHandle.areFromSamePackage(a, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(b, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(c, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(a, null)); assertFalse(PhoneAccountHandle.areFromSamePackage(b, null)); assertFalse(PhoneAccountHandle.areFromSamePackage(c, null)); assertFalse(PhoneAccountHandle.areFromSamePackage(null, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(null, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(null, d)); } private static ComponentName makeQuickConnectionServiceComponentName() { return new ComponentName( "com.android.server.telecom.tests", Loading Loading
src/com/android/server/telecom/CallsManager.java +32 −9 Original line number Diff line number Diff line Loading @@ -2152,8 +2152,17 @@ public class CallsManager extends Call.ListenerBase // 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 (activeCall.getConnectionService() != call.getConnectionService()) { if (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { if (!activeCall.isEmergencyCall()) { activeCall.disconnect("Swap to " + call.getId()); } else { Log.w(this, "unholdCall: % is an emergency call, aborting swap to %s", activeCall.getId(), call.getId()); // Don't unhold the call as requested; we don't want to drop an // emergency call. return; } } else { activeCall.hold("Swap to " + call.getId()); } Loading Loading @@ -2371,7 +2380,8 @@ public class CallsManager extends Call.ListenerBase activeCall.hold(); return true; } else if (supportsHold(activeCall) && activeCall.getConnectionService() == call.getConnectionService()) { && PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { // 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. Loading @@ -2383,7 +2393,7 @@ public class CallsManager extends Call.ListenerBase // Call C - Incoming // Here we need to disconnect A prior to holding B so that C can be answered. // This case is driven by telephony requirements ultimately. Call heldCall = getHeldCallByConnectionService(call.getConnectionService()); Call heldCall = getHeldCallByConnectionService(call.getTargetPhoneAccount()); if (heldCall != null) { heldCall.disconnect(); Log.i(this, "holdActiveCallForNewCall: Disconnect held call %s before " Loading @@ -2398,10 +2408,21 @@ public class CallsManager extends Call.ListenerBase // 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()) { if (!PhoneAccountHandle.areFromSamePackage(activeCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be " + "made active.", activeCall.getId(), call.getId()); if (!activeCall.isEmergencyCall()) { activeCall.disconnect(); } else { // It's not possible to hold the active call, and its an emergency call so // we will silently reject the incoming call instead of answering it. Log.w(this, "holdActiveCallForNewCall: rejecting incoming call %s as " + "the active call is an emergency call and it cannot be held.", call.getId()); call.reject(false /* rejectWithMessage */, "" /* message */, "active emergency call can't be held"); } } } } Loading Loading @@ -2672,9 +2693,10 @@ public class CallsManager extends Call.ListenerBase return getFirstCallWithState(CallState.ON_HOLD); } public Call getHeldCallByConnectionService(ConnectionServiceWrapper connSvr) { public Call getHeldCallByConnectionService(PhoneAccountHandle targetPhoneAccount) { Optional<Call> heldCall = mCalls.stream() .filter(call -> call.getConnectionService() == connSvr .filter(call -> PhoneAccountHandle.areFromSamePackage(call.getTargetPhoneAccount(), targetPhoneAccount) && call.getParentCall() == null && call.getState() == CallState.ON_HOLD) .findFirst(); Loading Loading @@ -3392,7 +3414,8 @@ public class CallsManager extends Call.ListenerBase // First thing, if we are trying to make a call with the same phone account as the live // call, then allow it so that the connection service can make its own decision about // how to handle the new call relative to the current one. if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) { if (PhoneAccountHandle.areFromSamePackage(liveCallPhoneAccount, call.getTargetPhoneAccount())) { Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches."); call.getAnalytics().setCallIsAdditional(true); liveCall.getAnalytics().setCallIsInterrupted(true); Loading
tests/src/com/android/server/telecom/tests/CallsManagerTest.java +74 −51 Original line number Diff line number Diff line Loading @@ -56,14 +56,12 @@ import com.android.server.telecom.CallAudioManager; import com.android.server.telecom.CallAudioModeStateMachine; import com.android.server.telecom.CallAudioRouteStateMachine; import com.android.server.telecom.CallState; import com.android.server.telecom.CallerInfoAsyncQueryFactory; import com.android.server.telecom.CallerInfoLookupHelper; import com.android.server.telecom.CallsManager; import com.android.server.telecom.ClockProxy; import com.android.server.telecom.ConnectionServiceFocusManager; import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory; import com.android.server.telecom.ConnectionServiceWrapper; import com.android.server.telecom.ContactsAsyncHelper; import com.android.server.telecom.DefaultDialerCache; import com.android.server.telecom.EmergencyCallHelper; import com.android.server.telecom.HeadsetMediaButton; Loading Loading @@ -114,6 +112,8 @@ 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 VOIP_1_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.voip/.Stuff"), "Voip1"); private static final PhoneAccountHandle SELF_MANAGED_HANDLE = new PhoneAccountHandle( ComponentName.unflattenFromString("com.foo/.Self"), "Self"); private static final PhoneAccount SIM_1_ACCOUNT = new PhoneAccount.Builder(SIM_1_HANDLE, "Sim1") Loading Loading @@ -141,6 +141,7 @@ public class CallsManagerTest extends TelecomTestCase { put(TEST_ADDRESS3, SIM_2_HANDLE); }}; private static int sCallId = 1; private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { }; @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper; @Mock private MissedCallNotifier mMissedCallNotifier; Loading Loading @@ -523,17 +524,14 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testUnholdCallWhenOngoingCallCanNotBeHeldAndFromDifferentConnectionService() { 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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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 Call heldCall = addSpyCallWithConnectionService(connSvr2); Call heldCall = addSpyCall(VOIP_1_HANDLE); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); Loading @@ -548,17 +546,39 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() { ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class); public void testUnholdCallWhenOngoingEmergCallCanNotBeHeldAndFromDifferentConnectionService() { // GIVEN a CallsManager with ongoing call, and this call can not be held, but it also an // emergency call. Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(true).when(ongoingCall).isEmergencyCall(); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); // and a held call which has different ConnectionService Call heldCall = addSpyCall(VOIP_1_HANDLE); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); // THEN the ongoing call will not be disconnected (because its an emergency call) verify(ongoingCall, never()).disconnect(any()); // and held call is not un-held verify(heldCall, never()).unhold(any()); } @SmallTest @Test public void testUnholdCallWhenOngoingCallCanNotBeHeldAndHasSameConnectionService() { // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCallWithConnectionService(connSvr); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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 Call heldCall = addSpyCallWithConnectionService(connSvr); // and a held call which has the same ConnectionService Call heldCall = addSpyCall(SIM_2_HANDLE); // WHEN unhold the held call mCallsManager.unholdCall(heldCall); Loading Loading @@ -595,15 +615,13 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testAnswerCallWhenOngoingHasSameConnectionService() { ConnectionServiceWrapper connSvr = Mockito.mock(ConnectionServiceWrapper.class); // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCallWithConnectionService(connSvr); Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); // WHEN answer an incoming call Call incomingCall = addSpyCallWithConnectionService(connSvr); Call incomingCall = addSpyCall(VOIP_1_HANDLE); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); // THEN nothing happened on the ongoing call and the focus request for incoming call is sent Loading @@ -616,17 +634,14 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test 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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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 Call incomingCall = addSpyCallWithConnectionService(connSvr2); Call incomingCall = addSpyCall(VOIP_1_HANDLE); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); // THEN the ongoing call is disconnected and the focus request for incoming call is sent Loading @@ -639,27 +654,46 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testAnswerCallWhenMultipleHeldCallsExisted() { ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class); ConnectionServiceWrapper connSvr2 = Mockito.mock(ConnectionServiceWrapper.class); public void testAnswerCallWhenOngoingHasDifferentConnectionServiceButIsEmerg() { // GIVEN a CallsManager with ongoing call, and this call can not be held Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(true).when(ongoingCall).isEmergencyCall(); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); // WHEN answer an incoming call Call incomingCall = addSpyCall(VOIP_1_HANDLE); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); // THEN the ongoing call is not disconnected verify(ongoingCall, never()).disconnect(); // and the incoming call is not answered, but is rejected instead. verify(incomingCall, never()).answer(VideoProfile.STATE_AUDIO_ONLY); verify(incomingCall).reject(eq(false), any(), any()); } @SmallTest @Test public void testAnswerCallWhenMultipleHeldCallsExisted() { // Given an ongoing call and held call with the ConnectionService connSvr1. The // ConnectionService connSvr1 can handle one held call Call ongoingCall = addSpyCallWithConnectionService(connSvr1); Call ongoingCall = addSpyCall(SIM_1_HANDLE); doReturn(false).when(ongoingCall).can(Connection.CAPABILITY_HOLD); doReturn(true).when(ongoingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(CallState.ACTIVE).when(ongoingCall).getState(); when(mConnectionSvrFocusMgr.getCurrentFocusCall()).thenReturn(ongoingCall); Call heldCall = addSpyCallWithConnectionService(connSvr1); Call heldCall = addSpyCall(SIM_1_HANDLE); doReturn(CallState.ON_HOLD).when(heldCall).getState(); // and other held call has difference ConnectionService Call heldCall2 = addSpyCallWithConnectionService(connSvr2); Call heldCall2 = addSpyCall(VOIP_1_HANDLE); doReturn(CallState.ON_HOLD).when(heldCall2).getState(); // WHEN answer an incoming call which ConnectionService is connSvr1 Call incomingCall = addSpyCallWithConnectionService(connSvr1); Call incomingCall = addSpyCall(SIM_1_HANDLE); doReturn(true).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); mCallsManager.answerCall(incomingCall, VideoProfile.STATE_AUDIO_ONLY); Loading Loading @@ -717,17 +751,14 @@ public class CallsManagerTest extends TelecomTestCase { @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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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); Call newCall = addSpyCall(VOIP_1_HANDLE); doReturn(true).when(newCall).isSelfManaged(); // WHEN active the new call Loading @@ -744,16 +775,14 @@ public class CallsManagerTest extends TelecomTestCase { @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); Call ongoingCall = addSpyCall(SIM_1_HANDLE); 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); Call newCall = addSpyCall(SIM_1_HANDLE); doReturn(true).when(newCall).isSelfManaged(); // WHEN active the new call Loading Loading @@ -794,10 +823,8 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testNoFilteringOfSelfManagedCalls() { ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class); // GIVEN an incoming call which is self managed. Call incomingCall = addSpyCallWithConnectionService(connSvr1); Call incomingCall = addSpyCall(SELF_MANAGED_HANDLE); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_HOLD); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); doReturn(true).when(incomingCall).isSelfManaged(); Loading Loading @@ -913,10 +940,8 @@ public class CallsManagerTest extends TelecomTestCase { @SmallTest @Test public void testNoFilteringOfCallsWhenPhoneAccountRequestsSkipped() { ConnectionServiceWrapper connSvr1 = Mockito.mock(ConnectionServiceWrapper.class); // GIVEN an incoming call which is from a PhoneAccount that requested to skip filtering. Call incomingCall = addSpyCallWithConnectionService(connSvr1); Call incomingCall = addSpyCall(SIM_1_HANDLE); Bundle extras = new Bundle(); extras.putBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING, true); PhoneAccount skipRequestedAccount = new PhoneAccount.Builder(SIM_2_HANDLE, "Skipper") Loading @@ -925,7 +950,7 @@ public class CallsManagerTest extends TelecomTestCase { .setExtras(extras) .setIsEnabled(true) .build(); when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(SIM_2_HANDLE)) when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(SIM_1_HANDLE)) .thenReturn(skipRequestedAccount); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_HOLD); doReturn(false).when(incomingCall).can(Connection.CAPABILITY_SUPPORT_HOLD); Loading Loading @@ -1026,14 +1051,12 @@ public class CallsManagerTest extends TelecomTestCase { Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)); } private Call addSpyCallWithConnectionService(ConnectionServiceWrapper connSvr) { Call call = addSpyCall(); doReturn(connSvr).when(call).getConnectionService(); return call; private Call addSpyCall() { return addSpyCall(SIM_2_HANDLE); } private Call addSpyCall() { Call ongoingCall = new Call("1", /* callId */ private Call addSpyCall(PhoneAccountHandle targetPhoneAccount) { Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */ mComponentContextFixture.getTestDouble(), mCallsManager, mLock, /* ConnectionServiceRepository */ Loading @@ -1042,7 +1065,7 @@ public class CallsManagerTest extends TelecomTestCase { TEST_ADDRESS, null /* GatewayInfo */, null /* connectionManagerPhoneAccountHandle */, SIM_2_HANDLE, targetPhoneAccount, Call.CALL_DIRECTION_INCOMING, false /* shouldAttachToExistingConnection*/, false /* isConference */, Loading
tests/src/com/android/server/telecom/tests/ComponentContextFixture.java +1 −1 Original line number Diff line number Diff line Loading @@ -440,7 +440,7 @@ public class ComponentContextFixture implements TestFixture<Context> { private final ApplicationInfo mTestApplicationInfo = new ApplicationInfo(); // private final RoleManager mRoleManager = mock(RoleManager.class); private TelecomManager mTelecomManager = null; private TelecomManager mTelecomManager = mock(TelecomManager.class); public ComponentContextFixture() { MockitoAnnotations.initMocks(this); Loading
tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java +32 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.telecom.Log; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.MediumTest; import android.util.Xml; Loading Loading @@ -910,6 +911,37 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase { assertEquals(account1, account2); } /** * Tests {@link PhoneAccountHandle#areFromSamePackage(PhoneAccountHandle, * PhoneAccountHandle)} comparison. */ @SmallTest @Test public void testSamePhoneAccountHandlePackage() { PhoneAccountHandle a = new PhoneAccountHandle(new ComponentName("packageA", "class1"), "id1"); PhoneAccountHandle b = new PhoneAccountHandle(new ComponentName("packageA", "class2"), "id2"); PhoneAccountHandle c = new PhoneAccountHandle(new ComponentName("packageA", "class1"), "id3"); PhoneAccountHandle d = new PhoneAccountHandle(new ComponentName("packageB", "class1"), "id1"); assertTrue(PhoneAccountHandle.areFromSamePackage(null, null)); assertTrue(PhoneAccountHandle.areFromSamePackage(a, b)); assertTrue(PhoneAccountHandle.areFromSamePackage(a, c)); assertTrue(PhoneAccountHandle.areFromSamePackage(b, c)); assertFalse(PhoneAccountHandle.areFromSamePackage(a, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(b, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(c, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(a, null)); assertFalse(PhoneAccountHandle.areFromSamePackage(b, null)); assertFalse(PhoneAccountHandle.areFromSamePackage(c, null)); assertFalse(PhoneAccountHandle.areFromSamePackage(null, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(null, d)); assertFalse(PhoneAccountHandle.areFromSamePackage(null, d)); } private static ComponentName makeQuickConnectionServiceComponentName() { return new ComponentName( "com.android.server.telecom.tests", Loading