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