Home | History | Annotate | Download | only in hfpclient
      1 /*
      2  * Copyright (c) 2016 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 /**
     18  * Bluetooth Headset Client StateMachine
     19  *                      (Disconnected)
     20  *                           | ^  ^
     21  *                   CONNECT | |  | DISCONNECTED
     22  *                           V |  |
     23  *                   (Connecting) |
     24  *                           |    |
     25  *                 CONNECTED |    | DISCONNECT
     26  *                           V    |
     27  *                        (Connected)
     28  *                           |    ^
     29  *             CONNECT_AUDIO |    | DISCONNECT_AUDIO
     30  *                           V    |
     31  *                         (AudioOn)
     32  */
     33 
     34 package com.android.bluetooth.hfpclient;
     35 
     36 import android.bluetooth.BluetoothAdapter;
     37 import android.bluetooth.BluetoothDevice;
     38 import android.bluetooth.BluetoothHeadsetClient;
     39 import android.bluetooth.BluetoothHeadsetClientCall;
     40 import android.bluetooth.BluetoothProfile;
     41 import android.bluetooth.BluetoothUuid;
     42 import android.content.Intent;
     43 import android.media.AudioAttributes;
     44 import android.media.AudioFocusRequest;
     45 import android.media.AudioManager;
     46 import android.os.Bundle;
     47 import android.os.Looper;
     48 import android.os.Message;
     49 import android.os.ParcelUuid;
     50 import android.os.SystemClock;
     51 import android.support.annotation.VisibleForTesting;
     52 import android.util.Log;
     53 import android.util.Pair;
     54 
     55 import com.android.bluetooth.BluetoothMetricsProto;
     56 import com.android.bluetooth.R;
     57 import com.android.bluetooth.Utils;
     58 import com.android.bluetooth.btservice.AdapterService;
     59 import com.android.bluetooth.btservice.MetricsLogger;
     60 import com.android.bluetooth.btservice.ProfileService;
     61 import com.android.internal.util.IState;
     62 import com.android.internal.util.State;
     63 import com.android.internal.util.StateMachine;
     64 
     65 import java.util.ArrayList;
     66 import java.util.Arrays;
     67 import java.util.HashSet;
     68 import java.util.Hashtable;
     69 import java.util.LinkedList;
     70 import java.util.List;
     71 import java.util.Queue;
     72 import java.util.Set;
     73 
     74 public class HeadsetClientStateMachine extends StateMachine {
     75     private static final String TAG = "HeadsetClientStateMachine";
     76     private static final boolean DBG = false;
     77 
     78     static final int NO_ACTION = 0;
     79     static final int IN_BAND_RING_ENABLED = 1;
     80 
     81     // external actions
     82     public static final int AT_OK = 0;
     83     public static final int CONNECT = 1;
     84     public static final int DISCONNECT = 2;
     85     public static final int CONNECT_AUDIO = 3;
     86     public static final int DISCONNECT_AUDIO = 4;
     87     public static final int VOICE_RECOGNITION_START = 5;
     88     public static final int VOICE_RECOGNITION_STOP = 6;
     89     public static final int SET_MIC_VOLUME = 7;
     90     public static final int SET_SPEAKER_VOLUME = 8;
     91     public static final int DIAL_NUMBER = 10;
     92     public static final int ACCEPT_CALL = 12;
     93     public static final int REJECT_CALL = 13;
     94     public static final int HOLD_CALL = 14;
     95     public static final int TERMINATE_CALL = 15;
     96     public static final int ENTER_PRIVATE_MODE = 16;
     97     public static final int SEND_DTMF = 17;
     98     public static final int EXPLICIT_CALL_TRANSFER = 18;
     99     public static final int DISABLE_NREC = 20;
    100 
    101     // internal actions
    102     private static final int QUERY_CURRENT_CALLS = 50;
    103     private static final int QUERY_OPERATOR_NAME = 51;
    104     private static final int SUBSCRIBER_INFO = 52;
    105     private static final int CONNECTING_TIMEOUT = 53;
    106 
    107     // special action to handle terminating specific call from multiparty call
    108     static final int TERMINATE_SPECIFIC_CALL = 53;
    109 
    110     // Timeouts.
    111     @VisibleForTesting
    112     static final int CONNECTING_TIMEOUT_MS = 10000;  // 10s
    113     private static final int ROUTING_DELAY_MS = 250;
    114 
    115     private static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec.
    116     private static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec.
    117 
    118     static final int HF_ORIGINATED_CALL_ID = -1;
    119     private static final long OUTGOING_TIMEOUT_MILLI = 10 * 1000; // 10 seconds
    120     private static final long QUERY_CURRENT_CALLS_WAIT_MILLIS = 2 * 1000; // 2 seconds
    121 
    122     // Keep track of audio routing across all devices.
    123     private static boolean sAudioIsRouted = false;
    124 
    125     private final Disconnected mDisconnected;
    126     private final Connecting mConnecting;
    127     private final Connected mConnected;
    128     private final AudioOn mAudioOn;
    129     private State mPrevState;
    130     private long mClccTimer = 0;
    131 
    132     private final HeadsetClientService mService;
    133 
    134     // Set of calls that represent the accurate state of calls that exists on AG and the calls that
    135     // are currently in process of being notified to the AG from HF.
    136     private final Hashtable<Integer, BluetoothHeadsetClientCall> mCalls = new Hashtable<>();
    137     // Set of calls received from AG via the AT+CLCC command. We use this map to update the mCalls
    138     // which is eventually used to inform the telephony stack of any changes to call on HF.
    139     private final Hashtable<Integer, BluetoothHeadsetClientCall> mCallsUpdate = new Hashtable<>();
    140 
    141     private int mIndicatorNetworkState;
    142     private int mIndicatorNetworkType;
    143     private int mIndicatorNetworkSignal;
    144     private int mIndicatorBatteryLevel;
    145     private boolean mInBandRing;
    146 
    147     private String mOperatorName;
    148     private String mSubscriberInfo;
    149 
    150     private static int sMaxAmVcVol;
    151     private static int sMinAmVcVol;
    152 
    153     // queue of send actions (pair action, action_data)
    154     private Queue<Pair<Integer, Object>> mQueuedActions;
    155 
    156     // last executed command, before action is complete e.g. waiting for some
    157     // indicator
    158     private Pair<Integer, Object> mPendingAction;
    159 
    160     private int mAudioState;
    161     private boolean mAudioWbs;
    162     private int mVoiceRecognitionActive;
    163     private final BluetoothAdapter mAdapter;
    164 
    165     // currently connected device
    166     private BluetoothDevice mCurrentDevice = null;
    167 
    168     // general peer features and call handling features
    169     private int mPeerFeatures;
    170     private int mChldFeatures;
    171 
    172     // This is returned when requesting focus from AudioManager
    173     private AudioFocusRequest mAudioFocusRequest;
    174 
    175     private AudioManager mAudioManager;
    176 
    177     // Accessor for the states, useful for reusing the state machines
    178     public IState getDisconnectedState() {
    179         return mDisconnected;
    180     }
    181 
    182     // Get if in band ring is currently enabled on device.
    183     public boolean getInBandRing() {
    184         return mInBandRing;
    185     }
    186 
    187     public void dump(StringBuilder sb) {
    188         ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
    189         ProfileService.println(sb, "mAudioState: " + mAudioState);
    190         ProfileService.println(sb, "mAudioWbs: " + mAudioWbs);
    191         ProfileService.println(sb, "mIndicatorNetworkState: " + mIndicatorNetworkState);
    192         ProfileService.println(sb, "mIndicatorNetworkType: " + mIndicatorNetworkType);
    193         ProfileService.println(sb, "mIndicatorNetworkSignal: " + mIndicatorNetworkSignal);
    194         ProfileService.println(sb, "mIndicatorBatteryLevel: " + mIndicatorBatteryLevel);
    195         ProfileService.println(sb, "mOperatorName: " + mOperatorName);
    196         ProfileService.println(sb, "mSubscriberInfo: " + mSubscriberInfo);
    197 
    198         ProfileService.println(sb, "mCalls:");
    199         if (mCalls != null) {
    200             for (BluetoothHeadsetClientCall call : mCalls.values()) {
    201                 ProfileService.println(sb, "  " + call);
    202             }
    203         }
    204 
    205         ProfileService.println(sb, "mCallsUpdate:");
    206         if (mCallsUpdate != null) {
    207             for (BluetoothHeadsetClientCall call : mCallsUpdate.values()) {
    208                 ProfileService.println(sb, "  " + call);
    209             }
    210         }
    211 
    212         ProfileService.println(sb, "State machine stats:");
    213         ProfileService.println(sb, this.toString());
    214     }
    215 
    216     private void clearPendingAction() {
    217         mPendingAction = new Pair<Integer, Object>(NO_ACTION, 0);
    218     }
    219 
    220     private void addQueuedAction(int action) {
    221         addQueuedAction(action, 0);
    222     }
    223 
    224     private void addQueuedAction(int action, Object data) {
    225         mQueuedActions.add(new Pair<Integer, Object>(action, data));
    226     }
    227 
    228     private void addQueuedAction(int action, int data) {
    229         mQueuedActions.add(new Pair<Integer, Object>(action, data));
    230     }
    231 
    232     private BluetoothHeadsetClientCall getCall(int... states) {
    233         if (DBG) {
    234             Log.d(TAG, "getFromCallsWithStates states:" + Arrays.toString(states));
    235         }
    236         for (BluetoothHeadsetClientCall c : mCalls.values()) {
    237             for (int s : states) {
    238                 if (c.getState() == s) {
    239                     return c;
    240                 }
    241             }
    242         }
    243         return null;
    244     }
    245 
    246     private int callsInState(int state) {
    247         int i = 0;
    248         for (BluetoothHeadsetClientCall c : mCalls.values()) {
    249             if (c.getState() == state) {
    250                 i++;
    251             }
    252         }
    253 
    254         return i;
    255     }
    256 
    257     private void sendCallChangedIntent(BluetoothHeadsetClientCall c) {
    258         if (DBG) {
    259             Log.d(TAG, "sendCallChangedIntent " + c);
    260         }
    261         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
    262         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    263         intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
    264         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
    265     }
    266 
    267     private boolean queryCallsStart() {
    268         if (DBG) {
    269             Log.d(TAG, "queryCallsStart");
    270         }
    271         clearPendingAction();
    272         NativeInterface.queryCurrentCallsNative(getByteAddress(mCurrentDevice));
    273         addQueuedAction(QUERY_CURRENT_CALLS, 0);
    274         return true;
    275     }
    276 
    277     private void queryCallsDone() {
    278         if (DBG) {
    279             Log.d(TAG, "queryCallsDone");
    280         }
    281         // mCalls has two types of calls:
    282         // (a) Calls that are received from AG of a previous iteration of queryCallsStart()
    283         // (b) Calls that are outgoing initiated from HF
    284         // mCallsUpdate has all calls received from queryCallsUpdate() in current iteration of
    285         // queryCallsStart().
    286         //
    287         // We use the following steps to make sure that calls are update correctly.
    288         //
    289         // If there are no calls initiated from HF (i.e. ID = -1) then:
    290         // 1. All IDs which are common in mCalls & mCallsUpdate are updated and the upper layers are
    291         // informed of the change calls (if any changes).
    292         // 2. All IDs that are in mCalls but *not* in mCallsUpdate will be removed from mCalls and
    293         // the calls should be terminated
    294         // 3. All IDs that are new in mCallsUpdated should be added as new calls to mCalls.
    295         //
    296         // If there is an outgoing HF call, it is important to associate that call with one of the
    297         // mCallsUpdated calls hence,
    298         // 1. If from the above procedure we get N extra calls (i.e. {3}):
    299         // choose the first call as the one to associate with the HF call.
    300 
    301         // Create set of IDs for added calls, removed calls and consitent calls.
    302         // WARN!!! Java Map -> Set has association hence changes to Set are reflected in the Map
    303         // itself (i.e. removing an element from Set removes it from the Map hence use copy).
    304         Set<Integer> currCallIdSet = new HashSet<Integer>();
    305         currCallIdSet.addAll(mCalls.keySet());
    306         // Remove the entry for unassigned call.
    307         currCallIdSet.remove(HF_ORIGINATED_CALL_ID);
    308 
    309         Set<Integer> newCallIdSet = new HashSet<Integer>();
    310         newCallIdSet.addAll(mCallsUpdate.keySet());
    311 
    312         // Added.
    313         Set<Integer> callAddedIds = new HashSet<Integer>();
    314         callAddedIds.addAll(newCallIdSet);
    315         callAddedIds.removeAll(currCallIdSet);
    316 
    317         // Removed.
    318         Set<Integer> callRemovedIds = new HashSet<Integer>();
    319         callRemovedIds.addAll(currCallIdSet);
    320         callRemovedIds.removeAll(newCallIdSet);
    321 
    322         // Retained.
    323         Set<Integer> callRetainedIds = new HashSet<Integer>();
    324         callRetainedIds.addAll(currCallIdSet);
    325         callRetainedIds.retainAll(newCallIdSet);
    326 
    327         if (DBG) {
    328             Log.d(TAG, "currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet
    329                     + " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds
    330                     + " callRetainedIds " + callRetainedIds);
    331         }
    332 
    333         // First thing is to try to associate the outgoing HF with a valid call.
    334         Integer hfOriginatedAssoc = -1;
    335         if (mCalls.containsKey(HF_ORIGINATED_CALL_ID)) {
    336             BluetoothHeadsetClientCall c = mCalls.get(HF_ORIGINATED_CALL_ID);
    337             long cCreationElapsed = c.getCreationElapsedMilli();
    338             if (callAddedIds.size() > 0) {
    339                 if (DBG) {
    340                     Log.d(TAG, "Associating the first call with HF originated call");
    341                 }
    342                 hfOriginatedAssoc = (Integer) callAddedIds.toArray()[0];
    343                 mCalls.put(hfOriginatedAssoc, mCalls.get(HF_ORIGINATED_CALL_ID));
    344                 mCalls.remove(HF_ORIGINATED_CALL_ID);
    345 
    346                 // Adjust this call in above sets.
    347                 callAddedIds.remove(hfOriginatedAssoc);
    348                 callRetainedIds.add(hfOriginatedAssoc);
    349             } else if (SystemClock.elapsedRealtime() - cCreationElapsed > OUTGOING_TIMEOUT_MILLI) {
    350                 Log.w(TAG, "Outgoing call did not see a response, clear the calls and send CHUP");
    351                 // We send a terminate because we are in a bad state and trying to
    352                 // recover.
    353                 terminateCall();
    354 
    355                 // Clean out the state for outgoing call.
    356                 for (Integer idx : mCalls.keySet()) {
    357                     BluetoothHeadsetClientCall c1 = mCalls.get(idx);
    358                     c1.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
    359                     sendCallChangedIntent(c1);
    360                 }
    361                 mCalls.clear();
    362 
    363                 // We return here, if there's any update to the phone we should get a
    364                 // follow up by getting some call indicators and hence update the calls.
    365                 return;
    366             }
    367         }
    368 
    369         if (DBG) {
    370             Log.d(TAG, "ADJUST: currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet
    371                     + " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds
    372                     + " callRetainedIds " + callRetainedIds);
    373         }
    374 
    375         // Terminate & remove the calls that are done.
    376         for (Integer idx : callRemovedIds) {
    377             BluetoothHeadsetClientCall c = mCalls.remove(idx);
    378             c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
    379             sendCallChangedIntent(c);
    380         }
    381 
    382         // Add the new calls.
    383         for (Integer idx : callAddedIds) {
    384             BluetoothHeadsetClientCall c = mCallsUpdate.get(idx);
    385             mCalls.put(idx, c);
    386             sendCallChangedIntent(c);
    387         }
    388 
    389         // Update the existing calls.
    390         for (Integer idx : callRetainedIds) {
    391             BluetoothHeadsetClientCall cOrig = mCalls.get(idx);
    392             BluetoothHeadsetClientCall cUpdate = mCallsUpdate.get(idx);
    393 
    394             // Update the necessary fields.
    395             cOrig.setNumber(cUpdate.getNumber());
    396             cOrig.setState(cUpdate.getState());
    397             cOrig.setMultiParty(cUpdate.isMultiParty());
    398 
    399             // Send update with original object (UUID, idx).
    400             sendCallChangedIntent(cOrig);
    401         }
    402 
    403         if (mCalls.size() > 0) {
    404             if (mService.getResources().getBoolean(R.bool.hfp_clcc_poll_during_call)) {
    405                 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
    406             } else {
    407                 if (getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING) != null) {
    408                     Log.d(TAG, "Still have incoming call; polling");
    409                     sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
    410                 } else {
    411                     removeMessages(QUERY_CURRENT_CALLS);
    412                 }
    413             }
    414         }
    415 
    416         mCallsUpdate.clear();
    417     }
    418 
    419     private void queryCallsUpdate(int id, int state, String number, boolean multiParty,
    420             boolean outgoing) {
    421         if (DBG) {
    422             Log.d(TAG, "queryCallsUpdate: " + id);
    423         }
    424         mCallsUpdate.put(id,
    425                 new BluetoothHeadsetClientCall(mCurrentDevice, id, state, number, multiParty,
    426                         outgoing, mInBandRing));
    427     }
    428 
    429     private void acceptCall(int flag) {
    430         int action = -1;
    431 
    432         if (DBG) {
    433             Log.d(TAG, "acceptCall: (" + flag + ")");
    434         }
    435 
    436         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING,
    437                 BluetoothHeadsetClientCall.CALL_STATE_WAITING);
    438         if (c == null) {
    439             c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
    440                     BluetoothHeadsetClientCall.CALL_STATE_HELD);
    441 
    442             if (c == null) {
    443                 return;
    444             }
    445         }
    446 
    447         if (DBG) {
    448             Log.d(TAG, "Call to accept: " + c);
    449         }
    450         switch (c.getState()) {
    451             case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
    452                 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
    453                     return;
    454                 }
    455                 action = HeadsetClientHalConstants.CALL_ACTION_ATA;
    456                 break;
    457             case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
    458                 if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) {
    459                     // if no active calls present only plain accept is allowed
    460                     if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
    461                         return;
    462                     }
    463                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
    464                     break;
    465                 }
    466 
    467                 // if active calls are present then we have the option to either terminate the
    468                 // existing call or hold the existing call. We hold the other call by default.
    469                 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD
    470                         || flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
    471                     if (DBG) {
    472                         Log.d(TAG, "Accepting call with accept and hold");
    473                     }
    474                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
    475                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
    476                     if (DBG) {
    477                         Log.d(TAG, "Accepting call with accept and reject");
    478                     }
    479                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
    480                 } else {
    481                     Log.e(TAG, "Aceept call with invalid flag: " + flag);
    482                     return;
    483                 }
    484                 break;
    485             case BluetoothHeadsetClientCall.CALL_STATE_HELD:
    486                 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
    487                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
    488                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) {
    489                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1;
    490                 } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) {
    491                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3;
    492                 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) {
    493                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
    494                 } else {
    495                     action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
    496                 }
    497                 break;
    498             case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
    499                 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_1;
    500                 break;
    501             case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
    502             case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
    503             case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
    504             default:
    505                 return;
    506         }
    507 
    508         if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) {
    509             // When unholding a call over Bluetooth make sure to route audio.
    510             routeHfpAudio(true);
    511         }
    512 
    513         if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) {
    514             addQueuedAction(ACCEPT_CALL, action);
    515         } else {
    516             Log.e(TAG, "ERROR: Couldn't accept a call, action:" + action);
    517         }
    518     }
    519 
    520     private void rejectCall() {
    521         int action;
    522 
    523         if (DBG) {
    524             Log.d(TAG, "rejectCall");
    525         }
    526 
    527         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING,
    528                 BluetoothHeadsetClientCall.CALL_STATE_WAITING,
    529                 BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD,
    530                 BluetoothHeadsetClientCall.CALL_STATE_HELD);
    531         if (c == null) {
    532             if (DBG) {
    533                 Log.d(TAG, "No call to reject, returning.");
    534             }
    535             return;
    536         }
    537 
    538         switch (c.getState()) {
    539             case BluetoothHeadsetClientCall.CALL_STATE_INCOMING:
    540                 action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
    541                 break;
    542             case BluetoothHeadsetClientCall.CALL_STATE_WAITING:
    543             case BluetoothHeadsetClientCall.CALL_STATE_HELD:
    544                 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
    545                 break;
    546             case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD:
    547                 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_2;
    548                 break;
    549             case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE:
    550             case BluetoothHeadsetClientCall.CALL_STATE_DIALING:
    551             case BluetoothHeadsetClientCall.CALL_STATE_ALERTING:
    552             default:
    553                 return;
    554         }
    555 
    556         if (DBG) {
    557             Log.d(TAG, "Reject call action " + action);
    558         }
    559         if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) {
    560             addQueuedAction(REJECT_CALL, action);
    561         } else {
    562             Log.e(TAG, "ERROR: Couldn't reject a call, action:" + action);
    563         }
    564     }
    565 
    566     private void holdCall() {
    567         int action;
    568 
    569         if (DBG) {
    570             Log.d(TAG, "holdCall");
    571         }
    572 
    573         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING);
    574         if (c != null) {
    575             action = HeadsetClientHalConstants.CALL_ACTION_BTRH_0;
    576         } else {
    577             c = getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
    578             if (c == null) {
    579                 return;
    580             }
    581 
    582             action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2;
    583         }
    584 
    585         if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) {
    586             addQueuedAction(HOLD_CALL, action);
    587         } else {
    588             Log.e(TAG, "ERROR: Couldn't hold a call, action:" + action);
    589         }
    590     }
    591 
    592     private void terminateCall() {
    593         if (DBG) {
    594             Log.d(TAG, "terminateCall");
    595         }
    596 
    597         int action = HeadsetClientHalConstants.CALL_ACTION_CHUP;
    598 
    599         BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_DIALING,
    600                 BluetoothHeadsetClientCall.CALL_STATE_ALERTING,
    601                 BluetoothHeadsetClientCall.CALL_STATE_ACTIVE);
    602         if (c == null) {
    603             // If the call being terminated is currently held, switch the action to CHLD_0
    604             c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD);
    605             action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0;
    606         }
    607         if (c != null) {
    608             if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) {
    609                 addQueuedAction(TERMINATE_CALL, action);
    610             } else {
    611                 Log.e(TAG, "ERROR: Couldn't terminate outgoing call");
    612             }
    613         }
    614     }
    615 
    616     private void enterPrivateMode(int idx) {
    617         if (DBG) {
    618             Log.d(TAG, "enterPrivateMode: " + idx);
    619         }
    620 
    621         BluetoothHeadsetClientCall c = mCalls.get(idx);
    622 
    623         if (c == null || c.getState() != BluetoothHeadsetClientCall.CALL_STATE_ACTIVE
    624                 || !c.isMultiParty()) {
    625             return;
    626         }
    627 
    628         if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice),
    629                 HeadsetClientHalConstants.CALL_ACTION_CHLD_2X, idx)) {
    630             addQueuedAction(ENTER_PRIVATE_MODE, c);
    631         } else {
    632             Log.e(TAG, "ERROR: Couldn't enter private " + " id:" + idx);
    633         }
    634     }
    635 
    636     private void explicitCallTransfer() {
    637         if (DBG) {
    638             Log.d(TAG, "explicitCallTransfer");
    639         }
    640 
    641         // can't transfer call if there is not enough call parties
    642         if (mCalls.size() < 2) {
    643             return;
    644         }
    645 
    646         if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice),
    647                 HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1)) {
    648             addQueuedAction(EXPLICIT_CALL_TRANSFER);
    649         } else {
    650             Log.e(TAG, "ERROR: Couldn't transfer call");
    651         }
    652     }
    653 
    654     public Bundle getCurrentAgFeatures() {
    655         Bundle b = new Bundle();
    656         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
    657                 == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
    658             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
    659         }
    660         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
    661                 == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
    662             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
    663         }
    664         if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
    665                 == HeadsetClientHalConstants.PEER_FEAT_ECC) {
    666             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
    667         }
    668 
    669         // add individual CHLD support extras
    670         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
    671                 == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
    672             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true);
    673         }
    674         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
    675                 == HeadsetClientHalConstants.CHLD_FEAT_REL) {
    676             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL,
    677                     true);
    678         }
    679         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
    680                 == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
    681             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
    682         }
    683         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
    684                 == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
    685             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
    686         }
    687         if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
    688                 == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
    689             b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
    690         }
    691 
    692         return b;
    693     }
    694 
    695     HeadsetClientStateMachine(HeadsetClientService context, Looper looper) {
    696         super(TAG, looper);
    697         mService = context;
    698         mAudioManager = mService.getAudioManager();
    699 
    700         mAdapter = BluetoothAdapter.getDefaultAdapter();
    701         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
    702         mAudioWbs = false;
    703         mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED;
    704 
    705         mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
    706         mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
    707         mIndicatorNetworkSignal = 0;
    708         mIndicatorBatteryLevel = 0;
    709 
    710         sMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
    711         sMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
    712 
    713         mOperatorName = null;
    714         mSubscriberInfo = null;
    715 
    716         mQueuedActions = new LinkedList<Pair<Integer, Object>>();
    717         clearPendingAction();
    718 
    719         mCalls.clear();
    720         mCallsUpdate.clear();
    721 
    722         mDisconnected = new Disconnected();
    723         mConnecting = new Connecting();
    724         mConnected = new Connected();
    725         mAudioOn = new AudioOn();
    726 
    727         addState(mDisconnected);
    728         addState(mConnecting);
    729         addState(mConnected);
    730         addState(mAudioOn, mConnected);
    731 
    732         setInitialState(mDisconnected);
    733     }
    734 
    735     static HeadsetClientStateMachine make(HeadsetClientService context, Looper l) {
    736         if (DBG) {
    737             Log.d(TAG, "make");
    738         }
    739         HeadsetClientStateMachine hfcsm = new HeadsetClientStateMachine(context, l);
    740         hfcsm.start();
    741         return hfcsm;
    742     }
    743 
    744     synchronized void routeHfpAudio(boolean enable) {
    745         if (mAudioManager == null) {
    746             Log.e(TAG, "AudioManager is null!");
    747             return;
    748         }
    749         if (DBG) {
    750             Log.d(TAG, "hfp_enable=" + enable);
    751         }
    752         if (enable && !sAudioIsRouted) {
    753             mAudioManager.setParameters("hfp_enable=true");
    754         } else if (!enable) {
    755             mAudioManager.setParameters("hfp_enable=false");
    756         }
    757         sAudioIsRouted = enable;
    758     }
    759 
    760     private AudioFocusRequest requestAudioFocus() {
    761         AudioAttributes streamAttributes =
    762                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
    763                         .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
    764                         .build();
    765         AudioFocusRequest focusRequest =
    766                 new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
    767                         .setAudioAttributes(streamAttributes)
    768                         .build();
    769         int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);
    770         if (DBG) {
    771             String s = (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
    772                     ? "AudioFocus granted" : "AudioFocus NOT granted";
    773             Log.d(TAG, "AudioManager requestAudioFocus returned: " + s);
    774         }
    775         return focusRequest;
    776     }
    777 
    778     public void doQuit() {
    779         Log.d(TAG, "doQuit");
    780         routeHfpAudio(false);
    781         returnAudioFocusIfNecessary();
    782         quitNow();
    783     }
    784 
    785     private void returnAudioFocusIfNecessary() {
    786         if (mAudioFocusRequest == null) return;
    787         mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest);
    788         mAudioFocusRequest = null;
    789     }
    790 
    791     static int hfToAmVol(int hfVol) {
    792         int amRange = sMaxAmVcVol - sMinAmVcVol;
    793         int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
    794         int amOffset = (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange;
    795         int amVol = sMinAmVcVol + amOffset;
    796         Log.d(TAG, "HF -> AM " + hfVol + " " + amVol);
    797         return amVol;
    798     }
    799 
    800     static int amToHfVol(int amVol) {
    801         int amRange = (sMaxAmVcVol > sMinAmVcVol) ? (sMaxAmVcVol - sMinAmVcVol) : 1;
    802         int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME;
    803         int hfOffset = (hfRange * (amVol - sMinAmVcVol)) / amRange;
    804         int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset;
    805         Log.d(TAG, "AM -> HF " + amVol + " " + hfVol);
    806         return hfVol;
    807     }
    808 
    809     class Disconnected extends State {
    810         @Override
    811         public void enter() {
    812             Log.d(TAG, "Enter Disconnected: " + getCurrentMessage().what);
    813 
    814             // cleanup
    815             mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE;
    816             mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME;
    817             mIndicatorNetworkSignal = 0;
    818             mIndicatorBatteryLevel = 0;
    819             mInBandRing = false;
    820 
    821             mAudioWbs = false;
    822 
    823             // will be set on connect
    824 
    825             mOperatorName = null;
    826             mSubscriberInfo = null;
    827 
    828             mQueuedActions = new LinkedList<Pair<Integer, Object>>();
    829             clearPendingAction();
    830 
    831             mCalls.clear();
    832             mCallsUpdate.clear();
    833 
    834             mPeerFeatures = 0;
    835             mChldFeatures = 0;
    836 
    837             removeMessages(QUERY_CURRENT_CALLS);
    838 
    839             if (mPrevState == mConnecting) {
    840                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
    841                         BluetoothProfile.STATE_CONNECTING);
    842             } else if (mPrevState == mConnected || mPrevState == mAudioOn) {
    843                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
    844                         BluetoothProfile.STATE_CONNECTED);
    845             } else if (mPrevState != null) { // null is the default state before Disconnected
    846                 Log.e(TAG, "Connected: Illegal state transition from " + mPrevState.getName()
    847                         + " to Connecting, mCurrentDevice=" + mCurrentDevice);
    848             }
    849             mCurrentDevice = null;
    850         }
    851 
    852         @Override
    853         public synchronized boolean processMessage(Message message) {
    854             Log.d(TAG, "Disconnected process message: " + message.what);
    855 
    856             if (mCurrentDevice != null) {
    857                 Log.e(TAG, "ERROR: current device not null in Disconnected");
    858                 return NOT_HANDLED;
    859             }
    860 
    861             switch (message.what) {
    862                 case CONNECT:
    863                     BluetoothDevice device = (BluetoothDevice) message.obj;
    864                     if (!NativeInterface.connectNative(getByteAddress(device))) {
    865                         // No state transition is involved, fire broadcast immediately
    866                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    867                                 BluetoothProfile.STATE_DISCONNECTED);
    868                         break;
    869                     }
    870                     mCurrentDevice = device;
    871                     transitionTo(mConnecting);
    872                     break;
    873                 case DISCONNECT:
    874                     // ignore
    875                     break;
    876                 case StackEvent.STACK_EVENT:
    877                     StackEvent event = (StackEvent) message.obj;
    878                     if (DBG) {
    879                         Log.d(TAG, "Stack event type: " + event.type);
    880                     }
    881                     switch (event.type) {
    882                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
    883                             if (DBG) {
    884                                 Log.d(TAG, "Disconnected: Connection " + event.device
    885                                         + " state changed:" + event.valueInt);
    886                             }
    887                             processConnectionEvent(event.valueInt, event.device);
    888                             break;
    889                         default:
    890                             Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type);
    891                             break;
    892                     }
    893                     break;
    894                 default:
    895                     return NOT_HANDLED;
    896             }
    897             return HANDLED;
    898         }
    899 
    900         // in Disconnected state
    901         private void processConnectionEvent(int state, BluetoothDevice device) {
    902             switch (state) {
    903                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
    904                     Log.w(TAG, "HFPClient Connecting from Disconnected state");
    905                     if (okToConnect(device)) {
    906                         Log.i(TAG, "Incoming AG accepted");
    907                         mCurrentDevice = device;
    908                         transitionTo(mConnecting);
    909                     } else {
    910                         Log.i(TAG, "Incoming AG rejected. priority=" + mService.getPriority(device)
    911                                 + " bondState=" + device.getBondState());
    912                         // reject the connection and stay in Disconnected state
    913                         // itself
    914                         NativeInterface.disconnectNative(getByteAddress(device));
    915                         // the other profile connection should be initiated
    916                         AdapterService adapterService = AdapterService.getAdapterService();
    917                         // No state transition is involved, fire broadcast immediately
    918                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    919                                 BluetoothProfile.STATE_DISCONNECTED);
    920                     }
    921                     break;
    922                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
    923                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
    924                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
    925                 default:
    926                     Log.i(TAG, "ignoring state: " + state);
    927                     break;
    928             }
    929         }
    930 
    931         @Override
    932         public void exit() {
    933             if (DBG) {
    934                 Log.d(TAG, "Exit Disconnected: " + getCurrentMessage().what);
    935             }
    936             mPrevState = this;
    937         }
    938     }
    939 
    940     class Connecting extends State {
    941         @Override
    942         public void enter() {
    943             if (DBG) {
    944                 Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what);
    945             }
    946             // This message is either consumed in processMessage or
    947             // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since
    948             // the only transition is when connection attempt is initiated.
    949             sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS);
    950             if (mPrevState == mDisconnected) {
    951                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTING,
    952                         BluetoothProfile.STATE_DISCONNECTED);
    953             } else {
    954                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
    955                 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName
    956                         + " to Connecting, mCurrentDevice=" + mCurrentDevice);
    957             }
    958         }
    959 
    960         @Override
    961         public synchronized boolean processMessage(Message message) {
    962             if (DBG) {
    963                 Log.d(TAG, "Connecting process message: " + message.what);
    964             }
    965 
    966             switch (message.what) {
    967                 case CONNECT:
    968                 case CONNECT_AUDIO:
    969                 case DISCONNECT:
    970                     deferMessage(message);
    971                     break;
    972                 case StackEvent.STACK_EVENT:
    973                     StackEvent event = (StackEvent) message.obj;
    974                     if (DBG) {
    975                         Log.d(TAG, "Connecting: event type: " + event.type);
    976                     }
    977                     switch (event.type) {
    978                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
    979                             if (DBG) {
    980                                 Log.d(TAG,
    981                                         "Connecting: Connection " + event.device + " state changed:"
    982                                                 + event.valueInt);
    983                             }
    984                             processConnectionEvent(event.valueInt, event.valueInt2, event.valueInt3,
    985                                     event.device);
    986                             break;
    987                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
    988                         case StackEvent.EVENT_TYPE_NETWORK_STATE:
    989                         case StackEvent.EVENT_TYPE_ROAMING_STATE:
    990                         case StackEvent.EVENT_TYPE_NETWORK_SIGNAL:
    991                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
    992                         case StackEvent.EVENT_TYPE_CALL:
    993                         case StackEvent.EVENT_TYPE_CALLSETUP:
    994                         case StackEvent.EVENT_TYPE_CALLHELD:
    995                         case StackEvent.EVENT_TYPE_RESP_AND_HOLD:
    996                         case StackEvent.EVENT_TYPE_CLIP:
    997                         case StackEvent.EVENT_TYPE_CALL_WAITING:
    998                         case StackEvent.EVENT_TYPE_VOLUME_CHANGED:
    999                             deferMessage(message);
   1000                             break;
   1001                         case StackEvent.EVENT_TYPE_CMD_RESULT:
   1002                         case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO:
   1003                         case StackEvent.EVENT_TYPE_CURRENT_CALLS:
   1004                         case StackEvent.EVENT_TYPE_OPERATOR_NAME:
   1005                         default:
   1006                             Log.e(TAG, "Connecting: ignoring stack event: " + event.type);
   1007                             break;
   1008                     }
   1009                     break;
   1010                 case CONNECTING_TIMEOUT:
   1011                     // We timed out trying to connect, transition to disconnected.
   1012                     Log.w(TAG, "Connection timeout for " + mCurrentDevice);
   1013                     transitionTo(mDisconnected);
   1014                     break;
   1015 
   1016                 default:
   1017                     Log.w(TAG, "Message not handled " + message);
   1018                     return NOT_HANDLED;
   1019             }
   1020             return HANDLED;
   1021         }
   1022 
   1023         // in Connecting state
   1024         private void processConnectionEvent(int state, int peerFeat, int chldFeat,
   1025                 BluetoothDevice device) {
   1026             switch (state) {
   1027                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
   1028                     transitionTo(mDisconnected);
   1029                     break;
   1030 
   1031                 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED:
   1032                     Log.d(TAG, "HFPClient Connected from Connecting state");
   1033 
   1034                     mPeerFeatures = peerFeat;
   1035                     mChldFeatures = chldFeat;
   1036 
   1037                     // We do not support devices which do not support enhanced call status (ECS).
   1038                     if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) {
   1039                         NativeInterface.disconnectNative(getByteAddress(device));
   1040                         return;
   1041                     }
   1042 
   1043                     // Send AT+NREC to remote if supported by audio
   1044                     if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED && (
   1045                             (mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR)
   1046                                     == HeadsetClientHalConstants.PEER_FEAT_ECNR)) {
   1047                         if (NativeInterface.sendATCmdNative(getByteAddress(mCurrentDevice),
   1048                                 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC, 1, 0,
   1049                                 null)) {
   1050                             addQueuedAction(DISABLE_NREC);
   1051                         } else {
   1052                             Log.e(TAG, "Failed to send NREC");
   1053                         }
   1054                     }
   1055 
   1056                     int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
   1057                     deferMessage(
   1058                             obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0));
   1059                     // Mic is either in ON state (full volume) or OFF state. There is no way in
   1060                     // Android to change the MIC volume.
   1061                     deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME,
   1062                             mAudioManager.isMicrophoneMute() ? 0 : 15, 0));
   1063                     // query subscriber info
   1064                     deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO));
   1065                     transitionTo(mConnected);
   1066                     break;
   1067 
   1068                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED:
   1069                     if (!mCurrentDevice.equals(device)) {
   1070                         Log.w(TAG, "incoming connection event, device: " + device);
   1071                         // No state transition is involved, fire broadcast immediately
   1072                         broadcastConnectionState(mCurrentDevice,
   1073                                 BluetoothProfile.STATE_DISCONNECTED,
   1074                                 BluetoothProfile.STATE_CONNECTING);
   1075                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
   1076                                 BluetoothProfile.STATE_DISCONNECTED);
   1077 
   1078                         mCurrentDevice = device;
   1079                     }
   1080                     break;
   1081                 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING:
   1082                     /* outgoing connecting started */
   1083                     if (DBG) {
   1084                         Log.d(TAG, "outgoing connection started, ignore");
   1085                     }
   1086                     break;
   1087                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING:
   1088                 default:
   1089                     Log.e(TAG, "Incorrect state: " + state);
   1090                     break;
   1091             }
   1092         }
   1093 
   1094         @Override
   1095         public void exit() {
   1096             if (DBG) {
   1097                 Log.d(TAG, "Exit Connecting: " + getCurrentMessage().what);
   1098             }
   1099             removeMessages(CONNECTING_TIMEOUT);
   1100             mPrevState = this;
   1101         }
   1102     }
   1103 
   1104     class Connected extends State {
   1105         int mCommandedSpeakerVolume = -1;
   1106 
   1107         @Override
   1108         public void enter() {
   1109             if (DBG) {
   1110                 Log.d(TAG, "Enter Connected: " + getCurrentMessage().what);
   1111             }
   1112             mAudioWbs = false;
   1113             mCommandedSpeakerVolume = -1;
   1114             if (mPrevState == mConnecting) {
   1115                 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
   1116                         BluetoothProfile.STATE_CONNECTING);
   1117                 MetricsLogger.logProfileConnectionEvent(
   1118                         BluetoothMetricsProto.ProfileId.HEADSET_CLIENT);
   1119             } else if (mPrevState != mAudioOn) {
   1120                 String prevStateName = mPrevState == null ? "null" : mPrevState.getName();
   1121                 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName
   1122                         + " to Connecting, mCurrentDevice=" + mCurrentDevice);
   1123             }
   1124         }
   1125 
   1126         @Override
   1127         public synchronized boolean processMessage(Message message) {
   1128             if (DBG) {
   1129                 Log.d(TAG, "Connected process message: " + message.what);
   1130             }
   1131             if (DBG) {
   1132                 if (mCurrentDevice == null) {
   1133                     Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
   1134                     return NOT_HANDLED;
   1135                 }
   1136             }
   1137 
   1138             switch (message.what) {
   1139                 case CONNECT:
   1140                     BluetoothDevice device = (BluetoothDevice) message.obj;
   1141                     if (mCurrentDevice.equals(device)) {
   1142                         // already connected to this device, do nothing
   1143                         break;
   1144                     }
   1145                     NativeInterface.connectNative(getByteAddress(device));
   1146                     break;
   1147                 case DISCONNECT:
   1148                     BluetoothDevice dev = (BluetoothDevice) message.obj;
   1149                     if (!mCurrentDevice.equals(dev)) {
   1150                         break;
   1151                     }
   1152                     if (!NativeInterface.disconnectNative(getByteAddress(dev))) {
   1153                         Log.e(TAG, "disconnectNative failed for " + dev);
   1154                     }
   1155                     break;
   1156 
   1157                 case CONNECT_AUDIO:
   1158                     if (!NativeInterface.connectAudioNative(getByteAddress(mCurrentDevice))) {
   1159                         Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice);
   1160                         // No state transition is involved, fire broadcast immediately
   1161                         broadcastAudioState(mCurrentDevice,
   1162                                 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
   1163                                 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
   1164                     } else { // We have successfully sent a connect request!
   1165                         mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
   1166                     }
   1167                     break;
   1168 
   1169                 case DISCONNECT_AUDIO:
   1170                     if (!NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) {
   1171                         Log.e(TAG, "ERROR: Couldn't disconnect Audio for device " + mCurrentDevice);
   1172                     }
   1173                     break;
   1174 
   1175                 case VOICE_RECOGNITION_START:
   1176                     if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) {
   1177                         if (NativeInterface.startVoiceRecognitionNative(
   1178                                     getByteAddress(mCurrentDevice))) {
   1179                             addQueuedAction(VOICE_RECOGNITION_START);
   1180                         } else {
   1181                             Log.e(TAG, "ERROR: Couldn't start voice recognition");
   1182                         }
   1183                     }
   1184                     break;
   1185 
   1186                 case VOICE_RECOGNITION_STOP:
   1187                     if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) {
   1188                         if (NativeInterface.stopVoiceRecognitionNative(
   1189                                     getByteAddress(mCurrentDevice))) {
   1190                             addQueuedAction(VOICE_RECOGNITION_STOP);
   1191                         } else {
   1192                             Log.e(TAG, "ERROR: Couldn't stop voice recognition");
   1193                         }
   1194                     }
   1195                     break;
   1196 
   1197                 // Called only for Mute/Un-mute - Mic volume change is not allowed.
   1198                 case SET_MIC_VOLUME:
   1199                     break;
   1200                 case SET_SPEAKER_VOLUME:
   1201                     // This message should always contain the volume in AudioManager max normalized.
   1202                     int amVol = message.arg1;
   1203                     int hfVol = amToHfVol(amVol);
   1204                     if (amVol != mCommandedSpeakerVolume) {
   1205                         Log.d(TAG, "Volume" + amVol + ":" + mCommandedSpeakerVolume);
   1206                         // Volume was changed by a 3rd party
   1207                         mCommandedSpeakerVolume = -1;
   1208                         if (NativeInterface.setVolumeNative(getByteAddress(mCurrentDevice),
   1209                                 HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) {
   1210                             addQueuedAction(SET_SPEAKER_VOLUME);
   1211                         }
   1212                     }
   1213                     break;
   1214                 case DIAL_NUMBER:
   1215                     // Add the call as an outgoing call.
   1216                     BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj;
   1217                     mCalls.put(HF_ORIGINATED_CALL_ID, c);
   1218 
   1219                     if (NativeInterface.dialNative(getByteAddress(mCurrentDevice), c.getNumber())) {
   1220                         addQueuedAction(DIAL_NUMBER, c.getNumber());
   1221                         // Start looping on calling current calls.
   1222                         sendMessage(QUERY_CURRENT_CALLS);
   1223                     } else {
   1224                         Log.e(TAG,
   1225                                 "ERROR: Cannot dial with a given number:" + (String) message.obj);
   1226                         // Set the call to terminated remove.
   1227                         c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED);
   1228                         sendCallChangedIntent(c);
   1229                         mCalls.remove(HF_ORIGINATED_CALL_ID);
   1230                     }
   1231                     break;
   1232                 case ACCEPT_CALL:
   1233                     acceptCall(message.arg1);
   1234                     break;
   1235                 case REJECT_CALL:
   1236                     rejectCall();
   1237                     break;
   1238                 case HOLD_CALL:
   1239                     holdCall();
   1240                     break;
   1241                 case TERMINATE_CALL:
   1242                     terminateCall();
   1243                     break;
   1244                 case ENTER_PRIVATE_MODE:
   1245                     enterPrivateMode(message.arg1);
   1246                     break;
   1247                 case EXPLICIT_CALL_TRANSFER:
   1248                     explicitCallTransfer();
   1249                     break;
   1250                 case SEND_DTMF:
   1251                     if (NativeInterface.sendDtmfNative(getByteAddress(mCurrentDevice),
   1252                             (byte) message.arg1)) {
   1253                         addQueuedAction(SEND_DTMF);
   1254                     } else {
   1255                         Log.e(TAG, "ERROR: Couldn't send DTMF");
   1256                     }
   1257                     break;
   1258                 case SUBSCRIBER_INFO:
   1259                     if (NativeInterface.retrieveSubscriberInfoNative(
   1260                             getByteAddress(mCurrentDevice))) {
   1261                         addQueuedAction(SUBSCRIBER_INFO);
   1262                     } else {
   1263                         Log.e(TAG, "ERROR: Couldn't retrieve subscriber info");
   1264                     }
   1265                     break;
   1266                 case QUERY_CURRENT_CALLS:
   1267                     removeMessages(QUERY_CURRENT_CALLS);
   1268                     if (mCalls.size() > 0) {
   1269                         // If there are ongoing calls periodically check their status.
   1270                         sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS);
   1271                     }
   1272                     queryCallsStart();
   1273                     break;
   1274                 case StackEvent.STACK_EVENT:
   1275                     Intent intent = null;
   1276                     StackEvent event = (StackEvent) message.obj;
   1277                     if (DBG) {
   1278                         Log.d(TAG, "Connected: event type: " + event.type);
   1279                     }
   1280 
   1281                     switch (event.type) {
   1282                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
   1283                             if (DBG) {
   1284                                 Log.d(TAG, "Connected: Connection state changed: " + event.device
   1285                                         + ": " + event.valueInt);
   1286                             }
   1287                             processConnectionEvent(event.valueInt, event.device);
   1288                             break;
   1289                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
   1290                             if (DBG) {
   1291                                 Log.d(TAG, "Connected: Audio state changed: " + event.device + ": "
   1292                                         + event.valueInt);
   1293                             }
   1294                             processAudioEvent(event.valueInt, event.device);
   1295                             break;
   1296                         case StackEvent.EVENT_TYPE_NETWORK_STATE:
   1297                             if (DBG) {
   1298                                 Log.d(TAG, "Connected: Network state: " + event.valueInt);
   1299                             }
   1300                             mIndicatorNetworkState = event.valueInt;
   1301 
   1302                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1303                             intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS,
   1304                                     event.valueInt);
   1305 
   1306                             if (mIndicatorNetworkState
   1307                                     == HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) {
   1308                                 mOperatorName = null;
   1309                                 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME,
   1310                                         mOperatorName);
   1311                             }
   1312 
   1313                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1314                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1315 
   1316                             if (mIndicatorNetworkState
   1317                                     == HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) {
   1318                                 if (NativeInterface.queryCurrentOperatorNameNative(
   1319                                         getByteAddress(mCurrentDevice))) {
   1320                                     addQueuedAction(QUERY_OPERATOR_NAME);
   1321                                 } else {
   1322                                     Log.e(TAG, "ERROR: Couldn't querry operator name");
   1323                                 }
   1324                             }
   1325                             break;
   1326                         case StackEvent.EVENT_TYPE_ROAMING_STATE:
   1327                             mIndicatorNetworkType = event.valueInt;
   1328 
   1329                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1330                             intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING,
   1331                                     event.valueInt);
   1332                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1333                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1334                             break;
   1335                         case StackEvent.EVENT_TYPE_NETWORK_SIGNAL:
   1336                             mIndicatorNetworkSignal = event.valueInt;
   1337 
   1338                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1339                             intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH,
   1340                                     event.valueInt);
   1341                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1342                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1343                             break;
   1344                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
   1345                             mIndicatorBatteryLevel = event.valueInt;
   1346 
   1347                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1348                             intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL,
   1349                                     event.valueInt);
   1350                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1351                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1352                             break;
   1353                         case StackEvent.EVENT_TYPE_OPERATOR_NAME:
   1354                             mOperatorName = event.valueString;
   1355 
   1356                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1357                             intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME,
   1358                                     event.valueString);
   1359                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1360                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1361                             break;
   1362                         case StackEvent.EVENT_TYPE_VR_STATE_CHANGED:
   1363                             if (mVoiceRecognitionActive != event.valueInt) {
   1364                                 mVoiceRecognitionActive = event.valueInt;
   1365 
   1366                                 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1367                                 intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION,
   1368                                         mVoiceRecognitionActive);
   1369                                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1370                                 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1371                             }
   1372                             break;
   1373                         case StackEvent.EVENT_TYPE_CALL:
   1374                         case StackEvent.EVENT_TYPE_CALLSETUP:
   1375                         case StackEvent.EVENT_TYPE_CALLHELD:
   1376                         case StackEvent.EVENT_TYPE_RESP_AND_HOLD:
   1377                         case StackEvent.EVENT_TYPE_CLIP:
   1378                         case StackEvent.EVENT_TYPE_CALL_WAITING:
   1379                             sendMessage(QUERY_CURRENT_CALLS);
   1380                             break;
   1381                         case StackEvent.EVENT_TYPE_CURRENT_CALLS:
   1382                             queryCallsUpdate(event.valueInt, event.valueInt3, event.valueString,
   1383                                     event.valueInt4
   1384                                             == HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI,
   1385                                     event.valueInt2
   1386                                             == HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING);
   1387                             break;
   1388                         case StackEvent.EVENT_TYPE_VOLUME_CHANGED:
   1389                             if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) {
   1390                                 mCommandedSpeakerVolume = hfToAmVol(event.valueInt2);
   1391                                 Log.d(TAG, "AM volume set to " + mCommandedSpeakerVolume);
   1392                                 mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
   1393                                         +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI);
   1394                             } else if (event.valueInt
   1395                                     == HeadsetClientHalConstants.VOLUME_TYPE_MIC) {
   1396                                 mAudioManager.setMicrophoneMute(event.valueInt2 == 0);
   1397                             }
   1398                             break;
   1399                         case StackEvent.EVENT_TYPE_CMD_RESULT:
   1400                             Pair<Integer, Object> queuedAction = mQueuedActions.poll();
   1401 
   1402                             // should not happen but...
   1403                             if (queuedAction == null || queuedAction.first == NO_ACTION) {
   1404                                 clearPendingAction();
   1405                                 break;
   1406                             }
   1407 
   1408                             if (DBG) {
   1409                                 Log.d(TAG, "Connected: command result: " + event.valueInt
   1410                                         + " queuedAction: " + queuedAction.first);
   1411                             }
   1412 
   1413                             switch (queuedAction.first) {
   1414                                 case QUERY_CURRENT_CALLS:
   1415                                     queryCallsDone();
   1416                                     break;
   1417                                 case VOICE_RECOGNITION_START:
   1418                                     if (event.valueInt == AT_OK) {
   1419                                         mVoiceRecognitionActive =
   1420                                                 HeadsetClientHalConstants.VR_STATE_STARTED;
   1421                                     }
   1422                                     break;
   1423                                 case VOICE_RECOGNITION_STOP:
   1424                                     if (event.valueInt == AT_OK) {
   1425                                         mVoiceRecognitionActive =
   1426                                                 HeadsetClientHalConstants.VR_STATE_STOPPED;
   1427                                     }
   1428                                     break;
   1429                                 default:
   1430                                     Log.w(TAG, "Unhandled AT OK " + event);
   1431                                     break;
   1432                             }
   1433 
   1434                             break;
   1435                         case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO:
   1436                             mSubscriberInfo = event.valueString;
   1437                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1438                             intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO,
   1439                                     mSubscriberInfo);
   1440                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1441                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1442                             break;
   1443                         case StackEvent.EVENT_TYPE_IN_BAND_RINGTONE:
   1444                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
   1445                             mInBandRing = event.valueInt == IN_BAND_RING_ENABLED;
   1446                             intent.putExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING,
   1447                                     event.valueInt);
   1448                             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device);
   1449                             mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1450                             if (DBG) {
   1451                                 Log.d(TAG,
   1452                                         event.device.toString() + "onInBandRing" + event.valueInt);
   1453                             }
   1454                             break;
   1455                         case StackEvent.EVENT_TYPE_RING_INDICATION:
   1456                             // Ringing is not handled at this indication and rather should be
   1457                             // implemented (by the client of this service). Use the
   1458                             // CALL_STATE_INCOMING (and similar) handle ringing.
   1459                             break;
   1460                         default:
   1461                             Log.e(TAG, "Unknown stack event: " + event.type);
   1462                             break;
   1463                     }
   1464 
   1465                     break;
   1466                 default:
   1467                     return NOT_HANDLED;
   1468             }
   1469             return HANDLED;
   1470         }
   1471 
   1472         // in Connected state
   1473         private void processConnectionEvent(int state, BluetoothDevice device) {
   1474             switch (state) {
   1475                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
   1476                     if (DBG) {
   1477                         Log.d(TAG, "Connected disconnects.");
   1478                     }
   1479                     // AG disconnects
   1480                     if (mCurrentDevice.equals(device)) {
   1481                         transitionTo(mDisconnected);
   1482                     } else {
   1483                         Log.e(TAG, "Disconnected from unknown device: " + device);
   1484                     }
   1485                     break;
   1486                 default:
   1487                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
   1488                     break;
   1489             }
   1490         }
   1491 
   1492         // in Connected state
   1493         private void processAudioEvent(int state, BluetoothDevice device) {
   1494             // message from old device
   1495             if (!mCurrentDevice.equals(device)) {
   1496                 Log.e(TAG, "Audio changed on disconnected device: " + device);
   1497                 return;
   1498             }
   1499 
   1500             switch (state) {
   1501                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC:
   1502                     mAudioWbs = true;
   1503                     // fall through
   1504                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED:
   1505                     // Audio state is split in two parts, the audio focus is maintained by the
   1506                     // entity exercising this service (typically the Telecom stack) and audio
   1507                     // routing is handled by the bluetooth stack itself. The only reason to do so is
   1508                     // because Bluetooth SCO connection from the HF role is not entirely supported
   1509                     // for routing and volume purposes.
   1510                     // NOTE: All calls here are routed via the setParameters which changes the
   1511                     // routing at the Audio HAL level.
   1512 
   1513                     if (mService.isScoRouted()) {
   1514                         StackEvent event =
   1515                                 new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
   1516                         event.valueInt = state;
   1517                         event.device = device;
   1518                         sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS);
   1519                         break;
   1520                     }
   1521 
   1522                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED;
   1523 
   1524                     // We need to set the volume after switching into HFP mode as some Audio HALs
   1525                     // reset the volume to a known-default on mode switch.
   1526                     final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
   1527                     final int hfVol = amToHfVol(amVol);
   1528 
   1529                     if (DBG) {
   1530                         Log.d(TAG, "hfp_enable=true mAudioWbs is " + mAudioWbs);
   1531                     }
   1532                     if (mAudioWbs) {
   1533                         if (DBG) {
   1534                             Log.d(TAG, "Setting sampling rate as 16000");
   1535                         }
   1536                         mAudioManager.setParameters("hfp_set_sampling_rate=16000");
   1537                     } else {
   1538                         if (DBG) {
   1539                             Log.d(TAG, "Setting sampling rate as 8000");
   1540                         }
   1541                         mAudioManager.setParameters("hfp_set_sampling_rate=8000");
   1542                     }
   1543                     if (DBG) {
   1544                         Log.d(TAG, "hf_volume " + hfVol);
   1545                     }
   1546                     routeHfpAudio(true);
   1547                     mAudioFocusRequest = requestAudioFocus();
   1548                     mAudioManager.setParameters("hfp_volume=" + hfVol);
   1549                     transitionTo(mAudioOn);
   1550                     break;
   1551 
   1552                 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING:
   1553                     // No state transition is involved, fire broadcast immediately
   1554                     broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING,
   1555                             mAudioState);
   1556                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING;
   1557                     break;
   1558 
   1559                 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
   1560                     // No state transition is involved, fire broadcast immediately
   1561                     broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
   1562                             mAudioState);
   1563                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
   1564                     break;
   1565 
   1566                 default:
   1567                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
   1568                     break;
   1569             }
   1570         }
   1571 
   1572         @Override
   1573         public void exit() {
   1574             if (DBG) {
   1575                 Log.d(TAG, "Exit Connected: " + getCurrentMessage().what);
   1576             }
   1577             mPrevState = this;
   1578         }
   1579     }
   1580 
   1581     class AudioOn extends State {
   1582         @Override
   1583         public void enter() {
   1584             if (DBG) {
   1585                 Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what);
   1586             }
   1587             broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED,
   1588                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTING);
   1589         }
   1590 
   1591         @Override
   1592         public synchronized boolean processMessage(Message message) {
   1593             if (DBG) {
   1594                 Log.d(TAG, "AudioOn process message: " + message.what);
   1595             }
   1596             if (DBG) {
   1597                 if (mCurrentDevice == null) {
   1598                     Log.e(TAG, "ERROR: mCurrentDevice is null in Connected");
   1599                     return NOT_HANDLED;
   1600                 }
   1601             }
   1602 
   1603             switch (message.what) {
   1604                 case DISCONNECT:
   1605                     BluetoothDevice device = (BluetoothDevice) message.obj;
   1606                     if (!mCurrentDevice.equals(device)) {
   1607                         break;
   1608                     }
   1609                     deferMessage(message);
   1610                     /*
   1611                      * fall through - disconnect audio first then expect
   1612                      * deferred DISCONNECT message in Connected state
   1613                      */
   1614                 case DISCONNECT_AUDIO:
   1615                     /*
   1616                      * just disconnect audio and wait for
   1617                      * StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State
   1618                      * Machines state changing
   1619                      */
   1620                     if (NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) {
   1621                         routeHfpAudio(false);
   1622                         returnAudioFocusIfNecessary();
   1623                     }
   1624                     break;
   1625 
   1626                 case HOLD_CALL:
   1627                     holdCall();
   1628                     break;
   1629 
   1630                 case StackEvent.STACK_EVENT:
   1631                     StackEvent event = (StackEvent) message.obj;
   1632                     if (DBG) {
   1633                         Log.d(TAG, "AudioOn: event type: " + event.type);
   1634                     }
   1635                     switch (event.type) {
   1636                         case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
   1637                             if (DBG) {
   1638                                 Log.d(TAG, "AudioOn connection state changed" + event.device + ": "
   1639                                         + event.valueInt);
   1640                             }
   1641                             processConnectionEvent(event.valueInt, event.device);
   1642                             break;
   1643                         case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
   1644                             if (DBG) {
   1645                                 Log.d(TAG, "AudioOn audio state changed" + event.device + ": "
   1646                                         + event.valueInt);
   1647                             }
   1648                             processAudioEvent(event.valueInt, event.device);
   1649                             break;
   1650                         default:
   1651                             return NOT_HANDLED;
   1652                     }
   1653                     break;
   1654                 default:
   1655                     return NOT_HANDLED;
   1656             }
   1657             return HANDLED;
   1658         }
   1659 
   1660         // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this
   1661         private void processConnectionEvent(int state, BluetoothDevice device) {
   1662             switch (state) {
   1663                 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED:
   1664                     if (mCurrentDevice.equals(device)) {
   1665                         processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED,
   1666                                 device);
   1667                         transitionTo(mDisconnected);
   1668                     } else {
   1669                         Log.e(TAG, "Disconnected from unknown device: " + device);
   1670                     }
   1671                     break;
   1672                 default:
   1673                     Log.e(TAG, "Connection State Device: " + device + " bad state: " + state);
   1674                     break;
   1675             }
   1676         }
   1677 
   1678         // in AudioOn state
   1679         private void processAudioEvent(int state, BluetoothDevice device) {
   1680             if (!mCurrentDevice.equals(device)) {
   1681                 Log.e(TAG, "Audio changed on disconnected device: " + device);
   1682                 return;
   1683             }
   1684 
   1685             switch (state) {
   1686                 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED:
   1687                     removeMessages(DISCONNECT_AUDIO);
   1688                     mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
   1689                     // Audio focus may still be held by the entity controlling the actual call
   1690                     // (such as Telecom) and hence this will still keep the call around, there
   1691                     // is not much we can do here since dropping the call without user consent
   1692                     // even if the audio connection snapped may not be a good idea.
   1693                     routeHfpAudio(false);
   1694                     returnAudioFocusIfNecessary();
   1695                     transitionTo(mConnected);
   1696                     break;
   1697 
   1698                 default:
   1699                     Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
   1700                     break;
   1701             }
   1702         }
   1703 
   1704         @Override
   1705         public void exit() {
   1706             if (DBG) {
   1707                 Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what);
   1708             }
   1709             mPrevState = this;
   1710             broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED,
   1711                     BluetoothHeadsetClient.STATE_AUDIO_CONNECTED);
   1712         }
   1713     }
   1714 
   1715     /**
   1716      * @hide
   1717      */
   1718     public synchronized int getConnectionState(BluetoothDevice device) {
   1719         if (mCurrentDevice == null) {
   1720             return BluetoothProfile.STATE_DISCONNECTED;
   1721         }
   1722 
   1723         if (!mCurrentDevice.equals(device)) {
   1724             return BluetoothProfile.STATE_DISCONNECTED;
   1725         }
   1726 
   1727         IState currentState = getCurrentState();
   1728         if (currentState == mConnecting) {
   1729             return BluetoothProfile.STATE_CONNECTING;
   1730         }
   1731 
   1732         if (currentState == mConnected || currentState == mAudioOn) {
   1733             return BluetoothProfile.STATE_CONNECTED;
   1734         }
   1735 
   1736         Log.e(TAG, "Bad currentState: " + currentState);
   1737         return BluetoothProfile.STATE_DISCONNECTED;
   1738     }
   1739 
   1740     private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) {
   1741         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
   1742         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
   1743         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
   1744         if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) {
   1745             intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs);
   1746         }
   1747         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   1748         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1749         if (DBG) {
   1750             Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState);
   1751         }
   1752     }
   1753 
   1754     // This method does not check for error condition (newState == prevState)
   1755     private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
   1756         if (DBG) {
   1757             Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState);
   1758         }
   1759         /*
   1760          * Notifying the connection state change of the profile before sending
   1761          * the intent for connection state change, as it was causing a race
   1762          * condition, with the UI not being updated with the correct connection
   1763          * state.
   1764          */
   1765         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
   1766         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
   1767         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
   1768         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
   1769 
   1770         // add feature extras when connected
   1771         if (newState == BluetoothProfile.STATE_CONNECTED) {
   1772             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY)
   1773                     == HeadsetClientHalConstants.PEER_FEAT_3WAY) {
   1774                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true);
   1775             }
   1776             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT)
   1777                     == HeadsetClientHalConstants.PEER_FEAT_REJECT) {
   1778                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true);
   1779             }
   1780             if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC)
   1781                     == HeadsetClientHalConstants.PEER_FEAT_ECC) {
   1782                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true);
   1783             }
   1784 
   1785             // add individual CHLD support extras
   1786             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)
   1787                     == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) {
   1788                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL,
   1789                         true);
   1790             }
   1791             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL)
   1792                     == HeadsetClientHalConstants.CHLD_FEAT_REL) {
   1793                 intent.putExtra(
   1794                         BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true);
   1795             }
   1796             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)
   1797                     == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) {
   1798                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true);
   1799             }
   1800             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE)
   1801                     == HeadsetClientHalConstants.CHLD_FEAT_MERGE) {
   1802                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true);
   1803             }
   1804             if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)
   1805                     == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) {
   1806                 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true);
   1807             }
   1808         }
   1809         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
   1810     }
   1811 
   1812     boolean isConnected() {
   1813         IState currentState = getCurrentState();
   1814         return (currentState == mConnected || currentState == mAudioOn);
   1815     }
   1816 
   1817     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
   1818         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
   1819         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
   1820         int connectionState;
   1821         synchronized (this) {
   1822             for (BluetoothDevice device : bondedDevices) {
   1823                 ParcelUuid[] featureUuids = device.getUuids();
   1824                 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.Handsfree_AG)) {
   1825                     continue;
   1826                 }
   1827                 connectionState = getConnectionState(device);
   1828                 for (int state : states) {
   1829                     if (connectionState == state) {
   1830                         deviceList.add(device);
   1831                     }
   1832                 }
   1833             }
   1834         }
   1835         return deviceList;
   1836     }
   1837 
   1838     boolean okToConnect(BluetoothDevice device) {
   1839         int priority = mService.getPriority(device);
   1840         boolean ret = false;
   1841         // check priority and accept or reject the connection. if priority is
   1842         // undefined
   1843         // it is likely that our SDP has not completed and peer is initiating
   1844         // the
   1845         // connection. Allow this connection, provided the device is bonded
   1846         if ((BluetoothProfile.PRIORITY_OFF < priority) || (
   1847                 (BluetoothProfile.PRIORITY_UNDEFINED == priority) && (device.getBondState()
   1848                         != BluetoothDevice.BOND_NONE))) {
   1849             ret = true;
   1850         }
   1851         return ret;
   1852     }
   1853 
   1854     boolean isAudioOn() {
   1855         return (getCurrentState() == mAudioOn);
   1856     }
   1857 
   1858     synchronized int getAudioState(BluetoothDevice device) {
   1859         if (mCurrentDevice == null || !mCurrentDevice.equals(device)) {
   1860             return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED;
   1861         }
   1862         return mAudioState;
   1863     }
   1864 
   1865     List<BluetoothDevice> getConnectedDevices() {
   1866         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
   1867         synchronized (this) {
   1868             if (isConnected()) {
   1869                 devices.add(mCurrentDevice);
   1870             }
   1871         }
   1872         return devices;
   1873     }
   1874 
   1875     private byte[] getByteAddress(BluetoothDevice device) {
   1876         return Utils.getBytesFromAddress(device.getAddress());
   1877     }
   1878 
   1879     public List<BluetoothHeadsetClientCall> getCurrentCalls() {
   1880         return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values());
   1881     }
   1882 
   1883     public Bundle getCurrentAgEvents() {
   1884         Bundle b = new Bundle();
   1885         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState);
   1886         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal);
   1887         b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType);
   1888         b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel);
   1889         b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName);
   1890         b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo);
   1891         return b;
   1892     }
   1893 }
   1894