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

Commit b8b5a697 authored by Hall Liu's avatar Hall Liu
Browse files

Implement mid-call RTT initiation and teardown

Adds functionality to testapps and to framework to allow for local and
remote initiation and disconnection of RTT.

Test: manual
Merged-In: I0e8248b495a7d3750c840591f1fa5388b34a32e2
Change-Id: I0e8248b495a7d3750c840591f1fa5388b34a32e2
parent 115c06ee
Loading
Loading
Loading
Loading
+69 −4
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@ import android.telecom.GatewayInfo;
import android.telecom.Log;
import android.telecom.Logging.EventManager;
import android.telecom.ParcelableConnection;
import android.telecom.ParcelableRttCall;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.Response;
@@ -87,6 +86,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {

    private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
    private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;

    private static final int INVALID_RTT_REQUEST_ID = -1;
    /**
     * Listener for events on the call.
     */
@@ -123,6 +124,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
        void onHoldToneRequested(Call call);
        void onConnectionEvent(Call call, String event, Bundle extras);
        void onExternalCallChanged(Call call, boolean isExternalCall);
        void onRttInitiationFailure(Call call, int reason);
        void onRemoteRttRequest(Call call, int requestId);
    }

    public abstract static class ListenerBase implements Listener {
@@ -184,13 +187,16 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
        public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
            return false;
        }

        @Override
        public void onHoldToneRequested(Call call) {}
        @Override
        public void onConnectionEvent(Call call, String event, Bundle extras) {}
        @Override
        public void onExternalCallChanged(Call call, boolean isExternalCall) {}
        @Override
        public void onRttInitiationFailure(Call call, int reason) {}
        @Override
        public void onRemoteRttRequest(Call call, int requestId) {}
    }

    private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
@@ -425,6 +431,11 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
     */
    private int mRttMode;

    /**
     * Integer indicating the remote RTT request ID that is pending a response from the user.
     */
    private int mPendingRttRequestId = INVALID_RTT_REQUEST_ID;

    /**
     * Persists the specified parameters and initializes the new instance.
     *
@@ -1111,7 +1122,7 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
        if (changedProperties != 0) {
            int previousProperties = mConnectionProperties;
            mConnectionProperties = connectionProperties;
            setIsRttCall((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
            setRttStreams((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
                    Connection.PROPERTY_IS_RTT);
            boolean didRttChange =
                    (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
@@ -2027,7 +2038,22 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
        return mSpeakerphoneOn;
    }

    public void setIsRttCall(boolean shouldBeRtt) {
    public void stopRtt() {
        if (mConnectionService != null) {
            mConnectionService.stopRtt(this);
        } else {
            // If this gets called by the in-call app before the connection service is set, we'll
            // just ignore it since it's really not supposed to happen.
            Log.w(this, "stopRtt() called before connection service is set.");
        }
    }

    public void sendRttRequest() {
        setRttStreams(true);
        mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
    }

    public void setRttStreams(boolean shouldBeRtt) {
        boolean areStreamsInitialized = mInCallToConnectionServiceStreams != null
                && mConnectionServiceToInCallStreams != null;
        if (shouldBeRtt && !areStreamsInitialized) {
@@ -2044,6 +2070,45 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable {
        }
    }

    public void onRttConnectionFailure(int reason) {
        setRttStreams(false);
        for (Listener l : mListeners) {
            l.onRttInitiationFailure(this, reason);
        }
    }

    public void onRemoteRttRequest() {
        if (isRttCall()) {
            Log.w(this, "Remote RTT request on a call that's already RTT");
            return;
        }

        mPendingRttRequestId = mCallsManager.getNextRttRequestId();
        for (Listener l : mListeners) {
            l.onRemoteRttRequest(this, mPendingRttRequestId);
        }
    }

    public void handleRttRequestResponse(int id, boolean accept) {
        if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
            Log.w(this, "Response received to a nonexistent RTT request: %d", id);
            return;
        }
        if (id != mPendingRttRequestId) {
            Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
            return;
        }
        setRttStreams(accept);
        if (accept) {
            Log.i(this, "RTT request %d accepted.", id);
            mConnectionService.respondToRttRequest(
                    this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
        } else {
            Log.i(this, "RTT request %d rejected.", id);
            mConnectionService.respondToRttRequest(this, null, null);
        }
    }

    public void closeRttPipes() {
        // TODO: may defer this until call is removed?
    }
+10 −5
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.AsyncEmergencyContactNotifier;
@@ -69,7 +68,6 @@ import com.android.server.telecom.callfiltering.IncomingCallFilter;
import com.android.server.telecom.components.ErrorDialogActivity;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -177,6 +175,7 @@ public class CallsManager extends Call.ListenerBase
     */
    private int mCallId = 0;

    private int mRttRequestId = 0;
    /**
     * Stores the current foreground user.
     */
@@ -724,7 +723,7 @@ public class CallsManager extends Call.ListenerBase
        if (extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
            if (phoneAccount != null &&
                    phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                call.setIsRttCall(true);
                call.setRttStreams(true);
            }
        }

@@ -969,7 +968,7 @@ public class CallsManager extends Call.ListenerBase
                    && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
                if (accountToUse != null
                        && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                    call.setIsRttCall(true);
                    call.setRttStreams(true);
                }
            }
        }
@@ -1422,7 +1421,7 @@ public class CallsManager extends Call.ListenerBase
                        mPhoneAccountRegistrar.getPhoneAccountUnchecked(account);
                if (realPhoneAccount != null
                        && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) {
                    call.setIsRttCall(true);
                    call.setRttStreams(true);
                }
            }

@@ -2274,6 +2273,12 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    public int getNextRttRequestId() {
        synchronized (mLock) {
            return (++mRttRequestId);
        }
    }

    /**
     * Callback when foreground user is switched. We will reload missed call in all profiles
     * including the user itself. There may be chances that profiles are not started yet.
+86 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
import android.telecom.CallAudioState;
@@ -771,6 +772,54 @@ public class ConnectionServiceWrapper extends ServiceBinder {
                Log.endSession();
            }
        }

        @Override
        public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
                throws RemoteException {

        }

        @Override
        public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)
                throws RemoteException {
            Log.startSession(sessionInfo, "CSW.oRIF");
            long token = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        call.onRttConnectionFailure(reason);
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
                Log.endSession();
            }
        }

        @Override
        public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
                throws RemoteException {

        }

        @Override
        public void onRemoteRttRequest(String callId, Session.Info sessionInfo)
                throws RemoteException {
            Log.startSession(sessionInfo, "CSW.oRRR");
            long token = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        call.onRemoteRttRequest();
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
                Log.endSession();
            }
        }
    }

    private final Adapter mAdapter = new Adapter();
@@ -860,7 +909,8 @@ public class ConnectionServiceWrapper extends ServiceBinder {
                            gatewayInfo.getOriginalAddress());
                }

                Log.addEvent(call, LogUtils.Events.START_CONNECTION, Log.piiHandle(call.getHandle()));
                Log.addEvent(call, LogUtils.Events.START_CONNECTION,
                        Log.piiHandle(call.getHandle()));

                // For self-managed incoming calls, if there is another ongoing call Telecom is
                // responsible for showing a UI to ask the user if they'd like to answer this
@@ -1214,6 +1264,41 @@ public class ConnectionServiceWrapper extends ServiceBinder {
        }
    }

    void startRtt(Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
        final String callId = mCallIdMapper.getCallId(call);
        if (callId != null && isServiceValid("startRtt")) {
            try {
                logOutgoing("startRtt: %s %s %s", callId, fromInCall, toInCall);
                mServiceInterface.startRtt(callId, fromInCall, toInCall, Log.getExternalSession());
            } catch (RemoteException ignored) {
            }
        }
    }

    void stopRtt(Call call) {
        final String callId = mCallIdMapper.getCallId(call);
        if (callId != null && isServiceValid("stopRtt")) {
            try {
                logOutgoing("stopRtt: %s", callId);
                mServiceInterface.stopRtt(callId, Log.getExternalSession());
            } catch (RemoteException ignored) {
            }
        }
    }

    void respondToRttRequest(
            Call call, ParcelFileDescriptor fromInCall, ParcelFileDescriptor toInCall) {
        final String callId = mCallIdMapper.getCallId(call);
        if (callId != null && isServiceValid("respondToRttRequest")) {
            try {
                logOutgoing("respondToRttRequest: %s %s %s", callId, fromInCall, toInCall);
                mServiceInterface.respondToRttUpgradeRequest(
                        callId, fromInCall, toInCall, Log.getExternalSession());
            } catch (RemoteException ignored) {
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    protected void setServiceInterface(IBinder binder) {
+22 −7
Original line number Diff line number Diff line
@@ -496,13 +496,18 @@ class InCallAdapter extends IInCallAdapter.Stub {
    }

    @Override
    public void sendRttRequest() {
    public void sendRttRequest(String callId) {
        try {
            Log.startSession("ICA.sRR");
            long token = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    // TODO
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        call.sendRttRequest();
                    } else {
                        Log.w(this, "stopRtt(): call %s not found", callId);
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
@@ -513,13 +518,18 @@ class InCallAdapter extends IInCallAdapter.Stub {
    }

    @Override
    public void respondToRttRequest(int id, boolean accept) {
    public void respondToRttRequest(String callId, int id, boolean accept) {
        try {
            Log.startSession("ICA.rTRR");
            long token = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    // TODO
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        call.handleRttRequestResponse(id, accept);
                    } else {
                        Log.w(this, "respondToRttRequest(): call %s not found", callId);
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
@@ -530,13 +540,18 @@ class InCallAdapter extends IInCallAdapter.Stub {
    }

    @Override
    public void stopRtt() {
    public void stopRtt(String callId) {
        try {
            Log.startSession("ICA.sRTT");
            long token = Binder.clearCallingIdentity();
            try {
                synchronized (mLock) {
                    // TODO
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        call.stopRtt();
                    } else {
                        Log.w(this, "stopRtt(): call %s not found", callId);
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
@@ -547,7 +562,7 @@ class InCallAdapter extends IInCallAdapter.Stub {
    }

    @Override
    public void setRttMode(int mode) {
    public void setRttMode(String callId, int mode) {
        try {
            Log.startSession("ICA.sRM");
            long token = Binder.clearCallingIdentity();
+42 −1
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -611,6 +610,17 @@ public final class InCallController extends CallsManagerListenerBase {
        public void onConnectionEvent(Call call, String event, Bundle extras) {
            notifyConnectionEvent(call, event, extras);
        }

        @Override
        public void onRttInitiationFailure(Call call, int reason) {
            notifyRttInitiationFailure(call, reason);
            updateCall(call, false, true);
        }

        @Override
        public void onRemoteRttRequest(Call call, int requestId) {
            notifyRemoteRttRequest(call, requestId);
        }
    };

    private final SystemStateListener mSystemStateListener = new SystemStateListener() {
@@ -899,6 +909,37 @@ public final class InCallController extends CallsManagerListenerBase {
        }
    }

    private void notifyRttInitiationFailure(Call call, int reason) {
        if (!mInCallServices.isEmpty()) {
             mInCallServices.entrySet().stream()
                    .filter((entry) -> entry.getKey().equals(mInCallServiceConnection.getInfo()))
                    .forEach((entry) -> {
                        try {
                            Log.i(this, "notifyRttFailure, call %s, incall %s",
                                    call, entry.getKey());
                            entry.getValue().onRttInitiationFailure(mCallIdMapper.getCallId(call),
                                    reason);
                        } catch (RemoteException ignored) {
                        }
                    });
        }
    }

    private void notifyRemoteRttRequest(Call call, int requestId) {
        if (!mInCallServices.isEmpty()) {
            mInCallServices.entrySet().stream()
                    .filter((entry) -> entry.getKey().equals(mInCallServiceConnection.getInfo()))
                    .forEach((entry) -> {
                        try {
                            Log.i(this, "notifyRemoteRttRequest, call %s, incall %s",
                                    call, entry.getKey());
                            entry.getValue().onRttUpgradeRequest(
                                    mCallIdMapper.getCallId(call), requestId);
                        } catch (RemoteException ignored) {
                        }
                    });
        }
    }
    /**
     * Unbinds an existing bound connection to the in-call app.
     */
Loading