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