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

Commit 462b388b authored by Hunsuk Choi's avatar Hunsuk Choi
Browse files

Reject incoming call for a normal routing emergency call

An emergency call fails when receiving a call at the same time.

Case1. Reject incoming call when starting a normal routing emergency call.
Case2. Reject incoming call if it receives a new incoming call after
starting a normal routing emergency call.

Bug: 339159523
Test: atest EmergencyStateTrackerTest
Manual test:
1. Make a call from A to DUT.
2. Check whether the call is in alerting state in A.
3. At that moment, dial a normal routing emergency number in DUT
   before the incoming call is notified.

Change-Id: I42b4efbb075d4392064f2a177e4611d3257fcdcb
parent 1d770256
Loading
Loading
Loading
Loading
+86 −5
Original line number Diff line number Diff line
@@ -163,6 +163,7 @@ public class EmergencyStateTracker {
    private Runnable mOnEcmExitCompleteRunnable;
    private int mOngoingCallProperties;
    private boolean mSentEmergencyCallState;
    private android.telecom.Connection mNormalRoutingEmergencyConnection;

    /** For emergency SMS */
    private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>();
@@ -633,8 +634,14 @@ public class EmergencyStateTracker {
        mOngoingConnection = c;
        mIsTestEmergencyNumber = isTestEmergencyNumber;
        sendEmergencyCallStateChange(mPhone, true);
        final android.telecom.Connection expectedConnection = mOngoingConnection;
        maybeRejectIncomingCall(result -> {
            Rlog.i(TAG, "maybeRejectIncomingCall : result = " + result);
            if (!Objects.equals(mOngoingConnection, expectedConnection)) {
                Rlog.i(TAG, "maybeRejectIncomingCall "
                        + expectedConnection.getTelecomCallId() + " canceled.");
                return;
            }
            turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber);
        });
        return mCallEmergencyModeFuture;
@@ -1933,6 +1940,27 @@ public class EmergencyStateTracker {
        }
    }

    private Call getRingingCall(Phone phone) {
        if (phone == null) return null;
        Call ringingCall = phone.getRingingCall();
        if (ringingCall != null
                && ringingCall.getState() != Call.State.IDLE
                && ringingCall.getState() != Call.State.DISCONNECTED) {
            return ringingCall;
        }
        // Check the ImsPhoneCall in DISCONNECTING state.
        Phone imsPhone = phone.getImsPhone();
        if (imsPhone != null) {
            ringingCall = imsPhone.getRingingCall();
        }
        if (imsPhone != null && ringingCall != null
                && ringingCall.getState() != Call.State.IDLE
                && ringingCall.getState() != Call.State.DISCONNECTED) {
            return ringingCall;
        }
        return null;
    }

    /**
     * Ensures that there is no incoming call.
     *
@@ -1951,14 +1979,14 @@ public class EmergencyStateTracker {

        Call ringingCall = null;
        for (Phone phone : phones) {
            ringingCall = phone.getRingingCall();
            if (ringingCall != null && ringingCall.isRinging()) {
            ringingCall = getRingingCall(phone);
            if (ringingCall != null) {
                Rlog.i(TAG, "maybeRejectIncomingCall found a ringing call");
                break;
            }
        }

        if (ringingCall == null || !ringingCall.isRinging()) {
        if (ringingCall == null) {
            if (completeConsumer != null) {
                completeConsumer.accept(true);
            }
@@ -2007,9 +2035,13 @@ public class EmergencyStateTracker {
     */
    private void handleNewRingingConnection(Message msg) {
        Connection c = (Connection) ((AsyncResult) msg.obj).result;
        if (c == null || mOngoingConnection == null
        if (c == null) return;
        if ((mNormalRoutingEmergencyConnection == null
                || mNormalRoutingEmergencyConnection.getState() == STATE_ACTIVE
                || mNormalRoutingEmergencyConnection.getState() == STATE_DISCONNECTED)
                && (mOngoingConnection == null
                || mOngoingConnection.getState() == STATE_ACTIVE
                || mOngoingConnection.getState() == STATE_DISCONNECTED) {
                || mOngoingConnection.getState() == STATE_DISCONNECTED)) {
            return;
        }
        if ((c.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS)
@@ -2024,4 +2056,53 @@ public class EmergencyStateTracker {
            }
        }
    }

    /**
     * Indicates the start of a normal routing emergency call.
     *
     * <p>
     * Handles turning on radio and switching DDS.
     *
     * @param phone the {@code Phone} on which to process the emergency call.
     * @param c the {@code Connection} on which to process the emergency call.
     * @param completeConsumer The consumer to call once rejecting incoming call completes,
     *        provides {@code true} result if operation completes successfully
     *        or {@code false} if the operation timed out/failed.
     */
    public void startNormalRoutingEmergencyCall(@NonNull Phone phone,
            @NonNull android.telecom.Connection c, @NonNull Consumer<Boolean> completeConsumer) {
        Rlog.i(TAG, "startNormalRoutingEmergencyCall: phoneId=" + phone.getPhoneId()
                + ", callId=" + c.getTelecomCallId());

        mNormalRoutingEmergencyConnection = c;
        maybeRejectIncomingCall(completeConsumer);
    }

    /**
     * Indicates the termination of a normal routing emergency call.
     *
     * @param c the normal routing emergency call disconnected.
     */
    public void endNormalRoutingEmergencyCall(@NonNull android.telecom.Connection c) {
        if (c != mNormalRoutingEmergencyConnection) return;
        Rlog.i(TAG, "endNormalRoutingEmergencyCall: callId=" + c.getTelecomCallId());
        mNormalRoutingEmergencyConnection = null;
    }

    /**
     * Handles the normal routing emergency call state change.
     *
     * @param c the call whose state has changed
     * @param state the new call state
     */
    public void onNormalRoutingEmergencyCallStateChanged(android.telecom.Connection c,
            @android.telecom.Connection.ConnectionState int state) {
        if (c != mNormalRoutingEmergencyConnection) return;

        // If the call is connected, we don't need to monitor incoming call any more.
        if (state == android.telecom.Connection.STATE_ACTIVE
                || state == android.telecom.Connection.STATE_DISCONNECTED) {
            endNormalRoutingEmergencyCall(c);
        }
    }
}
+115 −1
Original line number Diff line number Diff line
@@ -2687,7 +2687,7 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
        Connection c = mock(Connection.class);
        Call call = mock(Call.class);
        doReturn(c).when(call).getLatestConnection();
        doReturn(true).when(call).isRinging();
        doReturn(Call.State.INCOMING).when(call).getState();
        doReturn(call).when(phone).getRingingCall();
        setEcmSupportedConfig(phone, true);

@@ -2789,6 +2789,120 @@ public class EmergencyStateTrackerTest extends TelephonyTest {
        }
    }

    /**
     * Test that the EmergencyStateTracker rejects incoming call when starting
     * a normal routing emergency call.
     */
    @Test
    @SmallTest
    public void testNormalRoutingEmergencyCallRejectRingingCall() {
        Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
                false /* isRadioOn */);
        when(phone.getSubId()).thenReturn(1);
        Connection c = mock(Connection.class);
        Call call = mock(Call.class);
        doReturn(c).when(call).getLatestConnection();
        doReturn(Call.State.DISCONNECTING).when(call).getState();
        doReturn(call).when(phone).getRingingCall();
        setEcmSupportedConfig(phone, true);

        EmergencyStateTracker testEst = setupEmergencyStateTracker(
                false /* isSuplDdsSwitchRequiredForEmergencyCall */);

        // There is an ongoing normal routing emergency call.
        testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {});

        // Verify rejecting ringing call.
        try {
            verify(call).hangup();
        } catch (CallStateException e) {
        }
    }


    /**
     * Test that the EmergencyStateTracker rejects incoming call if there is
     * a normal routing emergency call in dialing state.
     */
    @Test
    @SmallTest
    public void testNormalRoutingRejectNewIncomingCall() {
        Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
                false /* isRadioOn */);
        when(phone.getSubId()).thenReturn(1);
        setEcmSupportedConfig(phone, true);

        EmergencyStateTracker testEst = setupEmergencyStateTracker(
                false /* isSuplDdsSwitchRequiredForEmergencyCall */);

        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
        ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);

        verify(phone).registerForNewRingingConnection(handlerCaptor.capture(),
                intCaptor.capture(), any());
        assertNotNull(handlerCaptor.getValue());
        assertNotNull(intCaptor.getValue());

        // There is an ongoing normal routing emergency call.
        testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {});

        Connection c = mock(Connection.class);
        Call call = mock(Call.class);
        doReturn(call).when(c).getCall();

        Message msg = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue());
        AsyncResult.forMessage(msg, c, null);
        msg.sendToTarget();
        processAllMessages();

        // Verify rejecting incoming call.
        try {
            verify(call).hangup();
        } catch (CallStateException e) {
        }
    }

    @Test
    @SmallTest
    public void testNormalRoutingDiscardedNotRejectNewIncomingCall() {
        Phone phone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
                false /* isRadioOn */);
        when(phone.getSubId()).thenReturn(1);
        setEcmSupportedConfig(phone, true);

        EmergencyStateTracker testEst = setupEmergencyStateTracker(
                false /* isSuplDdsSwitchRequiredForEmergencyCall */);

        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
        ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);

        verify(phone).registerForNewRingingConnection(handlerCaptor.capture(),
                intCaptor.capture(), any());
        assertNotNull(handlerCaptor.getValue());
        assertNotNull(intCaptor.getValue());

        // Start normal routing emergency call.
        testEst.startNormalRoutingEmergencyCall(phone, mTestConnection1, result -> {});

        // Discard normal routing emergency call.
        testEst.endNormalRoutingEmergencyCall(mTestConnection1);

        Connection c = mock(Connection.class);
        Call call = mock(Call.class);
        doReturn(call).when(c).getCall();

        Message msg = Message.obtain(handlerCaptor.getValue(), intCaptor.getValue());
        AsyncResult.forMessage(msg, c, null);
        msg.sendToTarget();
        processAllMessages();

        // Verify not rejecting incoming call.
        try {
            verify(call, never()).hangup();
        } catch (CallStateException e) {
        }
    }

    /**
     * Test that emergency call state changes are sent.
     */