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

Commit 1662ec7f authored by Brad Ebinger's avatar Brad Ebinger
Browse files

Remove CAPABILITY_CAN_PULL_CALL if the user is in an emergency call

When the user is in an emergency call, remove the ability for the user
to pull an external call to the device by removing
CAPABILITY_CAN_PULL_CALL.

Fixes: 153376754
Fixes: 144593522
Test: atest CtsTelecomTestCases
Merged-In: I592587cdcceaf8c6fcf844fdc2c74d7dc6989333
Change-Id: I592587cdcceaf8c6fcf844fdc2c74d7dc6989333
parent 072e4198
Loading
Loading
Loading
Loading
+58 −5
Original line number Original line Diff line number Diff line
@@ -507,6 +507,15 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     */
     */
    private boolean mIsVideoCallingSupportedByPhoneAccount = false;
    private boolean mIsVideoCallingSupportedByPhoneAccount = false;


    /**
     * Indicates whether or not this call can be pulled if it is an external call. If true, respect
     * the Connection Capability set by the ConnectionService. If false, override the capability
     * set and always remove the ability to pull this external call.
     *
     * See {@link #setIsPullExternalCallSupported(boolean)}
     */
    private boolean mIsPullExternalCallSupported = true;

    private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
    private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;


    /**
    /**
@@ -1462,6 +1471,26 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        setConnectionCapabilities(getConnectionCapabilities(), true /* force */);
        setConnectionCapabilities(getConnectionCapabilities(), true /* force */);
    }
    }


    /**
     * Determines if pulling this external call is supported. If it is supported, we will allow the
     * {@link Connection#CAPABILITY_CAN_PULL_CALL} capability to be added to this call's
     * capabilities. If it is not supported, we will strip this capability before sending this
     * call's capabilities to the InCallService.
     * @param isPullExternalCallSupported true, if pulling this external call is supported, false
     *                                    otherwise.
     */
    public void setIsPullExternalCallSupported(boolean isPullExternalCallSupported) {
        if (!isExternalCall()) return;
        if (isPullExternalCallSupported == mIsPullExternalCallSupported) return;

        Log.i(this, "setCanPullExternalCall: canPull=%b", isPullExternalCallSupported);

        mIsPullExternalCallSupported = isPullExternalCallSupported;

        // Use mConnectionCapabilities here to get the unstripped capabilities.
        setConnectionCapabilities(mConnectionCapabilities, true /* force */);
    }

    /**
    /**
     * @return {@code true} if the {@link Call} locally supports video.
     * @return {@code true} if the {@link Call} locally supports video.
     */
     */
@@ -1668,7 +1697,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    }
    }


    public int getConnectionCapabilities() {
    public int getConnectionCapabilities() {
        return mConnectionCapabilities;
        return stripUnsupportedCapabilities(mConnectionCapabilities);
    }
    }


    int getConnectionProperties() {
    int getConnectionProperties() {
@@ -1689,15 +1718,33 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
                l.onConnectionCapabilitiesChanged(this);
                l.onConnectionCapabilitiesChanged(this);
            }
            }


            int xorCaps = previousCapabilities ^ mConnectionCapabilities;
            int strippedCaps = getConnectionCapabilities();
            int xorCaps = previousCapabilities ^ strippedCaps;
            Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
            Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
                    "Current: [%s], Removed [%s], Added [%s]",
                    "Current: [%s], Removed [%s], Added [%s]",
                    Connection.capabilitiesToStringShort(mConnectionCapabilities),
                    Connection.capabilitiesToStringShort(strippedCaps),
                    Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
                    Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
                    Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps));
                    Connection.capabilitiesToStringShort(strippedCaps & xorCaps));
        }
        }
    }
    }


    /**
     * For some states of Telecom, we need to modify this connection's capabilities:
     * - A user should not be able to pull an external call during an emergency call, so
     *   CAPABILITY_CAN_PULL_CALL should be removed until the emergency call ends.
     * @param capabilities The original capabilities.
     * @return The stripped capabilities.
     */
    private int stripUnsupportedCapabilities(int capabilities) {
        if (!mIsPullExternalCallSupported) {
            if ((capabilities |= Connection.CAPABILITY_CAN_PULL_CALL) > 0) {
                capabilities &= ~Connection.CAPABILITY_CAN_PULL_CALL;
                Log.i(this, "stripCapabilitiesBasedOnState: CAPABILITY_CAN_PULL_CALL removed.");
            }
        }
        return capabilities;
    }

    public void setConnectionProperties(int connectionProperties) {
    public void setConnectionProperties(int connectionProperties) {
        Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
        Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
                connectionProperties));
                connectionProperties));
@@ -1749,6 +1796,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
                Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
                Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
                        isExternal);
                        isExternal);
                Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
                Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
                if (isExternal) {
                    // If there is an ongoing emergency call, remove the ability for this call to
                    // be pulled.
                    boolean isInEmergencyCall = mCallsManager.isInEmergencyCall();
                    setIsPullExternalCallSupported(!isInEmergencyCall);
                }
                for (Listener l : mListeners) {
                for (Listener l : mListeners) {
                    l.onExternalCallChanged(this, isExternal);
                    l.onExternalCallChanged(this, isExternal);
                }
                }
@@ -2847,7 +2900,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,


    @VisibleForTesting
    @VisibleForTesting
    public boolean can(int capability) {
    public boolean can(int capability) {
        return (mConnectionCapabilities & capability) == capability;
        return (getConnectionCapabilities() & capability) == capability;
    }
    }


    @VisibleForTesting
    @VisibleForTesting
+17 −2
Original line number Original line Diff line number Diff line
@@ -3418,6 +3418,8 @@ public class CallsManager extends Call.ListenerBase
                SystemClock.elapsedRealtime());
                SystemClock.elapsedRealtime());


        updateCanAddCall();
        updateCanAddCall();

        updateExternalCallCanPullSupport();
        // onCallAdded for calls which immediately take the foreground (like the first call).
        // onCallAdded for calls which immediately take the foreground (like the first call).
        for (CallsManagerListener listener : mListeners) {
        for (CallsManagerListener listener : mListeners) {
            if (LogUtils.SYSTRACE_DEBUG) {
            if (LogUtils.SYSTRACE_DEBUG) {
@@ -3431,7 +3433,8 @@ public class CallsManager extends Call.ListenerBase
        Trace.endSection();
        Trace.endSection();
    }
    }


    private void removeCall(Call call) {
    @VisibleForTesting
    public void removeCall(Call call) {
        Trace.beginSection("removeCall");
        Trace.beginSection("removeCall");
        Log.v(this, "removeCall(%s)", call);
        Log.v(this, "removeCall(%s)", call);


@@ -3447,7 +3450,7 @@ public class CallsManager extends Call.ListenerBase
        }
        }


        call.destroy();
        call.destroy();

        updateExternalCallCanPullSupport();
        // Only broadcast changes for calls that are being tracked.
        // Only broadcast changes for calls that are being tracked.
        if (shouldNotify) {
        if (shouldNotify) {
            updateCanAddCall();
            updateCanAddCall();
@@ -5086,6 +5089,18 @@ public class CallsManager extends Call.ListenerBase
                || c.isNetworkIdentifiedEmergencyCall()) && !c.isDisconnected()).count() > 0;
                || c.isNetworkIdentifiedEmergencyCall()) && !c.isDisconnected()).count() > 0;
    }
    }


    /**
     * Trigger a recalculation of support for CAPABILITY_CAN_PULL_CALL for external calls due to
     * a possible emergency call being added/removed.
     */
    private void updateExternalCallCanPullSupport() {
        boolean isInEmergencyCall = isInEmergencyCall();
        // Remove the capability to pull an external call in the case that we are in an emergency
        // call.
        mCalls.stream().filter(Call::isExternalCall).forEach(
                c->c.setIsPullExternalCallSupported(!isInEmergencyCall));
    }

    /**
    /**
     * Trigger display of an error message to the user; we do this outside of dialer for calls which
     * Trigger display of an error message to the user; we do this outside of dialer for calls which
     * fail to be created and added to Dialer.
     * fail to be created and added to Dialer.
+48 −0
Original line number Original line Diff line number Diff line
@@ -223,6 +223,54 @@ public class CallTest extends TelecomTestCase {
        assertEquals(DisconnectCause.REJECTED, call.getDisconnectCause().getCode());
        assertEquals(DisconnectCause.REJECTED, call.getDisconnectCause().getCode());
    }
    }


    @Test
    @SmallTest
    public void testCanPullCallRemovedDuringEmergencyCall() {
        Call call = new Call(
                "1", /* callId */
                mContext,
                mMockCallsManager,
                mLock,
                null /* ConnectionServiceRepository */,
                mMockPhoneNumberUtilsAdapter,
                TEST_ADDRESS,
                null /* GatewayInfo */,
                null /* connectionManagerPhoneAccountHandle */,
                SIM_1_HANDLE,
                Call.CALL_DIRECTION_INCOMING,
                false /* shouldAttachToExistingConnection*/,
                false /* isConference */,
                mMockClockProxy,
                mMockToastProxy);
        boolean[] hasCalledConnectionCapabilitiesChanged = new boolean[1];
        call.addListener(new Call.ListenerBase() {
            @Override
            public void onConnectionCapabilitiesChanged(Call call) {
                hasCalledConnectionCapabilitiesChanged[0] = true;
            }
        });
        call.setConnectionService(mMockConnectionService);
        call.setConnectionProperties(Connection.PROPERTY_IS_EXTERNAL_CALL);
        call.setConnectionCapabilities(Connection.CAPABILITY_CAN_PULL_CALL);
        call.setState(CallState.ACTIVE, "");
        assertTrue(hasCalledConnectionCapabilitiesChanged[0]);
        // Capability should be present
        assertTrue((call.getConnectionCapabilities() | Connection.CAPABILITY_CAN_PULL_CALL) > 0);
        hasCalledConnectionCapabilitiesChanged[0] = false;
        // Emergency call in progress
        call.setIsPullExternalCallSupported(false /*isPullCallSupported*/);
        assertTrue(hasCalledConnectionCapabilitiesChanged[0]);
        // Capability should not be present
        assertEquals(0, call.getConnectionCapabilities() & Connection.CAPABILITY_CAN_PULL_CALL);
        hasCalledConnectionCapabilitiesChanged[0] = false;
        // Emergency call complete
        call.setIsPullExternalCallSupported(true /*isPullCallSupported*/);
        assertTrue(hasCalledConnectionCapabilitiesChanged[0]);
        // Capability should be present
        assertEquals(Connection.CAPABILITY_CAN_PULL_CALL,
                call.getConnectionCapabilities() & Connection.CAPABILITY_CAN_PULL_CALL);
    }

    @Test
    @Test
    @SmallTest
    @SmallTest
    public void testCanNotPullCallDuringEmergencyCall() {
    public void testCanNotPullCallDuringEmergencyCall() {
+60 −1
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.telecom.tests;
package com.android.server.telecom.tests;


import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.fail;
import static junit.framework.TestCase.fail;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
@@ -1222,6 +1223,41 @@ public class CallsManagerTest extends TelecomTestCase {
        assertTrue(ongoingCall.isVideoCallingSupportedByPhoneAccount());
        assertTrue(ongoingCall.isVideoCallingSupportedByPhoneAccount());
    }
    }


    /**
     * Verifies that adding and removing a call triggers external calls to have capabilities
     * recalculated.
     */
    @SmallTest
    @Test
    public void testExternalCallCapabilitiesUpdated() throws InterruptedException {
        Call externalCall = addSpyCall(SIM_2_HANDLE, null, CallState.ACTIVE,
                Connection.CAPABILITY_CAN_PULL_CALL, Connection.PROPERTY_IS_EXTERNAL_CALL);
        LinkedBlockingQueue<Integer> capabilitiesQueue = new LinkedBlockingQueue<>(1);
        externalCall.addListener(new Call.ListenerBase() {
            @Override
            public void onConnectionCapabilitiesChanged(Call call) {
                try {
                    capabilitiesQueue.put(call.getConnectionCapabilities());
                } catch (InterruptedException e) {
                    fail();
                }
            }
        });

        Call call = createSpyCall(SIM_2_HANDLE, CallState.DIALING);
        doReturn(true).when(call).isEmergencyCall();
        mCallsManager.addCall(call);
        Integer result = capabilitiesQueue.poll(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
        assertNotNull(result);
        assertEquals(0, Connection.CAPABILITY_CAN_PULL_CALL & result);

        mCallsManager.removeCall(call);
        result = capabilitiesQueue.poll(TEST_TIMEOUT, TimeUnit.MILLISECONDS);
        assertNotNull(result);
        assertEquals(Connection.CAPABILITY_CAN_PULL_CALL,
                Connection.CAPABILITY_CAN_PULL_CALL & result);
    }

    /**
    /**
     * Verifies that speakers is disabled when there's no video capabilities, even if a video call
     * Verifies that speakers is disabled when there's no video capabilities, even if a video call
     * tried to place.
     * tried to place.
@@ -1363,12 +1399,21 @@ public class CallsManagerTest extends TelecomTestCase {
    }
    }


    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, int initialState) {
    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount, int initialState) {
        return addSpyCall(targetPhoneAccount, null, initialState);
        return addSpyCall(targetPhoneAccount, null, initialState, 0 /*caps*/, 0 /*props*/);
    }
    }


    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount,
    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount,
            PhoneAccountHandle connectionMgrAcct, int initialState) {
            PhoneAccountHandle connectionMgrAcct, int initialState) {
        return addSpyCall(targetPhoneAccount, connectionMgrAcct, initialState, 0 /*caps*/,
                0 /*props*/);
    }

    private Call addSpyCall(PhoneAccountHandle targetPhoneAccount,
            PhoneAccountHandle connectionMgrAcct, int initialState,
            int connectionCapabilities, int connectionProperties) {
        Call ongoingCall = createCall(targetPhoneAccount, connectionMgrAcct, initialState);
        Call ongoingCall = createCall(targetPhoneAccount, connectionMgrAcct, initialState);
        ongoingCall.setConnectionProperties(connectionProperties);
        ongoingCall.setConnectionCapabilities(connectionCapabilities);
        Call callSpy = Mockito.spy(ongoingCall);
        Call callSpy = Mockito.spy(ongoingCall);


        // Mocks some methods to not call the real method.
        // Mocks some methods to not call the real method.
@@ -1382,6 +1427,20 @@ public class CallsManagerTest extends TelecomTestCase {
        return callSpy;
        return callSpy;
    }
    }


    private Call createSpyCall(PhoneAccountHandle handle, int initialState) {
        Call ongoingCall = createCall(handle, initialState);
        Call callSpy = Mockito.spy(ongoingCall);

        // Mocks some methods to not call the real method.
        doNothing().when(callSpy).unhold();
        doNothing().when(callSpy).hold();
        doNothing().when(callSpy).disconnect();
        doNothing().when(callSpy).answer(Matchers.anyInt());
        doNothing().when(callSpy).setStartWithSpeakerphoneOn(Matchers.anyBoolean());

        return callSpy;
    }

    private Call createCall(PhoneAccountHandle targetPhoneAccount, int initialState) {
    private Call createCall(PhoneAccountHandle targetPhoneAccount, int initialState) {
        return createCall(targetPhoneAccount, null /* connectionManager */, initialState);
        return createCall(targetPhoneAccount, null /* connectionManager */, initialState);
    }
    }