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