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