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