• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.telecom;
18 
19 import android.media.AudioAttributes;
20 import android.media.AudioManager;
21 import android.os.Looper;
22 import android.os.Message;
23 import android.telecom.Log;
24 import android.telecom.Logging.Runnable;
25 import android.telecom.Logging.Session;
26 import android.util.LocalLog;
27 import android.util.SparseArray;
28 import com.android.internal.util.IState;
29 import com.android.internal.util.IndentingPrintWriter;
30 import com.android.internal.util.State;
31 import com.android.internal.util.StateMachine;
32 import com.android.server.telecom.flags.FeatureFlags;
33 
34 public class CallAudioModeStateMachine extends StateMachine {
35     /**
36      * Captures the most recent CallAudioModeStateMachine state transitions and the corresponding
37      * changes to the {@link AudioManager#setMode}.
38      */
39     private LocalLog mLocalLog = new LocalLog(20);
40     public static class Factory {
create(SystemStateHelper systemStateHelper, AudioManager am, FeatureFlags featureFlags, CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker)41         public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper,
42                 AudioManager am, FeatureFlags featureFlags,
43                 CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker) {
44             return new CallAudioModeStateMachine(systemStateHelper, am,
45                     featureFlags, callAudioCommunicationDeviceTracker);
46         }
47     }
48 
49     public static class MessageArgs {
50         public boolean hasActiveOrDialingCalls;
51         public boolean hasRingingCalls;
52         public boolean hasHoldingCalls;
53         public boolean hasAudioProcessingCalls;
54         public boolean isTonePlaying;
55         public boolean foregroundCallIsVoip;
56         public boolean isStreaming;
57         public Session session;
58 
MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls, boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip, boolean isStreaming, Session session)59         private MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
60                 boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying,
61                 boolean foregroundCallIsVoip, boolean isStreaming, Session session) {
62             this.hasActiveOrDialingCalls = hasActiveOrDialingCalls;
63             this.hasRingingCalls = hasRingingCalls;
64             this.hasHoldingCalls = hasHoldingCalls;
65             this.hasAudioProcessingCalls = hasAudioProcessingCalls;
66             this.isTonePlaying = isTonePlaying;
67             this.foregroundCallIsVoip = foregroundCallIsVoip;
68             this.isStreaming = isStreaming;
69             this.session = session;
70         }
71 
72         @Override
toString()73         public String toString() {
74             return "MessageArgs{" +
75                     "hasActiveCalls=" + hasActiveOrDialingCalls +
76                     ", hasRingingCalls=" + hasRingingCalls +
77                     ", hasHoldingCalls=" + hasHoldingCalls +
78                     ", hasAudioProcessingCalls=" + hasAudioProcessingCalls +
79                     ", isTonePlaying=" + isTonePlaying +
80                     ", foregroundCallIsVoip=" + foregroundCallIsVoip +
81                     ", isStreaming=" + isStreaming +
82                     ", session=" + session +
83                     '}';
84         }
85 
86         public static class Builder {
87             private boolean mHasActiveOrDialingCalls;
88             private boolean mHasRingingCalls;
89             private boolean mHasHoldingCalls;
90             private boolean mHasAudioProcessingCalls;
91             private boolean mIsTonePlaying;
92             private boolean mForegroundCallIsVoip;
93             private boolean mIsStreaming;
94             private Session mSession;
95 
setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls)96             public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) {
97                 mHasActiveOrDialingCalls = hasActiveOrDialingCalls;
98                 return this;
99             }
100 
setHasRingingCalls(boolean hasRingingCalls)101             public Builder setHasRingingCalls(boolean hasRingingCalls) {
102                 mHasRingingCalls = hasRingingCalls;
103                 return this;
104             }
105 
setHasHoldingCalls(boolean hasHoldingCalls)106             public Builder setHasHoldingCalls(boolean hasHoldingCalls) {
107                 mHasHoldingCalls = hasHoldingCalls;
108                 return this;
109             }
110 
setHasAudioProcessingCalls(boolean hasAudioProcessingCalls)111             public Builder setHasAudioProcessingCalls(boolean hasAudioProcessingCalls) {
112                 mHasAudioProcessingCalls = hasAudioProcessingCalls;
113                 return this;
114             }
115 
setIsTonePlaying(boolean isTonePlaying)116             public Builder setIsTonePlaying(boolean isTonePlaying) {
117                 mIsTonePlaying = isTonePlaying;
118                 return this;
119             }
120 
setForegroundCallIsVoip(boolean foregroundCallIsVoip)121             public Builder setForegroundCallIsVoip(boolean foregroundCallIsVoip) {
122                 mForegroundCallIsVoip = foregroundCallIsVoip;
123                 return this;
124             }
125 
setSession(Session session)126             public Builder setSession(Session session) {
127                 mSession = session;
128                 return this;
129             }
130 
setIsStreaming(boolean isStraeming)131             public Builder setIsStreaming(boolean isStraeming) {
132                 mIsStreaming = isStraeming;
133                 return this;
134             }
135 
build()136             public MessageArgs build() {
137                 return new MessageArgs(mHasActiveOrDialingCalls, mHasRingingCalls, mHasHoldingCalls,
138                         mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip,
139                         mIsStreaming, mSession);
140             }
141         }
142     }
143 
144     // TODO: remove this and replace when the new audio mode gets pushed to AOSP.
145     public static final int NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING = 4;
146 
147     public static final int INITIALIZE = 1;
148     // These ENTER_*_FOCUS commands are for testing.
149     public static final int ENTER_CALL_FOCUS_FOR_TESTING = 2;
150     public static final int ENTER_COMMS_FOCUS_FOR_TESTING = 3;
151     public static final int ENTER_RING_FOCUS_FOR_TESTING = 4;
152     public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5;
153     public static final int ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING = 6;
154     public static final int ENTER_STREAMING_FOCUS_FOR_TESTING = 7;
155     public static final int ABANDON_FOCUS_FOR_TESTING = 8;
156 
157     public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001;
158     public static final int NO_MORE_RINGING_CALLS = 1002;
159     public static final int NO_MORE_HOLDING_CALLS = 1003;
160     public static final int NO_MORE_AUDIO_PROCESSING_CALLS = 1004;
161 
162     public static final int NEW_ACTIVE_OR_DIALING_CALL = 2001;
163     public static final int NEW_RINGING_CALL = 2002;
164     public static final int NEW_HOLDING_CALL = 2003;
165     public static final int NEW_AUDIO_PROCESSING_CALL = 2004;
166 
167     public static final int TONE_STARTED_PLAYING = 3001;
168     public static final int TONE_STOPPED_PLAYING = 3002;
169 
170     public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001;
171 
172     public static final int RINGER_MODE_CHANGE = 5001;
173 
174     // Used to indicate that Telecom is done doing things to the AudioManager and that it's safe
175     // to release focus for other apps to take over.
176     public static final int AUDIO_OPERATIONS_COMPLETE = 6001;
177 
178     public static final int START_CALL_STREAMING = 7001;
179     public static final int STOP_CALL_STREAMING = 7002;
180 
181     public static final int RUN_RUNNABLE = 9001;
182 
183     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
184         put(ENTER_CALL_FOCUS_FOR_TESTING, "ENTER_CALL_FOCUS_FOR_TESTING");
185         put(ENTER_COMMS_FOCUS_FOR_TESTING, "ENTER_COMMS_FOCUS_FOR_TESTING");
186         put(ENTER_RING_FOCUS_FOR_TESTING, "ENTER_RING_FOCUS_FOR_TESTING");
187         put(ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, "ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING");
188         put(ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, "ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING");
189         put(ABANDON_FOCUS_FOR_TESTING, "ABANDON_FOCUS_FOR_TESTING");
190         put(NO_MORE_ACTIVE_OR_DIALING_CALLS, "NO_MORE_ACTIVE_OR_DIALING_CALLS");
191         put(NO_MORE_RINGING_CALLS, "NO_MORE_RINGING_CALLS");
192         put(NO_MORE_HOLDING_CALLS, "NO_MORE_HOLDING_CALLS");
193         put(NO_MORE_AUDIO_PROCESSING_CALLS, "NO_MORE_AUDIO_PROCESSING_CALLS");
194         put(NEW_ACTIVE_OR_DIALING_CALL, "NEW_ACTIVE_OR_DIALING_CALL");
195         put(NEW_RINGING_CALL, "NEW_RINGING_CALL");
196         put(NEW_HOLDING_CALL, "NEW_HOLDING_CALL");
197         put(NEW_AUDIO_PROCESSING_CALL, "NEW_AUDIO_PROCESSING_CALL");
198         put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING");
199         put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING");
200         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
201         put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE");
202         put(AUDIO_OPERATIONS_COMPLETE, "AUDIO_OPERATIONS_COMPLETE");
203         put(START_CALL_STREAMING, "START_CALL_STREAMING");
204         put(STOP_CALL_STREAMING, "STOP_CALL_STREAMING");
205 
206         put(RUN_RUNNABLE, "RUN_RUNNABLE");
207     }};
208 
209     public static final String TONE_HOLD_STATE_NAME = OtherFocusState.class.getSimpleName();
210     public static final String UNFOCUSED_STATE_NAME = UnfocusedState.class.getSimpleName();
211     public static final String AUDIO_PROCESSING_STATE_NAME =
212             AudioProcessingFocusState.class.getSimpleName();
213     public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName();
214     public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName();
215     public static final String STREAMING_STATE_NAME = StreamingFocusState.class.getSimpleName();
216     public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName();
217 
218     private class BaseState extends State {
219         @Override
processMessage(Message msg)220         public boolean processMessage(Message msg) {
221             switch (msg.what) {
222                 case ENTER_CALL_FOCUS_FOR_TESTING:
223                     transitionTo(mSimCallFocusState);
224                     return HANDLED;
225                 case ENTER_COMMS_FOCUS_FOR_TESTING:
226                     transitionTo(mVoipCallFocusState);
227                     return HANDLED;
228                 case ENTER_RING_FOCUS_FOR_TESTING:
229                     transitionTo(mRingingFocusState);
230                     return HANDLED;
231                 case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING:
232                     transitionTo(mOtherFocusState);
233                     return HANDLED;
234                 case ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING:
235                     transitionTo(mAudioProcessingFocusState);
236                     return HANDLED;
237                 case ENTER_STREAMING_FOCUS_FOR_TESTING:
238                     transitionTo(mStreamingFocusState);
239                     return HANDLED;
240                 case ABANDON_FOCUS_FOR_TESTING:
241                     transitionTo(mUnfocusedState);
242                     return HANDLED;
243                 case INITIALIZE:
244                     mIsInitialized = true;
245                     return HANDLED;
246                 case RUN_RUNNABLE:
247                     java.lang.Runnable r = (java.lang.Runnable) msg.obj;
248                     r.run();
249                     return HANDLED;
250                 default:
251                     return NOT_HANDLED;
252             }
253         }
254     }
255 
256     private class UnfocusedState extends BaseState {
257         @Override
enter()258         public void enter() {
259             Log.i(LOG_TAG, "Audio focus entering UNFOCUSED state");
260             mLocalLog.log("Enter UNFOCUSED");
261             if (mIsInitialized) {
262                 // Clear any communication device that was requested previously.
263                 // Todo: Remove once clearCommunicationDeviceAfterAudioOpsComplete is
264                 // completely rolled out.
265                 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()
266                         && !mFeatureFlags.clearCommunicationDeviceAfterAudioOpsComplete()) {
267                     mCommunicationDeviceTracker.clearCommunicationDevice(mCommunicationDeviceTracker
268                             .getCurrentLocallyRequestedCommunicationDevice());
269                 }
270                 if (mFeatureFlags.setAudioModeBeforeAbandonFocus()) {
271                     Log.i(this, "enter: AudioManager#setMode(MODE_NORMAL)");
272                     mAudioManager.setMode(AudioManager.MODE_NORMAL);
273                     mCallAudioManager.setCallAudioRouteFocusState(
274                             CallAudioRouteStateMachine.NO_FOCUS);
275                 } else {
276                     mCallAudioManager.setCallAudioRouteFocusState(
277                             CallAudioRouteStateMachine.NO_FOCUS);
278                     Log.i(this, "enter: AudioManager#setMode(MODE_NORMAL)");
279                     mAudioManager.setMode(AudioManager.MODE_NORMAL);
280                 }
281                 mLocalLog.log("Mode MODE_NORMAL");
282                 mMostRecentMode = AudioManager.MODE_NORMAL;
283                 // Don't release focus here -- wait until we get a signal that any other audio
284                 // operations triggered by this are done before releasing focus.
285             }
286         }
287 
288         @Override
processMessage(Message msg)289         public boolean processMessage(Message msg) {
290             if (super.processMessage(msg) == HANDLED) {
291                 return HANDLED;
292             }
293             MessageArgs args = (MessageArgs) msg.obj;
294             switch (msg.what) {
295                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
296                     // Do nothing.
297                     return HANDLED;
298                 case NO_MORE_RINGING_CALLS:
299                     // Do nothing.
300                     return HANDLED;
301                 case NO_MORE_HOLDING_CALLS:
302                     // Do nothing.
303                     return HANDLED;
304                 case NO_MORE_AUDIO_PROCESSING_CALLS:
305                     // Do nothing.
306                     return HANDLED;
307                 case NEW_ACTIVE_OR_DIALING_CALL:
308                     transitionTo(args.foregroundCallIsVoip
309                             ? mVoipCallFocusState : mSimCallFocusState);
310                     return HANDLED;
311                 case NEW_RINGING_CALL:
312                     transitionTo(mRingingFocusState);
313                     return HANDLED;
314                 case NEW_AUDIO_PROCESSING_CALL:
315                     transitionTo(mAudioProcessingFocusState);
316                     return HANDLED;
317                 case NEW_HOLDING_CALL:
318                     // This really shouldn't happen, but transition to the focused state anyway.
319                     Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
320                             " Args are: \n" + args.toString());
321                     transitionTo(mOtherFocusState);
322                     return HANDLED;
323                 case START_CALL_STREAMING:
324                     transitionTo(mStreamingFocusState);
325                     return HANDLED;
326                 case TONE_STARTED_PLAYING:
327                     // This shouldn't happen either, but perform the action anyway.
328                     Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
329                             + args.toString());
330                     return HANDLED;
331                 case AUDIO_OPERATIONS_COMPLETE:
332                     Log.i(this, "AudioOperationsComplete: "
333                             + "AudioManager#abandonAudioFocusRequest(); now unfocused");
334                     mAudioManager.abandonAudioFocusForCall();
335                     // Clear requested communication device after the call ends.
336                     if (mFeatureFlags.clearCommunicationDeviceAfterAudioOpsComplete()) {
337                         // Oh flags!  If we're using the refactored audio route switching, we should
338                         // not be using the communication device tracker; that is exclusively for
339                         // the old code path.
340                         if (!mFeatureFlags.dontUseCommunicationDeviceTracker()
341                                 || !mFeatureFlags.useRefactoredAudioRouteSwitching()) {
342                             mCommunicationDeviceTracker.clearCommunicationDevice(
343                                     mCommunicationDeviceTracker
344                                             .getCurrentLocallyRequestedCommunicationDevice());
345                         }
346                     }
347                     return HANDLED;
348                 default:
349                     // The forced focus switch commands are handled by BaseState.
350                     return NOT_HANDLED;
351             }
352         }
353     }
354 
355     private class AudioProcessingFocusState extends BaseState {
356         @Override
enter()357         public void enter() {
358             Log.i(LOG_TAG, "Audio focus entering AUDIO_PROCESSING state");
359             mLocalLog.log("Enter AUDIO_PROCESSING");
360             if (mIsInitialized) {
361                 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
362                 Log.i(this, "enter: AudioManager#setMode(MODE_AUDIO_PROCESSING)");
363                 mAudioManager.setMode(NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING);
364                 mLocalLog.log("Mode MODE_CALL_SCREENING");
365                 mMostRecentMode = NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING;
366             }
367         }
368 
369         @Override
processMessage(Message msg)370         public boolean processMessage(Message msg) {
371             if (super.processMessage(msg) == HANDLED) {
372                 return HANDLED;
373             }
374             MessageArgs args = (MessageArgs) msg.obj;
375             switch (msg.what) {
376                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
377                     // Do nothing.
378                     return HANDLED;
379                 case NO_MORE_RINGING_CALLS:
380                     // Do nothing.
381                     return HANDLED;
382                 case NO_MORE_HOLDING_CALLS:
383                     // Do nothing.
384                     return HANDLED;
385                 case NO_MORE_AUDIO_PROCESSING_CALLS:
386                     BaseState destState = calculateProperStateFromArgs(args);
387                     if (destState == this) {
388                         Log.w(LOG_TAG, "Got spurious NO_MORE_AUDIO_PROCESSING_CALLS");
389                     }
390                     transitionTo(destState);
391                     return HANDLED;
392                 case NEW_ACTIVE_OR_DIALING_CALL:
393                     transitionTo(args.foregroundCallIsVoip
394                             ? mVoipCallFocusState : mSimCallFocusState);
395                     return HANDLED;
396                 case NEW_RINGING_CALL:
397                     transitionTo(mRingingFocusState);
398                     return HANDLED;
399                 case NEW_HOLDING_CALL:
400                     // This really shouldn't happen, but recalculate from args and do the transition
401                     Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
402                             " Args are: \n" + args.toString());
403                     transitionTo(mOtherFocusState);
404                     return HANDLED;
405                 case NEW_AUDIO_PROCESSING_CALL:
406                     // Can happen as a duplicate message
407                     return HANDLED;
408                 case TONE_STARTED_PLAYING:
409                     // This shouldn't happen either, but perform the action anyway.
410                     Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
411                             + args.toString());
412                     return HANDLED;
413                 case START_CALL_STREAMING:
414                     transitionTo(mStreamingFocusState);
415                     return HANDLED;
416                 case AUDIO_OPERATIONS_COMPLETE:
417                     Log.i(LOG_TAG, "AudioManager#abandonAudioFocusRequest: now "
418                             + "AUDIO_PROCESSING");
419                     mAudioManager.abandonAudioFocusForCall();
420                     return HANDLED;
421                 default:
422                     // The forced focus switch commands are handled by BaseState.
423                     return NOT_HANDLED;
424             }
425         }
426     }
427 
428     private class RingingFocusState extends BaseState {
429         // Keeps track of whether we're ringing with audio focus or if we've just entered the state
430         // without acquiring focus because of a silent ringtone or something.
431         private boolean mHasFocus = false;
432 
tryStartRinging()433         private void tryStartRinging() {
434             if (mHasFocus && mCallAudioManager.isRingtonePlaying()) {
435                 Log.i(LOG_TAG,
436                     "RingingFocusState#tryStartRinging -- audio focus previously"
437                         + " acquired and ringtone already playing -- skipping.");
438                 return;
439             }
440 
441             if (mCallAudioManager.startRinging()) {
442                 Log.i(this, "tryStartRinging: AudioManager#requestAudioFocus(RING)");
443                 mAudioManager.requestAudioFocusForCall(
444                         AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
445 
446                 // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode --
447                 // this trips up the audio system.
448                 if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) {
449                     Log.i(this, "enter: AudioManager#setMode(MODE_RINGTONE)");
450                     mAudioManager.setMode(AudioManager.MODE_RINGTONE);
451                     mLocalLog.log("Mode MODE_RINGTONE");
452                 }
453                 mCallAudioManager.setCallAudioRouteFocusState(
454                     CallAudioRouteStateMachine.RINGING_FOCUS);
455                 mHasFocus = true;
456             } else {
457                 Log.i(
458                     LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
459             }
460         }
461 
462         @Override
enter()463         public void enter() {
464             Log.i(LOG_TAG, "Audio focus entering RINGING state");
465             mLocalLog.log("Enter RINGING");
466             tryStartRinging();
467             mCallAudioManager.stopCallWaiting();
468         }
469 
470         @Override
exit()471         public void exit() {
472             // Audio mode and audio stream will be set by the next state.
473             mCallAudioManager.stopRinging();
474             mHasFocus = false;
475         }
476 
477         @Override
processMessage(Message msg)478         public boolean processMessage(Message msg) {
479             if (super.processMessage(msg) == HANDLED) {
480                 return HANDLED;
481             }
482             MessageArgs args = (MessageArgs) msg.obj;
483             switch (msg.what) {
484                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
485                     // Do nothing. Loss of an active call should not impact ringer.
486                     return HANDLED;
487                 case NO_MORE_HOLDING_CALLS:
488                     // Do nothing and keep ringing.
489                     return HANDLED;
490                 case NO_MORE_RINGING_CALLS:
491                     BaseState destState = calculateProperStateFromArgs(args);
492                     if (destState == this) {
493                         Log.w(LOG_TAG, "Got spurious NO_MORE_RINGING_CALLS");
494                     }
495                     transitionTo(destState);
496                     return HANDLED;
497                 case NEW_ACTIVE_OR_DIALING_CALL:
498                     // If a call becomes active suddenly, give it priority over ringing.
499                     transitionTo(args.foregroundCallIsVoip
500                             ? mVoipCallFocusState : mSimCallFocusState);
501                     return HANDLED;
502                 case NEW_AUDIO_PROCESSING_CALL:
503                     // If we don't have any more ringing calls, transition to audio processing.
504                     if (!args.hasRingingCalls) {
505                         transitionTo(mAudioProcessingFocusState);
506                     } else {
507                         Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
508                                 + "ringing");
509                     }
510                 case NEW_RINGING_CALL:
511                     // Can happen as a duplicate message
512                     return HANDLED;
513                 case NEW_HOLDING_CALL:
514                     // This really shouldn't happen, but transition to the focused state anyway.
515                     Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." +
516                             " Args are: " + args.toString());
517                     transitionTo(mOtherFocusState);
518                     return HANDLED;
519                 case RINGER_MODE_CHANGE: {
520                     Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE");
521                     tryStartRinging();
522                     return HANDLED;
523                 }
524                 case AUDIO_OPERATIONS_COMPLETE:
525                     Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused"
526                             + " state");
527                     return HANDLED;
528                 default:
529                     // The forced focus switch commands are handled by BaseState.
530                     return NOT_HANDLED;
531             }
532         }
533     }
534 
535     private class SimCallFocusState extends BaseState {
536         @Override
enter()537         public void enter() {
538             Log.i(LOG_TAG, "Audio focus entering SIM CALL state");
539             mLocalLog.log("Enter SIM_CALL");
540             Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)");
541             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
542                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
543             Log.i(this, "enter: AudioManager#setMode(MODE_IN_CALL)");
544             mAudioManager.setMode(AudioManager.MODE_IN_CALL);
545             mLocalLog.log("Mode MODE_IN_CALL");
546             mMostRecentMode = AudioManager.MODE_IN_CALL;
547             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
548         }
549 
550         @Override
processMessage(Message msg)551         public boolean processMessage(Message msg) {
552             if (super.processMessage(msg) == HANDLED) {
553                 return HANDLED;
554             }
555             MessageArgs args = (MessageArgs) msg.obj;
556             switch (msg.what) {
557                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
558                     // Switch to either ringing, holding, or inactive
559                     transitionTo(calculateProperStateFromArgs(args));
560                     return HANDLED;
561                 case NO_MORE_RINGING_CALLS:
562                     // Don't transition state, but stop any call-waiting tones that may have been
563                     // playing.
564                     if (args.isTonePlaying) {
565                         mCallAudioManager.stopCallWaiting();
566                     }
567                     // If a MT-audio-speedup call gets disconnected by the connection service
568                     // concurrently with the user answering it, we may get this message
569                     // indicating that a ringing call has disconnected while this state machine
570                     // is in the SimCallFocusState.
571                     if (!args.hasActiveOrDialingCalls) {
572                         transitionTo(calculateProperStateFromArgs(args));
573                     }
574                     return HANDLED;
575                 case NO_MORE_HOLDING_CALLS:
576                     if (args.foregroundCallIsVoip) {
577                         transitionTo(mVoipCallFocusState);
578                     }
579                     return HANDLED;
580                 case NEW_ACTIVE_OR_DIALING_CALL:
581                     if (args.foregroundCallIsVoip) {
582                         transitionTo(mVoipCallFocusState);
583                     }
584                     return HANDLED;
585                 case NEW_RINGING_CALL:
586                     // Don't make a call ring over an active call, but do play a call waiting tone.
587                     mCallAudioManager.startCallWaiting("call already active");
588                     return HANDLED;
589                 case NEW_HOLDING_CALL:
590                     // Just check the voip mode. Putting an active call on hold will be handled when
591                     // NO_MORE_ACTIVE_CALLS is processed.
592                     if (args.foregroundCallIsVoip) {
593                         transitionTo(mVoipCallFocusState);
594                     }
595                     return HANDLED;
596                 case NEW_AUDIO_PROCESSING_CALL:
597                     // If we don't have any more active calls, transition to audio processing.
598                     if (!args.hasActiveOrDialingCalls) {
599                         transitionTo(mAudioProcessingFocusState);
600                     } else {
601                         Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
602                                 + "active");
603                     }
604                 case FOREGROUND_VOIP_MODE_CHANGE:
605                     if (args.foregroundCallIsVoip) {
606                         transitionTo(mVoipCallFocusState);
607                     }
608                     return HANDLED;
609                 case AUDIO_OPERATIONS_COMPLETE:
610                     Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused"
611                             + " state");
612                     return HANDLED;
613                 default:
614                     // The forced focus switch commands are handled by BaseState.
615                     return NOT_HANDLED;
616             }
617         }
618     }
619 
620     private class VoipCallFocusState extends BaseState {
621         @Override
enter()622         public void enter() {
623             Log.i(LOG_TAG, "Audio focus entering VOIP CALL state");
624             mLocalLog.log("Enter VOIP_CALL");
625             Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)");
626             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
627                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
628             Log.i(this, "enter: AudioManager#setMode(MODE_IN_COMMUNICATION)");
629             mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
630             mLocalLog.log("Mode MODE_IN_COMMUNICATION");
631             mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION;
632             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
633         }
634 
635         @Override
processMessage(Message msg)636         public boolean processMessage(Message msg) {
637             if (super.processMessage(msg) == HANDLED) {
638                 return HANDLED;
639             }
640             MessageArgs args = (MessageArgs) msg.obj;
641             switch (msg.what) {
642                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
643                     // Switch to either ringing, holding, or inactive
644                     transitionTo(calculateProperStateFromArgs(args));
645                     return HANDLED;
646                 case NO_MORE_RINGING_CALLS:
647                     // Don't transition state, but stop any call-waiting tones that may have been
648                     // playing.
649                     if (args.isTonePlaying) {
650                         mCallAudioManager.stopCallWaiting();
651                     }
652                     return HANDLED;
653                 case NO_MORE_HOLDING_CALLS:
654                     if (!args.foregroundCallIsVoip) {
655                         transitionTo(mSimCallFocusState);
656                     }
657                     return HANDLED;
658                 case NEW_ACTIVE_OR_DIALING_CALL:
659                     if (!args.foregroundCallIsVoip) {
660                         transitionTo(mSimCallFocusState);
661                     }
662                     return HANDLED;
663                 case NEW_RINGING_CALL:
664                     // Don't make a call ring over an active call, but do play a call waiting tone.
665                     mCallAudioManager.startCallWaiting("call already active");
666                     return HANDLED;
667                 case NEW_HOLDING_CALL:
668                     // Just check the voip mode. Putting an active call on hold will be handled when
669                     // NO_MORE_ACTIVE_CALLS is processed.
670                     if (!args.foregroundCallIsVoip) {
671                         transitionTo(mSimCallFocusState);
672                     }
673                     return HANDLED;
674                 case NEW_AUDIO_PROCESSING_CALL:
675                     // If we don't have any more active calls, transition to audio processing.
676                     if (!args.hasActiveOrDialingCalls) {
677                         transitionTo(mAudioProcessingFocusState);
678                     } else {
679                         Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
680                                 + "active");
681                     }
682                 case FOREGROUND_VOIP_MODE_CHANGE:
683                     if (!args.foregroundCallIsVoip) {
684                         transitionTo(mSimCallFocusState);
685                     }
686                     return HANDLED;
687                 case AUDIO_OPERATIONS_COMPLETE:
688                     Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused"
689                             + " state");
690                     return HANDLED;
691                 case START_CALL_STREAMING:
692                     transitionTo(mStreamingFocusState);
693                     return HANDLED;
694                 default:
695                     // The forced focus switch commands are handled by BaseState.
696                     return NOT_HANDLED;
697             }
698         }
699     }
700 
701     private class StreamingFocusState extends BaseState {
702         @Override
enter()703         public void enter() {
704             Log.i(LOG_TAG, "Audio focus entering streaming state");
705             mLocalLog.log("Enter Streaming");
706             mLocalLog.log("Mode MODE_COMMUNICATION_REDIRECT");
707             Log.i(this, "enter: AudioManager#setMode(MODE_COMMUNICATION_REDIRECT");
708             mAudioManager.setMode(AudioManager.MODE_COMMUNICATION_REDIRECT);
709             mMostRecentMode = AudioManager.MODE_NORMAL;
710             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
711             mCallAudioManager.getCallAudioRouteAdapter().sendMessageWithSessionInfo(
712                     CallAudioRouteStateMachine.STREAMING_FORCE_ENABLED);
713         }
714 
preExit()715         private void preExit() {
716             mCallAudioManager.getCallAudioRouteAdapter().sendMessageWithSessionInfo(
717                     CallAudioRouteStateMachine.STREAMING_FORCE_DISABLED);
718         }
719 
720         @Override
processMessage(Message msg)721         public boolean processMessage(Message msg) {
722             if (super.processMessage(msg) == HANDLED) {
723                 return HANDLED;
724             }
725             MessageArgs args = (MessageArgs) msg.obj;
726             switch (msg.what) {
727                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
728                     // Switch to either ringing, holding, or inactive
729                     transitionTo(calculateProperStateFromArgs(args));
730                     return HANDLED;
731                 case NO_MORE_RINGING_CALLS:
732                     // Do nothing.
733                     return HANDLED;
734                 case NO_MORE_HOLDING_CALLS:
735                     // Do nothing.
736                     return HANDLED;
737                 case NO_MORE_AUDIO_PROCESSING_CALLS:
738                     // Do nothing.
739                     return HANDLED;
740                 case NEW_ACTIVE_OR_DIALING_CALL:
741                     // Only possible for emergency call
742                     BaseState destState = calculateProperStateFromArgs(args);
743                     if (destState != this) {
744                         preExit();
745                         transitionTo(destState);
746                     }
747                     return HANDLED;
748                 case NEW_RINGING_CALL:
749                     // Only possible for emergency call
750                     preExit();
751                     transitionTo(mRingingFocusState);
752                     return HANDLED;
753                 case NEW_HOLDING_CALL:
754                     // Do nothing.
755                     return HANDLED;
756                 case NEW_AUDIO_PROCESSING_CALL:
757                     // Do nothing.
758                     return HANDLED;
759                 case START_CALL_STREAMING:
760                     // Can happen as a duplicate message
761                     return HANDLED;
762                 case TONE_STARTED_PLAYING:
763                     // Do nothing.
764                     return HANDLED;
765                 case STOP_CALL_STREAMING:
766                     transitionTo(calculateProperStateFromArgs(args));
767                     return HANDLED;
768                 default:
769                     // The forced focus switch commands are handled by BaseState.
770                     return NOT_HANDLED;
771             }
772         }
773     }
774 
775     /**
776      * This class is used for calls on hold and end-of-call tones.
777      */
778     private class OtherFocusState extends BaseState {
779         @Override
enter()780         public void enter() {
781             Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state");
782             mLocalLog.log("Enter TONE/HOLDING");
783             Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)");
784             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
785                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
786             Log.i(this, "enter: AudioManager#setMode(%d)", mMostRecentMode);
787             mAudioManager.setMode(mMostRecentMode);
788             mLocalLog.log("Mode " + mMostRecentMode);
789             mCallAudioManager.setCallAudioRouteFocusStateForEndTone();
790         }
791 
792         @Override
processMessage(Message msg)793         public boolean processMessage(Message msg) {
794             if (super.processMessage(msg) == HANDLED) {
795                 return HANDLED;
796             }
797             MessageArgs args = (MessageArgs) msg.obj;
798             switch (msg.what) {
799                 case NO_MORE_HOLDING_CALLS:
800                     if (args.hasActiveOrDialingCalls) {
801                         transitionTo(args.foregroundCallIsVoip
802                                 ? mVoipCallFocusState : mSimCallFocusState);
803                     } else if (args.hasRingingCalls) {
804                         transitionTo(mRingingFocusState);
805                     } else if (!args.isTonePlaying) {
806                         transitionTo(mUnfocusedState);
807                     }
808                     // Do nothing if a tone is playing.
809                     return HANDLED;
810                 case NEW_ACTIVE_OR_DIALING_CALL:
811                     transitionTo(args.foregroundCallIsVoip
812                             ? mVoipCallFocusState : mSimCallFocusState);
813                     return HANDLED;
814                 case NEW_RINGING_CALL:
815                     // TODO: consider whether to move this into MessageArgs if more things start
816                     // to use it.
817                     if (args.hasHoldingCalls && mSystemStateHelper.isDeviceAtEar()) {
818                         mCallAudioManager.startCallWaiting(
819                                 "Device is at ear with held call");
820                     } else {
821                         transitionTo(mRingingFocusState);
822                     }
823                     return HANDLED;
824                 case NEW_HOLDING_CALL:
825                     // Do nothing.
826                     return HANDLED;
827                 case NO_MORE_RINGING_CALLS:
828                     // If there are no more ringing calls in this state, then stop any call-waiting
829                     // tones that may be playing.
830                     mCallAudioManager.stopCallWaiting();
831                     return HANDLED;
832                 case TONE_STOPPED_PLAYING:
833                     transitionTo(calculateProperStateFromArgs(args));
834                     return HANDLED;
835                 case AUDIO_OPERATIONS_COMPLETE:
836                     Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused"
837                             + " state");
838                     return HANDLED;
839                 default:
840                     return NOT_HANDLED;
841             }
842         }
843     }
844 
845     private static final String LOG_TAG = CallAudioModeStateMachine.class.getSimpleName();
846 
847     private final BaseState mUnfocusedState = new UnfocusedState();
848     private final BaseState mRingingFocusState = new RingingFocusState();
849     private final BaseState mSimCallFocusState = new SimCallFocusState();
850     private final BaseState mVoipCallFocusState = new VoipCallFocusState();
851     private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState();
852     private final BaseState mStreamingFocusState = new StreamingFocusState();
853     private final BaseState mOtherFocusState = new OtherFocusState();
854 
855     private final AudioManager mAudioManager;
856     private final SystemStateHelper mSystemStateHelper;
857     private CallAudioManager mCallAudioManager;
858     private FeatureFlags mFeatureFlags;
859     private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
860 
861     private int mMostRecentMode;
862     private boolean mIsInitialized = false;
863 
CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager, FeatureFlags featureFlags, CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker)864     public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
865             AudioManager audioManager, FeatureFlags featureFlags,
866             CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker) {
867         super(CallAudioModeStateMachine.class.getSimpleName());
868         mAudioManager = audioManager;
869         mSystemStateHelper = systemStateHelper;
870         mMostRecentMode = AudioManager.MODE_NORMAL;
871         mFeatureFlags = featureFlags;
872         mCommunicationDeviceTracker = callAudioCommunicationDeviceTracker;
873 
874         createStates();
875     }
876 
877     /**
878      * Used for testing
879      */
CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager, Looper looper, FeatureFlags featureFlags, CallAudioCommunicationDeviceTracker communicationDeviceTracker)880     public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
881             AudioManager audioManager, Looper looper, FeatureFlags featureFlags,
882             CallAudioCommunicationDeviceTracker communicationDeviceTracker) {
883         super(CallAudioModeStateMachine.class.getSimpleName(), looper);
884         mAudioManager = audioManager;
885         mSystemStateHelper = systemStateHelper;
886         mMostRecentMode = AudioManager.MODE_NORMAL;
887         mFeatureFlags = featureFlags;
888         mCommunicationDeviceTracker = communicationDeviceTracker;
889 
890         createStates();
891     }
892 
createStates()893     private void createStates() {
894         addState(mUnfocusedState);
895         addState(mRingingFocusState);
896         addState(mSimCallFocusState);
897         addState(mVoipCallFocusState);
898         addState(mAudioProcessingFocusState);
899         addState(mStreamingFocusState);
900         addState(mOtherFocusState);
901         setInitialState(mUnfocusedState);
902         start();
903         sendMessage(INITIALIZE, new MessageArgs.Builder()
904                 .setHasActiveOrDialingCalls(false)
905                 .setHasRingingCalls(false)
906                 .setHasHoldingCalls(false)
907                 .setIsTonePlaying(false)
908                 .setForegroundCallIsVoip(false)
909                 .setIsStreaming(false)
910                 .setSession(Log.createSubsession())
911                 .build());
912     }
913 
setCallAudioManager(CallAudioManager callAudioManager)914     public void setCallAudioManager(CallAudioManager callAudioManager) {
915         mCallAudioManager = callAudioManager;
916     }
917 
getCurrentStateName()918     public String getCurrentStateName() {
919         IState currentState = getCurrentState();
920         return currentState == null ? "no state" : currentState.getName();
921     }
922 
sendMessageWithArgs(int messageCode, MessageArgs args)923     public void sendMessageWithArgs(int messageCode, MessageArgs args) {
924         sendMessage(messageCode, args);
925     }
926 
927     @Override
onPreHandleMessage(Message msg)928     protected void onPreHandleMessage(Message msg) {
929         if (msg.obj != null && msg.obj instanceof MessageArgs) {
930             Log.continueSession(((MessageArgs) msg.obj).session, "CAMSM.pM_" + msg.what);
931             Log.i(LOG_TAG, "Message received: %s.", MESSAGE_CODE_TO_NAME.get(msg.what));
932         } else if (msg.what == RUN_RUNNABLE && msg.obj instanceof Runnable) {
933             Log.i(LOG_TAG, "Running runnable for testing");
934         } else {
935                 Log.w(LOG_TAG, "Message sent must be of type nonnull MessageArgs, but got " +
936                         (msg.obj == null ? "null" : msg.obj.getClass().getSimpleName()));
937                 Log.w(LOG_TAG, "The message was of code %d = %s",
938                         msg.what, MESSAGE_CODE_TO_NAME.get(msg.what));
939         }
940     }
941 
dumpPendingMessages(IndentingPrintWriter pw)942     public void dumpPendingMessages(IndentingPrintWriter pw) {
943         getHandler().getLooper().dump(pw::println, "");
944     }
945 
dump(IndentingPrintWriter pw)946     public void dump(IndentingPrintWriter pw) {
947         pw.println("History:");
948         mLocalLog.dump(pw);
949         pw.println("Pending Msg:");
950         dumpPendingMessages(pw);
951     }
952 
953     @Override
onPostHandleMessage(Message msg)954     protected void onPostHandleMessage(Message msg) {
955         Log.endSession();
956     }
957 
calculateProperStateFromArgs(MessageArgs args)958     private BaseState calculateProperStateFromArgs(MessageArgs args) {
959         // If there are active, audio-processing, holding, or ringing calls,
960         // switch to the appropriate focus.
961         // Otherwise abandon focus.
962 
963         // The order matters here. If there is streaming call, holding streaming route for them
964         // takes priority. After that, holding focus for active calls takes priority. After that, we
965         // want to prioritize holding calls over ringing calls so that when a call-waiting call gets
966         // answered, there's no transition in and out of the ringing focus state. After that, we
967         // want tones since we actually hold focus during them, then the audio processing state
968         // because that will release focus.
969         if (args.isStreaming) {
970             return mSimCallFocusState;
971         } else if (args.hasActiveOrDialingCalls) {
972             if (args.foregroundCallIsVoip) {
973                 return mVoipCallFocusState;
974             } else {
975                 return mSimCallFocusState;
976             }
977         } else if (args.hasHoldingCalls) {
978             return mOtherFocusState;
979         } else if (args.hasRingingCalls) {
980             return mRingingFocusState;
981         } else if (args.isTonePlaying) {
982             return mOtherFocusState;
983         } else if (args.hasAudioProcessingCalls) {
984             return mAudioProcessingFocusState;
985         }
986         return mUnfocusedState;
987     }
988 
989 }
990