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

Commit 65ec57cd authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Handle ringback changes between call state changes.

There was an assumption baked into the framework that the vendor IMS
stack would ONLY report a media direction change when the call state
changes.  This means that on some devices/networks when the audio becomes
active/inactive between the call state changes we would not start or stop
the ringback.

Refactored the ringback code and am now invoking it from
ImsPhoneCallTracker#processCallStateChange when it is handling updates
where the state did not change.

Test: Verified ringback operation on live network.
Test: Added unit tests for mid-state ringback changes.
Fixes: 190578101
Change-Id: Ic35154a45e3d7a0a5f4e067ec5d55a4721fe943a
parent dbe48e2c
Loading
Loading
Loading
Loading
+43 −17
Original line number Diff line number Diff line
@@ -334,44 +334,70 @@ public class ImsPhoneCall extends Call {
        }

        ImsStreamMediaProfile mediaProfile = imsCall.getCallProfile().mMediaProfile;

        return (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
        boolean shouldPlayRingback =
                (mediaProfile.mAudioDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE)
                        ? true : false;
        Rlog.i(LOG_TAG, "isLocalTone: audioDirection=" + mediaProfile.mAudioDirection
                + ", playRingback=" + shouldPlayRingback);
        return shouldPlayRingback;
    }

    public boolean update(ImsPhoneConnection conn, ImsCall imsCall, State state) {
        boolean changed = false;
        State oldState = mState;

        // We will try to start or stop ringback whenever the call has major call state changes.
        maybeChangeRingbackState(imsCall, state);

        if ((state != mState) && (state != State.DISCONNECTED)) {
            mState = state;
            changed = true;
        } else if (state == State.DISCONNECTED) {
            changed = true;
        }

        if (VDBG) {
            Rlog.v(LOG_TAG, "update : " + mCallContext + " state: " + oldState + " --> " + mState);
        }

        return changed;
    }

    /**
     * Determines whether to change the ringback state for a call.
     * @param imsCall The call.
     */
    public void maybeChangeRingbackState(ImsCall imsCall) {
        maybeChangeRingbackState(imsCall, mState);
    }

    /**
     * Determines whether local ringback should be playing for the call.  We will play local
     * ringback when a call is in an ALERTING state and the audio direction is DIRECTION_INACTIVE.
     * @param imsCall The call the change pertains to.
     * @param state The current state of the call.
     */
    private void maybeChangeRingbackState(ImsCall imsCall, State state) {
        //ImsCall.Listener.onCallProgressing can be invoked several times
        //and ringback tone mode can be changed during the call setup procedure
        Rlog.i(LOG_TAG, "maybeChangeRingbackState: state=" + state);
        if (state == State.ALERTING) {
            if (mIsRingbackTonePlaying && !isLocalTone(imsCall)) {
                Rlog.i(LOG_TAG, "maybeChangeRingbackState: stop ringback");
                getPhone().stopRingbackTone();
                mIsRingbackTonePlaying = false;
            } else if (!mIsRingbackTonePlaying && isLocalTone(imsCall)) {
                Rlog.i(LOG_TAG, "maybeChangeRingbackState: start ringback");
                getPhone().startRingbackTone();
                mIsRingbackTonePlaying = true;
            }
        } else {
            if (mIsRingbackTonePlaying) {
                Rlog.i(LOG_TAG, "maybeChangeRingbackState: stop ringback");
                getPhone().stopRingbackTone();
                mIsRingbackTonePlaying = false;
            }
        }

        if ((state != mState) && (state != State.DISCONNECTED)) {
            mState = state;
            changed = true;
        } else if (state == State.DISCONNECTED) {
            changed = true;
        }

        if (VDBG) {
            Rlog.v(LOG_TAG, "update : " + mCallContext + " state: " + oldState + " --> " + mState);
        }

        return changed;
    }

    /* package */ ImsPhoneConnection
+3 −0
Original line number Diff line number Diff line
@@ -2579,6 +2579,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
        if (ignoreState) {
            conn.updateAddressDisplay(imsCall);
            conn.updateExtras(imsCall);
            // Some devices will change the audio direction between major call state changes, so we
            // need to check whether to start or stop ringback
            conn.maybeChangeRingbackState();

            maybeSetVideoCallProvider(conn, imsCall);
            return;
+8 −0
Original line number Diff line number Diff line
@@ -854,6 +854,14 @@ public class ImsPhoneConnection extends Connection implements
        return updateParent || updateAddressDisplay || updateMediaCapabilities || updateExtras;
    }

    /**
     * Re-evaluate whether ringback should be playing.
     */
    public void maybeChangeRingbackState() {
        Rlog.i(LOG_TAG, "maybeChangeRingbackState");
        mParent.maybeChangeRingbackState(mImsCall);
    }

    @Override
    public int getPreciseDisconnectCause() {
        return mPreciseDisconnectCause;
+58 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -139,6 +140,63 @@ public class ImsPhoneCallTest extends TelephonyTest {
        assertEquals(Call.State.ACTIVE, mImsCallUT.getState());
    }

    /**
     * Verifies we can handle starting ringback between call state changes.
     */
    @Test
    @SmallTest
    public void testUpdateRingBackToneBetweenStateChange() {
        mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
        mImsCallProfile.mMediaProfile = mMediaProfile;

        // This call state change should NOT start ringback since it the direction is wrong.
        mImsCallUT.update(null, mImsCall, Call.State.ALERTING);
        verify(mImsPhone, never()).startRingbackTone();
        assertEquals(Call.State.ALERTING, mImsCallUT.getState());

        // Simulate a change to the profile without a state change.
        mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_INACTIVE;
        mImsCallUT.maybeChangeRingbackState(mImsCall);
        verify(mImsPhone, times(1)).startRingbackTone();

        // And then assume the call goes active, which would stop the ringback.
        mImsCallUT.update(null, mImsCall, Call.State.ACTIVE);
        verify(mImsPhone, times(1)).stopRingbackTone();
        assertEquals(Call.State.ACTIVE, mImsCallUT.getState());
    }

    /**
     * Verifies we can handle ringback start/stop entirely between call state changes.
     */
    @Test
    @SmallTest
    public void testUpdateRingBackToneBetweenStateChangeTwo() {
        mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
        mImsCallProfile.mMediaProfile = mMediaProfile;

        // This call state change should NOT start ringback since it the direction is wrong.
        mImsCallUT.update(null, mImsCall, Call.State.ALERTING);
        verify(mImsPhone, never()).startRingbackTone();
        assertEquals(Call.State.ALERTING, mImsCallUT.getState());

        // Simulate a change to the profile without a state change.
        mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_INACTIVE;
        mImsCallUT.maybeChangeRingbackState(mImsCall);
        verify(mImsPhone, times(1)).startRingbackTone();

        // Simulate another change to the profile without a state change.
        mMediaProfile.mAudioDirection = ImsStreamMediaProfile.DIRECTION_SEND_RECEIVE;
        mImsCallUT.maybeChangeRingbackState(mImsCall);
        verify(mImsPhone, times(1)).stopRingbackTone();

        // And then assume the call goes active, which should not impact ringback state.
        mImsCallUT.update(null, mImsCall, Call.State.ACTIVE);
        assertEquals(Call.State.ACTIVE, mImsCallUT.getState());
        // Should still have only started and stopped once
        verify(mImsPhone, times(1)).startRingbackTone();
        verify(mImsPhone, times(1)).stopRingbackTone();
    }

    @Test
    @SmallTest
    public void testStopRingingOnHandover() {