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