Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f6e6aaf authored by Tyler Gunn's avatar Tyler Gunn Committed by Gerrit Code Review
Browse files

Merge "Correct issues with answer/unhold call auto-disconnect."

parents 3b3c5c59 e5b8b203
Loading
Loading
Loading
Loading
+32 −9
Original line number Diff line number Diff line
@@ -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());
                    }
@@ -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.
@@ -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 "
@@ -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");
                    }
                }
            }
        }
@@ -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();
@@ -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);
+74 −51
Original line number Diff line number Diff line
@@ -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;
@@ -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")
@@ -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;
@@ -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);
@@ -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);
@@ -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
@@ -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
@@ -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);

@@ -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
@@ -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
@@ -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();
@@ -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")
@@ -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);
@@ -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 */
@@ -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 */,
+1 −1
Original line number Diff line number Diff line
@@ -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);
+32 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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",