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