Loading src/com/android/server/telecom/Call.java +55 −3 Original line number Diff line number Diff line Loading @@ -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(); /** Loading Loading @@ -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; } Loading Loading @@ -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."); Loading Loading @@ -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. * Loading Loading @@ -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; Loading src/com/android/server/telecom/CallsManager.java +85 −1 Original line number Diff line number Diff line Loading @@ -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}. Loading Loading @@ -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); } Loading Loading @@ -976,6 +986,10 @@ public class CallsManager extends Call.ListenerBase return mEmergencyCallHelper; } public DefaultDialerCache getDefaultDialerCache() { return mDefaultDialerCache; } @VisibleForTesting public PhoneAccountRegistrar.Listener getPhoneAccountListener() { return mPhoneAccountListener; Loading Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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); Loading src/com/android/server/telecom/ConnectionServiceFocusManager.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading src/com/android/server/telecom/InCallAdapter.java +16 −1 Original line number Diff line number Diff line Loading @@ -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 Loading src/com/android/server/telecom/LogUtils.java +5 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 Loading
src/com/android/server/telecom/Call.java +55 −3 Original line number Diff line number Diff line Loading @@ -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(); /** Loading Loading @@ -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; } Loading Loading @@ -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."); Loading Loading @@ -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. * Loading Loading @@ -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; Loading
src/com/android/server/telecom/CallsManager.java +85 −1 Original line number Diff line number Diff line Loading @@ -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}. Loading Loading @@ -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); } Loading Loading @@ -976,6 +986,10 @@ public class CallsManager extends Call.ListenerBase return mEmergencyCallHelper; } public DefaultDialerCache getDefaultDialerCache() { return mDefaultDialerCache; } @VisibleForTesting public PhoneAccountRegistrar.Listener getPhoneAccountListener() { return mPhoneAccountListener; Loading Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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); Loading
src/com/android/server/telecom/ConnectionServiceFocusManager.java +1 −1 Original line number Diff line number Diff line Loading @@ -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; Loading
src/com/android/server/telecom/InCallAdapter.java +16 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
src/com/android/server/telecom/LogUtils.java +5 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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