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