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

Commit 69d03c41 authored by Tyler Gunn's avatar Tyler Gunn
Browse files

Pipe through received DTMF tone pathway and RTP send/receive.

Also add test intent for emulating received DTMF and RTP data locally.


Test: Manual via test intents
Test: Added unit tests where possible.
Bug: 163085177
Change-Id: I7a05d0729e350da7d3813ebd36aaee59b981e277
parent db266b7a
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.internal.telephony;

import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Build;
@@ -25,6 +26,7 @@ import android.telephony.DisconnectCause;
import android.telephony.ServiceState;
import android.telephony.ServiceState.RilRadioTechnology;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.ims.RtpHeaderExtension;
import android.util.Log;

import com.android.ims.internal.ConferenceParticipant;
@@ -119,6 +121,18 @@ public abstract class Connection {
        public void onRttTerminated();
        public void onOriginalConnectionReplaced(Connection newConnection);
        public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall);

        /**
         * Indicates a DTMF digit has been received from the network.
         * @param digit The DTMF digit.
         */
        public void onReceivedDtmfDigit(char digit);

        /**
         * Indicates data from an RTP header extension has been received from the network.
         * @param extensionData The extension data.
         */
        public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData);
    }

    /**
@@ -168,6 +182,10 @@ public abstract class Connection {
        public void onOriginalConnectionReplaced(Connection newConnection) {}
        @Override
        public void onIsNetworkEmergencyCallChanged(boolean isEmergencyCall) {}
        @Override
        public void onReceivedDtmfDigit(char digit) {}
        @Override
        public void onReceivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {}
    }

    public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -1427,4 +1445,24 @@ public abstract class Connection {
            @android.telecom.Connection.VerificationStatus int verificationStatus) {
        mNumberVerificationStatus = verificationStatus;
    }

    /**
     * Called to report a DTMF digit received from the network.
     * @param digit the received digit.
     */
    public void receivedDtmfDigit(char digit) {
        for (Listener l : mListeners) {
            l.onReceivedDtmfDigit(digit);
        }
    }

    /**
     * Called to report RTP header extensions received from the network.
     * @param extensionData the received extension data.
     */
    public void receivedRtpHeaderExtensions(@NonNull Set<RtpHeaderExtension> extensionData) {
        for (Listener l : mListeners) {
            l.onReceivedRtpHeaderExtensions(extensionData);
        }
    }
}
+43 −21
Original line number Diff line number Diff line
@@ -124,6 +124,14 @@ public class TelephonyTester {
    private static final String ACTION_TEST_IMS_E_CALL =
            "com.android.internal.telephony.TestImsECall";

    /**
     * Test-only intent used to trigger signalling that an IMS call received a DTMF tone.
     */
    private static final String ACTION_TEST_RECEIVE_DTMF =
            "com.android.internal.telephony.TestReceiveDtmf";

    private static final String EXTRA_DIGIT = "digit";

    /**
     * Test-only intent used to trigger a change to the current call's phone number.
     * Use the {@link #EXTRA_NUMBER} extra to specify the new phone number.
@@ -197,6 +205,9 @@ public class TelephonyTester {
                } else if (action.equals(ACTION_TEST_IMS_E_CALL)) {
                    log("handle test IMS ecall intent");
                    testImsECall();
                } else if (action.equals(ACTION_TEST_RECEIVE_DTMF)) {
                    log("handle test DTMF intent");
                    testImsReceiveDtmf(intent);
                } else if (action.equals(ACTION_TEST_CHANGE_NUMBER)) {
                    log("handle test change number intent");
                    testChangeNumber(intent);
@@ -229,6 +240,7 @@ public class TelephonyTester {
                filter.addAction(ACTION_TEST_HANDOVER_FAIL);
                filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
                filter.addAction(ACTION_TEST_IMS_E_CALL);
                filter.addAction(ACTION_TEST_RECEIVE_DTMF);
                mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
            }

@@ -262,17 +274,7 @@ public class TelephonyTester {

    private void handleHandoverFailedIntent() {
        // Attempt to get the active IMS call
        ImsPhone imsPhone = (ImsPhone) mPhone;
        if (imsPhone == null) {
            return;
        }

        ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
        if (imsPhoneCall == null) {
            return;
        }

        ImsCall imsCall = imsPhoneCall.getImsCall();
        ImsCall imsCall = getImsCall();
        if (imsCall == null) {
            return;
        }
@@ -491,30 +493,50 @@ public class TelephonyTester {

    void testImsECall() {
        // Attempt to get the active IMS call before parsing the test XML file.
        ImsCall imsCall = getImsCall();
        if (imsCall == null) return;

        ImsCallProfile callProfile = imsCall.getCallProfile();
        Bundle extras = callProfile.getCallExtras();
        if (extras == null) {
            extras = new Bundle();
        }
        extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
        callProfile.mCallExtras = extras;
        imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
                callProfile);
    }

    private ImsCall getImsCall() {
        ImsPhone imsPhone = (ImsPhone) mPhone;
        if (imsPhone == null) {
            return;
            return null;
        }

        ImsPhoneCall imsPhoneCall = imsPhone.getForegroundCall();
        if (imsPhoneCall == null) {
            return;
            return null;
        }

        ImsCall imsCall = imsPhoneCall.getImsCall();
        if (imsCall == null) {
            return null;
        }
        return imsCall;
    }

    void testImsReceiveDtmf(Intent intent) {
        if (!intent.hasExtra(EXTRA_DIGIT)) {
            return;
        }
        char digit = intent.getStringExtra(EXTRA_DIGIT).charAt(0);

        ImsCallProfile callProfile = imsCall.getCallProfile();
        Bundle extras = callProfile.getCallExtras();
        if (extras == null) {
            extras = new Bundle();
        ImsCall imsCall = getImsCall();
        if (imsCall == null) {
            return;
        }
        extras.putBoolean(ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
        callProfile.mCallExtras = extras;
        imsCall.getImsCallSessionListenerProxy().callSessionUpdated(imsCall.getSession(),
                callProfile);

        imsCall.getImsCallSessionListenerProxy().callSessionDtmfReceived(digit);
    }

    void testChangeNumber(Intent intent) {
+27 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.telephony.ims.ImsSuppServiceNotification;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.RtpHeaderExtension;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -124,6 +125,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
@@ -3576,6 +3578,15 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
            mPhone.notifySuppServiceFailed(Phone.SuppService.TRANSFER);
        }

        @Override
        public void onCallSessionDtmfReceived(ImsCall imsCall, char digit) {
            log("onCallSessionDtmfReceived digit=" + digit);
            ImsPhoneConnection conn = findConnection(imsCall);
            if (conn != null) {
                conn.receivedDtmfDigit(digit);
            }
        }

        /**
         * Handles a change to the multiparty state for an {@code ImsCall}.  Notifies the associated
         * {@link ImsPhoneConnection} of the change.
@@ -3610,6 +3621,22 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
            cqm.saveCallQuality(callQuality);
            mCallQualityMetrics.put(callId, cqm);
        }

        /**
         * Handles reception of RTP header extension data from the network.
         * @param imsCall The ImsCall the data was received on.
         * @param rtpHeaderExtensionData The RTP extension data received.
         */
        @Override
        public void onCallSessionRtpHeaderExtensionsReceived(ImsCall imsCall,
                @NonNull Set<RtpHeaderExtension> rtpHeaderExtensionData) {
            log("onCallSessionRtpHeaderExtensionsReceived numExtensions="
                    + rtpHeaderExtensionData.size());
            ImsPhoneConnection conn = findConnection(imsCall);
            if (conn != null) {
                conn.receivedRtpHeaderExtensions(rtpHeaderExtensionData);
            }
        }
    };

    /**
+56 −0
Original line number Diff line number Diff line
@@ -16,21 +16,29 @@

package android.telephony.ims;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.fail;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.util.ArraySet;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;

@RunWith(AndroidJUnit4.class)
public class ImsCallSessionListenerTests {

@@ -75,4 +83,52 @@ public class ImsCallSessionListenerTests {
                eq(TelephonyManager.NETWORK_TYPE_IWLAN), eq(imsReasonInfo));
    }

    @Test
    public void testCallSessionDtmfReceived() throws Exception {
        ImsCallSessionListener mTestListener = new ImsCallSessionListener(mMockListener);
        mTestListener.callSessionDtmfReceived('A');
        mTestListener.callSessionDtmfReceived('a');
        verify(mMockListener, times(2)).callSessionDtmfReceived(eq('A'));

        mTestListener.callSessionDtmfReceived('B');
        mTestListener.callSessionDtmfReceived('b');
        verify(mMockListener, times(2)).callSessionDtmfReceived(eq('B'));

        mTestListener.callSessionDtmfReceived('0');
        verify(mMockListener, times(1)).callSessionDtmfReceived(eq('0'));

        mTestListener.callSessionDtmfReceived('*');
        verify(mMockListener, times(1)).callSessionDtmfReceived(eq('*'));
        mTestListener.callSessionDtmfReceived('#');
        verify(mMockListener, times(1)).callSessionDtmfReceived(eq('#'));

        try {
            mTestListener.callSessionDtmfReceived('P');
            fail("expected exception");
        } catch (IllegalArgumentException illegalArgumentException) {
            // expected
        }
    }

    @Test
    public void testCallSessionRtpExtensionHeadersReceived() throws Exception {
        ImsCallSessionListener mTestListener = new ImsCallSessionListener(mMockListener);
        ArraySet<RtpHeaderExtension> headers = new ArraySet<RtpHeaderExtension>();
        RtpHeaderExtension extension = new RtpHeaderExtension(1, new byte[1]);
        headers.add(extension);
        mTestListener.callSessionRtpHeaderExtensionsReceived(headers);
        final ArgumentCaptor<List<RtpHeaderExtension>> listCaptor =
                ArgumentCaptor.forClass((Class) List.class);
        verify(mMockListener).callSessionRtpHeaderExtensionsReceived(
                listCaptor.capture());
        assertEquals(1, listCaptor.getValue().size());
        assertEquals(extension.getLocalIdentifier(),
                listCaptor.getValue().get(0).getLocalIdentifier());
        try {
            mTestListener.callSessionRtpHeaderExtensionsReceived(null);
            fail("expected exception");
        } catch (NullPointerException npe) {
            // expected
        }
    }
}