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