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

Commit c19f68c9 authored by Hakjun Choi's avatar Hakjun Choi
Browse files

Unable to accept incoming call when using main Executor in MmTelFeature

Add new API notfyIncomingCall() which returns ImsCallSessionListener as blocking API to prevent deadlock

Bug: 257554011
Test: atest ImsPhoneCallTrackerTest, E2E Call regression Test
Change-Id: Ia19462dc733be23a72f283e71b6c5011f3f2c1b0
parent 251ef71f
Loading
Loading
Loading
Loading
+42 −12
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.SrvccCall;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.telephony.ims.aidl.ISrvccStartedCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
@@ -168,10 +169,12 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@@ -234,16 +237,18 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
    private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener();
    private class MmTelFeatureListener extends MmTelFeature.Listener {

        private void processIncomingCall(IImsCallSession c, Bundle extras) {
        private IImsCallSessionListener processIncomingCall(@NonNull IImsCallSession c,
                @Nullable String callId, @Nullable Bundle extras) {
            if (DBG) log("processIncomingCall: incoming call intent");

            if (extras == null) extras = new Bundle();
            if (mImsManager == null) return;
            if (mImsManager == null) return null;

            try {
                IImsCallSessionListener iimsCallSessionListener;
                // Network initiated USSD will be treated by mImsUssdListener
                boolean isUssd = extras.getBoolean(MmTelFeature.EXTRA_IS_USSD, false);
                // For compatibility purposes with older vendor implmentations.
                // For compatibility purposes with older vendor implementations.
                isUssd |= extras.getBoolean(ImsManager.EXTRA_USSD, false);
                if (isUssd) {
                    if (DBG) log("processIncomingCall: USSD");
@@ -252,11 +257,14 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
                    if (mUssdSession != null) {
                        mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
                    }
                    return;
                    if (callId != null) mUssdSession.getCallSession().setCallId(callId);
                    iimsCallSessionListener = (IImsCallSessionListener) mUssdSession
                            .getCallSession().getIImsCallSessionListenerProxy();
                    return iimsCallSessionListener;
                }

                boolean isUnknown = extras.getBoolean(MmTelFeature.EXTRA_IS_UNKNOWN_CALL, false);
                // For compatibility purposes with older vendor implmentations.
                // For compatibility purposes with older vendor implementations.
                isUnknown |= extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
                if (DBG) {
                    log("processIncomingCall: isUnknown = " + isUnknown
@@ -266,6 +274,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {

                // Normal MT/Unknown call
                ImsCall imsCall = mImsManager.takeCall(c, mImsCallListener);
                if (callId != null) imsCall.getCallSession().setCallId(callId);
                iimsCallSessionListener = (IImsCallSessionListener) imsCall
                        .getCallSession().getIImsCallSessionListenerProxy();
                ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
                        ImsPhoneCallTracker.this,
                        (isUnknown ? mForegroundCall : mRingingCall), isUnknown);
@@ -340,18 +351,19 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
                updatePhoneState();
                mPhone.notifyPreciseCallStateChanged();
                mImsCallInfoTracker.addImsCallStatus(conn);
                return iimsCallSessionListener;
            } catch (ImsException | RemoteException e) {
                loge("processIncomingCall: exception " + e);
                mOperationLocalLog.log("onIncomingCall: exception processing: "  + e);
                return null;
            }
        }

        @Override
        public void onIncomingCall(IImsCallSession c, Bundle extras) {
            // we want to ensure we block this binder thread until incoming call setup completes
            // as to avoid race conditions where the ImsService tries to update the state of the
            // call before the listeners have been attached.
            executeAndWait(()-> processIncomingCall(c, extras));
        @Nullable
        public IImsCallSessionListener onIncomingCall(
                @NonNull IImsCallSession c, @Nullable String callId, @Nullable Bundle extras) {
            return executeAndWaitForReturn(()-> processIncomingCall(c, callId, extras));
        }

        @Override
@@ -413,6 +425,24 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
                logw("Binder - exception: " + e.getMessage());
            }
        }

        /**
         * Schedule the given Runnable on mExecutor and block this thread until it finishes.
         * @param r The Runnable to run.
         */
        private <T> T executeAndWaitForReturn(Supplier<T> r) {

            CompletableFuture<T> future = CompletableFuture.supplyAsync(
                    () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);

            try {
                return future.get();
            } catch (ExecutionException | InterruptedException e) {
                Log.w(LOG_TAG, "ImsPhoneCallTracker : executeAndWaitForReturn exception: "
                        + e.getMessage());
                return null;
            }
        }
    }

    /**
+16 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -151,11 +152,25 @@ public class MmTelFeatureTests extends ImsTestBase {

        mFeature.incomingCall(session);
        ArgumentCaptor<IImsCallSession> captor = ArgumentCaptor.forClass(IImsCallSession.class);
        verify(mListener).onIncomingCall(captor.capture(), any());
        verify(mListener).onIncomingCall(captor.capture(), eq(null), any());

        assertEquals(sessionBinder, captor.getValue());
    }

    @SmallTest
    @Test
    public void testNewIncomingCallReturnListener() throws Exception {
        IImsCallSession sessionBinder = Mockito.mock(IImsCallSession.class);
        ImsCallSessionImplBase session = new ImsCallSessionImplBase();
        session.setServiceImpl(sessionBinder);
        String callId = "callID";
        Bundle extra = new Bundle();
        mFeature.incomingCall(session, callId, extra);
        ArgumentCaptor<IImsCallSession> captor = ArgumentCaptor.forClass(IImsCallSession.class);
        verify(mListener).onIncomingCall(captor.capture(), eq(callId), eq(extra));
        assertEquals(sessionBinder, captor.getValue());
    }

    @SmallTest
    @Test
    public void testSetTtyMessageMessenger() throws Exception {
+5 −0
Original line number Diff line number Diff line
@@ -64,6 +64,11 @@ public class TestMmTelFeature extends MmTelFeature {
        notifyIncomingCall(c, new Bundle());
    }

    public ImsCallSessionListener incomingCall(
            ImsCallSessionImplBase c, String callId, Bundle extra) {
        return notifyIncomingCall(c, callId, extra);
    }

    @Override
    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
        return super.createCallProfile(callSessionType, callType);
+6 −6
Original line number Diff line number Diff line
@@ -556,7 +556,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
        assertFalse(mCTUT.mRingingCall.isRinging());
        // mock a MT call
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
        verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
        verify(mImsPhone, times(1)).notifyIncomingRing();
        assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -708,7 +708,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
            ex.printStackTrace();
            Assert.fail("unexpected exception thrown" + ex.getMessage());
        }
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);

        verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
        verify(mImsPhone, times(2)).notifyIncomingRing();
@@ -746,7 +746,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
            ex.printStackTrace();
            Assert.fail("unexpected exception thrown" + ex.getMessage());
        }
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);

        verify(mImsPhone, times(2)).notifyNewRingingConnection((Connection) any());
        verify(mImsPhone, times(2)).notifyIncomingRing();
@@ -938,7 +938,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
        try {
            doReturn(mSecondImsCall).when(mImsManager).takeCall(any(IImsCallSession.class),
                    any(ImsCall.Listener.class));
            mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
            mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
            mCTUT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
        } catch (Exception ex) {
            ex.printStackTrace();
@@ -1756,7 +1756,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
        assertFalse(mCTUT.mRingingCall.isRinging());
        // mock a MT call
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
        verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
        verify(mImsPhone, times(1)).notifyIncomingRing();
        assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
@@ -1794,7 +1794,7 @@ public class ImsPhoneCallTrackerTest extends TelephonyTest {
        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
        assertFalse(mCTUT.mRingingCall.isRinging());
        // mock a MT call
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), Bundle.EMPTY);
        mMmTelListener.onIncomingCall(mock(IImsCallSession.class), null, Bundle.EMPTY);
        verify(mImsPhone, times(1)).notifyNewRingingConnection((Connection) any());
        verify(mImsPhone, times(1)).notifyIncomingRing();
        assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());