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 {
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 
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
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 
83             public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) {
84                 mHasActiveOrDialingCalls = hasActiveOrDialingCalls;
85                 return this;
86             }
87 
88             public Builder setHasRingingCalls(boolean hasRingingCalls) {
89                 mHasRingingCalls = hasRingingCalls;
90                 return this;
91             }
92 
93             public Builder setHasHoldingCalls(boolean hasHoldingCalls) {
94                 mHasHoldingCalls = hasHoldingCalls;
95                 return this;
96             }
97 
98             public Builder setHasAudioProcessingCalls(boolean hasAudioProcessingCalls) {
99                 mHasAudioProcessingCalls = hasAudioProcessingCalls;
100                 return this;
101             }
102 
103             public Builder setIsTonePlaying(boolean isTonePlaying) {
104                 mIsTonePlaying = isTonePlaying;
105                 return this;
106             }
107 
108             public Builder setForegroundCallIsVoip(boolean foregroundCallIsVoip) {
109                 mForegroundCallIsVoip = foregroundCallIsVoip;
110                 return this;
111             }
112 
113             public Builder setSession(Session session) {
114                 mSession = session;
115                 return this;
116             }
117 
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
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
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
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
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
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 
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
384         public void enter() {
385             Log.i(LOG_TAG, "Audio focus entering RINGING state");
386             tryStartRinging();
387             mCallAudioManager.stopCallWaiting();
388         }
389 
390         @Override
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
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
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
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
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
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
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
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 
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      */
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 
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 
734     public void setCallAudioManager(CallAudioManager callAudioManager) {
735         mCallAudioManager = callAudioManager;
736     }
737 
738     public String getCurrentStateName() {
739         IState currentState = getCurrentState();
740         return currentState == null ? "no state" : currentState.getName();
741     }
742 
743     public void sendMessageWithArgs(int messageCode, MessageArgs args) {
744         sendMessage(messageCode, args);
745     }
746 
747     @Override
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 
762     public void dumpPendingMessages(IndentingPrintWriter pw) {
763         getHandler().getLooper().dump(pw::println, "");
764     }
765 
766     @Override
767     protected void onPostHandleMessage(Message msg) {
768         Log.endSession();
769     }
770 
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