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

Commit 910ccfea authored by Hall Liu's avatar Hall Liu Committed by android-build-merger
Browse files

Merge "Implement parts of the audio call screening API" am: d985e1d4 am: 25ee2702

am: efb44ff8

Change-Id: I16b2ba873e9072b02f4c3168c991ec6a03ed2578
parents 54d7c877 efb44ff8
Loading
Loading
Loading
Loading
+55 −3
Original line number Diff line number Diff line
@@ -365,6 +365,12 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     */
    private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);

    /**
     * Override the disconnect cause set by the connection service. Used for audio processing and
     * simulated ringing calls.
     */
    private int mOverrideDisconnectCauseCode = DisconnectCause.UNKNOWN;

    private Bundle mIntentExtras = new Bundle();

    /**
@@ -1145,6 +1151,11 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
    public void setDisconnectCause(DisconnectCause disconnectCause) {
        // TODO: Consider combining this method with a setDisconnected() method that is totally
        // separate from setState.
        if (mOverrideDisconnectCauseCode != DisconnectCause.UNKNOWN) {
            disconnectCause = new DisconnectCause(mOverrideDisconnectCauseCode,
                    disconnectCause.getLabel(), disconnectCause.getDescription(),
                    disconnectCause.getReason(), disconnectCause.getTone());
        }
        mAnalytics.setCallDisconnectCause(disconnectCause);
        mDisconnectCause = disconnectCause;
    }
@@ -1924,6 +1935,13 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
            Log.v(this, "Aborting call %s", this);
            abort(disconnectionTimeout);
        } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
            if (mState == CallState.AUDIO_PROCESSING) {
                mOverrideDisconnectCauseCode = DisconnectCause.REJECTED;
            } else if (mState == CallState.SIMULATED_RINGING) {
                // This is the case where the dialer calls disconnect() because the call timed out
                // Override the disconnect cause to MISSED
                mOverrideDisconnectCauseCode = DisconnectCause.MISSED;
            }
            if (mConnectionService == null) {
                Log.e(this, new Exception(), "disconnect() request on a call without a"
                        + " connection service.");
@@ -2001,6 +2019,30 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        }
    }

    /**
     * Answers the call on the connectionservice side in order to start audio processing.
     *
     * This pathway keeps the call in the ANSWERED state until the connection service confirms the
     * answer, at which point we'll set it to AUDIO_PROCESSING. However, to prevent any other
     * components from seeing the churn between RINGING -> ANSWERED -> AUDIO_PROCESSING, we'll
     * refrain from tracking this call in CallsManager until we've stabilized in AUDIO_PROCESSING
     */
    public void answerForAudioProcessing() {
        if (mState != CallState.RINGING) {
            Log.w(this, "Trying to audio-process a non-ringing call: id=%s", mId);
            return;
        }

        if (mConnectionService != null) {
            mConnectionService.answer(this, VideoProfile.STATE_AUDIO_ONLY);
        } else {
            Log.e(this, new NullPointerException(),
                    "answer call (audio processing) failed due to null CS callId=%s", getId());
        }

        Log.addEvent(this, LogUtils.Events.REQUEST_PICKUP_FOR_AUDIO_PROCESSING);
    }

    /**
     * Deflects the call if it is ringing.
     *
@@ -2047,9 +2089,19 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
     */
    @VisibleForTesting
    public void reject(boolean rejectWithMessage, String textMessage, String reason) {
        // Check to verify that the call is still in the ringing state. A call can change states
        // between the time the user hits 'reject' and Telecomm receives the command.
        if (isRinging("reject")) {
        if (mState == CallState.SIMULATED_RINGING) {
            // This handles the case where the user manually rejects a call that's in simulated
            // ringing. Since the call is already active on the connectionservice side, we want to
            // hangup, not reject.
            mOverrideDisconnectCauseCode = DisconnectCause.REJECTED;
            if (mConnectionService != null) {
                mConnectionService.disconnect(this);
            } else {
                Log.e(this, new NullPointerException(),
                        "reject call failed due to null CS callId=%s", getId());
            }
            Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
        } else if (isRinging("reject")) {
            // Ensure video state history tracks video state at time of rejection.
            mVideoStateHistory |= mVideoState;

+85 −1
Original line number Diff line number Diff line
@@ -252,6 +252,13 @@ public class CallsManager extends Call.ListenerBase
     * Used by {@link #onCallRedirectionComplete}.
     */
    private Call mPendingRedirectedOutgoingCall;

    /**
     * Cached call that's been answered but will be added to mCalls pending confirmation of active
     * status from the connection service.
     */
    private Call mPendingAudioProcessingCall;

    /**
     * Cached latest pending redirected call information which require user-intervention in order
     * to be placed. Used by {@link #onCallRedirectionComplete}.
@@ -683,6 +690,9 @@ public class CallsManager extends Call.ListenerBase
                Log.i(this, "onCallFilteringCompleted: setting the call to silent ringing state");
                incomingCall.setSilentRingingRequested(true);
                addCall(incomingCall);
            } else if (result.shouldScreenViaAudio) {
                Log.i(this, "onCallFilteringCompleted: starting background audio processing");
                answerCallForAudioProcessing(incomingCall);
            } else {
                addCall(incomingCall);
            }
@@ -976,6 +986,10 @@ public class CallsManager extends Call.ListenerBase
        return mEmergencyCallHelper;
    }

    public DefaultDialerCache getDefaultDialerCache() {
        return mDefaultDialerCache;
    }

    @VisibleForTesting
    public PhoneAccountRegistrar.Listener getPhoneAccountListener() {
        return mPhoneAccountListener;
@@ -1978,6 +1992,62 @@ public class CallsManager extends Call.ListenerBase
        }
    }

    private void answerCallForAudioProcessing(Call call) {
        // We don't check whether the call has been added to the internal lists yet -- it's optional
        // until the call is actually in the AUDIO_PROCESSING state.
        Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
        if (activeCall != null && activeCall != call) {
            Log.w(this, "answerCallForAudioProcessing: another active call already exists. "
                    + "Ignoring request for audio processing and letting the incoming call "
                    + "through.");
            // The call should already be in the RINGING state, so all we have to do is add the
            // call to the internal tracker.
            addCall(call);
            return;
        }
        Log.d(this, "answerCallForAudioProcessing: Incoming call = %s", call);
        mConnectionSvrFocusMgr.requestFocus(
                call,
                new RequestCallback(() -> {
                    synchronized (mLock) {
                        Log.d(this, "answering call %s for audio processing with cs focus", call);
                        call.answerForAudioProcessing();
                        // Skip setting the call state to ANSWERED -- that's only for calls that
                        // were answered by user intervention.
                        mPendingAudioProcessingCall = call;
                    }
                }));

    }

    /**
     * Instructs Telecom to bring a call out of the AUDIO_PROCESSING state.
     *
     * Used by the background audio call screener (also the default dialer) to signal that it's
     * finished doing its thing and the user should be made aware of the call.
     *
     * @param call The call to manipulate
     * @param shouldRing if true, puts the call into SIMULATED_RINGING. Otherwise, makes the call
     *                   active.
     */
    public void exitBackgroundAudioProcessing(Call call, boolean shouldRing) {
        if (!mCalls.contains(call)) {
            Log.w(this, "Trying to exit audio processing on an untracked call");
            return;
        }

        Call activeCall = getActiveCall();
        if (activeCall != null) {
            Log.w(this, "Ignoring exit audio processing because there's already a call active");
        }

        if (shouldRing) {
            setCallState(call, CallState.SIMULATED_RINGING, "exitBackgroundAudioProcessing");
        } else {
            setCallState(call, CallState.ACTIVE, "exitBackgroundAudioProcessing");
        }
    }

    /**
     * Instructs Telecom to deflect the specified call. Intended to be invoked by the in-call
     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
@@ -2483,6 +2553,15 @@ public class CallsManager extends Call.ListenerBase
                            CallState.ACTIVE,
                            "active set explicitly for self-managed")));
        } else {
            if (mPendingAudioProcessingCall == call) {
                if (mCalls.contains(call)) {
                    setCallState(call, CallState.AUDIO_PROCESSING, "active set explicitly");
                } else {
                    call.setState(CallState.AUDIO_PROCESSING, "active set explicitly and adding");
                    addCall(call);
                }
                return;
            }
            setCallState(call, CallState.ACTIVE, "active set explicitly");
            maybeMoveToSpeakerPhone(call);
            ensureCallAudible();
@@ -4554,9 +4633,14 @@ public class CallsManager extends Call.ListenerBase

                // We do not update the UI until we get confirmation of the answer() through
                // {@link #markCallAsActive}.
                mCall.answer(mVideoState);
                if (mCall.getState() == CallState.RINGING) {
                    mCall.answer(mVideoState);
                    setCallState(mCall, CallState.ANSWERED, "answered");
                } else if (mCall.getState() == CallState.SIMULATED_RINGING) {
                    // If the call's in simulated ringing, we don't have to wait for the CS --
                    // we can just declare it active.
                    setCallState(mCall, CallState.ACTIVE, "answering simulated ringing");
                    Log.addEvent(mCall, LogUtils.Events.REQUEST_SIMULATED_ACCEPT);
                }
                if (isSpeakerphoneAutoEnabledForVideoCalls(mVideoState)) {
                    mCall.setStartWithSpeakerphoneOn(true);
+1 −1
Original line number Diff line number Diff line
@@ -154,7 +154,7 @@ public class ConnectionServiceFocusManager {
    }

    private static final int[] PRIORITY_FOCUS_CALL_STATE = new int[] {
            CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING
            CallState.ACTIVE, CallState.CONNECTING, CallState.DIALING, CallState.AUDIO_PROCESSING
    };

    private static final int MSG_REQUEST_FOCUS = 1;
+16 −1
Original line number Diff line number Diff line
@@ -326,7 +326,22 @@ class InCallAdapter extends IInCallAdapter.Stub {

    @Override
    public void exitBackgroundAudioProcessing(String callId, boolean shouldRing) {
        // TODO: implement this
        try {
            Log.startSession(LogUtils.Sessions.ICA_EXIT_AUDIO_PROCESSING, mOwnerComponentName);
            Binder.withCleanCallingIdentity(() -> {
                synchronized (mLock) {
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        mCallsManager.exitBackgroundAudioProcessing(call, shouldRing);
                    } else {
                        Log.w(InCallAdapter.this,
                                "exitBackgroundAudioProcessing, unknown call id: %s", callId);
                    }
                }
            });
        } finally {
            Log.endSession();
        }
    }

    @Override
+5 −0
Original line number Diff line number Diff line
@@ -68,6 +68,8 @@ public class LogUtils {
        public static final String ICA_UNHOLD_CALL = "ICA.uC";
        public static final String ICA_MUTE = "ICA.m";
        public static final String ICA_SET_AUDIO_ROUTE = "ICA.sAR";
        public static final String ICA_ENTER_AUDIO_PROCESSING = "ICA.enBAP";
        public static final String ICA_EXIT_AUDIO_PROCESSING = "ICA.exBAP";
        public static final String ICA_CONFERENCE = "ICA.c";
        public static final String CSW_HANDLE_CREATE_CONNECTION_COMPLETE = "CSW.hCCC";
        public static final String CSW_SET_ACTIVE = "CSW.sA";
@@ -101,6 +103,9 @@ public class LogUtils {
        public static final String REQUEST_UNHOLD = "REQUEST_UNHOLD";
        public static final String REQUEST_DISCONNECT = "REQUEST_DISCONNECT";
        public static final String REQUEST_ACCEPT = "REQUEST_ACCEPT";
        public static final String REQUEST_SIMULATED_ACCEPT = "REQUEST_SIMULATED_ACCEPT";
        public static final String REQUEST_PICKUP_FOR_AUDIO_PROCESSING =
                "REQUEST_PICKUP_FOR_AUDIO_PROCESSING";
        public static final String REQUEST_DEFLECT = "REQUEST_DEFLECT";
        public static final String REQUEST_REJECT = "REQUEST_REJECT";
        public static final String START_DTMF = "START_DTMF";
Loading