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.ActivityManager;
21 import android.bluetooth.BluetoothDevice;
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.AudioDeviceInfo;
28 import android.media.AudioManager;
29 import android.media.IAudioService;
30 import android.os.Binder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.UserHandle;
35 import android.telecom.CallAudioState;
36 import android.telecom.Log;
37 import android.telecom.Logging.Session;
38 import android.util.SparseArray;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 import com.android.internal.os.SomeArgs;
42 import com.android.internal.util.IState;
43 import com.android.internal.util.IndentingPrintWriter;
44 import com.android.internal.util.State;
45 import com.android.internal.util.StateMachine;
46 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
47 
48 import java.util.Collection;
49 import java.util.HashMap;
50 import java.util.Objects;
51 
52 /**
53  * This class describes the available routes of a call as a state machine.
54  * Transitions are caused solely by the commands sent as messages. Possible values for msg.what
55  * are defined as event constants in this file.
56  *
57  * The eight states are all instances of the abstract base class, {@link AudioState}. Each state
58  * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and
59  * speakerphone) and audio focus status (active or quiescent).
60  *
61  * Messages are processed first by the processMessage method in the base class, AudioState.
62  * Any messages not completely handled by AudioState are further processed by the same method in
63  * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute},
64  * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at
65  * this level are then processed by the classes corresponding to the state instances themselves.
66  *
67  * There are several variables carrying additional state. These include:
68  * mAvailableRoutes: A bitmask describing which audio routes are available
69  * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting
70  *     from a wired headset
71  * mIsMuted: a boolean indicating whether the audio is muted
72  */
73 public class CallAudioRouteStateMachine extends StateMachine {
74 
75     public static class Factory {
76         public CallAudioRouteStateMachine create(
77                 Context context,
78                 CallsManager callsManager,
79                 BluetoothRouteManager bluetoothManager,
80                 WiredHeadsetManager wiredHeadsetManager,
81                 StatusBarNotifier statusBarNotifier,
82                 CallAudioManager.AudioServiceFactory audioServiceFactory,
83                 int earpieceControl) {
84             return new CallAudioRouteStateMachine(context,
85                     callsManager,
86                     bluetoothManager,
87                     wiredHeadsetManager,
88                     statusBarNotifier,
89                     audioServiceFactory,
90                     earpieceControl);
91         }
92     }
93     /** Values for CallAudioRouteStateMachine constructor's earPieceRouting arg. */
94     public static final int EARPIECE_FORCE_DISABLED = 0;
95     public static final int EARPIECE_FORCE_ENABLED  = 1;
96     public static final int EARPIECE_AUTO_DETECT    = 2;
97 
98     /** Direct the audio stream through the device's earpiece. */
99     public static final int ROUTE_EARPIECE      = CallAudioState.ROUTE_EARPIECE;
100 
101     /** Direct the audio stream through Bluetooth. */
102     public static final int ROUTE_BLUETOOTH     = CallAudioState.ROUTE_BLUETOOTH;
103 
104     /** Direct the audio stream through a wired headset. */
105     public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET;
106 
107     /** Direct the audio stream through the device's speakerphone. */
108     public static final int ROUTE_SPEAKER       = CallAudioState.ROUTE_SPEAKER;
109 
110     /** Valid values for msg.what */
111     public static final int CONNECT_WIRED_HEADSET = 1;
112     public static final int DISCONNECT_WIRED_HEADSET = 2;
113     public static final int CONNECT_DOCK = 5;
114     public static final int DISCONNECT_DOCK = 6;
115     public static final int BLUETOOTH_DEVICE_LIST_CHANGED = 7;
116     public static final int BT_ACTIVE_DEVICE_PRESENT = 8;
117     public static final int BT_ACTIVE_DEVICE_GONE = 9;
118 
119     public static final int SWITCH_EARPIECE = 1001;
120     public static final int SWITCH_BLUETOOTH = 1002;
121     public static final int SWITCH_HEADSET = 1003;
122     public static final int SWITCH_SPEAKER = 1004;
123     // Wired headset, earpiece, or speakerphone, in that order of precedence.
124     public static final int SWITCH_BASELINE_ROUTE = 1005;
125 
126     // Messages denoting that the speakerphone was turned on/off. Used to update state when we
127     // weren't the ones who turned it on/off
128     public static final int SPEAKER_ON = 1006;
129     public static final int SPEAKER_OFF = 1007;
130 
131     public static final int USER_SWITCH_EARPIECE = 1101;
132     public static final int USER_SWITCH_BLUETOOTH = 1102;
133     public static final int USER_SWITCH_HEADSET = 1103;
134     public static final int USER_SWITCH_SPEAKER = 1104;
135     public static final int USER_SWITCH_BASELINE_ROUTE = 1105;
136 
137     public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201;
138 
139     // These three messages indicate state changes that come from BluetoothRouteManager.
140     // They may be triggered by the BT stack doing something on its own or they may be sent after
141     // we request that the BT stack do something. Any logic for these messages should take into
142     // account the possibility that the event indicated has already been processed (i.e. handling
143     // should be idempotent).
144     public static final int BT_AUDIO_DISCONNECTED = 1301;
145     public static final int BT_AUDIO_CONNECTED = 1302;
146     public static final int BT_AUDIO_PENDING = 1303;
147 
148     public static final int MUTE_ON = 3001;
149     public static final int MUTE_OFF = 3002;
150     public static final int TOGGLE_MUTE = 3003;
151     public static final int MUTE_EXTERNALLY_CHANGED = 3004;
152 
153     public static final int SWITCH_FOCUS = 4001;
154 
155     // Used in testing to execute verifications. Not compatible with subsessions.
156     public static final int RUN_RUNNABLE = 9001;
157 
158     /** Valid values for mAudioFocusType */
159     public static final int NO_FOCUS = 1;
160     public static final int ACTIVE_FOCUS = 2;
161     public static final int RINGING_FOCUS = 3;
162 
163     /** Valid values for the first argument for SWITCH_BASELINE_ROUTE */
164     public static final int NO_INCLUDE_BLUETOOTH_IN_BASELINE = 0;
165     public static final int INCLUDE_BLUETOOTH_IN_BASELINE = 1;
166 
167     @VisibleForTesting
168     public static final SparseArray<String> AUDIO_ROUTE_TO_LOG_EVENT = new SparseArray<String>() {{
169         put(CallAudioState.ROUTE_BLUETOOTH, LogUtils.Events.AUDIO_ROUTE_BT);
170         put(CallAudioState.ROUTE_EARPIECE, LogUtils.Events.AUDIO_ROUTE_EARPIECE);
171         put(CallAudioState.ROUTE_SPEAKER, LogUtils.Events.AUDIO_ROUTE_SPEAKER);
172         put(CallAudioState.ROUTE_WIRED_HEADSET, LogUtils.Events.AUDIO_ROUTE_HEADSET);
173     }};
174 
175     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
176         put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET");
177         put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET");
178         put(CONNECT_DOCK, "CONNECT_DOCK");
179         put(DISCONNECT_DOCK, "DISCONNECT_DOCK");
180         put(BLUETOOTH_DEVICE_LIST_CHANGED, "BLUETOOTH_DEVICE_LIST_CHANGED");
181         put(BT_ACTIVE_DEVICE_PRESENT, "BT_ACTIVE_DEVICE_PRESENT");
182         put(BT_ACTIVE_DEVICE_GONE, "BT_ACTIVE_DEVICE_GONE");
183 
184         put(SWITCH_EARPIECE, "SWITCH_EARPIECE");
185         put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH");
186         put(SWITCH_HEADSET, "SWITCH_HEADSET");
187         put(SWITCH_SPEAKER, "SWITCH_SPEAKER");
188         put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE");
189         put(SPEAKER_ON, "SPEAKER_ON");
190         put(SPEAKER_OFF, "SPEAKER_OFF");
191 
192         put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE");
193         put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH");
194         put(USER_SWITCH_HEADSET, "USER_SWITCH_HEADSET");
195         put(USER_SWITCH_SPEAKER, "USER_SWITCH_SPEAKER");
196         put(USER_SWITCH_BASELINE_ROUTE, "USER_SWITCH_BASELINE_ROUTE");
197 
198         put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE");
199 
200         put(BT_AUDIO_DISCONNECTED, "BT_AUDIO_DISCONNECTED");
201         put(BT_AUDIO_CONNECTED, "BT_AUDIO_CONNECTED");
202         put(BT_AUDIO_PENDING, "BT_AUDIO_PENDING");
203 
204         put(MUTE_ON, "MUTE_ON");
205         put(MUTE_OFF, "MUTE_OFF");
206         put(TOGGLE_MUTE, "TOGGLE_MUTE");
207         put(MUTE_EXTERNALLY_CHANGED, "MUTE_EXTERNALLY_CHANGED");
208 
209         put(SWITCH_FOCUS, "SWITCH_FOCUS");
210 
211         put(RUN_RUNNABLE, "RUN_RUNNABLE");
212     }};
213 
214     private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute";
215     private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute";
216     private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute";
217     private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute";
218     private static final String RINGING_BLUETOOTH_ROUTE_NAME = "RingingBluetoothRoute";
219     private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute";
220     private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute";
221     private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute";
222     private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute";
223 
224     public static final String NAME = CallAudioRouteStateMachine.class.getName();
225 
226     @Override
227     protected void onPreHandleMessage(Message msg) {
228         if (msg.obj != null && msg.obj instanceof SomeArgs) {
229             Session session = (Session) ((SomeArgs) msg.obj).arg1;
230             String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown");
231             Log.continueSession(session, "CARSM.pM_" + messageCodeName);
232             Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1);
233         }
234     }
235 
236     @Override
237     protected void onPostHandleMessage(Message msg) {
238         Log.endSession();
239         if (msg.obj != null && msg.obj instanceof SomeArgs) {
240             ((SomeArgs) msg.obj).recycle();
241         }
242     }
243 
244     abstract class AudioState extends State {
245         @Override
246         public void enter() {
247             super.enter();
248             Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
249                     "Entering state " + getName());
250             if (isActive()) {
251                 Log.addEvent(mCallsManager.getForegroundCall(),
252                         AUDIO_ROUTE_TO_LOG_EVENT.get(getRouteCode(), LogUtils.Events.AUDIO_ROUTE));
253             }
254         }
255 
256         @Override
257         public void exit() {
258             Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
259                     "Leaving state " + getName());
260             super.exit();
261         }
262 
263         @Override
264         public boolean processMessage(Message msg) {
265             int addedRoutes = 0;
266             int removedRoutes = 0;
267             boolean isHandled = NOT_HANDLED;
268 
269             Log.i(this, "Processing message %s",
270                     MESSAGE_CODE_TO_NAME.get(msg.what, Integer.toString(msg.what)));
271             switch (msg.what) {
272                 case CONNECT_WIRED_HEADSET:
273                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
274                             "Wired headset connected");
275                     removedRoutes |= ROUTE_EARPIECE;
276                     addedRoutes |= ROUTE_WIRED_HEADSET;
277                     break;
278                 case DISCONNECT_WIRED_HEADSET:
279                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
280                             "Wired headset disconnected");
281                     removedRoutes |= ROUTE_WIRED_HEADSET;
282                     if (mDoesDeviceSupportEarpieceRoute) {
283                         addedRoutes |= ROUTE_EARPIECE;
284                     }
285                     break;
286                 case BT_ACTIVE_DEVICE_PRESENT:
287                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
288                             "Bluetooth active device present");
289                     break;
290                 case BT_ACTIVE_DEVICE_GONE:
291                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
292                             "Bluetooth active device gone");
293                     break;
294                 case BLUETOOTH_DEVICE_LIST_CHANGED:
295                     Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE,
296                             "Bluetooth device list changed");
297                     Collection<BluetoothDevice> connectedDevices =
298                             mBluetoothRouteManager.getConnectedDevices();
299                     if (connectedDevices.size() > 0) {
300                         addedRoutes |= ROUTE_BLUETOOTH;
301                     } else {
302                         removedRoutes |= ROUTE_BLUETOOTH;
303                     }
304                     isHandled = HANDLED;
305                     break;
306                 case SWITCH_BASELINE_ROUTE:
307                     sendInternalMessage(calculateBaselineRouteMessage(false,
308                             msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE));
309                     return HANDLED;
310                 case USER_SWITCH_BASELINE_ROUTE:
311                     sendInternalMessage(calculateBaselineRouteMessage(true,
312                             msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE));
313                     return HANDLED;
314                 case USER_SWITCH_BLUETOOTH:
315                     // If the user tries to switch to BT, reset the explicitly-switched-away flag.
316                     mHasUserExplicitlyLeftBluetooth = false;
317                     return NOT_HANDLED;
318                 case SWITCH_FOCUS:
319                     // Perform BT hearing aid active device caching/restoration
320                     if (mAudioFocusType != NO_FOCUS && msg.arg1 == NO_FOCUS) {
321                         mBluetoothRouteManager.restoreHearingAidDevice();
322                     } else if (mAudioFocusType == NO_FOCUS && msg.arg1 != NO_FOCUS) {
323                         mBluetoothRouteManager.cacheHearingAidDevice();
324                     }
325                     mAudioFocusType = msg.arg1;
326                     return NOT_HANDLED;
327                 default:
328                     return NOT_HANDLED;
329             }
330 
331             if (addedRoutes != 0 || removedRoutes != 0
332                     || msg.what == BLUETOOTH_DEVICE_LIST_CHANGED) {
333                 mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true);
334                 mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes,
335                         addedRoutes, false);
336                 updateSystemAudioState();
337             }
338 
339             return isHandled;
340         }
341 
342         // Behavior will depend on whether the state is an active one or a quiescent one.
343         abstract public void updateSystemAudioState();
344         abstract public boolean isActive();
345         abstract public int getRouteCode();
346     }
347 
348     class ActiveEarpieceRoute extends EarpieceRoute {
349         @Override
350         public String getName() {
351             return ACTIVE_EARPIECE_ROUTE_NAME;
352         }
353 
354         @Override
355         public boolean isActive() {
356             return true;
357         }
358 
359         @Override
360         public void enter() {
361             super.enter();
362             setSpeakerphoneOn(false);
363             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
364                     mAvailableRoutes, null,
365                     mBluetoothRouteManager.getConnectedDevices());
366             setSystemAudioState(newState, true);
367             updateInternalCallAudioState();
368         }
369 
370         @Override
371         public void updateSystemAudioState() {
372             updateInternalCallAudioState();
373             setSystemAudioState(mCurrentCallAudioState);
374         }
375 
376         @Override
377         public boolean processMessage(Message msg) {
378             if (super.processMessage(msg) == HANDLED) {
379                 return HANDLED;
380             }
381             switch (msg.what) {
382                 case SWITCH_EARPIECE:
383                 case USER_SWITCH_EARPIECE:
384                 case SPEAKER_OFF:
385                     // Nothing to do here
386                     return HANDLED;
387                 case BT_AUDIO_CONNECTED:
388                     transitionTo(mActiveBluetoothRoute);
389                     return HANDLED;
390                 case SWITCH_BLUETOOTH:
391                 case USER_SWITCH_BLUETOOTH:
392                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
393                         if (mAudioFocusType == ACTIVE_FOCUS
394                                 || mBluetoothRouteManager.isInbandRingingEnabled()) {
395                             String address = (msg.obj instanceof SomeArgs) ?
396                                     (String) ((SomeArgs) msg.obj).arg2 : null;
397                             // Omit transition to ActiveBluetoothRoute
398                             setBluetoothOn(address);
399                         } else {
400                             transitionTo(mRingingBluetoothRoute);
401                         }
402                     } else {
403                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
404                     }
405                     return HANDLED;
406                 case SWITCH_HEADSET:
407                 case USER_SWITCH_HEADSET:
408                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
409                         transitionTo(mActiveHeadsetRoute);
410                     } else {
411                         Log.w(this, "Ignoring switch to headset command. Not available.");
412                     }
413                     return HANDLED;
414                 case SWITCH_SPEAKER:
415                 case USER_SWITCH_SPEAKER:
416                     setSpeakerphoneOn(true);
417                     // fall through
418                 case SPEAKER_ON:
419                     transitionTo(mActiveSpeakerRoute);
420                     return HANDLED;
421                 case SWITCH_FOCUS:
422                     if (msg.arg1 == NO_FOCUS) {
423                         reinitialize();
424                         mCallAudioManager.notifyAudioOperationsComplete();
425                     }
426                     return HANDLED;
427                 default:
428                     return NOT_HANDLED;
429             }
430         }
431     }
432 
433     class QuiescentEarpieceRoute extends EarpieceRoute {
434         @Override
435         public String getName() {
436             return QUIESCENT_EARPIECE_ROUTE_NAME;
437         }
438 
439         @Override
440         public boolean isActive() {
441             return false;
442         }
443 
444         @Override
445         public void enter() {
446             super.enter();
447             mHasUserExplicitlyLeftBluetooth = false;
448             updateInternalCallAudioState();
449         }
450 
451         @Override
452         public void updateSystemAudioState() {
453             updateInternalCallAudioState();
454         }
455 
456         @Override
457         public boolean processMessage(Message msg) {
458             if (super.processMessage(msg) == HANDLED) {
459                 return HANDLED;
460             }
461             switch (msg.what) {
462                 case SWITCH_EARPIECE:
463                 case USER_SWITCH_EARPIECE:
464                 case SPEAKER_ON:
465                     // Ignore speakerphone state changes outside of calls.
466                 case SPEAKER_OFF:
467                     // Nothing to do here
468                     return HANDLED;
469                 case BT_AUDIO_CONNECTED:
470                     Log.w(this, "BT Audio came on in quiescent earpiece route.");
471                     transitionTo(mActiveBluetoothRoute);
472                     return HANDLED;
473                 case SWITCH_BLUETOOTH:
474                 case USER_SWITCH_BLUETOOTH:
475                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
476                         transitionTo(mQuiescentBluetoothRoute);
477                     } else {
478                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
479                     }
480                     return HANDLED;
481                 case SWITCH_HEADSET:
482                 case USER_SWITCH_HEADSET:
483                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
484                         transitionTo(mQuiescentHeadsetRoute);
485                     } else {
486                         Log.w(this, "Ignoring switch to headset command. Not available.");
487                     }
488                     return HANDLED;
489                 case SWITCH_SPEAKER:
490                 case USER_SWITCH_SPEAKER:
491                     transitionTo(mQuiescentSpeakerRoute);
492                     return HANDLED;
493                 case SWITCH_FOCUS:
494                     if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
495                         transitionTo(mActiveEarpieceRoute);
496                     } else {
497                         mCallAudioManager.notifyAudioOperationsComplete();
498                     }
499                     return HANDLED;
500                 default:
501                     return NOT_HANDLED;
502             }
503         }
504     }
505 
506     abstract class EarpieceRoute extends AudioState {
507         @Override
508         public int getRouteCode() {
509             return CallAudioState.ROUTE_EARPIECE;
510         }
511 
512         @Override
513         public boolean processMessage(Message msg) {
514             if (super.processMessage(msg) == HANDLED) {
515                 return HANDLED;
516             }
517             switch (msg.what) {
518                 case CONNECT_WIRED_HEADSET:
519                     sendInternalMessage(SWITCH_HEADSET);
520                     return HANDLED;
521                 case BT_ACTIVE_DEVICE_PRESENT:
522                     if (!mHasUserExplicitlyLeftBluetooth) {
523                         sendInternalMessage(SWITCH_BLUETOOTH);
524                     } else {
525                         Log.i(this, "Not switching to BT route from earpiece because user has " +
526                                 "explicitly disconnected.");
527                     }
528                     return HANDLED;
529                 case BT_ACTIVE_DEVICE_GONE:
530                     // No change in audio route required
531                     return HANDLED;
532                 case DISCONNECT_WIRED_HEADSET:
533                     Log.e(this, new IllegalStateException(),
534                             "Wired headset should not go from connected to not when on " +
535                             "earpiece");
536                     return HANDLED;
537                 case BT_AUDIO_DISCONNECTED:
538                     // This may be sent as a confirmation by the BT stack after switch off BT.
539                     return HANDLED;
540                 case CONNECT_DOCK:
541                     setSpeakerphoneOn(true);
542                     sendInternalMessage(SWITCH_SPEAKER);
543                     return HANDLED;
544                 case DISCONNECT_DOCK:
545                     // Nothing to do here
546                     return HANDLED;
547                 default:
548                     return NOT_HANDLED;
549             }
550         }
551     }
552 
553     class ActiveHeadsetRoute extends HeadsetRoute {
554         @Override
555         public String getName() {
556             return ACTIVE_HEADSET_ROUTE_NAME;
557         }
558 
559         @Override
560         public boolean isActive() {
561             return true;
562         }
563 
564         @Override
565         public void enter() {
566             super.enter();
567             setSpeakerphoneOn(false);
568             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET,
569                     mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
570             setSystemAudioState(newState, true);
571             updateInternalCallAudioState();
572         }
573 
574         @Override
575         public void updateSystemAudioState() {
576             updateInternalCallAudioState();
577             setSystemAudioState(mCurrentCallAudioState);
578         }
579 
580         @Override
581         public boolean processMessage(Message msg) {
582             if (super.processMessage(msg) == HANDLED) {
583                 return HANDLED;
584             }
585             switch (msg.what) {
586                 case SWITCH_EARPIECE:
587                 case USER_SWITCH_EARPIECE:
588                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
589                         transitionTo(mActiveEarpieceRoute);
590                     } else {
591                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
592                     }
593                     return HANDLED;
594                 case BT_AUDIO_CONNECTED:
595                     transitionTo(mActiveBluetoothRoute);
596                     return HANDLED;
597                 case SWITCH_BLUETOOTH:
598                 case USER_SWITCH_BLUETOOTH:
599                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
600                         if (mAudioFocusType == ACTIVE_FOCUS
601                                 || mBluetoothRouteManager.isInbandRingingEnabled()) {
602                             String address = (msg.obj instanceof SomeArgs) ?
603                                     (String) ((SomeArgs) msg.obj).arg2 : null;
604                             // Omit transition to ActiveBluetoothRoute until actual connection.
605                             setBluetoothOn(address);
606                         } else {
607                             transitionTo(mRingingBluetoothRoute);
608                         }
609                     } else {
610                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
611                     }
612                     return HANDLED;
613                 case SWITCH_HEADSET:
614                 case USER_SWITCH_HEADSET:
615                 case SPEAKER_OFF:
616                     // Nothing to do
617                     return HANDLED;
618                 case SWITCH_SPEAKER:
619                 case USER_SWITCH_SPEAKER:
620                     setSpeakerphoneOn(true);
621                     // fall through
622                 case SPEAKER_ON:
623                     transitionTo(mActiveSpeakerRoute);
624                     return HANDLED;
625                 case SWITCH_FOCUS:
626                     if (msg.arg1 == NO_FOCUS) {
627                         reinitialize();
628                         mCallAudioManager.notifyAudioOperationsComplete();
629                     }
630                     return HANDLED;
631                 default:
632                     return NOT_HANDLED;
633             }
634         }
635     }
636 
637     class QuiescentHeadsetRoute extends HeadsetRoute {
638         @Override
639         public String getName() {
640             return QUIESCENT_HEADSET_ROUTE_NAME;
641         }
642 
643         @Override
644         public boolean isActive() {
645             return false;
646         }
647 
648         @Override
649         public void enter() {
650             super.enter();
651             mHasUserExplicitlyLeftBluetooth = false;
652             updateInternalCallAudioState();
653         }
654 
655         @Override
656         public void updateSystemAudioState() {
657             updateInternalCallAudioState();
658         }
659 
660         @Override
661         public boolean processMessage(Message msg) {
662             if (super.processMessage(msg) == HANDLED) {
663                 return HANDLED;
664             }
665             switch (msg.what) {
666                 case SWITCH_EARPIECE:
667                 case USER_SWITCH_EARPIECE:
668                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
669                         transitionTo(mQuiescentEarpieceRoute);
670                     } else {
671                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
672                     }
673                     return HANDLED;
674                 case BT_AUDIO_CONNECTED:
675                     transitionTo(mActiveBluetoothRoute);
676                     Log.w(this, "BT Audio came on in quiescent headset route.");
677                     return HANDLED;
678                 case SWITCH_BLUETOOTH:
679                 case USER_SWITCH_BLUETOOTH:
680                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
681                         transitionTo(mQuiescentBluetoothRoute);
682                     } else {
683                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
684                     }
685                     return HANDLED;
686                 case SWITCH_HEADSET:
687                 case USER_SWITCH_HEADSET:
688                 case SPEAKER_ON:
689                     // Ignore speakerphone state changes outside of calls.
690                 case SPEAKER_OFF:
691                     // Nothing to do
692                     return HANDLED;
693                 case SWITCH_SPEAKER:
694                 case USER_SWITCH_SPEAKER:
695                     transitionTo(mQuiescentSpeakerRoute);
696                     return HANDLED;
697                 case SWITCH_FOCUS:
698                     if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
699                         transitionTo(mActiveHeadsetRoute);
700                     } else {
701                         mCallAudioManager.notifyAudioOperationsComplete();
702                     }
703                     return HANDLED;
704                 default:
705                     return NOT_HANDLED;
706             }
707         }
708     }
709 
710     abstract class HeadsetRoute extends AudioState {
711         @Override
712         public int getRouteCode() {
713             return CallAudioState.ROUTE_WIRED_HEADSET;
714         }
715 
716         @Override
717         public boolean processMessage(Message msg) {
718             if (super.processMessage(msg) == HANDLED) {
719                 return HANDLED;
720             }
721             switch (msg.what) {
722                 case CONNECT_WIRED_HEADSET:
723                     Log.e(this, new IllegalStateException(),
724                             "Wired headset should already be connected.");
725                     return HANDLED;
726                 case BT_ACTIVE_DEVICE_PRESENT:
727                     if (!mHasUserExplicitlyLeftBluetooth) {
728                         sendInternalMessage(SWITCH_BLUETOOTH);
729                     } else {
730                         Log.i(this, "Not switching to BT route from headset because user has " +
731                                 "explicitly disconnected.");
732                     }
733                     return HANDLED;
734                 case BT_ACTIVE_DEVICE_GONE:
735                     // No change in audio route required
736                     return HANDLED;
737                 case DISCONNECT_WIRED_HEADSET:
738                     if (mWasOnSpeaker) {
739                         setSpeakerphoneOn(true);
740                         sendInternalMessage(SWITCH_SPEAKER);
741                     } else {
742                         sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
743                     }
744                     return HANDLED;
745                 case BT_AUDIO_DISCONNECTED:
746                     // This may be sent as a confirmation by the BT stack after switch off BT.
747                     return HANDLED;
748                 case CONNECT_DOCK:
749                     // Nothing to do here
750                     return HANDLED;
751                 case DISCONNECT_DOCK:
752                     // Nothing to do here
753                     return HANDLED;
754                 default:
755                     return NOT_HANDLED;
756             }
757         }
758     }
759 
760     // Note: transitions to/from this class work a bit differently -- we delegate to
761     // BluetoothRouteManager to manage all Bluetooth state, so instead of transitioning to one of
762     // the bluetooth states immediately when there's an request to do so, we wait for
763     // BluetoothRouteManager to report its state before we go into this state.
764     class ActiveBluetoothRoute extends BluetoothRoute {
765         @Override
766         public String getName() {
767             return ACTIVE_BLUETOOTH_ROUTE_NAME;
768         }
769 
770         @Override
771         public boolean isActive() {
772             return true;
773         }
774 
775         @Override
776         public void enter() {
777             super.enter();
778             setSpeakerphoneOn(false);
779             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
780                     mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
781                     mBluetoothRouteManager.getConnectedDevices());
782             setSystemAudioState(newState, true);
783             updateInternalCallAudioState();
784             // Do not send RINGER_MODE_CHANGE if no Bluetooth SCO audio device is available
785             if (mBluetoothRouteManager.getBluetoothAudioConnectedDevice() != null) {
786                 mCallAudioManager.onRingerModeChange();
787             }
788         }
789 
790         @Override
791         public void updateSystemAudioState() {
792             updateInternalCallAudioState();
793             setSystemAudioState(mCurrentCallAudioState);
794         }
795 
796         @Override
797         public void handleBtInitiatedDisconnect() {
798             // There's special-case state transitioning here -- if BT tells us that
799             // something got disconnected, we don't want to disconnect BT before
800             // transitioning, since BT might be trying to connect another device in the
801             // meantime.
802             int command = calculateBaselineRouteMessage(false, false);
803             switch (command) {
804                 case SWITCH_EARPIECE:
805                     transitionTo(mActiveEarpieceRoute);
806                     break;
807                 case SWITCH_HEADSET:
808                     transitionTo(mActiveHeadsetRoute);
809                     break;
810                 case SWITCH_SPEAKER:
811                     setSpeakerphoneOn(true);
812                     transitionTo(mActiveSpeakerRoute);
813                     break;
814                 default:
815                     Log.w(this, "Got unexpected code " + command + " when processing a"
816                             + " BT-initiated audio disconnect");
817                     // Some fallback logic to make sure we make it off the bluetooth route.
818                     super.handleBtInitiatedDisconnect();
819                     break;
820             }
821         }
822 
823         @Override
824         public boolean processMessage(Message msg) {
825             if (super.processMessage(msg) == HANDLED) {
826                 return HANDLED;
827             }
828             switch (msg.what) {
829                 case USER_SWITCH_EARPIECE:
830                     mHasUserExplicitlyLeftBluetooth = true;
831                     // fall through
832                 case SWITCH_EARPIECE:
833                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
834                         setBluetoothOff();
835                         transitionTo(mActiveEarpieceRoute);
836                     } else {
837                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
838                     }
839                     return HANDLED;
840                 case BT_AUDIO_CONNECTED:
841                     // Send ringer mode change because we transit to ActiveBluetoothState even
842                     // when HFP is connecting
843                     mCallAudioManager.onRingerModeChange();
844                     // Update the in-call app on the new active BT device in case that changed.
845                     updateSystemAudioState();
846                     return HANDLED;
847                 case SWITCH_BLUETOOTH:
848                 case USER_SWITCH_BLUETOOTH:
849                     String address = (msg.obj instanceof SomeArgs) ?
850                             (String) ((SomeArgs) msg.obj).arg2 : null;
851                     setBluetoothOn(address);
852                     return HANDLED;
853                 case USER_SWITCH_HEADSET:
854                     mHasUserExplicitlyLeftBluetooth = true;
855                     // fall through
856                 case SWITCH_HEADSET:
857                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
858                         setBluetoothOff();
859                         transitionTo(mActiveHeadsetRoute);
860                     } else {
861                         Log.w(this, "Ignoring switch to headset command. Not available.");
862                     }
863                     return HANDLED;
864                 case USER_SWITCH_SPEAKER:
865                     mHasUserExplicitlyLeftBluetooth = true;
866                     // fall through
867                 case SWITCH_SPEAKER:
868                     setSpeakerphoneOn(true);
869                     // fall through
870                 case SPEAKER_ON:
871                     setBluetoothOff();
872                     transitionTo(mActiveSpeakerRoute);
873                     return HANDLED;
874                 case SPEAKER_OFF:
875                     return HANDLED;
876                 case SWITCH_FOCUS:
877                     if (msg.arg1 == NO_FOCUS) {
878                         // Only disconnect SCO audio here instead of routing away from BT entirely.
879                         mBluetoothRouteManager.disconnectSco();
880                         reinitialize();
881                         mCallAudioManager.notifyAudioOperationsComplete();
882                     } else if (msg.arg1 == RINGING_FOCUS
883                             && !mBluetoothRouteManager.isInbandRingingEnabled()) {
884                         setBluetoothOff();
885                         transitionTo(mRingingBluetoothRoute);
886                     }
887                     return HANDLED;
888                 case BT_AUDIO_DISCONNECTED:
889                     handleBtInitiatedDisconnect();
890                     return HANDLED;
891                 default:
892                     return NOT_HANDLED;
893             }
894         }
895     }
896 
897     // This state is only used when the device doesn't support in-band ring. If it does,
898     // ActiveBluetoothRoute is used instead.
899     class RingingBluetoothRoute extends BluetoothRoute {
900         @Override
901         public String getName() {
902             return RINGING_BLUETOOTH_ROUTE_NAME;
903         }
904 
905         @Override
906         public boolean isActive() {
907             return false;
908         }
909 
910         @Override
911         public void enter() {
912             super.enter();
913             setSpeakerphoneOn(false);
914             // Do not enable SCO audio here, since RING is being sent to the headset.
915             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
916                     mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
917                     mBluetoothRouteManager.getConnectedDevices());
918             setSystemAudioState(newState);
919             updateInternalCallAudioState();
920         }
921 
922         @Override
923         public void updateSystemAudioState() {
924             updateInternalCallAudioState();
925             setSystemAudioState(mCurrentCallAudioState);
926         }
927 
928         @Override
929         public boolean processMessage(Message msg) {
930             if (super.processMessage(msg) == HANDLED) {
931                 return HANDLED;
932             }
933             switch (msg.what) {
934                 case USER_SWITCH_EARPIECE:
935                     mHasUserExplicitlyLeftBluetooth = true;
936                     // fall through
937                 case SWITCH_EARPIECE:
938                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
939                         transitionTo(mActiveEarpieceRoute);
940                     } else {
941                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
942                     }
943                     return HANDLED;
944                 case BT_AUDIO_CONNECTED:
945                     transitionTo(mActiveBluetoothRoute);
946                     return HANDLED;
947                 case SWITCH_BLUETOOTH:
948                 case USER_SWITCH_BLUETOOTH:
949                     // Nothing to do
950                     return HANDLED;
951                 case USER_SWITCH_HEADSET:
952                     mHasUserExplicitlyLeftBluetooth = true;
953                     // fall through
954                 case SWITCH_HEADSET:
955                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
956                         transitionTo(mActiveHeadsetRoute);
957                     } else {
958                         Log.w(this, "Ignoring switch to headset command. Not available.");
959                     }
960                     return HANDLED;
961                 case USER_SWITCH_SPEAKER:
962                     mHasUserExplicitlyLeftBluetooth = true;
963                     // fall through
964                 case SWITCH_SPEAKER:
965                     setSpeakerphoneOn(true);
966                     // fall through
967                 case SPEAKER_ON:
968                     transitionTo(mActiveSpeakerRoute);
969                     return HANDLED;
970                 case SPEAKER_OFF:
971                     return HANDLED;
972                 case SWITCH_FOCUS:
973                     if (msg.arg1 == NO_FOCUS) {
974                         reinitialize();
975                         mCallAudioManager.notifyAudioOperationsComplete();
976                     } else if (msg.arg1 == ACTIVE_FOCUS) {
977                         setBluetoothOn(null);
978                     }
979                     return HANDLED;
980                 case BT_AUDIO_DISCONNECTED:
981                     // Ignore this -- audio disconnecting while ringing w/o in-band should not
982                     // cause a route switch, since the device is still connected.
983                     return HANDLED;
984                 default:
985                     return NOT_HANDLED;
986             }
987         }
988     }
989 
990     class QuiescentBluetoothRoute extends BluetoothRoute {
991         @Override
992         public String getName() {
993             return QUIESCENT_BLUETOOTH_ROUTE_NAME;
994         }
995 
996         @Override
997         public boolean isActive() {
998             return false;
999         }
1000 
1001         @Override
1002         public void enter() {
1003             super.enter();
1004             mHasUserExplicitlyLeftBluetooth = false;
1005             updateInternalCallAudioState();
1006         }
1007 
1008         @Override
1009         public void updateSystemAudioState() {
1010             updateInternalCallAudioState();
1011         }
1012 
1013         @Override
1014         public boolean processMessage(Message msg) {
1015             if (super.processMessage(msg) == HANDLED) {
1016                 return HANDLED;
1017             }
1018             switch (msg.what) {
1019                 case SWITCH_EARPIECE:
1020                 case USER_SWITCH_EARPIECE:
1021                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
1022                         transitionTo(mQuiescentEarpieceRoute);
1023                     } else {
1024                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
1025                     }
1026                     return HANDLED;
1027                 case BT_AUDIO_CONNECTED:
1028                     transitionTo(mActiveBluetoothRoute);
1029                     return HANDLED;
1030                 case SWITCH_BLUETOOTH:
1031                 case USER_SWITCH_BLUETOOTH:
1032                 case SPEAKER_ON:
1033                     // Ignore speakerphone state changes outside of calls.
1034                 case SPEAKER_OFF:
1035                     // Nothing to do
1036                     return HANDLED;
1037                 case SWITCH_HEADSET:
1038                 case USER_SWITCH_HEADSET:
1039                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1040                         transitionTo(mQuiescentHeadsetRoute);
1041                     } else {
1042                         Log.w(this, "Ignoring switch to headset command. Not available.");
1043                     }
1044                     return HANDLED;
1045                 case SWITCH_SPEAKER:
1046                 case USER_SWITCH_SPEAKER:
1047                     transitionTo(mQuiescentSpeakerRoute);
1048                     return HANDLED;
1049                 case SWITCH_FOCUS:
1050                     if (msg.arg1 == ACTIVE_FOCUS) {
1051                         setBluetoothOn(null);
1052                     } else if (msg.arg1 == RINGING_FOCUS) {
1053                         if (mBluetoothRouteManager.isInbandRingingEnabled()) {
1054                             setBluetoothOn(null);
1055                         } else {
1056                             transitionTo(mRingingBluetoothRoute);
1057                         }
1058                     } else {
1059                         mCallAudioManager.notifyAudioOperationsComplete();
1060                     }
1061                     return HANDLED;
1062                 case BT_AUDIO_DISCONNECTED:
1063                     // Ignore this -- audio disconnecting while quiescent should not cause a
1064                     // route switch, since the device is still connected.
1065                     return HANDLED;
1066                 default:
1067                     return NOT_HANDLED;
1068             }
1069         }
1070     }
1071 
1072     abstract class BluetoothRoute extends AudioState {
1073         @Override
1074         public int getRouteCode() {
1075             return CallAudioState.ROUTE_BLUETOOTH;
1076         }
1077 
1078         public void handleBtInitiatedDisconnect() {
1079             sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE);
1080         }
1081 
1082         @Override
1083         public boolean processMessage(Message msg) {
1084             if (super.processMessage(msg) == HANDLED) {
1085                 return HANDLED;
1086             }
1087             switch (msg.what) {
1088                 case CONNECT_WIRED_HEADSET:
1089                     sendInternalMessage(SWITCH_HEADSET);
1090                     return HANDLED;
1091                 case BT_ACTIVE_DEVICE_PRESENT:
1092                     Log.w(this, "Bluetooth active device should not"
1093                             + " have been null while we were in BT route.");
1094                     return HANDLED;
1095                 case BT_ACTIVE_DEVICE_GONE:
1096                     handleBtInitiatedDisconnect();
1097                     mWasOnSpeaker = false;
1098                     return HANDLED;
1099                 case DISCONNECT_WIRED_HEADSET:
1100                     // No change in audio route required
1101                     return HANDLED;
1102                 case CONNECT_DOCK:
1103                     // Nothing to do here
1104                     return HANDLED;
1105                 case DISCONNECT_DOCK:
1106                     // Nothing to do here
1107                     return HANDLED;
1108                 default:
1109                     return NOT_HANDLED;
1110             }
1111         }
1112     }
1113 
1114     class ActiveSpeakerRoute extends SpeakerRoute {
1115         @Override
1116         public String getName() {
1117             return ACTIVE_SPEAKER_ROUTE_NAME;
1118         }
1119 
1120         @Override
1121         public boolean isActive() {
1122             return true;
1123         }
1124 
1125         @Override
1126         public void enter() {
1127             super.enter();
1128             // Don't set speakerphone on here -- we might end up in this state by following
1129             // the speaker state that some other app commanded.
1130             mWasOnSpeaker = true;
1131             CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
1132                     mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices());
1133             setSystemAudioState(newState, true);
1134             updateInternalCallAudioState();
1135         }
1136 
1137         @Override
1138         public void updateSystemAudioState() {
1139             updateInternalCallAudioState();
1140             setSystemAudioState(mCurrentCallAudioState);
1141         }
1142 
1143         @Override
1144         public boolean processMessage(Message msg) {
1145             if (super.processMessage(msg) == HANDLED) {
1146                 return HANDLED;
1147             }
1148             switch(msg.what) {
1149                 case USER_SWITCH_EARPIECE:
1150                     mWasOnSpeaker = false;
1151                     // fall through
1152                 case SWITCH_EARPIECE:
1153                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
1154                         transitionTo(mActiveEarpieceRoute);
1155                     } else {
1156                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
1157                     }
1158                     return HANDLED;
1159                 case BT_AUDIO_CONNECTED:
1160                     transitionTo(mActiveBluetoothRoute);
1161                     return HANDLED;
1162                 case USER_SWITCH_BLUETOOTH:
1163                     mWasOnSpeaker = false;
1164                     // fall through
1165                 case SWITCH_BLUETOOTH:
1166                     String address = (msg.obj instanceof SomeArgs) ?
1167                             (String) ((SomeArgs) msg.obj).arg2 : null;
1168                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
1169                         if (mAudioFocusType == ACTIVE_FOCUS
1170                                 || mBluetoothRouteManager.isInbandRingingEnabled()) {
1171                             // Omit transition to ActiveBluetoothRoute
1172                             setBluetoothOn(address);
1173                         } else {
1174                             transitionTo(mRingingBluetoothRoute);
1175                         }
1176                     } else {
1177                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
1178                     }
1179                     return HANDLED;
1180                 case USER_SWITCH_HEADSET:
1181                     mWasOnSpeaker = false;
1182                     // fall through
1183                 case SWITCH_HEADSET:
1184                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1185                         transitionTo(mActiveHeadsetRoute);
1186                     } else {
1187                         Log.w(this, "Ignoring switch to headset command. Not available.");
1188                     }
1189                     return HANDLED;
1190                 case SWITCH_SPEAKER:
1191                 case USER_SWITCH_SPEAKER:
1192                     // Nothing to do
1193                     return HANDLED;
1194                 case SPEAKER_ON:
1195                     // Expected, since we just transitioned here
1196                     return HANDLED;
1197                 case SPEAKER_OFF:
1198                     sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
1199                     return HANDLED;
1200                 case SWITCH_FOCUS:
1201                     if (msg.arg1 == NO_FOCUS) {
1202                         reinitialize();
1203                         mCallAudioManager.notifyAudioOperationsComplete();
1204                     }
1205                     return HANDLED;
1206                 default:
1207                     return NOT_HANDLED;
1208             }
1209         }
1210     }
1211 
1212     class QuiescentSpeakerRoute extends SpeakerRoute {
1213         @Override
1214         public String getName() {
1215             return QUIESCENT_SPEAKER_ROUTE_NAME;
1216         }
1217 
1218         @Override
1219         public boolean isActive() {
1220             return false;
1221         }
1222 
1223         @Override
1224         public void enter() {
1225             super.enter();
1226             mHasUserExplicitlyLeftBluetooth = false;
1227             // Omit setting mWasOnSpeaker to true here, since this does not reflect a call
1228             // actually being on speakerphone.
1229             updateInternalCallAudioState();
1230         }
1231 
1232         @Override
1233         public void updateSystemAudioState() {
1234             updateInternalCallAudioState();
1235         }
1236 
1237         @Override
1238         public boolean processMessage(Message msg) {
1239             if (super.processMessage(msg) == HANDLED) {
1240                 return HANDLED;
1241             }
1242             switch(msg.what) {
1243                 case SWITCH_EARPIECE:
1244                 case USER_SWITCH_EARPIECE:
1245                     if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
1246                         transitionTo(mQuiescentEarpieceRoute);
1247                     } else {
1248                         Log.w(this, "Ignoring switch to earpiece command. Not available.");
1249                     }
1250                     return HANDLED;
1251                 case BT_AUDIO_CONNECTED:
1252                     transitionTo(mActiveBluetoothRoute);
1253                     Log.w(this, "BT audio reported as connected while in quiescent speaker");
1254                     return HANDLED;
1255                 case SWITCH_BLUETOOTH:
1256                 case USER_SWITCH_BLUETOOTH:
1257                     if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
1258                         transitionTo(mQuiescentBluetoothRoute);
1259                     } else {
1260                         Log.w(this, "Ignoring switch to bluetooth command. Not available.");
1261                     }
1262                     return HANDLED;
1263                 case SWITCH_HEADSET:
1264                 case USER_SWITCH_HEADSET:
1265                     if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1266                         transitionTo(mQuiescentHeadsetRoute);
1267                     } else {
1268                         Log.w(this, "Ignoring switch to headset command. Not available.");
1269                     }
1270                     return HANDLED;
1271                 case SWITCH_SPEAKER:
1272                 case USER_SWITCH_SPEAKER:
1273                 case SPEAKER_ON:
1274                     // Nothing to do
1275                     return HANDLED;
1276                 case SPEAKER_OFF:
1277                     sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
1278                     return HANDLED;
1279                 case SWITCH_FOCUS:
1280                     if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
1281                         setSpeakerphoneOn(true);
1282                         transitionTo(mActiveSpeakerRoute);
1283                     } else {
1284                         mCallAudioManager.notifyAudioOperationsComplete();
1285                     }
1286                     return HANDLED;
1287                 default:
1288                     return NOT_HANDLED;
1289             }
1290         }
1291     }
1292 
1293     abstract class SpeakerRoute extends AudioState {
1294         @Override
1295         public int getRouteCode() {
1296             return CallAudioState.ROUTE_SPEAKER;
1297         }
1298 
1299         @Override
1300         public boolean processMessage(Message msg) {
1301             if (super.processMessage(msg) == HANDLED) {
1302                 return HANDLED;
1303             }
1304             switch (msg.what) {
1305                 case CONNECT_WIRED_HEADSET:
1306                     sendInternalMessage(SWITCH_HEADSET);
1307                     return HANDLED;
1308                 case BT_ACTIVE_DEVICE_PRESENT:
1309                     if (!mHasUserExplicitlyLeftBluetooth) {
1310                         sendInternalMessage(SWITCH_BLUETOOTH);
1311                     } else {
1312                         Log.i(this, "Not switching to BT route from speaker because user has " +
1313                                 "explicitly disconnected.");
1314                     }
1315                     return HANDLED;
1316                 case BT_ACTIVE_DEVICE_GONE:
1317                     // No change in audio route required
1318                     return HANDLED;
1319                 case DISCONNECT_WIRED_HEADSET:
1320                     // No change in audio route required
1321                     return HANDLED;
1322                 case BT_AUDIO_DISCONNECTED:
1323                     // This may be sent as a confirmation by the BT stack after switch off BT.
1324                     return HANDLED;
1325                 case CONNECT_DOCK:
1326                     // Nothing to do here
1327                     return HANDLED;
1328                 case DISCONNECT_DOCK:
1329                     sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE);
1330                     return HANDLED;
1331                default:
1332                     return NOT_HANDLED;
1333             }
1334         }
1335     }
1336 
1337     private final BroadcastReceiver mMuteChangeReceiver = new BroadcastReceiver() {
1338         @Override
1339         public void onReceive(Context context, Intent intent) {
1340             Log.startSession("CARSM.mCR");
1341             try {
1342                 if (AudioManager.ACTION_MICROPHONE_MUTE_CHANGED.equals(intent.getAction())) {
1343                     if (mCallsManager.isInEmergencyCall()) {
1344                         Log.i(this, "Mute was externally changed when there's an emergency call. " +
1345                                 "Forcing mute back off.");
1346                         sendInternalMessage(MUTE_OFF);
1347                     } else {
1348                         sendInternalMessage(MUTE_EXTERNALLY_CHANGED);
1349                     }
1350                 } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(intent.getAction())) {
1351                     int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
1352                     boolean isStreamMuted = intent.getBooleanExtra(
1353                             AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
1354 
1355                     if (streamType == AudioManager.STREAM_RING && !isStreamMuted) {
1356                         Log.i(this, "Ring stream was un-muted.");
1357                         mCallAudioManager.onRingerModeChange();
1358                     }
1359                 } else {
1360                     Log.w(this, "Received non-mute-change intent");
1361                 }
1362             } finally {
1363                 Log.endSession();
1364             }
1365         }
1366     };
1367 
1368     private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() {
1369         @Override
1370         public void onReceive(Context context, Intent intent) {
1371             Log.startSession("CARSM.mSPCR");
1372             try {
1373                 if (AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED.equals(intent.getAction())) {
1374                     if (mAudioManager != null) {
1375                         if (mAudioManager.isSpeakerphoneOn()) {
1376                             sendInternalMessage(SPEAKER_ON);
1377                         } else {
1378                             sendInternalMessage(SPEAKER_OFF);
1379                         }
1380                     }
1381                 } else {
1382                     Log.w(this, "Received non-speakerphone-change intent");
1383                 }
1384             } finally {
1385                 Log.endSession();
1386             }
1387         }
1388     };
1389 
1390     private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute();
1391     private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute();
1392     private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute();
1393     private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute();
1394     private final RingingBluetoothRoute mRingingBluetoothRoute = new RingingBluetoothRoute();
1395     private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute();
1396     private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute();
1397     private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute();
1398     private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute();
1399 
1400     /**
1401      * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
1402      * states
1403      */
1404     private int mDeviceSupportedRoutes;
1405     private int mAvailableRoutes;
1406     private int mAudioFocusType = NO_FOCUS;
1407     private boolean mWasOnSpeaker;
1408     private boolean mIsMuted;
1409 
1410     private final Context mContext;
1411     private final CallsManager mCallsManager;
1412     private final AudioManager mAudioManager;
1413     private final BluetoothRouteManager mBluetoothRouteManager;
1414     private final WiredHeadsetManager mWiredHeadsetManager;
1415     private final StatusBarNotifier mStatusBarNotifier;
1416     private final CallAudioManager.AudioServiceFactory mAudioServiceFactory;
1417     private boolean mDoesDeviceSupportEarpieceRoute;
1418     private final TelecomSystem.SyncRoot mLock;
1419     private boolean mHasUserExplicitlyLeftBluetooth = false;
1420 
1421     private HashMap<String, Integer> mStateNameToRouteCode;
1422     private HashMap<Integer, AudioState> mRouteCodeToQuiescentState;
1423 
1424     // CallAudioState is used as an interface to communicate with many other system components.
1425     // No internal state transitions should depend on this variable.
1426     private CallAudioState mCurrentCallAudioState;
1427     private CallAudioState mLastKnownCallAudioState;
1428 
1429     private CallAudioManager mCallAudioManager;
1430 
1431     public CallAudioRouteStateMachine(
1432             Context context,
1433             CallsManager callsManager,
1434             BluetoothRouteManager bluetoothManager,
1435             WiredHeadsetManager wiredHeadsetManager,
1436             StatusBarNotifier statusBarNotifier,
1437             CallAudioManager.AudioServiceFactory audioServiceFactory,
1438             int earpieceControl) {
1439         super(NAME);
1440         mContext = context;
1441         mCallsManager = callsManager;
1442         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
1443         mBluetoothRouteManager = bluetoothManager;
1444         mWiredHeadsetManager = wiredHeadsetManager;
1445         mStatusBarNotifier = statusBarNotifier;
1446         mAudioServiceFactory = audioServiceFactory;
1447         mLock = callsManager.getLock();
1448 
1449         createStates(earpieceControl);
1450     }
1451 
1452     /** Used for testing only */
1453     public CallAudioRouteStateMachine(
1454             Context context,
1455             CallsManager callsManager,
1456             BluetoothRouteManager bluetoothManager,
1457             WiredHeadsetManager wiredHeadsetManager,
1458             StatusBarNotifier statusBarNotifier,
1459             CallAudioManager.AudioServiceFactory audioServiceFactory,
1460             int earpieceControl, Looper looper) {
1461         super(NAME, looper);
1462         mContext = context;
1463         mCallsManager = callsManager;
1464         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
1465         mBluetoothRouteManager = bluetoothManager;
1466         mWiredHeadsetManager = wiredHeadsetManager;
1467         mStatusBarNotifier = statusBarNotifier;
1468         mAudioServiceFactory = audioServiceFactory;
1469         mLock = callsManager.getLock();
1470 
1471         createStates(earpieceControl);
1472     }
1473 
1474     private void createStates(int earpieceControl) {
1475         switch (earpieceControl) {
1476             case EARPIECE_FORCE_DISABLED:
1477                 mDoesDeviceSupportEarpieceRoute = false;
1478                 break;
1479             case EARPIECE_FORCE_ENABLED:
1480                 mDoesDeviceSupportEarpieceRoute = true;
1481                 break;
1482             default:
1483                 mDoesDeviceSupportEarpieceRoute = checkForEarpieceSupport();
1484         }
1485 
1486         addState(mActiveEarpieceRoute);
1487         addState(mActiveHeadsetRoute);
1488         addState(mActiveBluetoothRoute);
1489         addState(mActiveSpeakerRoute);
1490         addState(mRingingBluetoothRoute);
1491         addState(mQuiescentEarpieceRoute);
1492         addState(mQuiescentHeadsetRoute);
1493         addState(mQuiescentBluetoothRoute);
1494         addState(mQuiescentSpeakerRoute);
1495 
1496 
1497         mStateNameToRouteCode = new HashMap<>(8);
1498         mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE);
1499         mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1500         mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
1501         mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER);
1502         mStateNameToRouteCode.put(mRingingBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1503         mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE);
1504         mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1505         mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
1506         mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER);
1507 
1508         mRouteCodeToQuiescentState = new HashMap<>(4);
1509         mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute);
1510         mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute);
1511         mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute);
1512         mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
1513     }
1514 
1515     public void setCallAudioManager(CallAudioManager callAudioManager) {
1516         mCallAudioManager = callAudioManager;
1517     }
1518 
1519     /**
1520      * Initializes the state machine with info on initial audio route, supported audio routes,
1521      * and mute status.
1522      */
1523     public void initialize() {
1524         CallAudioState initState = getInitialAudioState();
1525         initialize(initState);
1526     }
1527 
1528     public void initialize(CallAudioState initState) {
1529         if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) {
1530             Log.e(this, new IllegalArgumentException(), "Route %d specified when supported call" +
1531                     " routes are: %d", initState.getRoute(), getCurrentCallSupportedRoutes());
1532         }
1533 
1534         mCurrentCallAudioState = initState;
1535         mLastKnownCallAudioState = initState;
1536         mDeviceSupportedRoutes = initState.getSupportedRouteMask();
1537         mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
1538         mIsMuted = initState.isMuted();
1539         mWasOnSpeaker = false;
1540         mContext.registerReceiver(mMuteChangeReceiver,
1541                 new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED));
1542         mContext.registerReceiver(mMuteChangeReceiver,
1543                 new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION));
1544         mContext.registerReceiver(mSpeakerPhoneChangeReceiver,
1545                 new IntentFilter(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED));
1546 
1547         mStatusBarNotifier.notifyMute(initState.isMuted());
1548         mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
1549         setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute()));
1550         start();
1551     }
1552 
1553     /**
1554      * Getter for the current CallAudioState object that the state machine is keeping track of.
1555      * Used for compatibility purposes.
1556      */
1557     public CallAudioState getCurrentCallAudioState() {
1558         return mCurrentCallAudioState;
1559     }
1560 
1561     public void sendMessageWithSessionInfo(int message, int arg) {
1562         sendMessageWithSessionInfo(message, arg, null);
1563     }
1564 
1565     public void sendMessageWithSessionInfo(int message) {
1566         sendMessageWithSessionInfo(message, 0, null);
1567     }
1568 
1569     public void sendMessageWithSessionInfo(int message, int arg, String data) {
1570         SomeArgs args = SomeArgs.obtain();
1571         args.arg1 = Log.createSubsession();
1572         args.arg2 = data;
1573         sendMessage(message, arg, 0, args);
1574     }
1575 
1576     /**
1577      * This is for state-independent changes in audio route (i.e. muting or runnables)
1578      * @param msg that couldn't be handled.
1579      */
1580     @Override
1581     protected void unhandledMessage(Message msg) {
1582         switch (msg.what) {
1583             case MUTE_ON:
1584                 setMuteOn(true);
1585                 updateSystemMuteState();
1586                 return;
1587             case MUTE_OFF:
1588                 setMuteOn(false);
1589                 updateSystemMuteState();
1590                 return;
1591             case MUTE_EXTERNALLY_CHANGED:
1592                 mIsMuted = mAudioManager.isMicrophoneMute();
1593                 if (isInActiveState()) {
1594                     updateSystemMuteState();
1595                 }
1596                 return;
1597             case TOGGLE_MUTE:
1598                 if (mIsMuted) {
1599                     sendInternalMessage(MUTE_OFF);
1600                 } else {
1601                     sendInternalMessage(MUTE_ON);
1602                 }
1603                 return;
1604             case UPDATE_SYSTEM_AUDIO_ROUTE:
1605                 updateInternalCallAudioState();
1606                 updateRouteForForegroundCall();
1607                 resendSystemAudioState();
1608                 return;
1609             case RUN_RUNNABLE:
1610                 java.lang.Runnable r = (java.lang.Runnable) msg.obj;
1611                 r.run();
1612                 return;
1613             default:
1614                 Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what);
1615         }
1616     }
1617 
1618     public void quitStateMachine() {
1619         quitNow();
1620     }
1621 
1622     public void dumpPendingMessages(IndentingPrintWriter pw) {
1623         getHandler().getLooper().dump(pw::println, "");
1624     }
1625 
1626     public boolean isHfpDeviceAvailable() {
1627         return mBluetoothRouteManager.isBluetoothAvailable();
1628     }
1629 
1630     private void setSpeakerphoneOn(boolean on) {
1631         Log.i(this, "turning speaker phone %s", on);
1632         mAudioManager.setSpeakerphoneOn(on);
1633         mStatusBarNotifier.notifySpeakerphone(on);
1634     }
1635 
1636     private void setBluetoothOn(String address) {
1637         if (mBluetoothRouteManager.isBluetoothAvailable()) {
1638             BluetoothDevice connectedDevice =
1639                     mBluetoothRouteManager.getBluetoothAudioConnectedDevice();
1640             if (address == null && connectedDevice != null) {
1641                 // null means connect to any device, so if we're already connected to some device,
1642                 // that means we can just tell ourselves that it's connected.
1643                 // Do still try to connect audio though, so that BluetoothRouteManager knows that
1644                 // there's an active call.
1645                 Log.i(this, "Bluetooth audio already on.");
1646                 sendInternalMessage(BT_AUDIO_CONNECTED);
1647                 mBluetoothRouteManager.connectBluetoothAudio(connectedDevice.getAddress());
1648                 return;
1649             }
1650             if (connectedDevice == null || !Objects.equals(address, connectedDevice.getAddress())) {
1651                 Log.i(this, "connecting bluetooth audio: %s", address);
1652                 mBluetoothRouteManager.connectBluetoothAudio(address);
1653             }
1654         }
1655     }
1656 
1657     private void setBluetoothOff() {
1658         if (mBluetoothRouteManager.isBluetoothAvailable()) {
1659             if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) {
1660                 Log.i(this, "disconnecting bluetooth audio");
1661                 mBluetoothRouteManager.disconnectBluetoothAudio();
1662             }
1663         }
1664     }
1665 
1666     private void setMuteOn(boolean mute) {
1667         mIsMuted = mute;
1668         Log.addEvent(mCallsManager.getForegroundCall(), mute ?
1669                 LogUtils.Events.MUTE : LogUtils.Events.UNMUTE);
1670         if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) {
1671             IAudioService audio = mAudioServiceFactory.getAudioService();
1672             Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
1673                     mute, audio == null);
1674             if (audio != null) {
1675                 try {
1676                     // We use the audio service directly here so that we can specify
1677                     // the current user. Telecom runs in the system_server process which
1678                     // may run as a separate user from the foreground user. If we
1679                     // used AudioManager directly, we would change mute for the system's
1680                     // user and not the current foreground, which we want to avoid.
1681                     audio.setMicrophoneMute(
1682                             mute, mContext.getOpPackageName(), getCurrentUserId());
1683                 } catch (RemoteException e) {
1684                     Log.e(this, e, "Remote exception while toggling mute.");
1685                 }
1686                 // TODO: Check microphone state after attempting to set to ensure that
1687                 // our state corroborates AudioManager's state.
1688             }
1689         }
1690     }
1691 
1692     private void updateSystemMuteState() {
1693         CallAudioState newCallAudioState = new CallAudioState(mIsMuted,
1694                 mCurrentCallAudioState.getRoute(),
1695                 mAvailableRoutes,
1696                 mCurrentCallAudioState.getActiveBluetoothDevice(),
1697                 mBluetoothRouteManager.getConnectedDevices());
1698         setSystemAudioState(newCallAudioState);
1699         updateInternalCallAudioState();
1700     }
1701 
1702     /**
1703      * Updates the CallAudioState object from current internal state. The result is used for
1704      * external communication only.
1705      */
1706     private void updateInternalCallAudioState() {
1707         IState currentState = getCurrentState();
1708         if (currentState == null) {
1709             Log.e(this, new IllegalStateException(), "Current state should never be null" +
1710                     " when updateInternalCallAudioState is called.");
1711             mCurrentCallAudioState = new CallAudioState(
1712                     mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes,
1713                     mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
1714                     mBluetoothRouteManager.getConnectedDevices());
1715             return;
1716         }
1717         int currentRoute = mStateNameToRouteCode.get(currentState.getName());
1718         mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes,
1719                 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(),
1720                 mBluetoothRouteManager.getConnectedDevices());
1721     }
1722 
1723     private void setSystemAudioState(CallAudioState newCallAudioState) {
1724         setSystemAudioState(newCallAudioState, false);
1725     }
1726 
1727     private void resendSystemAudioState() {
1728         setSystemAudioState(mLastKnownCallAudioState, true);
1729     }
1730 
1731     private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) {
1732         synchronized (mLock) {
1733             Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState,
1734                     newCallAudioState);
1735             if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) {
1736                 mStatusBarNotifier.notifyMute(newCallAudioState.isMuted());
1737                 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState);
1738                 updateAudioForForegroundCall(newCallAudioState);
1739                 mLastKnownCallAudioState = newCallAudioState;
1740             }
1741         }
1742     }
1743 
1744     private void updateAudioForForegroundCall(CallAudioState newCallAudioState) {
1745         Call call = mCallsManager.getForegroundCall();
1746         if (call != null && call.getConnectionService() != null) {
1747             call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState);
1748         }
1749     }
1750 
1751     private int calculateSupportedRoutes() {
1752         int routeMask = CallAudioState.ROUTE_SPEAKER;
1753 
1754         if (mWiredHeadsetManager.isPluggedIn()) {
1755             routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
1756         } else if (mDoesDeviceSupportEarpieceRoute){
1757             routeMask |= CallAudioState.ROUTE_EARPIECE;
1758         }
1759 
1760         if (mBluetoothRouteManager.isBluetoothAvailable()) {
1761             routeMask |=  CallAudioState.ROUTE_BLUETOOTH;
1762         }
1763 
1764         return routeMask;
1765     }
1766 
1767     private void sendInternalMessage(int messageCode) {
1768         sendInternalMessage(messageCode, 0);
1769     }
1770 
1771     private void sendInternalMessage(int messageCode, int arg1) {
1772         // Internal messages are messages which the state machine sends to itself in the
1773         // course of processing externally-sourced messages. We want to send these messages at
1774         // the front of the queue in order to make actions appear atomic to the user and to
1775         // prevent scenarios such as these:
1776         // 1. State machine handler thread is suspended for some reason.
1777         // 2. Headset gets connected (sends CONNECT_HEADSET).
1778         // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER).
1779         // 4. State machine handler is un-suspended.
1780         // 5. State machine handler processes the CONNECT_HEADSET message and sends
1781         //    SWITCH_HEADSET at end of queue.
1782         // 6. State machine handler processes SWITCH_SPEAKER.
1783         // 7. State machine handler processes SWITCH_HEADSET.
1784         Session subsession = Log.createSubsession();
1785         if(subsession != null) {
1786             SomeArgs args = SomeArgs.obtain();
1787             args.arg1 = subsession;
1788             sendMessageAtFrontOfQueue(messageCode, arg1, 0, args);
1789         } else {
1790             sendMessageAtFrontOfQueue(messageCode, arg1);
1791         }
1792     }
1793 
1794     private CallAudioState getInitialAudioState() {
1795         int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes();
1796         final int route;
1797 
1798         if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0
1799                 && mBluetoothRouteManager.hasBtActiveDevice()) {
1800             route = ROUTE_BLUETOOTH;
1801         } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) {
1802             route = ROUTE_WIRED_HEADSET;
1803         } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) {
1804             route = ROUTE_EARPIECE;
1805         } else {
1806             route = ROUTE_SPEAKER;
1807         }
1808 
1809         return new CallAudioState(false, route, supportedRouteMask, null,
1810                 mBluetoothRouteManager.getConnectedDevices());
1811     }
1812 
1813     private int getCurrentUserId() {
1814         final long ident = Binder.clearCallingIdentity();
1815         try {
1816             UserInfo currentUser = ActivityManager.getService().getCurrentUser();
1817             return currentUser.id;
1818         } catch (RemoteException e) {
1819             // Activity manager not running, nothing we can do assume user 0.
1820         } finally {
1821             Binder.restoreCallingIdentity(ident);
1822         }
1823         return UserHandle.USER_OWNER;
1824     }
1825 
1826     public boolean isInActiveState() {
1827         AudioState currentState = (AudioState) getCurrentState();
1828         if (currentState == null) {
1829             Log.w(this, "Current state is null, assuming inactive state");
1830             return false;
1831         }
1832         return currentState.isActive();
1833     }
1834 
1835     private boolean checkForEarpieceSupport() {
1836         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
1837         for (AudioDeviceInfo device: deviceList) {
1838             if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
1839                 return true;
1840             }
1841         }
1842         // No earpiece found
1843         return false;
1844     }
1845 
1846     private int calculateBaselineRouteMessage(boolean isExplicitUserRequest,
1847             boolean includeBluetooth) {
1848         boolean isSkipEarpiece = false;
1849         if (!isExplicitUserRequest) {
1850             synchronized (mLock) {
1851                 // Check video calls to skip earpiece since the baseline for video
1852                 // calls should be the speakerphone route
1853                 isSkipEarpiece = mCallsManager.hasVideoCall();
1854             }
1855         }
1856         if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0
1857                 && !mHasUserExplicitlyLeftBluetooth
1858                 && includeBluetooth) {
1859             return isExplicitUserRequest ? USER_SWITCH_BLUETOOTH : SWITCH_BLUETOOTH;
1860         } else if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) {
1861             return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE;
1862         } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1863             return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET;
1864         } else {
1865             return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER;
1866         }
1867     }
1868 
1869     private void reinitialize() {
1870         CallAudioState initState = getInitialAudioState();
1871         mDeviceSupportedRoutes = initState.getSupportedRouteMask();
1872         mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
1873         mIsMuted = initState.isMuted();
1874         setSpeakerphoneOn(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
1875         setMuteOn(mIsMuted);
1876         mWasOnSpeaker = false;
1877         mHasUserExplicitlyLeftBluetooth = false;
1878         mLastKnownCallAudioState = initState;
1879         transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute()));
1880     }
1881 
1882     private void updateRouteForForegroundCall() {
1883         mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
1884 
1885         CallAudioState currentState = getCurrentCallAudioState();
1886 
1887         // Move to baseline route in the case the current route is no longer available.
1888         if ((mAvailableRoutes & currentState.getRoute()) == 0) {
1889             sendInternalMessage(calculateBaselineRouteMessage(false, true));
1890         }
1891     }
1892 
1893     private int getCurrentCallSupportedRoutes() {
1894         int supportedRoutes = CallAudioState.ROUTE_ALL;
1895 
1896         if (mCallsManager.getForegroundCall() != null) {
1897             supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes();
1898         }
1899 
1900         return supportedRoutes;
1901     }
1902 
1903     private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) {
1904         base &= ~remove;
1905 
1906         if (considerCurrentCall) {
1907             add &= getCurrentCallSupportedRoutes();
1908         }
1909 
1910         base |= add;
1911 
1912         return base;
1913     }
1914 }
1915