| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.server.telecom; |
| |
| import android.media.AudioAttributes; |
| import android.media.AudioFocusRequest; |
| import android.media.AudioManager; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.telecom.Log; |
| import android.telecom.Logging.Runnable; |
| import android.telecom.Logging.Session; |
| import android.util.IndentingPrintWriter; |
| import android.util.LocalLog; |
| import android.util.SparseArray; |
| |
| import com.android.internal.util.IState; |
| import com.android.internal.util.State; |
| import com.android.internal.util.StateMachine; |
| import com.android.server.telecom.flags.FeatureFlags; |
| |
| public class CallAudioModeStateMachine extends StateMachine { |
| /** |
| * Captures the most recent CallAudioModeStateMachine state transitions and the corresponding |
| * changes to the {@link AudioManager#setMode}. |
| */ |
| private LocalLog mLocalLog = new LocalLog(20); |
| public static class Factory { |
| public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper, |
| AudioManager am, FeatureFlags featureFlags) { |
| return new CallAudioModeStateMachine(systemStateHelper, am, |
| featureFlags); |
| } |
| } |
| |
| public static class MessageArgs { |
| public boolean hasActiveOrDialingCalls; |
| public boolean hasRingingCalls; |
| public boolean hasHoldingCalls; |
| public boolean hasAudioProcessingCalls; |
| public boolean isTonePlaying; |
| public boolean foregroundCallIsVoip; |
| public boolean isStreaming; |
| public Session session; |
| |
| private MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls, |
| boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying, |
| boolean foregroundCallIsVoip, boolean isStreaming, Session session) { |
| this.hasActiveOrDialingCalls = hasActiveOrDialingCalls; |
| this.hasRingingCalls = hasRingingCalls; |
| this.hasHoldingCalls = hasHoldingCalls; |
| this.hasAudioProcessingCalls = hasAudioProcessingCalls; |
| this.isTonePlaying = isTonePlaying; |
| this.foregroundCallIsVoip = foregroundCallIsVoip; |
| this.isStreaming = isStreaming; |
| this.session = session; |
| } |
| |
| @Override |
| public String toString() { |
| return "MessageArgs{" + |
| "hasActiveCalls=" + hasActiveOrDialingCalls + |
| ", hasRingingCalls=" + hasRingingCalls + |
| ", hasHoldingCalls=" + hasHoldingCalls + |
| ", hasAudioProcessingCalls=" + hasAudioProcessingCalls + |
| ", isTonePlaying=" + isTonePlaying + |
| ", foregroundCallIsVoip=" + foregroundCallIsVoip + |
| ", isStreaming=" + isStreaming + |
| ", session=" + session + |
| '}'; |
| } |
| |
| public static class Builder { |
| private boolean mHasActiveOrDialingCalls; |
| private boolean mHasRingingCalls; |
| private boolean mHasHoldingCalls; |
| private boolean mHasAudioProcessingCalls; |
| private boolean mIsTonePlaying; |
| private boolean mForegroundCallIsVoip; |
| private boolean mIsStreaming; |
| private Session mSession; |
| |
| public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) { |
| mHasActiveOrDialingCalls = hasActiveOrDialingCalls; |
| return this; |
| } |
| |
| public Builder setHasRingingCalls(boolean hasRingingCalls) { |
| mHasRingingCalls = hasRingingCalls; |
| return this; |
| } |
| |
| public Builder setHasHoldingCalls(boolean hasHoldingCalls) { |
| mHasHoldingCalls = hasHoldingCalls; |
| return this; |
| } |
| |
| public Builder setHasAudioProcessingCalls(boolean hasAudioProcessingCalls) { |
| mHasAudioProcessingCalls = hasAudioProcessingCalls; |
| return this; |
| } |
| |
| public Builder setIsTonePlaying(boolean isTonePlaying) { |
| mIsTonePlaying = isTonePlaying; |
| return this; |
| } |
| |
| public Builder setForegroundCallIsVoip(boolean foregroundCallIsVoip) { |
| mForegroundCallIsVoip = foregroundCallIsVoip; |
| return this; |
| } |
| |
| public Builder setSession(Session session) { |
| mSession = session; |
| return this; |
| } |
| |
| public Builder setIsStreaming(boolean isStraeming) { |
| mIsStreaming = isStraeming; |
| return this; |
| } |
| |
| public MessageArgs build() { |
| return new MessageArgs(mHasActiveOrDialingCalls, mHasRingingCalls, mHasHoldingCalls, |
| mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip, |
| mIsStreaming, mSession); |
| } |
| } |
| } |
| |
| private static final AudioAttributes RING_AUDIO_ATTRIBUTES = new AudioAttributes.Builder() |
| .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) |
| .setLegacyStreamType(AudioManager.STREAM_RING) |
| .build(); |
| |
| /** |
| * Audio focus request used when ringing for a call. |
| */ |
| public static final AudioFocusRequest RING_AUDIO_FOCUS_REQUEST = new AudioFocusRequest |
| .Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) |
| .setAudioAttributes(RING_AUDIO_ATTRIBUTES) |
| // Important!!! Need to lock focus else other things can steal from us. |
| .setLocksFocus(true) |
| .build(); |
| |
| private static final AudioAttributes CALL_AUDIO_ATTRIBUTES = new AudioAttributes.Builder() |
| .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) |
| .setLegacyStreamType(AudioManager.STREAM_VOICE_CALL) |
| .build(); |
| |
| /** |
| * Audio focus request used while in a call. |
| */ |
| public static final AudioFocusRequest CALL_AUDIO_FOCUS_REQUEST = new AudioFocusRequest |
| .Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) |
| .setAudioAttributes(CALL_AUDIO_ATTRIBUTES) |
| // Important!!! Need to lock focus else other things can steal from us. |
| .setLocksFocus(true) |
| .build(); |
| |
| // TODO: remove this and replace when the new audio mode gets pushed to AOSP. |
| public static final int NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING = 4; |
| |
| public static final int INITIALIZE = 1; |
| // These ENTER_*_FOCUS commands are for testing. |
| public static final int ENTER_CALL_FOCUS_FOR_TESTING = 2; |
| public static final int ENTER_COMMS_FOCUS_FOR_TESTING = 3; |
| public static final int ENTER_RING_FOCUS_FOR_TESTING = 4; |
| public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5; |
| public static final int ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING = 6; |
| public static final int ENTER_STREAMING_FOCUS_FOR_TESTING = 7; |
| public static final int ABANDON_FOCUS_FOR_TESTING = 8; |
| |
| public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001; |
| public static final int NO_MORE_RINGING_CALLS = 1002; |
| public static final int NO_MORE_HOLDING_CALLS = 1003; |
| public static final int NO_MORE_AUDIO_PROCESSING_CALLS = 1004; |
| public static final int NO_MORE_LOCAL_VOICEMAIL_CALLS = 1005; |
| |
| public static final int NEW_ACTIVE_OR_DIALING_CALL = 2001; |
| public static final int NEW_RINGING_CALL = 2002; |
| public static final int NEW_HOLDING_CALL = 2003; |
| public static final int NEW_AUDIO_PROCESSING_CALL = 2004; |
| public static final int NEW_LOCAL_VOICEMAIL_CALL = 2005; |
| |
| |
| public static final int TONE_STARTED_PLAYING = 3001; |
| public static final int TONE_STOPPED_PLAYING = 3002; |
| |
| public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001; |
| |
| public static final int RINGER_MODE_CHANGE = 5001; |
| |
| public static final int CRS_FALLBACK_TO_LOCAL_RINGING = 5002; |
| |
| // Used to indicate that Telecom is done doing things to the AudioManager and that it's safe |
| // to release focus for other apps to take over. |
| public static final int AUDIO_OPERATIONS_COMPLETE = 6001; |
| |
| public static final int START_CALL_STREAMING = 7001; |
| public static final int STOP_CALL_STREAMING = 7002; |
| |
| public static final int RUN_RUNNABLE = 9001; |
| |
| private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{ |
| put(ENTER_CALL_FOCUS_FOR_TESTING, "ENTER_CALL_FOCUS_FOR_TESTING"); |
| put(ENTER_COMMS_FOCUS_FOR_TESTING, "ENTER_COMMS_FOCUS_FOR_TESTING"); |
| put(ENTER_RING_FOCUS_FOR_TESTING, "ENTER_RING_FOCUS_FOR_TESTING"); |
| put(ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, "ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING"); |
| put(ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, "ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING"); |
| put(ABANDON_FOCUS_FOR_TESTING, "ABANDON_FOCUS_FOR_TESTING"); |
| put(NO_MORE_ACTIVE_OR_DIALING_CALLS, "NO_MORE_ACTIVE_OR_DIALING_CALLS"); |
| put(NO_MORE_RINGING_CALLS, "NO_MORE_RINGING_CALLS"); |
| put(NO_MORE_HOLDING_CALLS, "NO_MORE_HOLDING_CALLS"); |
| put(NO_MORE_AUDIO_PROCESSING_CALLS, "NO_MORE_AUDIO_PROCESSING_CALLS"); |
| put(NEW_ACTIVE_OR_DIALING_CALL, "NEW_ACTIVE_OR_DIALING_CALL"); |
| put(NEW_RINGING_CALL, "NEW_RINGING_CALL"); |
| put(NEW_HOLDING_CALL, "NEW_HOLDING_CALL"); |
| put(NEW_AUDIO_PROCESSING_CALL, "NEW_AUDIO_PROCESSING_CALL"); |
| put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING"); |
| put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING"); |
| put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE"); |
| put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE"); |
| put(AUDIO_OPERATIONS_COMPLETE, "AUDIO_OPERATIONS_COMPLETE"); |
| put(START_CALL_STREAMING, "START_CALL_STREAMING"); |
| put(STOP_CALL_STREAMING, "STOP_CALL_STREAMING"); |
| put(NEW_LOCAL_VOICEMAIL_CALL, "START_LOCAL_VOICEMAIL"); |
| put(NO_MORE_LOCAL_VOICEMAIL_CALLS, "STOP_LOCAL_VOICEMAIL"); |
| put(CRS_FALLBACK_TO_LOCAL_RINGING, "CRS_FALLBACK_TO_LOCAL_RINGING"); |
| |
| put(RUN_RUNNABLE, "RUN_RUNNABLE"); |
| }}; |
| |
| public static final String TONE_HOLD_STATE_NAME = OtherFocusState.class.getSimpleName(); |
| public static final String UNFOCUSED_STATE_NAME = UnfocusedState.class.getSimpleName(); |
| public static final String AUDIO_PROCESSING_STATE_NAME = |
| AudioProcessingFocusState.class.getSimpleName(); |
| public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName(); |
| public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName(); |
| public static final String STREAMING_STATE_NAME = StreamingFocusState.class.getSimpleName(); |
| public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName(); |
| |
| /** |
| * Need to track the current ongoing audio focus request so we can abandon it later. |
| */ |
| private AudioFocusRequest mCurrentAudioFocusRequest = null; |
| |
| private class BaseState extends State { |
| @Override |
| public boolean processMessage(Message msg) { |
| switch (msg.what) { |
| case ENTER_CALL_FOCUS_FOR_TESTING: |
| transitionTo(mSimCallFocusState); |
| return HANDLED; |
| case ENTER_COMMS_FOCUS_FOR_TESTING: |
| transitionTo(mVoipCallFocusState); |
| return HANDLED; |
| case ENTER_RING_FOCUS_FOR_TESTING: |
| transitionTo(mRingingFocusState); |
| return HANDLED; |
| case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING: |
| transitionTo(mOtherFocusState); |
| return HANDLED; |
| case ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING: |
| transitionTo(mAudioProcessingFocusState); |
| return HANDLED; |
| case ENTER_STREAMING_FOCUS_FOR_TESTING: |
| transitionTo(mStreamingFocusState); |
| return HANDLED; |
| case ABANDON_FOCUS_FOR_TESTING: |
| transitionTo(mUnfocusedState); |
| return HANDLED; |
| case INITIALIZE: |
| mIsInitialized = true; |
| return HANDLED; |
| case RUN_RUNNABLE: |
| java.lang.Runnable r = (java.lang.Runnable) msg.obj; |
| r.run(); |
| return HANDLED; |
| default: |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private class UnfocusedState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering UNFOCUSED state"); |
| mLocalLog.log("Enter UNFOCUSED"); |
| if (mIsInitialized) { |
| Log.i(this, "enter: AudioManager#setMode(MODE_NORMAL)"); |
| setMode(AudioManager.MODE_NORMAL); |
| mCallAudioManager.setCallAudioRouteFocusState( |
| CallAudioRouteController.NO_FOCUS); |
| mLocalLog.log("Mode MODE_NORMAL"); |
| mMostRecentMode = AudioManager.MODE_NORMAL; |
| // Don't release focus here -- wait until we get a signal that any other audio |
| // operations triggered by this are done before releasing focus. |
| } |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_AUDIO_PROCESSING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| transitionTo(args.foregroundCallIsVoip |
| ? mVoipCallFocusState : mSimCallFocusState); |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| transitionTo(mRingingFocusState); |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| transitionTo(mAudioProcessingFocusState); |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // This really shouldn't happen, but transition to the focused state anyway. |
| Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." + |
| " Args are: \n" + args.toString()); |
| transitionTo(mOtherFocusState); |
| return HANDLED; |
| case START_CALL_STREAMING: |
| transitionTo(mStreamingFocusState); |
| return HANDLED; |
| case NEW_LOCAL_VOICEMAIL_CALL: |
| transitionTo(mLocalVoicemailFocusState); |
| return HANDLED; |
| case TONE_STARTED_PLAYING: |
| // This shouldn't happen either, but perform the action anyway. |
| Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n" |
| + args.toString()); |
| return HANDLED; |
| case AUDIO_OPERATIONS_COMPLETE: |
| abandonAudioFocus(); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private class AudioProcessingFocusState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering AUDIO_PROCESSING state"); |
| mLocalLog.log("Enter AUDIO_PROCESSING"); |
| if (mIsInitialized) { |
| mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteController.NO_FOCUS); |
| Log.i(this, "enter: AudioManager#setMode(MODE_AUDIO_PROCESSING)"); |
| setMode(NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING); |
| mLocalLog.log("Mode MODE_CALL_SCREENING"); |
| mMostRecentMode = NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING; |
| } |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_AUDIO_PROCESSING_CALLS: |
| BaseState destState = calculateProperStateFromArgs(args); |
| if (destState == this) { |
| Log.w(LOG_TAG, "Got spurious NO_MORE_AUDIO_PROCESSING_CALLS"); |
| } |
| transitionTo(destState); |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| transitionTo(args.foregroundCallIsVoip |
| ? mVoipCallFocusState : mSimCallFocusState); |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| transitionTo(mRingingFocusState); |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // This really shouldn't happen, but recalculate from args and do the transition |
| Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." + |
| " Args are: \n" + args.toString()); |
| transitionTo(mOtherFocusState); |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| // Can happen as a duplicate message |
| return HANDLED; |
| case TONE_STARTED_PLAYING: |
| // This shouldn't happen either, but perform the action anyway. |
| Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n" |
| + args.toString()); |
| return HANDLED; |
| case START_CALL_STREAMING: |
| transitionTo(mStreamingFocusState); |
| return HANDLED; |
| case NEW_LOCAL_VOICEMAIL_CALL: |
| transitionTo(mLocalVoicemailFocusState); |
| return HANDLED; |
| case AUDIO_OPERATIONS_COMPLETE: |
| Log.i(LOG_TAG, "AudioManager#abandonAudioFocusRequest: now " |
| + "AUDIO_PROCESSING"); |
| abandonAudioFocus(); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private class RingingFocusState extends BaseState { |
| // Keeps track of whether we're ringing with audio focus or if we've just entered the state |
| // without acquiring focus because of a silent ringtone or something. |
| private boolean mHasFocus = false; |
| |
| private void tryStartRinging() { |
| if (mHasFocus && mCallAudioManager.isRingtonePlaying()) { |
| Log.i(LOG_TAG, |
| "RingingFocusState#tryStartRinging -- audio focus previously" |
| + " acquired and ringtone already playing -- skipping."); |
| return; |
| } |
| |
| // Note: startRinging will take DND into account; if a call is suppressed by DND, |
| // the method will return false and we will not get audio focus. |
| if (mCallAudioManager.startRinging()) { |
| |
| mCurrentAudioFocusRequest = RING_AUDIO_FOCUS_REQUEST; |
| int focusResult = mAudioManager.requestAudioFocus(RING_AUDIO_FOCUS_REQUEST, |
| null /* no policy */); |
| Log.i(this, "tryStartRinging: AudioManager#requestAudioFocus(RING)=%s", |
| audioFocusRequestResultToString(focusResult)); |
| |
| // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode -- |
| // this trips up the audio system. |
| if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) { |
| if (mCallAudioManager.isCrsInCallMode() |
| && mCallAudioManager.getCrsAudioController() != null) { |
| mCallAudioManager.getCrsAudioController().setAudioModeForCrs(); |
| mLocalLog.log("Mode MODE_IN_CALL , It is CRS CALL"); |
| } else { |
| setMode(AudioManager.MODE_RINGTONE); |
| mLocalLog.log("Mode MODE_RINGTONE"); |
| } |
| } |
| mCallAudioManager.setCallAudioRouteFocusState( |
| CallAudioRouteController.RINGING_FOCUS); |
| mHasFocus = true; |
| } else { |
| Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus"); |
| } |
| } |
| |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering RINGING state"); |
| mLocalLog.log("Enter RINGING"); |
| tryStartRinging(); |
| mCallAudioManager.stopCallWaiting(); |
| } |
| |
| @Override |
| public void exit() { |
| // Audio mode and audio stream will be set by the next state. |
| if (mCallAudioManager.getCrsAudioController() != null) { |
| mCallAudioManager.getCrsAudioController().removeListener(); |
| } |
| mCallAudioManager.stopRinging(); |
| mHasFocus = false; |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Do nothing. Loss of an active call should not impact ringer. |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| // Do nothing and keep ringing. |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| BaseState destState = calculateProperStateFromArgs(args); |
| if (destState == this) { |
| Log.w(LOG_TAG, "Got spurious NO_MORE_RINGING_CALLS"); |
| } |
| transitionTo(destState); |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| // If a call becomes active suddenly, give it priority over ringing. |
| transitionTo(args.foregroundCallIsVoip |
| ? mVoipCallFocusState : mSimCallFocusState); |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| // If we don't have any more ringing calls, transition to audio processing. |
| if (!args.hasRingingCalls) { |
| transitionTo(mAudioProcessingFocusState); |
| } else { |
| Log.w(LOG_TAG, "Got a audio processing call while there's still a call " |
| + "ringing"); |
| } |
| case NEW_RINGING_CALL: |
| // Can happen as a duplicate message |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // This really shouldn't happen, but transition to the focused state anyway. |
| Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." + |
| " Args are: " + args.toString()); |
| transitionTo(mOtherFocusState); |
| return HANDLED; |
| case RINGER_MODE_CHANGE: { |
| Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE"); |
| tryStartRinging(); |
| return HANDLED; |
| } |
| case AUDIO_OPERATIONS_COMPLETE: |
| Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" |
| + " state"); |
| return HANDLED; |
| case CRS_FALLBACK_TO_LOCAL_RINGING: |
| Log.i(LOG_TAG, "RINGING state, received CRS_FALLBACK_TO_LOCAL_RINGING"); |
| //Ringing call changed, so stop current ring first. |
| mCallAudioManager.stopRinging(); |
| tryStartRinging(); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private class SimCallFocusState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering SIM CALL state"); |
| mLocalLog.log("Enter SIM_CALL"); |
| Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)"); |
| mCurrentAudioFocusRequest = CALL_AUDIO_FOCUS_REQUEST; |
| int focusResult = mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST, |
| null /* no policy */); |
| Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)=%s", |
| audioFocusRequestResultToString(focusResult)); |
| |
| Log.i(this, "enter: AudioManager#setMode(MODE_IN_CALL)"); |
| setMode(AudioManager.MODE_IN_CALL); |
| mLocalLog.log("Mode MODE_IN_CALL"); |
| mMostRecentMode = AudioManager.MODE_IN_CALL; |
| mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteController.ACTIVE_FOCUS); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Switch to either ringing, holding, or inactive |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // Don't transition state, but stop any call-waiting tones that may have been |
| // playing. |
| if (args.isTonePlaying) { |
| mCallAudioManager.stopCallWaiting(); |
| } |
| // If a MT-audio-speedup call gets disconnected by the connection service |
| // concurrently with the user answering it, we may get this message |
| // indicating that a ringing call has disconnected while this state machine |
| // is in the SimCallFocusState. |
| if (!args.hasActiveOrDialingCalls) { |
| transitionTo(calculateProperStateFromArgs(args)); |
| } |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| if (args.foregroundCallIsVoip) { |
| transitionTo(mVoipCallFocusState); |
| } |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| if (args.foregroundCallIsVoip) { |
| transitionTo(mVoipCallFocusState); |
| } |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| // Don't make a call ring over an active call, but do play a call waiting tone. |
| mCallAudioManager.startCallWaiting("call already active"); |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // Just check the voip mode. Putting an active call on hold will be handled when |
| // NO_MORE_ACTIVE_CALLS is processed. |
| if (args.foregroundCallIsVoip) { |
| transitionTo(mVoipCallFocusState); |
| } |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| // If we don't have any more active calls, transition to audio processing. |
| if (!args.hasActiveOrDialingCalls) { |
| transitionTo(mAudioProcessingFocusState); |
| } else { |
| Log.w(LOG_TAG, "Got a audio processing call while there's still a call " |
| + "active"); |
| } |
| case FOREGROUND_VOIP_MODE_CHANGE: |
| if (args.foregroundCallIsVoip) { |
| transitionTo(mVoipCallFocusState); |
| } |
| return HANDLED; |
| case AUDIO_OPERATIONS_COMPLETE: |
| Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" |
| + " state"); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private class VoipCallFocusState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering VOIP CALL state"); |
| mLocalLog.log("Enter VOIP_CALL"); |
| Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)"); |
| mCurrentAudioFocusRequest = CALL_AUDIO_FOCUS_REQUEST; |
| int focusResult = mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST, |
| null /* no policy */); |
| Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)=%s", |
| audioFocusRequestResultToString(focusResult)); |
| setMode(AudioManager.MODE_IN_COMMUNICATION); |
| mLocalLog.log("Mode MODE_IN_COMMUNICATION"); |
| mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION; |
| mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteController.ACTIVE_FOCUS); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Switch to either ringing, holding, or inactive |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // Don't transition state, but stop any call-waiting tones that may have been |
| // playing. |
| if (args.isTonePlaying) { |
| mCallAudioManager.stopCallWaiting(); |
| } |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| if (!args.foregroundCallIsVoip) { |
| transitionTo(mSimCallFocusState); |
| } |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| if (!args.foregroundCallIsVoip) { |
| transitionTo(mSimCallFocusState); |
| } |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| // Don't make a call ring over an active call, but do play a call waiting tone. |
| mCallAudioManager.startCallWaiting("call already active"); |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // Just check the voip mode. Putting an active call on hold will be handled when |
| // NO_MORE_ACTIVE_CALLS is processed. |
| if (!args.foregroundCallIsVoip) { |
| transitionTo(mSimCallFocusState); |
| } |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| // If we don't have any more active calls, transition to audio processing. |
| if (!args.hasActiveOrDialingCalls) { |
| transitionTo(mAudioProcessingFocusState); |
| } else { |
| Log.w(LOG_TAG, "Got a audio processing call while there's still a call " |
| + "active"); |
| } |
| case FOREGROUND_VOIP_MODE_CHANGE: |
| if (!args.foregroundCallIsVoip) { |
| transitionTo(mSimCallFocusState); |
| } |
| return HANDLED; |
| case AUDIO_OPERATIONS_COMPLETE: |
| Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" |
| + " state"); |
| return HANDLED; |
| case START_CALL_STREAMING: |
| transitionTo(mStreamingFocusState); |
| return HANDLED; |
| case NEW_LOCAL_VOICEMAIL_CALL: |
| transitionTo(mLocalVoicemailFocusState); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| /** |
| * This state is used when there is a call which is undergoing local voicemail processing via |
| * a {@link LocalVoicemailService}. |
| */ |
| private class LocalVoicemailFocusState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering local voicemail state"); |
| mLocalLog.log("Enter local voicemail"); |
| mLocalLog.log("Mode MODE_CALL_REDIRECT"); |
| Log.i(this, "enter: AudioManager#setMode(MODE_CALL_REDIRECT"); |
| setMode(AudioManager.MODE_CALL_REDIRECT); |
| mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteController.ACTIVE_FOCUS); |
| } |
| |
| private void preExit() { |
| // N/A at the moment |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Switch to either ringing, holding, or inactive |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_AUDIO_PROCESSING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| // Only possible for emergency call |
| BaseState destState = calculateProperStateFromArgs(args); |
| if (destState != this) { |
| preExit(); |
| transitionTo(destState); |
| } |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| // Only possible for emergency call |
| preExit(); |
| transitionTo(mRingingFocusState); |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // Do nothing. |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| // Do nothing. |
| return HANDLED; |
| case START_CALL_STREAMING: |
| // You can't go from local voicemail to streaming. |
| return HANDLED; |
| case NEW_LOCAL_VOICEMAIL_CALL: |
| return HANDLED; |
| case TONE_STARTED_PLAYING: |
| // Do nothing. |
| return HANDLED; |
| case STOP_CALL_STREAMING: |
| // Not possible. |
| return HANDLED; |
| case NO_MORE_LOCAL_VOICEMAIL_CALLS: |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private class StreamingFocusState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering streaming state"); |
| mLocalLog.log("Enter Streaming"); |
| mLocalLog.log("Mode MODE_COMMUNICATION_REDIRECT"); |
| Log.i(this, "enter: AudioManager#setMode(MODE_COMMUNICATION_REDIRECT"); |
| setMode(AudioManager.MODE_COMMUNICATION_REDIRECT); |
| mMostRecentMode = AudioManager.MODE_NORMAL; |
| mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteController.ACTIVE_FOCUS); |
| mCallAudioManager.getCallAudioRouteAdapter().sendMessageWithSessionInfo( |
| CallAudioRouteController.STREAMING_FORCE_ENABLED); |
| } |
| |
| private void preExit() { |
| mCallAudioManager.getCallAudioRouteAdapter().sendMessageWithSessionInfo( |
| CallAudioRouteController.STREAMING_FORCE_DISABLED); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_ACTIVE_OR_DIALING_CALLS: |
| // Switch to either ringing, holding, or inactive |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_HOLDING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_AUDIO_PROCESSING_CALLS: |
| // Do nothing. |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| // Only possible for emergency call |
| BaseState destState = calculateProperStateFromArgs(args); |
| if (destState != this) { |
| preExit(); |
| transitionTo(destState); |
| } |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| // Only possible for emergency call |
| preExit(); |
| transitionTo(mRingingFocusState); |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // Do nothing. |
| return HANDLED; |
| case NEW_AUDIO_PROCESSING_CALL: |
| // Do nothing. |
| return HANDLED; |
| case START_CALL_STREAMING: |
| // Can happen as a duplicate message |
| return HANDLED; |
| case NEW_LOCAL_VOICEMAIL_CALL: |
| // Do nothing; this can't happen. |
| return HANDLED; |
| case TONE_STARTED_PLAYING: |
| // Do nothing. |
| return HANDLED; |
| case STOP_CALL_STREAMING: |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| default: |
| // The forced focus switch commands are handled by BaseState. |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| /** |
| * This class is used for calls on hold and end-of-call tones. |
| */ |
| private class OtherFocusState extends BaseState { |
| @Override |
| public void enter() { |
| Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state"); |
| mLocalLog.log("Enter TONE/HOLDING"); |
| mCurrentAudioFocusRequest = CALL_AUDIO_FOCUS_REQUEST; |
| int focusResult = mAudioManager.requestAudioFocus(CALL_AUDIO_FOCUS_REQUEST, |
| null /* no policy */); |
| Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)=%s", |
| audioFocusRequestResultToString(focusResult)); |
| Log.i(this, "enter: AudioManager#setMode(%d)", mMostRecentMode); |
| setMode(mMostRecentMode); |
| mLocalLog.log("Mode " + mMostRecentMode); |
| mCallAudioManager.setCallAudioRouteFocusStateForEndTone(); |
| } |
| |
| @Override |
| public boolean processMessage(Message msg) { |
| if (super.processMessage(msg) == HANDLED) { |
| return HANDLED; |
| } |
| MessageArgs args = (MessageArgs) msg.obj; |
| switch (msg.what) { |
| case NO_MORE_HOLDING_CALLS: |
| if (args.hasActiveOrDialingCalls) { |
| transitionTo(args.foregroundCallIsVoip |
| ? mVoipCallFocusState : mSimCallFocusState); |
| } else if (args.hasRingingCalls) { |
| transitionTo(mRingingFocusState); |
| } else if (!args.isTonePlaying) { |
| transitionTo(mUnfocusedState); |
| } |
| // Do nothing if a tone is playing. |
| return HANDLED; |
| case NEW_ACTIVE_OR_DIALING_CALL: |
| transitionTo(args.foregroundCallIsVoip |
| ? mVoipCallFocusState : mSimCallFocusState); |
| return HANDLED; |
| case NEW_RINGING_CALL: |
| // TODO: consider whether to move this into MessageArgs if more things start |
| // to use it. |
| if (args.hasHoldingCalls && mSystemStateHelper.isDeviceAtEar()) { |
| mCallAudioManager.startCallWaiting( |
| "Device is at ear with held call"); |
| } else { |
| transitionTo(mRingingFocusState); |
| } |
| return HANDLED; |
| case NEW_HOLDING_CALL: |
| // Do nothing. |
| return HANDLED; |
| case NO_MORE_RINGING_CALLS: |
| // If there are no more ringing calls in this state, then stop any call-waiting |
| // tones that may be playing. |
| mCallAudioManager.stopCallWaiting(); |
| return HANDLED; |
| case TONE_STOPPED_PLAYING: |
| transitionTo(calculateProperStateFromArgs(args)); |
| return HANDLED; |
| case AUDIO_OPERATIONS_COMPLETE: |
| Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" |
| + " state"); |
| return HANDLED; |
| default: |
| return NOT_HANDLED; |
| } |
| } |
| } |
| |
| private static final String LOG_TAG = CallAudioModeStateMachine.class.getSimpleName(); |
| |
| private final BaseState mUnfocusedState = new UnfocusedState(); |
| private final BaseState mRingingFocusState = new RingingFocusState(); |
| private final BaseState mSimCallFocusState = new SimCallFocusState(); |
| private final BaseState mVoipCallFocusState = new VoipCallFocusState(); |
| private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState(); |
| private final BaseState mStreamingFocusState = new StreamingFocusState(); |
| private final BaseState mLocalVoicemailFocusState = new LocalVoicemailFocusState(); |
| private final BaseState mOtherFocusState = new OtherFocusState(); |
| |
| private final AudioManager mAudioManager; |
| private final SystemStateHelper mSystemStateHelper; |
| private CallAudioManager mCallAudioManager; |
| private FeatureFlags mFeatureFlags; |
| |
| private int mMostRecentMode; |
| private boolean mIsInitialized = false; |
| |
| public CallAudioModeStateMachine(SystemStateHelper systemStateHelper, |
| AudioManager audioManager, FeatureFlags featureFlags) { |
| super(CallAudioModeStateMachine.class.getSimpleName()); |
| mAudioManager = audioManager; |
| mSystemStateHelper = systemStateHelper; |
| mMostRecentMode = AudioManager.MODE_NORMAL; |
| mFeatureFlags = featureFlags; |
| createStates(); |
| } |
| |
| /** |
| * Used for testing |
| */ |
| public CallAudioModeStateMachine(SystemStateHelper systemStateHelper, |
| AudioManager audioManager, Looper looper, FeatureFlags featureFlags) { |
| super(CallAudioModeStateMachine.class.getSimpleName(), looper); |
| mAudioManager = audioManager; |
| mSystemStateHelper = systemStateHelper; |
| mMostRecentMode = AudioManager.MODE_NORMAL; |
| mFeatureFlags = featureFlags; |
| |
| createStates(); |
| } |
| |
| private void createStates() { |
| addState(mUnfocusedState); |
| addState(mRingingFocusState); |
| addState(mSimCallFocusState); |
| addState(mVoipCallFocusState); |
| addState(mAudioProcessingFocusState); |
| addState(mStreamingFocusState); |
| addState(mLocalVoicemailFocusState); |
| addState(mOtherFocusState); |
| setInitialState(mUnfocusedState); |
| start(); |
| sendMessage(INITIALIZE, new MessageArgs.Builder() |
| .setHasActiveOrDialingCalls(false) |
| .setHasRingingCalls(false) |
| .setHasHoldingCalls(false) |
| .setIsTonePlaying(false) |
| .setForegroundCallIsVoip(false) |
| .setIsStreaming(false) |
| .setSession(Log.createSubsession()) |
| .build()); |
| } |
| |
| public void setCallAudioManager(CallAudioManager callAudioManager) { |
| mCallAudioManager = callAudioManager; |
| } |
| |
| public String getCurrentStateName() { |
| IState currentState = getCurrentState(); |
| return currentState == null ? "no state" : currentState.getName(); |
| } |
| |
| public void sendMessageWithArgs(int messageCode, MessageArgs args) { |
| sendMessage(messageCode, args); |
| } |
| |
| @Override |
| protected void onPreHandleMessage(Message msg) { |
| if (msg.obj != null && msg.obj instanceof MessageArgs) { |
| Log.continueSession(((MessageArgs) msg.obj).session, "CAMSM.pM_" + msg.what); |
| Log.i(LOG_TAG, "Message received: %s.", MESSAGE_CODE_TO_NAME.get(msg.what)); |
| } else if (msg.what == RUN_RUNNABLE && msg.obj instanceof Runnable) { |
| Log.i(LOG_TAG, "Running runnable for testing"); |
| } else { |
| Log.w(LOG_TAG, "Message sent must be of type nonnull MessageArgs, but got " + |
| (msg.obj == null ? "null" : msg.obj.getClass().getSimpleName())); |
| Log.w(LOG_TAG, "The message was of code %d = %s", |
| msg.what, MESSAGE_CODE_TO_NAME.get(msg.what)); |
| } |
| } |
| |
| public void dumpPendingMessages(IndentingPrintWriter pw) { |
| getHandler().getLooper().dump(pw::println, ""); |
| } |
| |
| public void dump(IndentingPrintWriter pw) { |
| pw.println("History:"); |
| mLocalLog.dump(pw); |
| pw.println("Pending Msg:"); |
| dumpPendingMessages(pw); |
| } |
| |
| @Override |
| protected void onPostHandleMessage(Message msg) { |
| Log.endSession(); |
| } |
| |
| private BaseState calculateProperStateFromArgs(MessageArgs args) { |
| // If there are active, audio-processing, holding, or ringing calls, |
| // switch to the appropriate focus. |
| // Otherwise abandon focus. |
| |
| // The order matters here. If there is streaming call, holding streaming route for them |
| // takes priority. After that, holding focus for active calls takes priority. After that, we |
| // want to prioritize holding calls over ringing calls so that when a call-waiting call gets |
| // answered, there's no transition in and out of the ringing focus state. After that, we |
| // want tones since we actually hold focus during them, then the audio processing state |
| // because that will release focus. |
| if (args.isStreaming) { |
| return mSimCallFocusState; |
| } else if (args.hasActiveOrDialingCalls) { |
| if (args.foregroundCallIsVoip) { |
| return mVoipCallFocusState; |
| } else { |
| return mSimCallFocusState; |
| } |
| } else if (args.hasHoldingCalls) { |
| return mOtherFocusState; |
| } else if (args.hasRingingCalls) { |
| return mRingingFocusState; |
| } else if (args.isTonePlaying) { |
| return mOtherFocusState; |
| } else if (args.hasAudioProcessingCalls) { |
| return mAudioProcessingFocusState; |
| } |
| return mUnfocusedState; |
| } |
| |
| /** |
| * Abandons the current audio focus request. |
| */ |
| private void abandonAudioFocus() { |
| if (mCurrentAudioFocusRequest != null) { |
| Log.i(this, "abandonAudioFocus: " |
| + "AudioManager#abandonAudioFocusRequest(); now unfocused"); |
| mAudioManager.abandonAudioFocusRequest(mCurrentAudioFocusRequest); |
| mCurrentAudioFocusRequest = null; |
| } else { |
| Log.i(this, "abandonAudioFocus: already unfocused"); |
| } |
| } |
| |
| private void setMode(int mode) { |
| if (!com.android.internal.telecom.flags.Flags.callAudioRouteRf()) { |
| mAudioManager.setMode(mode); |
| } else { |
| mCallAudioManager.setAudioMode(mode); |
| } |
| } |
| |
| private String audioFocusRequestResultToString(int audioFocusRequest) { |
| switch (audioFocusRequest) { |
| case AudioManager.AUDIOFOCUS_REQUEST_FAILED: |
| return "AUDIOFOCUS_REQUEST_FAILED"; |
| case AudioManager.AUDIOFOCUS_REQUEST_GRANTED: |
| return "AUDIOFOCUS_REQUEST_GRANTED"; |
| case AudioManager.AUDIOFOCUS_REQUEST_DELAYED: |
| return "AUDIOFOCUS_REQUEST_DELAYED"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| } |