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