• 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 
20 import android.app.ActivityManagerNative;
21 import android.content.Context;
22 import android.content.pm.UserInfo;
23 import android.media.AudioManager;
24 import android.media.IAudioService;
25 import android.os.Binder;
26 import android.os.Message;
27 import android.os.RemoteException;
28 import android.os.SystemProperties;
29 import android.os.UserHandle;
30 import android.telecom.CallAudioState;
31 import android.util.SparseArray;
32 
33 import com.android.internal.util.IState;
34 import com.android.internal.util.State;
35 import com.android.internal.util.StateMachine;
36 
37 import java.util.HashMap;
38 
39 /**
40  * This class describes the available routes of a call as a state machine.
41  * Transitions are caused solely by the commands sent as messages. Possible values for msg.what
42  * are defined as event constants in this file.
43  *
44  * The eight states are all instances of the abstract base class, {@link AudioState}. Each state
45  * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and
46  * speakerphone) and audio focus status (active or quiescent).
47  *
48  * Messages are processed first by the processMessage method in the base class, AudioState.
49  * Any messages not completely handled by AudioState are further processed by the same method in
50  * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute},
51  * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at
52  * this level are then processed by the classes corresponding to the state instances themselves.
53  *
54  * There are several variables carrying additional state. These include:
55  * mAvailableRoutes: A bitmask describing which audio routes are available
56  * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting
57  *     from a wired headset
58  * mIsMuted: a boolean indicating whether the audio is muted
59  */
60 public class CallAudioRouteStateMachine extends StateMachine {
61     /** Direct the audio stream through the device's earpiece. */
62     public static final int ROUTE_EARPIECE      = CallAudioState.ROUTE_EARPIECE;
63 
64     /** Direct the audio stream through Bluetooth. */
65     public static final int ROUTE_BLUETOOTH     = CallAudioState.ROUTE_BLUETOOTH;
66 
67     /** Direct the audio stream through a wired headset. */
68     public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET;
69 
70     /** Direct the audio stream through the device's speakerphone. */
71     public static final int ROUTE_SPEAKER       = CallAudioState.ROUTE_SPEAKER;
72 
73     /** Valid values for msg.what */
74     public static final int CONNECT_WIRED_HEADSET = 1;
75     public static final int DISCONNECT_WIRED_HEADSET = 2;
76     public static final int CONNECT_BLUETOOTH = 3;
77     public static final int DISCONNECT_BLUETOOTH = 4;
78     public static final int CONNECT_DOCK = 5;
79     public static final int DISCONNECT_DOCK = 6;
80 
81     public static final int SWITCH_EARPIECE = 1001;
82     public static final int SWITCH_BLUETOOTH = 1002;
83     public static final int SWITCH_HEADSET = 1003;
84     public static final int SWITCH_SPEAKER = 1004;
85     // Wired headset, earpiece, or speakerphone, in that order of precedence.
86     public static final int SWITCH_BASELINE_ROUTE = 1005;
87     public static final int BT_AUDIO_DISCONNECT = 1006;
88 
89     public static final int USER_SWITCH_EARPIECE = 1101;
90     public static final int USER_SWITCH_BLUETOOTH = 1102;
91     public static final int USER_SWITCH_HEADSET = 1103;
92     public static final int USER_SWITCH_SPEAKER = 1104;
93     public static final int USER_SWITCH_BASELINE_ROUTE = 1105;
94 
95     public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201;
96 
97     public static final int MUTE_ON = 3001;
98     public static final int MUTE_OFF = 3002;
99     public static final int TOGGLE_MUTE = 3003;
100 
101     public static final int SWITCH_FOCUS = 4001;
102 
103     // Used in testing to execute verifications. Not compatible with subsessions.
104     public static final int RUN_RUNNABLE = 9001;
105 
106     /** Valid values for mAudioFocusType */
107     public static final int NO_FOCUS = 1;
108     public static final int HAS_FOCUS = 2;
109 
110     private static final SparseArray<String> AUDIO_ROUTE_TO_LOG_EVENT = new SparseArray<String>() {{
111         put(CallAudioState.ROUTE_BLUETOOTH, Log.Events.AUDIO_ROUTE_BT);
112         put(CallAudioState.ROUTE_EARPIECE, Log.Events.AUDIO_ROUTE_EARPIECE);
113         put(CallAudioState.ROUTE_SPEAKER, Log.Events.AUDIO_ROUTE_SPEAKER);
114         put(CallAudioState.ROUTE_WIRED_HEADSET, Log.Events.AUDIO_ROUTE_HEADSET);
115     }};
116 
117     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
118         put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET");
119         put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET");
120         put(CONNECT_BLUETOOTH, "CONNECT_BLUETOOTH");
121         put(DISCONNECT_BLUETOOTH, "DISCONNECT_BLUETOOTH");
122         put(CONNECT_DOCK, "CONNECT_DOCK");
123         put(DISCONNECT_DOCK, "DISCONNECT_DOCK");
124 
125         put(SWITCH_EARPIECE, "SWITCH_EARPIECE");
126         put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH");
127         put(SWITCH_HEADSET, "SWITCH_HEADSET");
128         put(SWITCH_SPEAKER, "SWITCH_SPEAKER");
129         put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE");
130         put(BT_AUDIO_DISCONNECT, "BT_AUDIO_DISCONNECT");
131 
132         put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE");
133         put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH");
134         put(USER_SWITCH_HEADSET, "USER_SWITCH_HEADSET");
135         put(USER_SWITCH_SPEAKER, "USER_SWITCH_SPEAKER");
136         put(USER_SWITCH_BASELINE_ROUTE, "USER_SWITCH_BASELINE_ROUTE");
137 
138         put(MUTE_ON, "MUTE_ON");
139         put(MUTE_OFF, "MUTE_OFF");
140         put(TOGGLE_MUTE, "TOGGLE_MUTE");
141 
142         put(SWITCH_FOCUS, "SWITCH_FOCUS");
143 
144         put(RUN_RUNNABLE, "RUN_RUNNABLE");
145     }};
146 
147     private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute";
148     private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute";
149     private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute";
150     private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute";
151     private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute";
152     private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute";
153     private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute";
154     private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute";
155 
156     public static final String NAME = CallAudioRouteStateMachine.class.getName();
157 
158     @Override
onPreHandleMessage(Message msg)159     protected void onPreHandleMessage(Message msg) {
160         if (msg.obj != null && msg.obj instanceof Session) {
161             String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown");
162             Log.continueSession((Session) msg.obj, "CARSM.pM_" + messageCodeName);
163             Log.i(this, "Message received: %s=%d", messageCodeName, msg.what);
164         }
165     }
166 
167     @Override
onPostHandleMessage(Message msg)168     protected void onPostHandleMessage(Message msg) {
169         Log.endSession();
170     }
171 
172     abstract class AudioState extends State {
173         @Override
enter()174         public void enter() {
175             super.enter();
176             Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
177                     "Entering state " + getName());
178         }
179 
180         @Override
exit()181         public void exit() {
182             Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
183                     "Leaving state " + getName());
184             super.exit();
185         }
186 
187         @Override
processMessage(Message msg)188         public boolean processMessage(Message msg) {
189             switch (msg.what) {
190                 case CONNECT_WIRED_HEADSET:
191                     Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
192                             "Wired headset connected");
193                     mAvailableRoutes &= ~ROUTE_EARPIECE;
194                     mAvailableRoutes |= ROUTE_WIRED_HEADSET;
195                     return NOT_HANDLED;
196                 case CONNECT_BLUETOOTH:
197                     Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
198                             "Bluetooth connected");
199                     mAvailableRoutes |= ROUTE_BLUETOOTH;
200                     return NOT_HANDLED;
201                 case DISCONNECT_WIRED_HEADSET:
202                     Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
203                             "Wired headset disconnected");
204                     mAvailableRoutes &= ~ROUTE_WIRED_HEADSET;
205                     if (mDoesDeviceSupportEarpieceRoute) {
206                         mAvailableRoutes |= ROUTE_EARPIECE;
207                     }
208                     return NOT_HANDLED;
209                 case DISCONNECT_BLUETOOTH:
210                     Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
211                             "Bluetooth disconnected");
212                     mAvailableRoutes &= ~ROUTE_BLUETOOTH;
213                     return NOT_HANDLED;
214                 case SWITCH_BASELINE_ROUTE:
215                     sendInternalMessage(calculateBaselineRouteMessage(false));
216                     return HANDLED;
217                 case USER_SWITCH_BASELINE_ROUTE:
218                     sendInternalMessage(calculateBaselineRouteMessage(true));
219                     return HANDLED;
220                 default:
221                     return NOT_HANDLED;
222             }
223         }
224 
225         // Behavior will depend on whether the state is an active one or a quiescent one.
updateSystemAudioState()226         abstract public void updateSystemAudioState();
isActive()227         abstract public boolean isActive();
228     }
229 
230     class ActiveEarpieceRoute extends EarpieceRoute {
231         @Override
getName()232         public String getName() {
233             return ACTIVE_EARPIECE_ROUTE_NAME;
234         }
235 
236         @Override
isActive()237         public boolean isActive() {
238             return true;
239         }
240 
241         @Override
enter()242         public void enter() {
243             super.enter();
244             setSpeakerphoneOn(false);
245             setBluetoothOn(false);
246             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
247                     mAvailableRoutes);
248             setSystemAudioState(newState);
249             updateInternalCallAudioState();
250         }
251 
252         @Override
updateSystemAudioState()253         public void updateSystemAudioState() {
254             updateInternalCallAudioState();
255             setSystemAudioState(mCurrentCallAudioState);
256         }
257 
258         @Override
processMessage(Message msg)259         public boolean processMessage(Message msg) {
260             if (super.processMessage(msg) == HANDLED) {
261                 return HANDLED;
262             }
263             switch (msg.what) {
264                 case SWITCH_EARPIECE:
265                 case USER_SWITCH_EARPIECE:
266                     // Nothing to do here
267                     return HANDLED;
268                 case SWITCH_BLUETOOTH:
269                 case USER_SWITCH_BLUETOOTH:
270                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
271                         transitionTo(mActiveBluetoothRoute);
272                     } else {
273                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
274                     }
275                     return HANDLED;
276                 case SWITCH_HEADSET:
277                 case USER_SWITCH_HEADSET:
278                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
279                         transitionTo(mActiveHeadsetRoute);
280                     } else {
281                         Log.w(this, "Ignoring switch to headset command. Not available.");
282                     }
283                     return HANDLED;
284                 case SWITCH_SPEAKER:
285                 case USER_SWITCH_SPEAKER:
286                     transitionTo(mActiveSpeakerRoute);
287                     return HANDLED;
288                 case SWITCH_FOCUS:
289                     if (msg.arg1 == NO_FOCUS) {
290                         reinitialize();
291                     }
292                     return HANDLED;
293                 default:
294                     return NOT_HANDLED;
295             }
296         }
297     }
298 
299     class QuiescentEarpieceRoute extends EarpieceRoute {
300         @Override
getName()301         public String getName() {
302             return QUIESCENT_EARPIECE_ROUTE_NAME;
303         }
304 
305         @Override
isActive()306         public boolean isActive() {
307             return false;
308         }
309 
310         @Override
enter()311         public void enter() {
312             super.enter();
313             mHasUserExplicitlyLeftBluetooth = false;
314             updateInternalCallAudioState();
315         }
316 
317         @Override
updateSystemAudioState()318         public void updateSystemAudioState() {
319             updateInternalCallAudioState();
320         }
321 
322         @Override
processMessage(Message msg)323         public boolean processMessage(Message msg) {
324             if (super.processMessage(msg) == HANDLED) {
325                 return HANDLED;
326             }
327             switch (msg.what) {
328                 case SWITCH_EARPIECE:
329                 case USER_SWITCH_EARPIECE:
330                     // Nothing to do here
331                     return HANDLED;
332                 case SWITCH_BLUETOOTH:
333                 case USER_SWITCH_BLUETOOTH:
334                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
335                         transitionTo(mQuiescentBluetoothRoute);
336                     } else {
337                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
338                     }
339                     return HANDLED;
340                 case SWITCH_HEADSET:
341                 case USER_SWITCH_HEADSET:
342                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
343                         transitionTo(mQuiescentHeadsetRoute);
344                     } else {
345                         Log.w(this, "Ignoring switch to headset command. Not available.");
346                     }
347                     return HANDLED;
348                 case SWITCH_SPEAKER:
349                 case USER_SWITCH_SPEAKER:
350                     transitionTo(mQuiescentSpeakerRoute);
351                     return HANDLED;
352                 case SWITCH_FOCUS:
353                     if (msg.arg1 == HAS_FOCUS) {
354                         transitionTo(mActiveEarpieceRoute);
355                     }
356                     return HANDLED;
357                 default:
358                     return NOT_HANDLED;
359             }
360         }
361     }
362 
363     abstract class EarpieceRoute extends AudioState {
364         @Override
processMessage(Message msg)365         public boolean processMessage(Message msg) {
366             if (super.processMessage(msg) == HANDLED) {
367                 return HANDLED;
368             }
369             switch (msg.what) {
370                 case CONNECT_WIRED_HEADSET:
371                     sendInternalMessage(SWITCH_HEADSET);
372                     return HANDLED;
373                 case CONNECT_BLUETOOTH:
374                     if (!mHasUserExplicitlyLeftBluetooth) {
375                         sendInternalMessage(SWITCH_BLUETOOTH);
376                     } else {
377                         Log.i(this, "Not switching to BT route from earpiece because user has " +
378                                 "explicitly disconnected.");
379                         updateSystemAudioState();
380                     }
381                     return HANDLED;
382                 case DISCONNECT_BLUETOOTH:
383                     updateSystemAudioState();
384                     // No change in audio route required
385                     return HANDLED;
386                 case DISCONNECT_WIRED_HEADSET:
387                     Log.e(this, new IllegalStateException(),
388                             "Wired headset should not go from connected to not when on " +
389                             "earpiece");
390                     updateSystemAudioState();
391                     return HANDLED;
392                 case BT_AUDIO_DISCONNECT:
393                     // This may be sent as a confirmation by the BT stack after switch off BT.
394                     return HANDLED;
395                 case CONNECT_DOCK:
396                     sendInternalMessage(SWITCH_SPEAKER);
397                     return HANDLED;
398                 case DISCONNECT_DOCK:
399                     // Nothing to do here
400                     return HANDLED;
401                 default:
402                     return NOT_HANDLED;
403             }
404         }
405     }
406 
407     class ActiveHeadsetRoute extends HeadsetRoute {
408         @Override
getName()409         public String getName() {
410             return ACTIVE_HEADSET_ROUTE_NAME;
411         }
412 
413         @Override
isActive()414         public boolean isActive() {
415             return true;
416         }
417 
418         @Override
enter()419         public void enter() {
420             super.enter();
421             setSpeakerphoneOn(false);
422             setBluetoothOn(false);
423             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET,
424                     mAvailableRoutes);
425             setSystemAudioState(newState);
426             updateInternalCallAudioState();
427         }
428 
429         @Override
updateSystemAudioState()430         public void updateSystemAudioState() {
431             updateInternalCallAudioState();
432             setSystemAudioState(mCurrentCallAudioState);
433         }
434 
435         @Override
processMessage(Message msg)436         public boolean processMessage(Message msg) {
437             if (super.processMessage(msg) == HANDLED) {
438                 return HANDLED;
439             }
440             switch (msg.what) {
441                 case SWITCH_EARPIECE:
442                 case USER_SWITCH_EARPIECE:
443                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
444                         transitionTo(mActiveEarpieceRoute);
445                     } else {
446                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
447                     }
448                     return HANDLED;
449                 case SWITCH_BLUETOOTH:
450                 case USER_SWITCH_BLUETOOTH:
451                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
452                         transitionTo(mActiveBluetoothRoute);
453                     } else {
454                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
455                     }
456                     return HANDLED;
457                 case SWITCH_HEADSET:
458                 case USER_SWITCH_HEADSET:
459                     // Nothing to do
460                     return HANDLED;
461                 case SWITCH_SPEAKER:
462                 case USER_SWITCH_SPEAKER:
463                     transitionTo(mActiveSpeakerRoute);
464                     return HANDLED;
465                 case SWITCH_FOCUS:
466                     if (msg.arg1 == NO_FOCUS) {
467                         reinitialize();
468                     }
469                     return HANDLED;
470                 default:
471                     return NOT_HANDLED;
472             }
473         }
474     }
475 
476     class QuiescentHeadsetRoute extends HeadsetRoute {
477         @Override
getName()478         public String getName() {
479             return QUIESCENT_HEADSET_ROUTE_NAME;
480         }
481 
482         @Override
isActive()483         public boolean isActive() {
484             return false;
485         }
486 
487         @Override
enter()488         public void enter() {
489             super.enter();
490             mHasUserExplicitlyLeftBluetooth = false;
491             updateInternalCallAudioState();
492         }
493 
494         @Override
updateSystemAudioState()495         public void updateSystemAudioState() {
496             updateInternalCallAudioState();
497         }
498 
499         @Override
processMessage(Message msg)500         public boolean processMessage(Message msg) {
501             if (super.processMessage(msg) == HANDLED) {
502                 return HANDLED;
503             }
504             switch (msg.what) {
505                 case SWITCH_EARPIECE:
506                 case USER_SWITCH_EARPIECE:
507                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
508                         transitionTo(mQuiescentEarpieceRoute);
509                     } else {
510                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
511                     }
512                     return HANDLED;
513                 case SWITCH_BLUETOOTH:
514                 case USER_SWITCH_BLUETOOTH:
515                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
516                         transitionTo(mQuiescentBluetoothRoute);
517                     } else {
518                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
519                     }
520                     return HANDLED;
521                 case SWITCH_HEADSET:
522                 case USER_SWITCH_HEADSET:
523                     // Nothing to do
524                     return HANDLED;
525                 case SWITCH_SPEAKER:
526                 case USER_SWITCH_SPEAKER:
527                     transitionTo(mQuiescentSpeakerRoute);
528                     return HANDLED;
529                 case SWITCH_FOCUS:
530                     if (msg.arg1 == HAS_FOCUS) {
531                         transitionTo(mActiveHeadsetRoute);
532                     }
533                     return HANDLED;
534                 default:
535                     return NOT_HANDLED;
536             }
537         }
538     }
539 
540     abstract class HeadsetRoute extends AudioState {
541         @Override
processMessage(Message msg)542         public boolean processMessage(Message msg) {
543             if (super.processMessage(msg) == HANDLED) {
544                 return HANDLED;
545             }
546             switch (msg.what) {
547                 case CONNECT_WIRED_HEADSET:
548                     Log.e(this, new IllegalStateException(),
549                             "Wired headset should already be connected.");
550                     mAvailableRoutes |= ROUTE_WIRED_HEADSET;
551                     updateSystemAudioState();
552                     return HANDLED;
553                 case CONNECT_BLUETOOTH:
554                     if (!mHasUserExplicitlyLeftBluetooth) {
555                         sendInternalMessage(SWITCH_BLUETOOTH);
556                     } else {
557                         Log.i(this, "Not switching to BT route from headset because user has " +
558                                 "explicitly disconnected.");
559                         updateSystemAudioState();
560                     }
561                     return HANDLED;
562                 case DISCONNECT_BLUETOOTH:
563                     updateSystemAudioState();
564                     // No change in audio route required
565                     return HANDLED;
566                 case DISCONNECT_WIRED_HEADSET:
567                     if (mWasOnSpeaker) {
568                         sendInternalMessage(SWITCH_SPEAKER);
569                     } else {
570                         sendInternalMessage(SWITCH_BASELINE_ROUTE);
571                     }
572                     return HANDLED;
573                 case BT_AUDIO_DISCONNECT:
574                     // This may be sent as a confirmation by the BT stack after switch off BT.
575                     return HANDLED;
576                 case CONNECT_DOCK:
577                     // Nothing to do here
578                     return HANDLED;
579                 case DISCONNECT_DOCK:
580                     // Nothing to do here
581                     return HANDLED;
582                 default:
583                     return NOT_HANDLED;
584             }
585         }
586     }
587 
588     class ActiveBluetoothRoute extends BluetoothRoute {
589         @Override
getName()590         public String getName() {
591             return ACTIVE_BLUETOOTH_ROUTE_NAME;
592         }
593 
594         @Override
isActive()595         public boolean isActive() {
596             return true;
597         }
598 
599         @Override
enter()600         public void enter() {
601             super.enter();
602             setSpeakerphoneOn(false);
603             setBluetoothOn(true);
604             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
605                     mAvailableRoutes);
606             setSystemAudioState(newState);
607             updateInternalCallAudioState();
608         }
609 
610         @Override
updateSystemAudioState()611         public void updateSystemAudioState() {
612             updateInternalCallAudioState();
613             setSystemAudioState(mCurrentCallAudioState);
614         }
615 
616         @Override
processMessage(Message msg)617         public boolean processMessage(Message msg) {
618             if (super.processMessage(msg) == HANDLED) {
619                 return HANDLED;
620             }
621             switch (msg.what) {
622                 case USER_SWITCH_EARPIECE:
623                     mHasUserExplicitlyLeftBluetooth = true;
624                     // fall through
625                 case SWITCH_EARPIECE:
626                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
627                         transitionTo(mActiveEarpieceRoute);
628                     } else {
629                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
630                     }
631                     return HANDLED;
632                 case SWITCH_BLUETOOTH:
633                 case USER_SWITCH_BLUETOOTH:
634                     // Nothing to do
635                     return HANDLED;
636                 case USER_SWITCH_HEADSET:
637                     mHasUserExplicitlyLeftBluetooth = true;
638                     // fall through
639                 case SWITCH_HEADSET:
640                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
641                         transitionTo(mActiveHeadsetRoute);
642                     } else {
643                         Log.w(this, "Ignoring switch to headset command. Not available.");
644                     }
645                     return HANDLED;
646                 case USER_SWITCH_SPEAKER:
647                     mHasUserExplicitlyLeftBluetooth = true;
648                     // fall through
649                 case SWITCH_SPEAKER:
650                     transitionTo(mActiveSpeakerRoute);
651                     return HANDLED;
652                 case SWITCH_FOCUS:
653                     if (msg.arg1 == NO_FOCUS) {
654                         reinitialize();
655                     }
656                     return HANDLED;
657                 case BT_AUDIO_DISCONNECT:
658                     sendInternalMessage(SWITCH_BASELINE_ROUTE);
659                     return HANDLED;
660                 default:
661                     return NOT_HANDLED;
662             }
663         }
664     }
665 
666     class QuiescentBluetoothRoute extends BluetoothRoute {
667         @Override
getName()668         public String getName() {
669             return QUIESCENT_BLUETOOTH_ROUTE_NAME;
670         }
671 
672         @Override
isActive()673         public boolean isActive() {
674             return false;
675         }
676 
677         @Override
enter()678         public void enter() {
679             super.enter();
680             mHasUserExplicitlyLeftBluetooth = false;
681             updateInternalCallAudioState();
682         }
683 
684         @Override
updateSystemAudioState()685         public void updateSystemAudioState() {
686             updateInternalCallAudioState();
687         }
688 
689         @Override
processMessage(Message msg)690         public boolean processMessage(Message msg) {
691             if (super.processMessage(msg) == HANDLED) {
692                 return HANDLED;
693             }
694             switch (msg.what) {
695                 case SWITCH_EARPIECE:
696                 case USER_SWITCH_EARPIECE:
697                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
698                         transitionTo(mQuiescentEarpieceRoute);
699                     } else {
700                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
701                     }
702                     return HANDLED;
703                 case SWITCH_BLUETOOTH:
704                 case USER_SWITCH_BLUETOOTH:
705                     // Nothing to do
706                     return HANDLED;
707                 case SWITCH_HEADSET:
708                 case USER_SWITCH_HEADSET:
709                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
710                         transitionTo(mQuiescentHeadsetRoute);
711                     } else {
712                         Log.w(this, "Ignoring switch to headset command. Not available.");
713                     }
714                     return HANDLED;
715                 case SWITCH_SPEAKER:
716                 case USER_SWITCH_SPEAKER:
717                     transitionTo(mQuiescentSpeakerRoute);
718                     return HANDLED;
719                 case SWITCH_FOCUS:
720                     if (msg.arg1 == HAS_FOCUS) {
721                         transitionTo(mActiveBluetoothRoute);
722                     }
723                     return HANDLED;
724                 case BT_AUDIO_DISCONNECT:
725                     // Ignore this -- audio disconnecting while quiescent should not cause a
726                     // route switch, since the device is still connected.
727                     return HANDLED;
728                 default:
729                     return NOT_HANDLED;
730             }
731         }
732     }
733 
734     abstract class BluetoothRoute extends AudioState {
735         @Override
processMessage(Message msg)736         public boolean processMessage(Message msg) {
737             if (super.processMessage(msg) == HANDLED) {
738                 return HANDLED;
739             }
740             switch (msg.what) {
741                 case CONNECT_WIRED_HEADSET:
742                     sendInternalMessage(SWITCH_HEADSET);
743                     return HANDLED;
744                 case CONNECT_BLUETOOTH:
745                     // We can't tell when a change in bluetooth state corresponds to an
746                     // actual connection or disconnection, so we'll just ignore it if we're already
747                     // in the bluetooth route.
748                     return HANDLED;
749                 case DISCONNECT_BLUETOOTH:
750                     sendInternalMessage(SWITCH_BASELINE_ROUTE);
751                     mWasOnSpeaker = false;
752                     return HANDLED;
753                 case DISCONNECT_WIRED_HEADSET:
754                     updateSystemAudioState();
755                     // No change in audio route required
756                     return HANDLED;
757                 case CONNECT_DOCK:
758                     // Nothing to do here
759                     return HANDLED;
760                 case DISCONNECT_DOCK:
761                     // Nothing to do here
762                     return HANDLED;
763                 default:
764                     return NOT_HANDLED;
765             }
766         }
767     }
768 
769     class ActiveSpeakerRoute extends SpeakerRoute {
770         @Override
getName()771         public String getName() {
772             return ACTIVE_SPEAKER_ROUTE_NAME;
773         }
774 
775         @Override
isActive()776         public boolean isActive() {
777             return true;
778         }
779 
780         @Override
enter()781         public void enter() {
782             super.enter();
783             mWasOnSpeaker = true;
784             setSpeakerphoneOn(true);
785             setBluetoothOn(false);
786             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
787                     mAvailableRoutes);
788             setSystemAudioState(newState);
789             updateInternalCallAudioState();
790         }
791 
792         @Override
updateSystemAudioState()793         public void updateSystemAudioState() {
794             updateInternalCallAudioState();
795             setSystemAudioState(mCurrentCallAudioState);
796         }
797 
798         @Override
processMessage(Message msg)799         public boolean processMessage(Message msg) {
800             if (super.processMessage(msg) == HANDLED) {
801                 return HANDLED;
802             }
803             switch(msg.what) {
804                 case USER_SWITCH_EARPIECE:
805                     mWasOnSpeaker = false;
806                     // fall through
807                 case SWITCH_EARPIECE:
808                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
809                         transitionTo(mActiveEarpieceRoute);
810                     } else {
811                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
812                     }
813                     return HANDLED;
814                 case USER_SWITCH_BLUETOOTH:
815                     mWasOnSpeaker = false;
816                     // fall through
817                 case SWITCH_BLUETOOTH:
818                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
819                         transitionTo(mActiveBluetoothRoute);
820                     } else {
821                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
822                     }
823                     return HANDLED;
824                 case USER_SWITCH_HEADSET:
825                     mWasOnSpeaker = false;
826                     // fall through
827                 case SWITCH_HEADSET:
828                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
829                         transitionTo(mActiveHeadsetRoute);
830                     } else {
831                         Log.w(this, "Ignoring switch to headset command. Not available.");
832                     }
833                     return HANDLED;
834                 case SWITCH_SPEAKER:
835                 case USER_SWITCH_SPEAKER:
836                     // Nothing to do
837                     return HANDLED;
838                 case SWITCH_FOCUS:
839                     if (msg.arg1 == NO_FOCUS) {
840                         reinitialize();
841                     }
842                     return HANDLED;
843                 default:
844                     return NOT_HANDLED;
845             }
846         }
847     }
848 
849     class QuiescentSpeakerRoute extends SpeakerRoute {
850         @Override
getName()851         public String getName() {
852             return QUIESCENT_SPEAKER_ROUTE_NAME;
853         }
854 
855         @Override
isActive()856         public boolean isActive() {
857             return false;
858         }
859 
860         @Override
enter()861         public void enter() {
862             super.enter();
863             mHasUserExplicitlyLeftBluetooth = false;
864             // Omit setting mWasOnSpeaker to true here, since this does not reflect a call
865             // actually being on speakerphone.
866             updateInternalCallAudioState();
867         }
868 
869         @Override
updateSystemAudioState()870         public void updateSystemAudioState() {
871             updateInternalCallAudioState();
872         }
873 
874         @Override
processMessage(Message msg)875         public boolean processMessage(Message msg) {
876             if (super.processMessage(msg) == HANDLED) {
877                 return HANDLED;
878             }
879             switch(msg.what) {
880                 case SWITCH_EARPIECE:
881                 case USER_SWITCH_EARPIECE:
882                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
883                         transitionTo(mQuiescentEarpieceRoute);
884                     } else {
885                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
886                     }
887                     return HANDLED;
888                 case SWITCH_BLUETOOTH:
889                 case USER_SWITCH_BLUETOOTH:
890                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
891                         transitionTo(mQuiescentBluetoothRoute);
892                     } else {
893                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
894                     }
895                     return HANDLED;
896                 case SWITCH_HEADSET:
897                 case USER_SWITCH_HEADSET:
898                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
899                         transitionTo(mQuiescentHeadsetRoute);
900                     } else {
901                         Log.w(this, "Ignoring switch to headset command. Not available.");
902                     }
903                     return HANDLED;
904                 case SWITCH_SPEAKER:
905                 case USER_SWITCH_SPEAKER:
906                     // Nothing to do
907                     return HANDLED;
908                 case SWITCH_FOCUS:
909                     if (msg.arg1 == HAS_FOCUS) {
910                         transitionTo(mActiveSpeakerRoute);
911                     }
912                     return HANDLED;
913                 default:
914                     return NOT_HANDLED;
915             }
916         }
917     }
918 
919     abstract class SpeakerRoute extends AudioState {
920         @Override
processMessage(Message msg)921         public boolean processMessage(Message msg) {
922             if (super.processMessage(msg) == HANDLED) {
923                 return HANDLED;
924             }
925             switch (msg.what) {
926                 case CONNECT_WIRED_HEADSET:
927                     sendInternalMessage(SWITCH_HEADSET);
928                     return HANDLED;
929                 case CONNECT_BLUETOOTH:
930                     if (!mHasUserExplicitlyLeftBluetooth) {
931                         sendInternalMessage(SWITCH_BLUETOOTH);
932                     } else {
933                         Log.i(this, "Not switching to BT route from speaker because user has " +
934                                 "explicitly disconnected.");
935                         updateSystemAudioState();
936                     }
937                     return HANDLED;
938                 case DISCONNECT_BLUETOOTH:
939                     updateSystemAudioState();
940                     // No change in audio route required
941                     return HANDLED;
942                 case DISCONNECT_WIRED_HEADSET:
943                     updateSystemAudioState();
944                     // No change in audio route required
945                     return HANDLED;
946                 case BT_AUDIO_DISCONNECT:
947                     // This may be sent as a confirmation by the BT stack after switch off BT.
948                     return HANDLED;
949                 case CONNECT_DOCK:
950                     // Nothing to do here
951                     return HANDLED;
952                 case DISCONNECT_DOCK:
953                     sendInternalMessage(SWITCH_BASELINE_ROUTE);
954                     return HANDLED;
955                default:
956                     return NOT_HANDLED;
957             }
958         }
959     }
960 
961     private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute();
962     private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute();
963     private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute();
964     private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute();
965     private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute();
966     private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute();
967     private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute();
968     private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute();
969 
970     /**
971      * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
972      * states
973      */
974     private int mAvailableRoutes;
975     private boolean mWasOnSpeaker;
976     private boolean mIsMuted;
977 
978     private final Context mContext;
979     private final CallsManager mCallsManager;
980     private final AudioManager mAudioManager;
981     private final BluetoothManager mBluetoothManager;
982     private final WiredHeadsetManager mWiredHeadsetManager;
983     private final StatusBarNotifier mStatusBarNotifier;
984     private final CallAudioManager.AudioServiceFactory mAudioServiceFactory;
985     private final boolean mDoesDeviceSupportEarpieceRoute;
986     private boolean mHasUserExplicitlyLeftBluetooth = false;
987 
988     private HashMap<String, Integer> mStateNameToRouteCode;
989     private HashMap<Integer, AudioState> mRouteCodeToQuiescentState;
990 
991     // CallAudioState is used as an interface to communicate with many other system components.
992     // No internal state transitions should depend on this variable.
993     private CallAudioState mCurrentCallAudioState;
994     private CallAudioState mLastKnownCallAudioState;
995 
CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, boolean doesDeviceSupportEarpieceRoute)996     public CallAudioRouteStateMachine(
997             Context context,
998             CallsManager callsManager,
999             BluetoothManager bluetoothManager,
1000             WiredHeadsetManager wiredHeadsetManager,
1001             StatusBarNotifier statusBarNotifier,
1002             CallAudioManager.AudioServiceFactory audioServiceFactory,
1003             boolean doesDeviceSupportEarpieceRoute) {
1004         super(NAME);
1005         addState(mActiveEarpieceRoute);
1006         addState(mActiveHeadsetRoute);
1007         addState(mActiveBluetoothRoute);
1008         addState(mActiveSpeakerRoute);
1009         addState(mQuiescentEarpieceRoute);
1010         addState(mQuiescentHeadsetRoute);
1011         addState(mQuiescentBluetoothRoute);
1012         addState(mQuiescentSpeakerRoute);
1013 
1014         mContext = context;
1015         mCallsManager = callsManager;
1016         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
1017         mBluetoothManager = bluetoothManager;
1018         mWiredHeadsetManager = wiredHeadsetManager;
1019         mStatusBarNotifier = statusBarNotifier;
1020         mAudioServiceFactory = audioServiceFactory;
1021         mDoesDeviceSupportEarpieceRoute = doesDeviceSupportEarpieceRoute;
1022 
1023         mStateNameToRouteCode = new HashMap<>(8);
1024         mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE);
1025         mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1026         mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
1027         mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER);
1028         mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE);
1029         mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1030         mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
1031         mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER);
1032 
1033         mRouteCodeToQuiescentState = new HashMap<>(4);
1034         mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute);
1035         mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute);
1036         mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute);
1037         mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
1038     }
1039 
1040     /**
1041      * Initializes the state machine with info on initial audio route, supported audio routes,
1042      * and mute status.
1043      */
initialize()1044     public void initialize() {
1045         CallAudioState initState = getInitialAudioState();
1046         initialize(initState);
1047     }
1048 
initialize(CallAudioState initState)1049     public void initialize(CallAudioState initState) {
1050         mCurrentCallAudioState = initState;
1051         mLastKnownCallAudioState = initState;
1052         mAvailableRoutes = initState.getSupportedRouteMask();
1053         mIsMuted = initState.isMuted();
1054         mWasOnSpeaker = false;
1055 
1056         mStatusBarNotifier.notifyMute(initState.isMuted());
1057         mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
1058         setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute()));
1059         start();
1060     }
1061 
1062     /**
1063      * Getter for the current CallAudioState object that the state machine is keeping track of.
1064      * Used for compatibility purposes.
1065      */
getCurrentCallAudioState()1066     public CallAudioState getCurrentCallAudioState() {
1067         return mCurrentCallAudioState;
1068     }
1069 
sendMessageWithSessionInfo(int message, int arg)1070     public void sendMessageWithSessionInfo(int message, int arg) {
1071         sendMessage(message, arg, 0, Log.createSubsession());
1072     }
1073 
sendMessageWithSessionInfo(int message)1074     public void sendMessageWithSessionInfo(int message) {
1075         sendMessage(message, 0, 0, Log.createSubsession());
1076     }
1077 
1078     /**
1079      * This is for state-independent changes in audio route (i.e. muting or runnables)
1080      * @param msg that couldn't be handled.
1081      */
1082     @Override
unhandledMessage(Message msg)1083     protected void unhandledMessage(Message msg) {
1084         CallAudioState newCallAudioState;
1085         switch (msg.what) {
1086             case MUTE_ON:
1087                 setMuteOn(true);
1088                 newCallAudioState = new CallAudioState(mIsMuted,
1089                         mCurrentCallAudioState.getRoute(),
1090                         mAvailableRoutes);
1091                 setSystemAudioState(newCallAudioState);
1092                 updateInternalCallAudioState();
1093                 return;
1094             case MUTE_OFF:
1095                 setMuteOn(false);
1096                 newCallAudioState = new CallAudioState(mIsMuted,
1097                         mCurrentCallAudioState.getRoute(),
1098                         mAvailableRoutes);
1099                 setSystemAudioState(newCallAudioState);
1100                 updateInternalCallAudioState();
1101                 return;
1102             case TOGGLE_MUTE:
1103                 if (mIsMuted) {
1104                     sendInternalMessage(MUTE_OFF);
1105                 } else {
1106                     sendInternalMessage(MUTE_ON);
1107                 }
1108                 return;
1109             case UPDATE_SYSTEM_AUDIO_ROUTE:
1110                 resendSystemAudioState();
1111                 return;
1112             case RUN_RUNNABLE:
1113                 java.lang.Runnable r = (java.lang.Runnable) msg.obj;
1114                 r.run();
1115                 return;
1116             default:
1117                 Log.e(this, new IllegalStateException(),
1118                         "Unexpected message code");
1119         }
1120     }
1121 
quitStateMachine()1122     public void quitStateMachine() {
1123         quitNow();
1124     }
1125 
setSpeakerphoneOn(boolean on)1126     private void setSpeakerphoneOn(boolean on) {
1127         if (mAudioManager.isSpeakerphoneOn() != on) {
1128             Log.i(this, "turning speaker phone %s", on);
1129             mAudioManager.setSpeakerphoneOn(on);
1130             mStatusBarNotifier.notifySpeakerphone(on);
1131         }
1132     }
1133 
setBluetoothOn(boolean on)1134     private void setBluetoothOn(boolean on) {
1135         if (mBluetoothManager.isBluetoothAvailable()) {
1136             boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
1137             if (on != isAlreadyOn) {
1138                 Log.i(this, "connecting bluetooth %s", on);
1139                 if (on) {
1140                     mBluetoothManager.connectBluetoothAudio();
1141                 } else {
1142                     mBluetoothManager.disconnectBluetoothAudio();
1143                 }
1144             }
1145         }
1146     }
1147 
setMuteOn(boolean mute)1148     private void setMuteOn(boolean mute) {
1149         mIsMuted = mute;
1150         Log.event(mCallsManager.getForegroundCall(), mute ? Log.Events.MUTE : Log.Events.UNMUTE);
1151 
1152         if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) {
1153             IAudioService audio = mAudioServiceFactory.getAudioService();
1154             Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
1155                     mute, audio == null);
1156             if (audio != null) {
1157                 try {
1158                     // We use the audio service directly here so that we can specify
1159                     // the current user. Telecom runs in the system_server process which
1160                     // may run as a separate user from the foreground user. If we
1161                     // used AudioManager directly, we would change mute for the system's
1162                     // user and not the current foreground, which we want to avoid.
1163                     audio.setMicrophoneMute(
1164                             mute, mContext.getOpPackageName(), getCurrentUserId());
1165                     mStatusBarNotifier.notifyMute(mute);
1166 
1167                 } catch (RemoteException e) {
1168                     Log.e(this, e, "Remote exception while toggling mute.");
1169                 }
1170                 // TODO: Check microphone state after attempting to set to ensure that
1171                 // our state corroborates AudioManager's state.
1172             }
1173         }
1174     }
1175 
1176     /**
1177      * Updates the CallAudioState object from current internal state. The result is used for
1178      * external communication only.
1179      */
updateInternalCallAudioState()1180     private void updateInternalCallAudioState() {
1181         IState currentState = getCurrentState();
1182         if (currentState == null) {
1183             Log.e(this, new IllegalStateException(), "Current state should never be null" +
1184                     " when updateInternalCallAudioState is called.");
1185             mCurrentCallAudioState = new CallAudioState(
1186                     mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes);
1187             return;
1188         }
1189         int currentRoute = mStateNameToRouteCode.get(currentState.getName());
1190         mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes);
1191     }
1192 
setSystemAudioState(CallAudioState newCallAudioState)1193     private void setSystemAudioState(CallAudioState newCallAudioState) {
1194         setSystemAudioState(newCallAudioState, false);
1195     }
1196 
resendSystemAudioState()1197     private void resendSystemAudioState() {
1198         setSystemAudioState(mLastKnownCallAudioState, true);
1199     }
1200 
setSystemAudioState(CallAudioState newCallAudioState, boolean force)1201     private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) {
1202         Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState,
1203                 newCallAudioState);
1204         if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) {
1205             if (newCallAudioState.getRoute() != mLastKnownCallAudioState.getRoute()) {
1206                 Log.event(mCallsManager.getForegroundCall(),
1207                         AUDIO_ROUTE_TO_LOG_EVENT.get(newCallAudioState.getRoute(),
1208                                 Log.Events.AUDIO_ROUTE));
1209             }
1210 
1211             mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState);
1212             updateAudioForForegroundCall(newCallAudioState);
1213             mLastKnownCallAudioState = newCallAudioState;
1214         }
1215     }
1216 
updateAudioForForegroundCall(CallAudioState newCallAudioState)1217     private void updateAudioForForegroundCall(CallAudioState newCallAudioState) {
1218         Call call = mCallsManager.getForegroundCall();
1219         if (call != null && call.getConnectionService() != null) {
1220             call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState);
1221         }
1222     }
1223 
calculateSupportedRoutes()1224     private int calculateSupportedRoutes() {
1225         int routeMask = CallAudioState.ROUTE_SPEAKER;
1226 
1227         if (mWiredHeadsetManager.isPluggedIn()) {
1228             routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
1229         } else if (mDoesDeviceSupportEarpieceRoute){
1230             routeMask |= CallAudioState.ROUTE_EARPIECE;
1231         }
1232 
1233         if (mBluetoothManager.isBluetoothAvailable()) {
1234             routeMask |=  CallAudioState.ROUTE_BLUETOOTH;
1235         }
1236 
1237         return routeMask;
1238     }
1239 
sendInternalMessage(int messageCode)1240     private void sendInternalMessage(int messageCode) {
1241         // Internal messages are messages which the state machine sends to itself in the
1242         // course of processing externally-sourced messages. We want to send these messages at
1243         // the front of the queue in order to make actions appear atomic to the user and to
1244         // prevent scenarios such as these:
1245         // 1. State machine handler thread is suspended for some reason.
1246         // 2. Headset gets connected (sends CONNECT_HEADSET).
1247         // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER).
1248         // 4. State machine handler is un-suspended.
1249         // 5. State machine handler processes the CONNECT_HEADSET message and sends
1250         //    SWITCH_HEADSET at end of queue.
1251         // 6. State machine handler processes SWITCH_SPEAKER.
1252         // 7. State machine handler processes SWITCH_HEADSET.
1253         Session subsession = Log.createSubsession();
1254         if(subsession != null) {
1255             sendMessageAtFrontOfQueue(messageCode, subsession);
1256         } else {
1257             sendMessageAtFrontOfQueue(messageCode);
1258         }
1259     }
1260 
getInitialAudioState()1261     private CallAudioState getInitialAudioState() {
1262         int supportedRouteMask = calculateSupportedRoutes();
1263         final int route;
1264 
1265         if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0) {
1266             route = ROUTE_BLUETOOTH;
1267         } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) {
1268             route = ROUTE_WIRED_HEADSET;
1269         } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) {
1270             route = ROUTE_EARPIECE;
1271         } else {
1272             route = ROUTE_SPEAKER;
1273         }
1274 
1275         return new CallAudioState(false, route, supportedRouteMask);
1276     }
1277 
getCurrentUserId()1278     private int getCurrentUserId() {
1279         final long ident = Binder.clearCallingIdentity();
1280         try {
1281             UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
1282             return currentUser.id;
1283         } catch (RemoteException e) {
1284             // Activity manager not running, nothing we can do assume user 0.
1285         } finally {
1286             Binder.restoreCallingIdentity(ident);
1287         }
1288         return UserHandle.USER_OWNER;
1289     }
1290 
isInActiveState()1291     private boolean isInActiveState() {
1292         AudioState currentState = (AudioState) getCurrentState();
1293         if (currentState == null) {
1294             Log.w(this, "Current state is null, assuming inactive state");
1295             return false;
1296         }
1297         return currentState.isActive();
1298     }
1299 
doesDeviceSupportEarpieceRoute()1300     public static boolean doesDeviceSupportEarpieceRoute() {
1301         String[] characteristics = SystemProperties.get("ro.build.characteristics").split(",");
1302         for (String characteristic : characteristics) {
1303             if ("watch".equals(characteristic)) {
1304                 return false;
1305             }
1306         }
1307         return true;
1308     }
1309 
calculateBaselineRouteMessage(boolean isExplicitUserRequest)1310     private int calculateBaselineRouteMessage(boolean isExplicitUserRequest) {
1311         if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
1312             return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE;
1313         } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1314             return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET;
1315         } else if (!mDoesDeviceSupportEarpieceRoute) {
1316             return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER;
1317         } else {
1318             Log.e(this, new IllegalStateException(),
1319                     "Neither headset nor earpiece are available, but there is an " +
1320                             "earpiece on the device. Defaulting to earpiece.");
1321             return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE;
1322         }
1323     }
1324 
reinitialize()1325     private void reinitialize() {
1326         CallAudioState initState = getInitialAudioState();
1327         mAvailableRoutes = initState.getSupportedRouteMask();
1328         mIsMuted = initState.isMuted();
1329         setMuteOn(mIsMuted);
1330         mWasOnSpeaker = false;
1331         mHasUserExplicitlyLeftBluetooth = false;
1332         mLastKnownCallAudioState = initState;
1333         transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute()));
1334     }
1335 }
1336