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

Commit 6e63c130 authored by Hunsuk Choi's avatar Hunsuk Choi
Browse files

Clear IMS call information when SRVCC has completed

Bug: 255450284
Test: atest ImsCallInfoNotifierTest
Change-Id: I82bd36dd736b2770e4ada47c4cb59e36318640a3
parent ef9e6afb
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -78,6 +78,11 @@ public class ImsCallInfo {
        return changed;
    }

    /** Called when clearing orphaned connection. */
    public void onDisconnect() {
        mState = Call.State.DISCONNECTED;
    }

    /** @return the call index. */
    public int getIndex() {
        return mIndex;
+60 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import com.android.telephony.Rlog;

import java.util.ArrayList;
import java.util.Collection;
@@ -39,6 +40,8 @@ import java.util.Map;
 * Contains the state of all IMS calls.
 */
public class ImsCallInfoTracker {
    private static final String LOG_TAG = "ImsCallInfoTracker";
    private static final boolean DBG = false;

    private final Phone mPhone;
    private final List<ImsCallInfo> mQueue = new ArrayList<>();
@@ -56,6 +59,8 @@ public class ImsCallInfoTracker {
     * @param c The instance of {@link ImsPhoneConnection}.
     */
    public void addImsCallStatus(@NonNull ImsPhoneConnection c) {
        if (DBG) Rlog.d(LOG_TAG, "addImsCallStatus");

        synchronized (mImsCallInfo) {
            if (mQueue.isEmpty()) {
                mQueue.add(new ImsCallInfo(mNextIndex++));
@@ -69,6 +74,8 @@ public class ImsCallInfoTracker {
            mImsCallInfo.put(c, imsCallInfo);

            notifyImsCallStatus();

            if (DBG) dump();
        }
    }

@@ -90,16 +97,26 @@ public class ImsCallInfoTracker {
     */
    public void updateImsCallStatus(@NonNull ImsPhoneConnection c,
            boolean holdReceived, boolean resumeReceived) {
        if (DBG) {
            Rlog.d(LOG_TAG, "updateImsCallStatus holdReceived=" + holdReceived
                    + ", resumeReceived=" + resumeReceived);
        }

        synchronized (mImsCallInfo) {
            ImsCallInfo info = mImsCallInfo.get(c);

            if (info == null) {
                // This happens when the user tries to hangup the call after handover has completed.
                return;
            }

            boolean changed = info.update(c, holdReceived, resumeReceived);

            if (changed) notifyImsCallStatus();

            Call.State state = c.getState();

            if (DBG) Rlog.d(LOG_TAG, "updateImsCallStatus state=" + state);
            // Call is disconnected. There are 2 cases in disconnected state:
            // if silent redial, state == IDLE, otherwise, state == DISCONNECTED.
            if (state == DISCONNECTED || state == IDLE) {
@@ -113,6 +130,42 @@ public class ImsCallInfoTracker {
                    mNextIndex--;
                }
            }

            if (DBG) dump();
        }
    }

    /** Clears all orphaned IMS call information. */
    public void clearAllOrphanedConnections() {
        if (DBG) Rlog.d(LOG_TAG, "clearAllOrphanedConnections");

        Collection<ImsCallInfo> infos = mImsCallInfo.values();
        infos.stream().forEach(info -> { info.onDisconnect(); });
        notifyImsCallStatus();
        clearAllCallInfo();

        if (DBG) dump();
    }

    /** Notifies that SRVCC has completed. */
    public void notifySrvccCompleted() {
        if (DBG) Rlog.d(LOG_TAG, "notifySrvccCompleted");

        clearAllCallInfo();
        notifyImsCallStatus();

        if (DBG) dump();
    }

    private void clearAllCallInfo() {
        try {
            Collection<ImsCallInfo> infos = mImsCallInfo.values();
            infos.stream().forEach(info -> { info.reset(); });
            mImsCallInfo.clear();
            mQueue.clear();
            mNextIndex = 1;
        } catch (UnsupportedOperationException e) {
            Rlog.e(LOG_TAG, "e=" + e);
        }
    }

@@ -142,4 +195,11 @@ public class ImsCallInfoTracker {
            }
        });
    }

    private void dump() {
        Collection<ImsCallInfo> infos = mImsCallInfo.values();
        ArrayList<ImsCallInfo> imsCallInfo = new ArrayList<ImsCallInfo>(infos);
        sort(imsCallInfo);
        Rlog.d(LOG_TAG, "imsCallInfos=" + imsCallInfo);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -1479,6 +1479,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        // above. Remove all references to it.
        mPendingMO = null;
        updatePhoneState();
        mImsCallInfoTracker.clearAllOrphanedConnections();
    }

    /**
@@ -4658,6 +4659,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
                transferHandoverConnections(mBackgroundCall);
                transferHandoverConnections(mRingingCall);
                updatePhoneState();
                mImsCallInfoTracker.notifySrvccCompleted();
                break;

            case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
+54 −0
Original line number Diff line number Diff line
@@ -363,6 +363,60 @@ public class ImsCallInfoTrackerTest extends TelephonyTest {
        assertEquals(2, imsCallInfos.get(1).getIndex());
    }

    @Test
    public void testSrvccCompleted() throws Exception {
        ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);

        ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
        mImsCallInfoTracker.addImsCallStatus(c);

        verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());

        List<ImsCallInfo> imsCallInfos = captor.getValue();

        assertNotNull(imsCallInfos);
        assertEquals(1, imsCallInfos.size());

        mImsCallInfoTracker.notifySrvccCompleted();

        verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());

        imsCallInfos = captor.getValue();

        assertNotNull(imsCallInfos);
        assertEquals(0, imsCallInfos.size());
    }

    @Test
    public void testClearAllOrphanedConnections() throws Exception {
        ArgumentCaptor<List<ImsCallInfo>> captor = ArgumentCaptor.forClass(List.class);

        ImsPhoneConnection c = getConnection(Call.State.DIALING, false);
        mImsCallInfoTracker.addImsCallStatus(c);

        verify(mImsPhone, times(1)).updateImsCallStatus(captor.capture(), any());

        List<ImsCallInfo> imsCallInfos = captor.getValue();

        assertNotNull(imsCallInfos);
        assertEquals(1, imsCallInfos.size());

        mImsCallInfoTracker.clearAllOrphanedConnections();

        verify(mImsPhone, times(2)).updateImsCallStatus(captor.capture(), any());

        imsCallInfos = captor.getValue();

        assertNotNull(imsCallInfos);
        assertEquals(1, imsCallInfos.size());

        ImsCallInfo info = imsCallInfos.get(0);

        assertNotNull(info);
        assertEquals(1, info.getIndex());
        assertEquals(Call.State.IDLE, info.getCallState());
    }

    private ImsPhoneConnection getConnection(Call.State state, boolean isEmergency) {
        ImsPhoneConnection c = mock(ImsPhoneConnection.class);
        doReturn(isEmergency).when(c).isEmergencyCall();
+35 −0
Original line number Diff line number Diff line
@@ -2464,6 +2464,41 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
        verify(mImsPhone, times(7)).updateImsCallStatus(any(), any());
    }

    @Test
    public void testUpdateImsCallStatusSrvccCompleted() throws Exception {
        // Incoming call
        setupRingingConnection();

        verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());

        // no interaction when SRVCC has started, failed, or canceled.
        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_STARTED);

        verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());

        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_FAILED);

        verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());

        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_CANCELED);

        verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());

        // interaction when SRVCC has completed
        mCTUT.notifySrvccState(SRVCC_STATE_HANDOVER_COMPLETED);

        verify(mImsPhone, times(2)).updateImsCallStatus(any(), any());
    }

    @Test
    public void testClearAllOrphanedConnectionInfo() throws Exception {
        verify(mImsPhone, times(0)).updateImsCallStatus(any(), any());

        mConnectorListener.connectionUnavailable(FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);

        verify(mImsPhone, times(1)).updateImsCallStatus(any(), any());
    }

    /** Verifies that the request from ImsService is passed to ImsPhone as expected. */
    @Test
    @SmallTest