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