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